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

Improve the SAML2 attribute mapping

parent 4c87d36f
No related branches found
No related tags found
No related merge requests found
...@@ -31,7 +31,6 @@ SSO_REQUIRED_OUTSIDE_MANAGE_PY = True # enabled to ensure that production (that ...@@ -31,7 +31,6 @@ 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_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" 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
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_saml2_dn_list" # 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 LOGIN_PERM_CODENAME = None # None or str; value "can_log_in" will require this permission for users to log in
...@@ -79,6 +78,14 @@ STAFF_PERM_CODENAME = "staff" ...@@ -79,6 +78,14 @@ STAFF_PERM_CODENAME = "staff"
PRETEND_AUTH_BACKEND = django_settings.AUTHENTICATION_BACKENDS[0] # pretend to be this backend; django does not expect that it is possible to log in without an authentication backend PRETEND_AUTH_BACKEND = django_settings.AUTHENTICATION_BACKENDS[0] # pretend to be this backend; django does not expect that it is possible to log in without an authentication backend
# the block below defines from which SAML2 attributes this SP receives user data
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_GROUPS = "urn:oid:1.3.6.1.4.1.5923.1.5.1.1" # "isMemberOf"
""" """
This information will be published in this SP metadata... This information will be published in this SP metadata...
......
...@@ -21,17 +21,6 @@ from django.utils import timezone ...@@ -21,17 +21,6 @@ from django.utils import timezone
from datetime import timedelta from datetime import timedelta
ATTRIBUTE_MAPPING = dict(
# https://commons.lbl.gov/display/IDMgmt/Attribute+Definitions
username="urn:oid:0.9.2342.19200300.100.1.1",
email="urn:oid:0.9.2342.19200300.100.1.3",
forename="urn:oid:2.5.4.42",
surname="urn:oid:2.5.4.4",
uuid="UUID",
groups=app_settings.GROUPS_SAML_ATTRIBUTE,
)
class SAMLMixin: class SAMLMixin:
""" Merges Django runtime info and settings for OneLogin toolkit """ """ Merges Django runtime info and settings for OneLogin toolkit """
...@@ -181,35 +170,42 @@ class ACSAuthNView(SAMLMixin, View): ...@@ -181,35 +170,42 @@ class ACSAuthNView(SAMLMixin, View):
def log_in_user(self, request, auth): def log_in_user(self, request, auth):
def get_attr(attribute_name, nullable=False, multivalued=False): def get_attr(attribute, nullable=False, multivalued=False):
attribute_id = ATTRIBUTE_MAPPING[attribute_name] values = auth.get_attributes().get(attribute, list())
values = auth.get_attributes().get(attribute_id, list()) logger.critical(attribute)
assert nullable or values, "Haven't received any {0}".format(attribute_name) logger.warning(values)
if not values and not nullable:
raise ValueError("Received no value(s) for {0}".format(attribute))
if multivalued: if multivalued:
return values return values
else: else:
assert len(values) <= 1, "Received too many {0}: {1}".format(attribute_name, values) if len(values) == 0:
return values[0] if values else None return None
if len(values) == 1:
return values[0]
else:
raise ValueError("Received {1} values for {0}, didn't expect that many.".format(attribute, len(values)))
logger.debug("Synchronizing user using SAML2 data: {}".format(auth.get_attributes())) logger.debug("Synchronizing user using SAML2 data: {}".format(auth.get_attributes()))
# get the user # get the user
user = auth_utils.get_or_create_user( user = auth_utils.get_or_create_user(
uuid=get_attr("uuid"), uuid=get_attr(app_settings.ATTRIBUTE_ACCOUNT_UUID),
username=get_attr("username"), username=get_attr(app_settings.ATTRIBUTE_USERNAME),
) )
# update user data # update user data
auth_utils.update_user_data( auth_utils.update_user_data(
user=user, user=user,
surname=get_attr("surname", nullable=True), surname=get_attr(app_settings.ATTRIBUTE_SURNAME, nullable=True),
forename=get_attr("forename", nullable=True), forename=get_attr(app_settings.ATTRIBUTE_FORENAME, nullable=True),
email=get_attr("email", nullable=True), email=get_attr(app_settings.ATTRIBUTE_EMAIL, nullable=True),
) )
auth_utils.set_user_groups( auth_utils.set_user_groups(
user=user, user=user,
saml2_groups=get_attr("groups", nullable=True, multivalued=True) or list() saml2_groups=get_attr(app_settings.ATTRIBUTE_GROUPS, nullable=True, multivalued=True) or list()
) )
auth_utils.cleanup_direct_permissions(user=user) # other necessary steps
auth_utils.set_user_compat_flags(user=user) auth_utils.cleanup_direct_permissions(user=user) # in case somebody was acting funny
auth_utils.set_user_compat_flags(user=user) # superuser, staff
user.backend = app_settings.PRETEND_AUTH_BACKEND user.backend = app_settings.PRETEND_AUTH_BACKEND
request.user = user request.user = user
if user.is_active: if user.is_active:
...@@ -301,7 +297,8 @@ class DevView(FormView): ...@@ -301,7 +297,8 @@ class DevView(FormView):
return self.request.GET.get(REDIRECT_FIELD_NAME, None) return self.request.GET.get(REDIRECT_FIELD_NAME, None)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
assert conf.settings.DEBUG if not conf.settings.DEBUG:
raise exceptions.PermissionDenied
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
user = self.request.user user = self.request.user
groups = list(user.groups.all()) if user.is_authenticated else list() groups = list(user.groups.all()) if user.is_authenticated else list()
...@@ -322,7 +319,8 @@ class DevView(FormView): ...@@ -322,7 +319,8 @@ class DevView(FormView):
return context return context
def form_valid(self, form): def form_valid(self, form):
assert conf.settings.DEBUG if not conf.settings.DEBUG:
raise exceptions.PermissionDenied
log_in_as_username = form.cleaned_data["username"] log_in_as_username = form.cleaned_data["username"]
toggle_group = form.cleaned_data["toggle_group"] toggle_group = form.cleaned_data["toggle_group"]
local_logout = bool(self.request.POST.get("local-logout", None)) local_logout = bool(self.request.POST.get("local-logout", None))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment