From 54c4f4018815056ee7ba14bab1942b2fbd927a63 Mon Sep 17 00:00:00 2001 From: ground7 Date: Fri, 27 Dec 2019 23:12:18 -0700 Subject: [PATCH 1/3] added LDAP import update defaults --- cps/admin.py | 19 +++--- cps/config_sql.py | 15 +++-- cps/services/simpleldap.py | 17 ++++- cps/templates/admin.html | 18 +++++- cps/templates/config_edit.html | 114 ++++++++++++++++++--------------- cps/web.py | 27 +++++++- 6 files changed, 142 insertions(+), 68 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 57796080..6ed48785 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -44,7 +44,7 @@ from .gdriveutils import is_gdrive_ready, gdrive_support from .web import admin_required, render_title_template, before_request, unconfigured, login_required_if_no_ano feature_support = { - 'ldap': False, # bool(services.ldap), + 'ldap': bool(services.ldap), 'goodreads': bool(services.goodreads_support) } @@ -326,13 +326,16 @@ def _configuration_update_helper(): return _configuration_result('Please enter a LDAP service account and password', gdriveError) config.set_from_dictionary(to_save, "config_ldap_serv_password", base64.b64encode) - _config_checkbox("config_ldap_use_ssl") - _config_checkbox("config_ldap_use_tls") - _config_checkbox("config_ldap_openldap") - _config_checkbox("config_ldap_require_cert") - _config_string("config_ldap_cert_path") - if config.config_ldap_cert_path and not os.path.isfile(config.config_ldap_cert_path): - return _configuration_result('LDAP Certfile location is not valid, please enter correct path', gdriveError) + _config_string("config_ldap_group_object_filter") + _config_string("config_ldap_group_members_field") + _config_string("config_ldap_group_name") + _config_checkbox("config_ldap_use_ssl") + _config_checkbox("config_ldap_use_tls") + _config_checkbox("config_ldap_openldap") + _config_checkbox("config_ldap_require_cert") + _config_string("config_ldap_cert_path") + if config.config_ldap_cert_path and not os.path.isfile(config.config_ldap_cert_path): + return _configuration_result('LDAP Certfile location is not valid, please enter correct path', gdriveError) # Remote login configuration _config_checkbox("config_remote_login") diff --git a/cps/config_sql.py b/cps/config_sql.py index 809e97d8..fcffc3bc 100644 --- a/cps/config_sql.py +++ b/cps/config_sql.py @@ -37,6 +37,8 @@ _Base = declarative_base() class _Settings(_Base): __tablename__ = 'settings' + config_is_initial = Column(Boolean, default=True) + id = Column(Integer, primary_key=True) mail_server = Column(String, default='mail.example.org') mail_port = Column(Integer, default=25) @@ -86,18 +88,21 @@ class _Settings(_Base): # config_oauth_provider = Column(Integer) - config_ldap_provider_url = Column(String, default='localhost') + config_ldap_provider_url = Column(String, default='example.org') config_ldap_port = Column(SmallInteger, default=389) config_ldap_schema = Column(String, default='ldap') - config_ldap_serv_username = Column(String) + config_ldap_serv_username = Column(String, default='cn=admin,dc=example,dc=org') config_ldap_serv_password = Column(String) config_ldap_use_ssl = Column(Boolean, default=False) config_ldap_use_tls = Column(Boolean, default=False) config_ldap_require_cert = Column(Boolean, default=False) config_ldap_cert_path = Column(String) - config_ldap_dn = Column(String) - config_ldap_user_object = Column(String) - config_ldap_openldap = Column(Boolean, default=False) + config_ldap_dn = Column(String, default='dc=example,dc=org') + config_ldap_user_object = Column(String, default='uid=%s') + config_ldap_openldap = Column(Boolean, default=True) + config_ldap_group_object_filter = Column(String, default='(&(objectclass=posixGroup)(cn=%s))') + config_ldap_group_members_field = Column(String, default='memberUid') + config_ldap_group_name = Column(String, default='calibreweb') config_ebookconverter = Column(Integer, default=0) config_converterpath = Column(String) diff --git a/cps/services/simpleldap.py b/cps/services/simpleldap.py index 42a9aacd..03f9704c 100644 --- a/cps/services/simpleldap.py +++ b/cps/services/simpleldap.py @@ -35,8 +35,7 @@ def init_app(app, config): app.config['LDAP_HOST'] = config.config_ldap_provider_url app.config['LDAP_PORT'] = config.config_ldap_port app.config['LDAP_SCHEMA'] = config.config_ldap_schema - app.config['LDAP_USERNAME'] = config.config_ldap_user_object.replace('%s', config.config_ldap_serv_username)\ - + ',' + config.config_ldap_dn + app.config['LDAP_USERNAME'] = config.config_ldap_serv_username app.config['LDAP_PASSWORD'] = base64.b64decode(config.config_ldap_serv_password) app.config['LDAP_REQUIRE_CERT'] = bool(config.config_ldap_require_cert) if config.config_ldap_require_cert: @@ -46,17 +45,29 @@ def init_app(app, config): app.config['LDAP_USE_SSL'] = bool(config.config_ldap_use_ssl) app.config['LDAP_USE_TLS'] = bool(config.config_ldap_use_tls) app.config['LDAP_OPENLDAP'] = bool(config.config_ldap_openldap) + app.config['LDAP_GROUP_OBJECT_FILTER'] = config.config_ldap_group_object_filter + app.config['LDAP_GROUP_MEMBERS_FIELD'] = config.config_ldap_group_members_field _ldap.init_app(app) +def get_object_details(user=None, group=None, query_filter=None, dn_only=False): + return _ldap.get_object_details(user, group, query_filter, dn_only) + + +def bind(): + return _ldap.bind() + + +def get_group_members(group): + return _ldap.get_group_members(group) + def basic_auth_required(func): return _ldap.basic_auth_required(func) def bind_user(username, password): - # ulf= _ldap.get_object_details('admin') '''Attempts a LDAP login. :returns: True if login succeeded, False if login failed, None if server unavailable. diff --git a/cps/templates/admin.html b/cps/templates/admin.html index 17b84f34..ef60ab33 100644 --- a/cps/templates/admin.html +++ b/cps/templates/admin.html @@ -32,7 +32,11 @@ {% endif %} {% endfor %} -
{{_('Add new user')}}
+ {% if not (config.config_login_type == 1) %} +
{{_('Add new user')}}
+ {% else %} + + {% endif %} @@ -190,3 +194,15 @@ {% endblock %} +{% block js %} + +{% endblock %} diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index 85b9598e..88d2631d 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -186,6 +186,7 @@ {% endif %} + {% if not config.config_is_initial %} {% if feature_support['ldap'] or feature_support['oauth'] %}
@@ -199,59 +200,71 @@ {% endif %}
- {% if feature_support['ldap'] %} -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
+ {% if feature_support['ldap'] %} +
- - + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
-
- - -
-
- - -
-
- - -
-
{% endif %} {% if feature_support['oauth'] %}
@@ -270,6 +283,7 @@ {% endfor %}
{% endif %} + {% endif %} {% endif %}
diff --git a/cps/web.py b/cps/web.py index 572ac969..e55cfc7d 100644 --- a/cps/web.py +++ b/cps/web.py @@ -54,7 +54,7 @@ from .pagination import Pagination from .redirect import redirect_back feature_support = { - 'ldap': False, # bool(services.ldap), + 'ldap': bool(services.ldap), 'goodreads': bool(services.goodreads_support) } @@ -253,6 +253,29 @@ def before_request(): return redirect(url_for('admin.basic_configuration')) +@app.route('/import_ldap_users') +def import_ldap_users(): + new_users = services.ldap.get_group_members(config.config_ldap_group_name) + for username in new_users: + user_data = services.ldap.get_object_details(user=username, group=None, query_filter=None, dn_only=False) + content = ub.User() + content.nickname = username + content.password = username # dummy password which will be replaced by ldap one + content.email = user_data['mail'][0] + if (len(user_data['mail']) > 1): + content.kindle_mail = user_data['mail'][1] + content.role = config.config_default_role + content.sidebar_view = config.config_default_show + content.mature_content = bool(config.config_default_show & constants.MATURE_CONTENT) + ub.session.add(content) + try: + ub.session.commit() + except Exception as e: + log.warning("Failed to create LDAP user: %s - %s", username, e) + ub.session.rollback() + return "" + + # ################################### data provider functions ######################################################### @@ -1155,10 +1178,12 @@ def login(): if user and check_password_hash(str(user.password), form['password']) and user.nickname != "Guest": login_user(user, remember=True) flash(_(u"You are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") + config.config_is_initial = False return redirect_back(url_for("web.index")) else: log.info('Login failed for user "%s" IP-adress: %s', form['username'], ipAdress) flash(_(u"Wrong Username or Password"), category="error") + settings = config.get_mail_settings() mail_configured = bool(settings.get("mail_server", "mail.example.org") != "mail.example.org") From 6555d5869faa6581f54a5cc4f90833c036c4eaee Mon Sep 17 00:00:00 2001 From: ground7 Date: Fri, 27 Dec 2019 23:45:42 -0700 Subject: [PATCH 2/3] attempt regular login if ldap login fails as fallback --- cps/web.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cps/web.py b/cps/web.py index e55cfc7d..f1351a55 100644 --- a/cps/web.py +++ b/cps/web.py @@ -1155,7 +1155,11 @@ def login(): flash(_(u"you are now logged in as: '%(nickname)s'", nickname=user.nickname), category="success") return redirect_back(url_for("web.index")) - if login_result is None: + elif user and check_password_hash(str(user.password), form['password']) and user.nickname != "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("web.index")) + elif login_result is None: flash(_(u"Could not login. LDAP server down, please contact your administrator"), category="error") else: ipAdress = request.headers.get('X-Forwarded-For', request.remote_addr) From b782489a8c9a4d946bbca3ec102d26bdb3305603 Mon Sep 17 00:00:00 2001 From: ground7 Date: Sat, 28 Dec 2019 21:52:26 -0700 Subject: [PATCH 3/3] ldap opds download bugged --- cps/opds.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cps/opds.py b/cps/opds.py index f5cc4673..ef83ad69 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -318,7 +318,6 @@ def feed_shelf(book_id): @opds.route("/opds/download///") @requires_basic_auth_if_no_ano -@download_required def opds_download_link(book_id, book_format): return get_download_link(book_id,book_format)