From ce9d92c3576dfbdf8bfa3c6fe56513d6011325a7 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 13 Sep 2023 08:38:38 +0200 Subject: [PATCH 01/12] [CLEAN] Moved up check that the user logged in with ldap already exists in IRIS --- source/app/iris_engine/access_control/ldap_handler.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index 7ca300166..ef5030231 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -42,8 +42,6 @@ def _get_unique_identifier(user_login): def _provision_user(connection, user_login): - if get_active_user_by_login(user_login): - return search_base = app.config.get('LDAP_SEARCH_DN') attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') unique_identifier = conv.escape_filter_chars(_get_unique_identifier(user_login)) @@ -118,7 +116,7 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): log.error(f"Cannot bind to ldap server: {conn.last_error} ") return False - if app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): + if not get_active_user_by_login(ldap_user_name) and app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): _provision_user(conn, ldap_user_name) except ldap3.core.exceptions.LDAPInvalidCredentialsResult as e: From 8b41ab43a40dfcb777cdc4d5e521e5815cf8dcc9 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 13 Sep 2023 08:43:18 +0200 Subject: [PATCH 02/12] [CLEAN] Improved comment wording --- source/app/iris_engine/access_control/ldap_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index ef5030231..4003acde7 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -70,8 +70,8 @@ def _provision_user(connection, user_login): # => should factor and reuse this code bit as a function # => also, it should probably be more secure to use the secrets module (instead of random) password = ''.join(random.choices(string.printable[:-6], k=16)) - # TODO It seems email unicity is required (a fixed email causes a problem at the second account creation) - # The email either comes from the ldap or is forged from the login to ensure unicity + # TODO It seems email uniqueness is required (a fixed email causes a problem at the second account creation) + # The email either comes from the ldap or is forged from the login to ensure uniqueness user = create_user(user_name, user_login, password, user_email, True) initial_group = get_group_by_name(app.config.get('IRIS_NEW_USERS_DEFAULT_GROUP')) add_user_to_group(user.id, initial_group.group_id) From 02b4f7bce0225c7039a4a487e3c9c72ff6b9159e Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 13 Sep 2023 09:16:44 +0200 Subject: [PATCH 03/12] [CLEAN] Isolate code to bind a user in ldap --- .../access_control/ldap_handler.py | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index 4003acde7..bd8664bf6 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -41,6 +41,25 @@ def _get_unique_identifier(user_login): return user_login +def _bind_user(server, ldap_user, ldap_user_pwd): + connection = Connection(server, + user=ldap_user, + password=ldap_user_pwd, + auto_referrals=False, + authentication=app.config.get('LDAP_AUTHENTICATION_TYPE')) + + try: + if not connection.bind(): + log.error(f"Cannot bind to ldap server: {connection.last_error} ") + return None + + except ldap3.core.exceptions.LDAPInvalidCredentialsResult as e: + log.error(f'Wrong credentials. Error : {e.__str__()}') + return None + + return connection + + def _provision_user(connection, user_login): search_base = app.config.get('LDAP_SEARCH_DN') attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') @@ -104,27 +123,12 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): server = Server(f'{app.config.get("LDAP_CONNECT_STRING")}', use_ssl=app.config.get('LDAP_USE_SSL')) - conn = Connection(server, - user=ldap_user, - password=ldap_user_pwd, - auto_referrals=False, - authentication=app.config.get('LDAP_AUTHENTICATION_TYPE')) - - try: - - if not conn.bind(): - log.error(f"Cannot bind to ldap server: {conn.last_error} ") - return False - - if not get_active_user_by_login(ldap_user_name) and app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): - _provision_user(conn, ldap_user_name) - - except ldap3.core.exceptions.LDAPInvalidCredentialsResult as e: - log.error(f'Wrong credentials. Error : {e.__str__()}') + connection = _bind_user(server, ldap_user, ldap_user_pwd) + if not connection: return False - except Exception as e: - raise Exception(e.__str__()) + if not get_active_user_by_login(ldap_user_name) and app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): + _provision_user(connection, ldap_user_name) log.info(f"Successful authenticated user") From 56548ead0f23c5d38cf9846280b83706d452c796 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 13 Sep 2023 09:18:58 +0200 Subject: [PATCH 04/12] [CLEAN] Factored access to configuration variable LDAP_AUTHENTICATION_TYPE --- source/app/iris_engine/access_control/ldap_handler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index bd8664bf6..3afc4de4c 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -33,10 +33,11 @@ from app.datamgmt.manage.manage_groups_db import get_group_by_name log = app.logger +ldap_authentication_type = app.config.get('LDAP_AUTHENTICATION_TYPE') def _get_unique_identifier(user_login): - if app.config.get('LDAP_AUTHENTICATION_TYPE').lower() == 'ntlm': + if ldap_authentication_type.lower() == 'ntlm': return user_login[user_login.find('\\')+1:] return user_login @@ -46,7 +47,7 @@ def _bind_user(server, ldap_user, ldap_user_pwd): user=ldap_user, password=ldap_user_pwd, auto_referrals=False, - authentication=app.config.get('LDAP_AUTHENTICATION_TYPE')) + authentication=ldap_authentication_type) try: if not connection.bind(): @@ -100,7 +101,7 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): """ Authenticate to the LDAP server """ - if app.config.get('LDAP_AUTHENTICATION_TYPE').lower() != 'ntlm': + if ldap_authentication_type.lower() != 'ntlm': ldap_user_name = conv.escape_filter_chars(ldap_user_name) ldap_user = f"{app.config.get('LDAP_USER_PREFIX')}{ldap_user_name.strip()}{ ','+app.config.get('LDAP_USER_SUFFIX') if app.config.get('LDAP_USER_SUFFIX') else ''}" else: From f770f4bd3998051d81eb0a9ddd99e3ab0b8d8e55 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:27:06 +0200 Subject: [PATCH 05/12] [ADD] A bind account to connect to ldap, and associated new configuration variables LDAP_BIND_DN and LDAP_BIND_PASSWORD in the configuration --- .env.model | 3 +++ source/app/configuration.py | 3 +++ .../app/iris_engine/access_control/ldap_handler.py | 13 +++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.env.model b/.env.model index 8e64a1a0f..21b9b1347 100644 --- a/.env.model +++ b/.env.model @@ -43,6 +43,9 @@ IRIS_AUTHENTICATION_TYPE=local #LDAP_USER_PREFIX=uid= #LDAP_USER_SUFFIX=ou=people,dc=example,dc=com #LDAP_USE_SSL=False +# bind account dn and password +#LDAP_BIND_DN= +#LDAP_BIND_PASSWORD= # base DN in which to search for users #LDAP_SEARCH_DN=ou=users,dc=example,dc=org # unique identifier to search the user diff --git a/source/app/configuration.py b/source/app/configuration.py index c3029629f..5b9b709ad 100644 --- a/source/app/configuration.py +++ b/source/app/configuration.py @@ -408,6 +408,9 @@ class Config: LDAP_AUTHENTICATION_TYPE = config.load('LDAP', 'AUTHENTICATION_TYPE') + LDAP_BIND_DN = config.load('LDAP', 'BIND_DN') + LDAP_BIND_PASSWORD = config.load('LDAP', 'BIND_PASSWORD') + LDAP_SEARCH_DN = config.load('LDAP', 'SEARCH_DN') if authentication_create_user_if_not_exists and LDAP_SEARCH_DN is None: raise Exception('LDAP enabled with user provisioning: LDAP_SEARCH_DN should be set') diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index 3afc4de4c..89cc2ef8b 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -42,7 +42,7 @@ def _get_unique_identifier(user_login): return user_login -def _bind_user(server, ldap_user, ldap_user_pwd): +def _connect(server, ldap_user, ldap_user_pwd): connection = Connection(server, user=ldap_user, password=ldap_user_pwd, @@ -61,6 +61,12 @@ def _bind_user(server, ldap_user, ldap_user_pwd): return connection +def _connect_bind_account(server): + ldap_bind_dn = app.config.get('LDAP_BIND_DN') + ldap_bind_password = app.config.get('LDAP_BIND_PASSWORD') + return _connect(server, ldap_bind_dn, ldap_bind_password) + + def _provision_user(connection, user_login): search_base = app.config.get('LDAP_SEARCH_DN') attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') @@ -124,11 +130,14 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): server = Server(f'{app.config.get("LDAP_CONNECT_STRING")}', use_ssl=app.config.get('LDAP_USE_SSL')) - connection = _bind_user(server, ldap_user, ldap_user_pwd) + connection = _connect(server, ldap_user, ldap_user_pwd) if not connection: return False if not get_active_user_by_login(ldap_user_name) and app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): + connection = _connect_bind_account(server) + if not connection: + return False _provision_user(connection, ldap_user_name) log.info(f"Successful authenticated user") From 42cff812c6e40416d222e87ebe6b484b8db46c09 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:25:22 +0200 Subject: [PATCH 06/12] [CLEAN] Added a slight comment to understand the code better --- source/app/models/authorization.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/app/models/authorization.py b/source/app/models/authorization.py index 2562e75e8..d9e7611ba 100644 --- a/source/app/models/authorization.py +++ b/source/app/models/authorization.py @@ -87,6 +87,8 @@ class Group(db.Model): server_default=text('gen_random_uuid()'), unique=True) group_name = Column(Text, nullable=False, unique=True) group_description = Column(Text) + + # this is a mask of values defined in enum Permissions group_permissions = Column(BigInteger, nullable=False) group_auto_follow = Column(Boolean, nullable=False, default=False) group_auto_follow_access_level = Column(BigInteger, nullable=False, default=0) From 8f94c0c25470d20e07d943b8d888031e2c03fded Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 20 Sep 2023 08:29:23 +0200 Subject: [PATCH 07/12] [CLEAN] Structured checks in a slightly more readable way --- source/app/iris_engine/access_control/ldap_handler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index 89cc2ef8b..822f76727 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -34,6 +34,7 @@ log = app.logger ldap_authentication_type = app.config.get('LDAP_AUTHENTICATION_TYPE') +attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') def _get_unique_identifier(user_login): @@ -69,7 +70,6 @@ def _connect_bind_account(server): def _provision_user(connection, user_login): search_base = app.config.get('LDAP_SEARCH_DN') - attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') unique_identifier = conv.escape_filter_chars(_get_unique_identifier(user_login)) attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') @@ -134,11 +134,12 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): if not connection: return False - if not get_active_user_by_login(ldap_user_name) and app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): + if app.config.get('AUTHENTICATION_CREATE_USER_IF_NOT_EXIST'): connection = _connect_bind_account(server) if not connection: return False - _provision_user(connection, ldap_user_name) + if not get_active_user_by_login(ldap_user_name): + _provision_user(connection, ldap_user_name) log.info(f"Successful authenticated user") From 8016bbbb57b370369a21ab7a964f1e19e67dae57 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:03:24 +0200 Subject: [PATCH 08/12] [ADD] User's groups are updated with respect to 'memberOf' field in ldap at each login --- .env.model | 2 - source/app/configuration.py | 1 - .../access_control/ldap_handler.py | 52 ++++++++++++++----- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/.env.model b/.env.model index 21b9b1347..6218de3bd 100644 --- a/.env.model +++ b/.env.model @@ -32,8 +32,6 @@ IRIS_AUTHENTICATION_TYPE=local #IRIS_ADM_USERNAME=administrator # requests the just-in-time creation of users with ldap authentification (see https://github.com/dfir-iris/iris-web/issues/203) #IRIS_AUTHENTICATION_CREATE_USER_IF_NOT_EXIST=True -# the group to which newly created users are initially added, default value is Analysts -#IRIS_NEW_USERS_DEFAULT_GROUP= # -- FOR LDAP AUTHENTICATION #IRIS_AUTHENTICATION_TYPE=ldap diff --git a/source/app/configuration.py b/source/app/configuration.py index 5b9b709ad..63e7c1073 100644 --- a/source/app/configuration.py +++ b/source/app/configuration.py @@ -369,7 +369,6 @@ class Config: AUTHENTICATION_TYPE = authentication_type AUTHENTICATION_CREATE_USER_IF_NOT_EXIST = (authentication_create_user_if_not_exists == "True") - IRIS_NEW_USERS_DEFAULT_GROUP = config.load('IRIS', 'NEW_USERS_DEFAULT_GROUP', fallback='Analysts') AUTHENTICATION_LOCAL_FALLBACK = config.load('IRIS', 'AUTHENTICATION_LOCAL_FALLBACK', fallback="True") == "True" if authentication_type == 'oidc_proxy': diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index 822f76727..b630c6e2a 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -29,12 +29,15 @@ from app import app from app.datamgmt.manage.manage_users_db import get_active_user_by_login from app.datamgmt.manage.manage_users_db import create_user -from app.datamgmt.manage.manage_users_db import add_user_to_group +from app.datamgmt.manage.manage_users_db import update_user_groups from app.datamgmt.manage.manage_groups_db import get_group_by_name +# TODO should make them private, prefix with _ log = app.logger ldap_authentication_type = app.config.get('LDAP_AUTHENTICATION_TYPE') attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') +attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') +attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') def _get_unique_identifier(user_login): @@ -68,24 +71,25 @@ def _connect_bind_account(server): return _connect(server, ldap_bind_dn, ldap_bind_password) -def _provision_user(connection, user_login): +def _search_user_in_ldap(connection, user_login): search_base = app.config.get('LDAP_SEARCH_DN') unique_identifier = conv.escape_filter_chars(_get_unique_identifier(user_login)) - attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') - attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') - attributes = [] + attributes = ['memberOf'] if attribute_display_name: attributes.append(attribute_display_name) if attribute_mail: attributes.append(attribute_mail) connection.search(search_base, f'({attribute_unique_identifier}={unique_identifier})', attributes=attributes) - entry = connection.entries[0] + return connection.entries[0] + + +def _provision_user(user_login, ldap_user_entry): if attribute_display_name: - user_name = entry[attribute_display_name].value + user_name = ldap_user_entry[attribute_display_name].value else: user_name = user_login if attribute_mail: - user_email = entry[attribute_mail].value + user_email = ldap_user_entry[attribute_mail].value else: user_email = f'{user_login}@ldap' @@ -98,9 +102,28 @@ def _provision_user(connection, user_login): password = ''.join(random.choices(string.printable[:-6], k=16)) # TODO It seems email uniqueness is required (a fixed email causes a problem at the second account creation) # The email either comes from the ldap or is forged from the login to ensure uniqueness - user = create_user(user_name, user_login, password, user_email, True) - initial_group = get_group_by_name(app.config.get('IRIS_NEW_USERS_DEFAULT_GROUP')) - add_user_to_group(user.id, initial_group.group_id) + return create_user(user_name, user_login, password, user_email, True) + + +def _parse_cn(distinguished_name): + relative_distinguished_names = distinguished_name.split(',') + common_name = relative_distinguished_names[0] + return common_name[len('cn='):] + + +def _update_user_groups(user, ldap_user_entry): + ldap_group_names = ldap_user_entry['memberOf'].value + if isinstance(ldap_group_names, str): + ldap_group_names = [ldap_group_names] + groups = [] + for ldap_group_name in ldap_group_names: + group_name = _parse_cn(ldap_group_name) + group = get_group_by_name(group_name) + if group is None: + log.warning(f'Ignoring group declared in LDAP which does not exist in DFIR-IRIS: \'{group_name}\'.') + continue + groups.append(group.group_id) + update_user_groups(user.id, groups) def ldap_authenticate(ldap_user_name, ldap_user_pwd): @@ -138,8 +161,11 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): connection = _connect_bind_account(server) if not connection: return False - if not get_active_user_by_login(ldap_user_name): - _provision_user(connection, ldap_user_name) + ldap_user_entry = _search_user_in_ldap(connection, ldap_user_name) + user = get_active_user_by_login(ldap_user_name) + if not user: + user = _provision_user(ldap_user_name, ldap_user_entry) + _update_user_groups(user, ldap_user_entry) log.info(f"Successful authenticated user") From aa34e05c405d1ee442025c5928ebd105e8bc15bf Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:06:41 +0200 Subject: [PATCH 09/12] [CLEAN] Prefixed local module variables with underscore --- .../access_control/ldap_handler.py | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index b630c6e2a..6a40be921 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -32,16 +32,15 @@ from app.datamgmt.manage.manage_users_db import update_user_groups from app.datamgmt.manage.manage_groups_db import get_group_by_name -# TODO should make them private, prefix with _ -log = app.logger -ldap_authentication_type = app.config.get('LDAP_AUTHENTICATION_TYPE') -attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') -attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') -attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') +_log = app.logger +_ldap_authentication_type = app.config.get('LDAP_AUTHENTICATION_TYPE') +_attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') +_attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') +_attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') def _get_unique_identifier(user_login): - if ldap_authentication_type.lower() == 'ntlm': + if _ldap_authentication_type.lower() == 'ntlm': return user_login[user_login.find('\\')+1:] return user_login @@ -51,15 +50,15 @@ def _connect(server, ldap_user, ldap_user_pwd): user=ldap_user, password=ldap_user_pwd, auto_referrals=False, - authentication=ldap_authentication_type) + authentication=_ldap_authentication_type) try: if not connection.bind(): - log.error(f"Cannot bind to ldap server: {connection.last_error} ") + _log.error(f"Cannot bind to ldap server: {connection.last_error} ") return None except ldap3.core.exceptions.LDAPInvalidCredentialsResult as e: - log.error(f'Wrong credentials. Error : {e.__str__()}') + _log.error(f'Wrong credentials. Error : {e.__str__()}') return None return connection @@ -75,25 +74,25 @@ def _search_user_in_ldap(connection, user_login): search_base = app.config.get('LDAP_SEARCH_DN') unique_identifier = conv.escape_filter_chars(_get_unique_identifier(user_login)) attributes = ['memberOf'] - if attribute_display_name: - attributes.append(attribute_display_name) - if attribute_mail: - attributes.append(attribute_mail) - connection.search(search_base, f'({attribute_unique_identifier}={unique_identifier})', attributes=attributes) + if _attribute_display_name: + attributes.append(_attribute_display_name) + if _attribute_mail: + attributes.append(_attribute_mail) + connection.search(search_base, f'({_attribute_unique_identifier}={unique_identifier})', attributes=attributes) return connection.entries[0] def _provision_user(user_login, ldap_user_entry): - if attribute_display_name: - user_name = ldap_user_entry[attribute_display_name].value + if _attribute_display_name: + user_name = ldap_user_entry[_attribute_display_name].value else: user_name = user_login - if attribute_mail: - user_email = ldap_user_entry[attribute_mail].value + if _attribute_mail: + user_email = ldap_user_entry[_attribute_mail].value else: user_email = f'{user_login}@ldap' - log.info(f'Provisioning user "{user_login}" which is present in LDAP but not yet in database.') + _log.info(f'Provisioning user "{user_login}" which is present in LDAP but not yet in database.') # TODO the user password is chosen randomly # ideally it should be possible to create a user without providing any password # TODO to create the user password, we use the same code as the one to generate the administrator password in post_init.py @@ -120,7 +119,7 @@ def _update_user_groups(user, ldap_user_entry): group_name = _parse_cn(ldap_group_name) group = get_group_by_name(group_name) if group is None: - log.warning(f'Ignoring group declared in LDAP which does not exist in DFIR-IRIS: \'{group_name}\'.') + _log.warning(f'Ignoring group declared in LDAP which does not exist in DFIR-IRIS: \'{group_name}\'.') continue groups.append(group.group_id) update_user_groups(user.id, groups) @@ -130,7 +129,7 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): """ Authenticate to the LDAP server """ - if ldap_authentication_type.lower() != 'ntlm': + if _ldap_authentication_type.lower() != 'ntlm': ldap_user_name = conv.escape_filter_chars(ldap_user_name) ldap_user = f"{app.config.get('LDAP_USER_PREFIX')}{ldap_user_name.strip()}{ ','+app.config.get('LDAP_USER_SUFFIX') if app.config.get('LDAP_USER_SUFFIX') else ''}" else: @@ -167,6 +166,6 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): user = _provision_user(ldap_user_name, ldap_user_entry) _update_user_groups(user, ldap_user_entry) - log.info(f"Successful authenticated user") + _log.info(f"Successful authenticated user") return True From 36b94ef42398b03e377910a5b4e369a56e5b7165 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Wed, 27 Sep 2023 09:17:27 +0200 Subject: [PATCH 10/12] [ADD] variable LDAP_GROUP_BASE_DN. Users will be added only to groups with this DN. Missing groups in IRIS are ignored. --- .env.model | 2 ++ source/app/configuration.py | 2 ++ source/app/datamgmt/manage/manage_groups_db.py | 7 +++++++ source/app/iris_engine/access_control/ldap_handler.py | 5 +++++ 4 files changed, 16 insertions(+) diff --git a/.env.model b/.env.model index 6218de3bd..b2f9c2c12 100644 --- a/.env.model +++ b/.env.model @@ -46,6 +46,8 @@ IRIS_AUTHENTICATION_TYPE=local #LDAP_BIND_PASSWORD= # base DN in which to search for users #LDAP_SEARCH_DN=ou=users,dc=example,dc=org +# base DN in which to search for groups +#LDAP_GROUP_BASE_DN=ou=IRIS,ou=groups,dc=example,dc=org # unique identifier to search the user #LDAP_ATTRIBUTE_IDENTIFIER=cn # name of the attribute to retrieve the user's display name diff --git a/source/app/configuration.py b/source/app/configuration.py index 63e7c1073..c86b0b25a 100644 --- a/source/app/configuration.py +++ b/source/app/configuration.py @@ -420,6 +420,8 @@ class Config: LDAP_ATTRIBUTE_DISPLAY_NAME = config.load('LDAP', 'ATTRIBUTE_DISPLAY_NAME') LDAP_ATTRIBUTE_MAIL = config.load('LDAP', 'ATTRIBUTE_MAIL') + LDAP_GROUP_BASE_DN = config.load('LDAP', 'GROUP_BASE_DN') + LDAP_USE_SSL = config.load('LDAP', 'USE_SSL', fallback='True') LDAP_USE_SSL = (LDAP_USE_SSL == 'True') diff --git a/source/app/datamgmt/manage/manage_groups_db.py b/source/app/datamgmt/manage/manage_groups_db.py index 52315e05f..9bdc70e73 100644 --- a/source/app/datamgmt/manage/manage_groups_db.py +++ b/source/app/datamgmt/manage/manage_groups_db.py @@ -34,6 +34,13 @@ from app.schema.marshables import AuthorizationGroupSchema +def create_group(name, description): + group = Group(group_name=name, group_description=description, group_permissions=0) + db.session.add(group) + db.session.commit() + return group + + def get_groups_list(): groups = Group.query.all() diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index 6a40be921..b4f055789 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -31,12 +31,14 @@ from app.datamgmt.manage.manage_users_db import create_user from app.datamgmt.manage.manage_users_db import update_user_groups from app.datamgmt.manage.manage_groups_db import get_group_by_name +from app.datamgmt.manage.manage_groups_db import create_group _log = app.logger _ldap_authentication_type = app.config.get('LDAP_AUTHENTICATION_TYPE') _attribute_unique_identifier = app.config.get('LDAP_ATTRIBUTE_IDENTIFIER') _attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') _attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') +_ldap_group_base_dn = app.config.get('LDAP_GROUP_BASE_DN') def _get_unique_identifier(user_login): @@ -114,8 +116,11 @@ def _update_user_groups(user, ldap_user_entry): ldap_group_names = ldap_user_entry['memberOf'].value if isinstance(ldap_group_names, str): ldap_group_names = [ldap_group_names] + groups = [] for ldap_group_name in ldap_group_names: + if not ldap_group_name.endswith(_ldap_group_base_dn): + continue group_name = _parse_cn(ldap_group_name) group = get_group_by_name(group_name) if group is None: From 3bd2d2ed2fc357a0807df95a8a3a687de075470d Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:39:17 +0200 Subject: [PATCH 11/12] [ADD] Using LDAP_USER_PREFIX to indicate the domain in case of ntlm ldap login --- .../access_control/ldap_handler.py | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index b4f055789..cc611868f 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -39,12 +39,8 @@ _attribute_display_name = app.config.get('LDAP_ATTRIBUTE_DISPLAY_NAME') _attribute_mail = app.config.get('LDAP_ATTRIBUTE_MAIL') _ldap_group_base_dn = app.config.get('LDAP_GROUP_BASE_DN') - - -def _get_unique_identifier(user_login): - if _ldap_authentication_type.lower() == 'ntlm': - return user_login[user_login.find('\\')+1:] - return user_login +_ldap_user_prefix = app.config.get('LDAP_USER_PREFIX') +_ldap_user_suffix = app.config.get('LDAP_USER_SUFFIX') def _connect(server, ldap_user, ldap_user_pwd): @@ -72,9 +68,18 @@ def _connect_bind_account(server): return _connect(server, ldap_bind_dn, ldap_bind_password) +def _connect_user(server, ldap_user_name, ldap_user_pwd): + ldap_user = ldap_user_name.strip() + ldap_user = f'{_ldap_user_prefix}{ldap_user}' + # TODO idea: ldap_user_suffix could include the ',' so that we don't need to make a special case for ntlm + if _ldap_user_suffix and _ldap_authentication_type.lower() != 'ntlm': + ldap_user = f'{ldap_user},{_ldap_user_suffix}' + return _connect(server, ldap_user, ldap_user_pwd) + + def _search_user_in_ldap(connection, user_login): search_base = app.config.get('LDAP_SEARCH_DN') - unique_identifier = conv.escape_filter_chars(_get_unique_identifier(user_login)) + unique_identifier = conv.escape_filter_chars(user_login) attributes = ['memberOf'] if _attribute_display_name: attributes.append(_attribute_display_name) @@ -134,12 +139,6 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): """ Authenticate to the LDAP server """ - if _ldap_authentication_type.lower() != 'ntlm': - ldap_user_name = conv.escape_filter_chars(ldap_user_name) - ldap_user = f"{app.config.get('LDAP_USER_PREFIX')}{ldap_user_name.strip()}{ ','+app.config.get('LDAP_USER_SUFFIX') if app.config.get('LDAP_USER_SUFFIX') else ''}" - else: - ldap_user = f"{ldap_user_name.strip()}" - if app.config.get('LDAP_CUSTOM_TLS_CONFIG') is True: tls_configuration = Tls(validate=ssl.CERT_REQUIRED, version=app.config.get('LDAP_TLS_VERSION'), @@ -157,7 +156,10 @@ def ldap_authenticate(ldap_user_name, ldap_user_pwd): server = Server(f'{app.config.get("LDAP_CONNECT_STRING")}', use_ssl=app.config.get('LDAP_USE_SSL')) - connection = _connect(server, ldap_user, ldap_user_pwd) + if _ldap_authentication_type.lower() != 'ntlm': + ldap_user_name = conv.escape_filter_chars(ldap_user_name) + + connection = _connect_user(server, ldap_user_name, ldap_user_pwd) if not connection: return False From 5a537e0b9456ce35d52868daf16f00690cc39601 Mon Sep 17 00:00:00 2001 From: c8y3 <25362953+c8y3@users.noreply.github.com> Date: Mon, 2 Oct 2023 14:04:38 +0200 Subject: [PATCH 12/12] [FIX] error when the authenticated user has no group in ldap --- source/app/iris_engine/access_control/ldap_handler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/app/iris_engine/access_control/ldap_handler.py b/source/app/iris_engine/access_control/ldap_handler.py index cc611868f..454ae8e71 100644 --- a/source/app/iris_engine/access_control/ldap_handler.py +++ b/source/app/iris_engine/access_control/ldap_handler.py @@ -119,6 +119,8 @@ def _parse_cn(distinguished_name): def _update_user_groups(user, ldap_user_entry): ldap_group_names = ldap_user_entry['memberOf'].value + if ldap_group_names is None: + ldap_group_names = [] if isinstance(ldap_group_names, str): ldap_group_names = [ldap_group_names]