diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index bdd72946..8c0e8281 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -171,6 +171,21 @@ {% endif %} +
+ + +
+
+
+ + +
+
+ + +
+
+ diff --git a/cps/ub.py b/cps/ub.py index 46c8bd93..dc0ffa75 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -169,6 +169,14 @@ class UserBase: 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) @@ -326,6 +334,9 @@ class Settings(Base): 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) @@ -400,6 +411,9 @@ class Config: 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: @@ -687,6 +701,14 @@ def migrate_Database(): conn.execute("ALTER TABLE Settings ADD column `config_converterpath` String DEFAULT ''") 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 @@ -700,7 +722,6 @@ def migrate_Database(): conn.execute("ALTER TABLE Settings ADD column `config_updatechannel` 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 !=''") @@ -810,6 +831,12 @@ else: 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/cps/web.py b/cps/web.py index c5de39a0..3eb9ff04 100644 --- a/cps/web.py +++ b/cps/web.py @@ -2288,7 +2288,18 @@ def login(): 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")) @@ -3044,6 +3055,21 @@ def configuration_helper(origin): 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: diff --git a/optional-requirements-ldap.txt b/optional-requirements-ldap.txt new file mode 100644 index 00000000..98519145 --- /dev/null +++ b/optional-requirements-ldap.txt @@ -0,0 +1 @@ +python_ldap>=3.0.0 \ No newline at end of file