From 30954cc27f3206fa62142c4881effafe77e7236c Mon Sep 17 00:00:00 2001 From: Krakinou Date: Thu, 10 Jan 2019 23:51:01 +0100 Subject: [PATCH 1/7] Initial LDAP support --- cps/ub.py | 15 +++++++++++++++ cps/web.py | 12 +++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/cps/ub.py b/cps/ub.py index 57dbde6e..14cf0b24 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -14,6 +14,7 @@ import json import datetime from binascii import hexlify import cli +import ldap engine = create_engine('sqlite:///{0}'.format(cli.settingspath), echo=False) Base = declarative_base() @@ -46,6 +47,8 @@ SIDEBAR_PUBLISHER = 4096 DEFAULT_PASS = "admin123" DEFAULT_PORT = int(os.environ.get("CALIBRE_PORT", 8083)) +LDAP_PROVIDER_URL = 'ldap://localhost:389/' +LDAP_PROTOCOL_VERSION = 3 class UserBase: @@ -152,6 +155,13 @@ class UserBase: def __repr__(self): return '' % self.nickname + @staticmethod + def try_login(username, password): + conn = get_ldap_connection() + conn.simple_bind_s( + 'uid={},ou=users,dc=yunohost,dc=org'.format(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) @@ -778,6 +788,11 @@ else: migrate_Database() clean_database() +#get LDAP connection +def get_ldap_connection(): + conn = ldap.initialize(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 da240211..c78ae132 100644 --- a/cps/web.py +++ b/cps/web.py @@ -57,6 +57,7 @@ from redirect import redirect_back import time import server from reverseproxy import ReverseProxied +import ldap try: from googleapiclient.errors import HttpError @@ -2342,7 +2343,16 @@ 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": + try: + app.logger.info("Tryong LDAP connexion") + 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) + if user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest" and not user.is_authenticated: 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")) From 8d284b151d79daa892656a20c668953409659f85 Mon Sep 17 00:00:00 2001 From: Krakinou Date: Sat, 12 Jan 2019 12:52:27 +0100 Subject: [PATCH 2/7] Edit html config --- cps/templates/config_edit.html | 15 +++++++++++++++ cps/ub.py | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index b2826b39..c12d3a9f 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -162,6 +162,21 @@ {% endif %} +
+ + +
+
+
+ + +
+
+ + +
+
+ diff --git a/cps/ub.py b/cps/ub.py index 14cf0b24..325b5132 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -320,6 +320,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) @@ -392,6 +395,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: @@ -678,7 +684,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() # Remove login capability of user Guest conn = engine.connect() conn.execute("UPDATE user SET password='' where nickname = 'Guest' and password !=''") From 91f0908059d56f80a2cce4d326fcd3ce34c9ca78 Mon Sep 17 00:00:00 2001 From: Krakinou Date: Sat, 12 Jan 2019 18:07:03 +0100 Subject: [PATCH 3/7] insert into db connect via LDAP config --- cps/ub.py | 8 +++++--- cps/web.py | 34 ++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/cps/ub.py b/cps/ub.py index 325b5132..6de9059c 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -47,8 +47,6 @@ SIDEBAR_PUBLISHER = 4096 DEFAULT_PASS = "admin123" DEFAULT_PORT = int(os.environ.get("CALIBRE_PORT", 8083)) -LDAP_PROVIDER_URL = 'ldap://localhost:389/' -LDAP_PROTOCOL_VERSION = 3 class UserBase: @@ -155,9 +153,12 @@ class UserBase: def __repr__(self): return '' % self.nickname + #Login via LDAP method @staticmethod def try_login(username, password): conn = get_ldap_connection() + print "bind : {}".format(config.config_ldap_dn) + print "replace :{}".format(config.config_ldap_dn.replace("%s", username)) conn.simple_bind_s( 'uid={},ou=users,dc=yunohost,dc=org'.format(username), password @@ -803,7 +804,8 @@ else: #get LDAP connection def get_ldap_connection(): - conn = ldap.initialize(LDAP_PROVIDER_URL) + print "login to LDAP server ldap://{}".format(config.config_ldap_provider_url) + conn = ldap.initialize('ldap://{}'.format(config.config_ldap_provider_url)) return conn # Generate global Settings Object accessible from every file diff --git a/cps/web.py b/cps/web.py index c78ae132..aaf3e02b 100644 --- a/cps/web.py +++ b/cps/web.py @@ -2343,16 +2343,15 @@ 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() - try: - app.logger.info("Tryong LDAP connexion") - 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) - if user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest" and not user.is_authenticated: + if config.config_use_ldap and ub.User.try_login(form['username'], form['password']): + try: + 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) + elif user and check_password_hash(user.password, form['password']) and user.nickname is not "Guest" and not user.is_authenticated: 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")) @@ -3075,6 +3074,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 "content.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: From 2e37c14d94e254c03f9733676cd144ce33c8af96 Mon Sep 17 00:00:00 2001 From: Krakinou Date: Sat, 12 Jan 2019 18:40:32 +0100 Subject: [PATCH 4/7] Clean some comment --- cps/ub.py | 5 +---- cps/web.py | 5 +++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cps/ub.py b/cps/ub.py index 6de9059c..e541763e 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -157,10 +157,8 @@ class UserBase: @staticmethod def try_login(username, password): conn = get_ldap_connection() - print "bind : {}".format(config.config_ldap_dn) - print "replace :{}".format(config.config_ldap_dn.replace("%s", username)) conn.simple_bind_s( - 'uid={},ou=users,dc=yunohost,dc=org'.format(username), + config.config_ldap_dn.replace("%s", username), password ) @@ -804,7 +802,6 @@ else: #get LDAP connection def get_ldap_connection(): - print "login to LDAP server ldap://{}".format(config.config_ldap_provider_url) conn = ldap.initialize('ldap://{}'.format(config.config_ldap_provider_url)) return conn diff --git a/cps/web.py b/cps/web.py index aaf3e02b..e7c687fd 100644 --- a/cps/web.py +++ b/cps/web.py @@ -2343,8 +2343,9 @@ 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 config.config_use_ldap and ub.User.try_login(form['username'], form['password']): + if config.config_use_ldap and user: 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")) @@ -3076,7 +3077,7 @@ def configuration_helper(origin): #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 "content.config_ldap_dn" in to_save: + 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, From 82e4f11334444f8650fbf5e0b16a4e8eb70253da Mon Sep 17 00:00:00 2001 From: Krakinou Date: Sat, 12 Jan 2019 19:24:21 +0100 Subject: [PATCH 5/7] Forgot requirements :/ --- optionnal-requirements-ldap.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 optionnal-requirements-ldap.txt diff --git a/optionnal-requirements-ldap.txt b/optionnal-requirements-ldap.txt new file mode 100644 index 00000000..98519145 --- /dev/null +++ b/optionnal-requirements-ldap.txt @@ -0,0 +1 @@ +python_ldap>=3.0.0 \ No newline at end of file From 7ccc40cf5b2260b4fe282dab92b6119eda7a22d9 Mon Sep 17 00:00:00 2001 From: Krakinou Date: Sun, 13 Jan 2019 11:02:03 +0100 Subject: [PATCH 6/7] Moving import LDAP Correct optional-requirements-ldap.txt spelling --- cps/ub.py | 2 +- cps/web.py | 3 ++- ...nal-requirements-ldap.txt => optional-requirements-ldap.txt | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename optionnal-requirements-ldap.txt => optional-requirements-ldap.txt (100%) diff --git a/cps/ub.py b/cps/ub.py index e541763e..605c8ba0 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -14,7 +14,6 @@ import json import datetime from binascii import hexlify import cli -import ldap engine = create_engine('sqlite:///{0}'.format(cli.settingspath), echo=False) Base = declarative_base() @@ -802,6 +801,7 @@ else: #get LDAP connection def get_ldap_connection(): + import ldap conn = ldap.initialize('ldap://{}'.format(config.config_ldap_provider_url)) return conn diff --git a/cps/web.py b/cps/web.py index e7c687fd..fca0e16f 100644 --- a/cps/web.py +++ b/cps/web.py @@ -57,7 +57,6 @@ from redirect import redirect_back import time import server from reverseproxy import ReverseProxied -import ldap try: from googleapiclient.errors import HttpError @@ -2344,6 +2343,7 @@ def login(): form = request.form.to_dict() user = ub.session.query(ub.User).filter(func.lower(ub.User.nickname) == form['username'].strip().lower()).first() if config.config_use_ldap and user: + import ldap try: ub.User.try_login(form['username'], form['password']) login_user(user, remember=True) @@ -2352,6 +2352,7 @@ def login(): 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" and not user.is_authenticated: login_user(user, remember=True) flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") diff --git a/optionnal-requirements-ldap.txt b/optional-requirements-ldap.txt similarity index 100% rename from optionnal-requirements-ldap.txt rename to optional-requirements-ldap.txt From d48cdcc789387f2d0711efa07d68e369506119be Mon Sep 17 00:00:00 2001 From: Krakinou Date: Sun, 13 Jan 2019 11:21:11 +0100 Subject: [PATCH 7/7] Correct authentication in case LDAP not activated --- cps/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cps/web.py b/cps/web.py index d304ffbd..dbc4c268 100644 --- a/cps/web.py +++ b/cps/web.py @@ -2374,7 +2374,7 @@ def login(): 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" and not user.is_authenticated: + 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"))