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