diff --git a/ssoauth/app_settings/defaults.py b/ssoauth/app_settings/defaults.py index a0ab7680fbe8fa705ceee226872136953f2c628b..5cf1fadbd31fe306754135c5950c6faaf9b7d8b9 100644 --- a/ssoauth/app_settings/defaults.py +++ b/ssoauth/app_settings/defaults.py @@ -31,7 +31,7 @@ SSO_REQUIRED_OUTSIDE_MANAGE_PY = True # enabled to ensure that production (that SP_SLS_ENABLED = False # single log out creates too many problems, so it is disabled for now SP_SLS_X_FRAME_OPTIONS = None # in case you encounter problems with SLS view not allowed inside of an iframe, e.g. "ALLOW-FROM idp-test.it.hs-hannover.de idp.hs-hannover.de" -GROUP_RESOLVER = "ssoauth.auth_utils.groups_from_saml2_dn_list" # in case you want to override how groups are resolved for users +GROUP_RESOLVER = "ssoauth.auth_utils.groups_from_raw_saml2_values" # in case you want to override how groups are resolved for users LOGIN_PERM_CODENAME = None # None or str; value "can_log_in" will require this permission for users to log in @@ -83,7 +83,7 @@ ATTRIBUTE_USERNAME = "urn:oid:0.9.2342.19200300.100.1.1" # "uid" ATTRIBUTE_EMAIL = "urn:oid:0.9.2342.19200300.100.1.3" # "mail" ATTRIBUTE_FORENAME = "urn:oid:2.5.4.42" # "givenName" ATTRIBUTE_SURNAME = "urn:oid:2.5.4.4" # "sn" -ATTRIBUTE_ACCOUNT_UUID = "UUID" # our custom stuff, this one has no OID +ATTRIBUTE_ACCOUNT_UUID = "UUID" # custom stuff, this one has no OID ATTRIBUTE_GROUPS = "urn:oid:1.3.6.1.4.1.5923.1.5.1.1" # "isMemberOf" diff --git a/ssoauth/auth_utils.py b/ssoauth/auth_utils.py index 1acfa8bd66e26d68d6bb8e89826fb87034ab1b3b..5a501cc776d75f1956d6a33e7e05bc2ab36a45ea 100644 --- a/ssoauth/auth_utils.py +++ b/ssoauth/auth_utils.py @@ -8,6 +8,7 @@ from . import logger from . import app_settings import functools import importlib +import re def _validate_username(username): @@ -97,7 +98,7 @@ def update_user_data(user, surname=None, forename=None, email=None): user.save() -def set_user_groups(user, saml2_groups): +def set_user_groups(user, raw_values): """ Updates groups for the user. """ # get the group resolver method try: @@ -110,27 +111,42 @@ def set_user_groups(user, saml2_groups): except Exception as e: raise ImportError("Could not import {r}. {e.__class__.__name__}: {e}".format(r=app_settings.GROUP_RESOLVER, e=e)) # resolve the groups - groups = resolver_method(user, saml2_groups) - assert isinstance(groups, (list, tuple, set,)) and all(isinstance(g, Group) for g in groups) + groups = set(resolver_method(user, raw_values)) # update user groups - if set(user.groups.all()) != set(groups): + if set(user.groups.all()) != groups: user.groups.set(groups) - assert set(user.groups.all()) == set(groups) + assert set(user.groups.all()) == groups logger.info("Groups for {user} were updated.".format(user=user)) # done logger.info("User {user} is member of: {groups}".format(user=user, groups=set(str(g) for g in groups))) -def groups_from_saml2_dn_list(user, groups_dn_list): +def groups_from_raw_saml2_values(user, raw_values): """ This default group resolver returns list of Group objects based on DN list """ # using Q to create ignore-case DN lookup since DS is case insensitive - q_list = [Q(sso_mapping__dn__iexact=dn) for dn in groups_dn_list] - if q_list: - q_combined = functools.reduce(lambda q1, q2: q1 | q2, q_list) - groups = list(Group.objects.filter(q_combined)) + + def from_dn_list(dn_list): + logger.warning("Receiving groups from DN list is deprecated. Reconfigure the project to receive group names instead.") + q_list = [Q(sso_mapping__dn__iexact=dn) for dn in dn_list] + if q_list: + q_combined = functools.reduce(lambda q1, q2: q1 | q2, q_list) + groups = set(Group.objects.filter(q_combined)) + else: + groups = set() + return groups + + def from_group_name_list(group_names): + groups = set(Group.objects.filter(name__in=group_names)) + group_names_resolved = {g.name for g in groups} + group_names_redundant = set(group_names) - group_names_resolved + if group_names_redundant: + logger.warning("Received redundant group names (clean up the configs): {0}".format(group_names_redundant)) + return groups + + if raw_values and bool(re.match('^\w+=\w+', str(raw_values[0]))): # if looks like DN + return from_dn_list(raw_values) else: - groups = list() - return groups + return from_group_name_list(raw_values) def cleanup_direct_permissions(user): diff --git a/ssoauth/views.py b/ssoauth/views.py index 142ba2c87e9fe4c36d8b35d1e76677af09188f98..fcab98b46113da5519bb250d1c7befbc87972dbc 100644 --- a/ssoauth/views.py +++ b/ssoauth/views.py @@ -199,7 +199,7 @@ class ACSAuthNView(SAMLMixin, View): ) auth_utils.set_user_groups( user=user, - saml2_groups=get_attr(app_settings.ATTRIBUTE_GROUPS, nullable=True, multivalued=True) or list() + raw_values=get_attr(app_settings.ATTRIBUTE_GROUPS, nullable=True, multivalued=True) or list() ) # other necessary steps auth_utils.cleanup_direct_permissions(user=user) # in case somebody was acting funny