diff --git a/README.md b/README.md index 1cc2eb8..dc4f95a 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ or chmod o+rw path/to/library ``` * Do not use a Nextcloud folder. It's all right if the folder is an external storage in Nextcloud but not if it's an internal one : Changing the data in the library will cause trouble with the sync +* "Magic link feature is not yet available + ## Links * Report a bug: https://github.com/YunoHost-Apps/calibre_ynh/issues @@ -68,6 +70,8 @@ sudo yunohost app upgrade calibreweb -u https://github.com/Yunohost-Apps/calibre - [ ] User and possibly LDAP integration - [X] Package_check integration - [X] On backup/remove/upgrade : check for database location to update settings +- [ ] enable magic link +- [ ] Add cronjob to reload database ## LICENSE diff --git a/conf/app.src b/conf/app.src index c765e11..33e5909 100644 --- a/conf/app.src +++ b/conf/app.src @@ -1,4 +1,3 @@ -#useless as long as V1.0 not issued -SOURCE_URL=https://github.com/janeczku/calibre-web/archive/d0fd1a46014006138977398ee65f48c98df937e7.zip -SOURCE_SUM=0d297719f32670840fb1fcee52d3e73fa6213127709809d9a232f862d037af03 +SOURCE_URL=https://github.com/janeczku/calibre-web/archive/c527d1f49a2213e523957fd90d814c075bacde89.zip +SOURCE_SUM=61aff733df259382ae53646e5445ac973a0c4fe70e54ec4d3ed63a7f66c4f04f SOURCE_FORMAT=zip diff --git a/conf/init_calibre_db_settings b/conf/init_calibre_db_settings index 8dacf91..f600faa 100644 --- a/conf/init_calibre_db_settings +++ b/conf/init_calibre_db_settings @@ -8,4 +8,7 @@ config_use_goodreads=0, config_logfile=\'\', config_converterpath=\'\', config_calibre=\'\', -config_uploading=\'$upload\' \ No newline at end of file +config_use_ldap=1, +config_ldap_provider_url=\'localhost:389\', +config_ldap_dn=\'uid=%s,ou=users,dc=yunohost,dc=org\', +config_uploading=\'$upload\' diff --git a/conf/nginx.conf b/conf/nginx.conf index 75e244a..64bc976 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -4,12 +4,13 @@ location __PATH__ { if ($scheme = http) { rewrite ^ https://$server_name$request_uri? permanent; } - proxy_pass http://localhost:__PORT__; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $http_host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Scheme $scheme; - proxy_set_header X-Script-Name __PATH__; + proxy_pass http://localhost:__PORT__; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Script-Name __PATH__; + proxy_set_header X-Remote-User $remote_user; # Include SSOWAT user panel. include conf.d/yunohost_panel.conf.inc; diff --git a/scripts/install b/scripts/install index e552210..a2e1473 100755 --- a/scripts/install +++ b/scripts/install @@ -92,7 +92,7 @@ pip install --target $final_path/vendor -r $final_path/requirements.txt #================================================= #Cannot use empty string for X-script-name, causes an issue in the python prg if [ $path_url = "/" ] ; then - ynh_replace_string "X-Script-Name __PATH__;" "X-Script-Name /$app;" ../conf/nginx.conf + ynh_replace_string "X-Script-Name __PATH__;" "X-Script-Name $app;" ../conf/nginx.conf fi # Create a dedicated nginx config diff --git a/sources/patches/app-config.patch b/sources/patches/app-config.patch new file mode 100644 index 0000000..c816372 --- /dev/null +++ b/sources/patches/app-config.patch @@ -0,0 +1,24 @@ +--- a/cps/templates/config_edit.html 2019-01-12 09:01:08.000000000 +0100 ++++ b/cps/templates/config_edit.html 2019-01-13 11:21:11.000000000 +0100 +@@ -162,6 +162,21 @@ + + + {% endif %} ++
++ ++ ++
++
++
++ ++ ++
++
++ ++ ++
++
++ + + + diff --git a/sources/patches/app-ub.patch b/sources/patches/app-ub.patch new file mode 100644 index 0000000..5da0e64 --- /dev/null +++ b/sources/patches/app-ub.patch @@ -0,0 +1,72 @@ +--- a/cps/ub.py 2019-01-12 09:01:08.000000000 +0100 ++++ b/cps/ub.py 2019-01-13 11:21:11.000000000 +0100 +@@ -148,6 +148,14 @@ + def __repr__(self): + return '' % self.nickname + ++ #Login via LDAP method ++ @staticmethod ++ def try_login(username, password): ++ conn = get_ldap_connection() ++ conn.simple_bind_s( ++ config.config_ldap_dn.replace("%s", username), ++ password ++ ) + + # Baseclass for Users in Calibre-Web, settings which are depending on certain users are stored here. It is derived from + # User Base (all access methods are declared there) +@@ -306,6 +314,9 @@ + config_use_goodreads = Column(Boolean) + config_goodreads_api_key = Column(String) + config_goodreads_api_secret = Column(String) ++ config_use_ldap = Column(Boolean) ++ config_ldap_provider_url = Column(String) ++ config_ldap_dn = Column(String) + config_mature_content_tags = Column(String) + config_logfile = Column(String) + config_ebookconverter = Column(Integer, default=0) +@@ -379,6 +390,9 @@ + self.config_use_goodreads = data.config_use_goodreads + self.config_goodreads_api_key = data.config_goodreads_api_key + self.config_goodreads_api_secret = data.config_goodreads_api_secret ++ self.config_use_ldap = data.config_use_ldap ++ self.config_ldap_provider_url = data.config_ldap_provider_url ++ self.config_ldap_dn = data.config_ldap_dn + if data.config_mature_content_tags: + self.config_mature_content_tags = data.config_mature_content_tags + else: +@@ -662,13 +676,20 @@ + conn.execute("ALTER TABLE Settings ADD column `config_calibre` String DEFAULT ''") + session.commit() + try: ++ session.query(exists().where(Settings.config_use_ldap)).scalar() ++ except exc.OperationalError: ++ conn = engine.connect() ++ conn.execute("ALTER TABLE Settings ADD column `config_use_ldap` INTEGER DEFAULT 0") ++ conn.execute("ALTER TABLE Settings ADD column `config_ldap_provider_url` String DEFAULT ''") ++ conn.execute("ALTER TABLE Settings ADD column `config_ldap_dn` String DEFAULT ''") ++ session.commit() ++ try: + session.query(exists().where(Settings.config_theme)).scalar() + except exc.OperationalError: # Database is not compatible, some rows are missing + conn = engine.connect() + conn.execute("ALTER TABLE Settings ADD column `config_theme` INTEGER DEFAULT 0") + session.commit() + +- + # Remove login capability of user Guest + conn = engine.connect() + conn.execute("UPDATE user SET password='' where nickname = 'Guest' and password !=''") +@@ -778,6 +799,12 @@ + migrate_Database() + clean_database() + ++#get LDAP connection ++def get_ldap_connection(): ++ import ldap ++ conn = ldap.initialize('ldap://{}'.format(config.config_ldap_provider_url)) ++ return conn ++ + # Generate global Settings Object accessible from every file + config = Config() + searched_ids = {} diff --git a/sources/patches/app-web.patch b/sources/patches/app-web.patch new file mode 100644 index 0000000..1145a8d --- /dev/null +++ b/sources/patches/app-web.patch @@ -0,0 +1,73 @@ +--- ./ori-calibre-web/cps/web.py 2019-01-12 09:01:08.000000000 +0100 ++++ ./calibre-web-Yunohost_integration/cps/web.py 2019-01-13 20:11:57.000000000 +0100 +@@ -58,6 +58,8 @@ + import server + from reverseproxy import ReverseProxied + ++vlogout = 0 ++ + try: + from googleapiclient.errors import HttpError + except ImportError: +@@ -2360,10 +2362,29 @@ + return redirect(url_for('basic_configuration')) + if current_user is not None and current_user.is_authenticated: + return redirect(url_for('index')) ++ auth_user = request.headers.get('X-Remote-User') ++ global vlogout ++ if auth_user and config.config_use_ldap and not vlogout: ++ vlogout = 0 ++ user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == auth_user.strip().lower()).first() ++ login_user(user, remember=True) ++ flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") ++ return redirect(url_for("index")) + if request.method == "POST": + form = request.form.to_dict() + user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == form['username'].strip().lower()).first() +- if user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest": ++ if config.config_use_ldap and user: ++ import ldap ++ try: ++ ub.User.try_login(form['username'], form['password']) ++ login_user(user, remember=True) ++ flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") ++ return redirect_back(url_for("index")) ++ except ldap.INVALID_CREDENTIALS: ++ ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr) ++ app.logger.info('LDAP Login failed for user "' + form['username'] + '" IP-adress: ' + ipAdress) ++ flash(_(u"Wrong Username or Password"), category="error") ++ elif user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest": + login_user(user, remember=True) + flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") + return redirect_back(url_for("index")) +@@ -2384,6 +2405,8 @@ + @login_required + def logout(): + if current_user is not None and current_user.is_authenticated: ++ global vlogout ++ vlogout = 1 + logout_user() + return redirect(url_for('login')) + +@@ -3088,6 +3111,21 @@ + if "config_ebookconverter" in to_save: + content.config_ebookconverter = int(to_save["config_ebookconverter"]) + ++ #LDAP configuratop, ++ if "config_use_ldap" in to_save and to_save["config_use_ldap"] == "on": ++ if not "config_ldap_provider_url" in to_save or not "config_ldap_dn" in to_save: ++ ub.session.commit() ++ flash(_(u'Please enter a LDAP provider and a DN'), category="error") ++ return render_title_template("config_edit.html", content=config, origin=origin, ++ gdrive=gdriveutils.gdrive_support, gdriveError=gdriveError, ++ goodreads=goodreads_support, title=_(u"Basic Configuration"), ++ page="config") ++ else: ++ content.config_use_ldap = 1 ++ content.config_ldap_provider_url = to_save["config_ldap_provider_url"] ++ content.config_ldap_dn = to_save["config_ldap_dn"] ++ db_change = True ++ + # Remote login configuration + content.config_remote_login = ("config_remote_login" in to_save and to_save["config_remote_login"] == "on") + if not content.config_remote_login: