Skip to content
Snippets Groups Projects
Commit 1d516df0 authored by Art's avatar Art :lizard:
Browse files

Add customizable group resolver (hshinfo can now rely on itself for perms)

parent f5024c3c
Branches
No related tags found
No related merge requests found
...@@ -33,6 +33,7 @@ SP_SLS_ENABLED = False # single log out creates too many problems, so it is dis ...@@ -33,6 +33,7 @@ SP_SLS_ENABLED = False # single log out creates too many problems, so it is dis
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" 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"
GROUPS_SAML_ATTRIBUTE = "IDMGroups" # this SAML attribute is expected to contain list of groups for a user GROUPS_SAML_ATTRIBUTE = "IDMGroups" # this SAML attribute is expected to contain list of groups for a user
GROUP_RESOLVER = "ssoauth.auth_utils.groups_from_saml2_dn_list" # in case you want to override how groups are resolved for users
""" """
Settings you might want to change on development (don't change them for production): Settings you might want to change on development (don't change them for production):
......
...@@ -7,6 +7,7 @@ from . import models ...@@ -7,6 +7,7 @@ from . import models
from . import logger from . import logger
from . import app_settings from . import app_settings
import functools import functools
import importlib
def _validate_username(username): def _validate_username(username):
...@@ -92,22 +93,41 @@ def update_user_data(user, surname=None, forename=None, email=None): ...@@ -92,22 +93,41 @@ def update_user_data(user, surname=None, forename=None, email=None):
user.save() user.save()
def set_user_groups(user, group_dn_list): def set_user_groups(user, saml2_groups):
""" Updates groups for the user. """ """ Updates groups for the user. """
# get the group resolver method
try:
resolver_path = app_settings.GROUP_RESOLVER.split(".")
assert len(resolver_path) >= 2, "need full path of the resolver method"
resolver_module_name = ".".join(resolver_path[:-1])
resolver_method_name = resolver_path[-1]
resolver_module = importlib.import_module(resolver_module_name)
resolver_method = getattr(resolver_module, resolver_method_name)
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), \
"{r} instead of a list/tuple/set of Group objects returned: {garbage}".format(r=app_settings.GROUP_RESOLVER, garbage=groups)
# update user groups
if set(user.groups.all()) != set(groups):
user.groups.set(groups)
assert set(user.groups.all()) == set(groups) # dunno how relation.set() behaves, better safe than sorry
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):
""" 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 # using Q to create ignore-case DN lookup since DS is case insensitive
q_list = [Q(sso_mapping__dn__iexact=dn) for dn in group_dn_list] q_list = [Q(sso_mapping__dn__iexact=dn) for dn in groups_dn_list]
if q_list: if q_list:
q_combined = functools.reduce(lambda q1, q2: q1 | q2, q_list) q_combined = functools.reduce(lambda q1, q2: q1 | q2, q_list)
groups = list(Group.objects.filter(q_combined)) groups = list(Group.objects.filter(q_combined))
else: else:
groups = list() groups = list()
# modify user groups return groups
if set(user.groups.all()) != set(groups):
user.groups.set(groups)
assert set(user.groups.all()) == set(groups) # dunno how relation.set() behaves, better safe than sorry
logger.info("Groups for {user} are updated to: {groups}".format(user=user, groups=", ".join(g.name for g in groups)))
logger.debug("User {user} has {g_n} group(s) based on {dn_n} DN(s): {g_names}".format(
user=user, g_n=len(groups), g_names=", ".join(str(g) for g in groups) or "(none)", dn_n=len(group_dn_list)))
def cleanup_direct_permissions(user): def cleanup_direct_permissions(user):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment