From ba1072d66353bf5f3e4b34eabc2928dda9452ee2 Mon Sep 17 00:00:00 2001 From: Art Lukyanchyk <artiom.lukyanchyk@hs-hannover.de> Date: Thu, 7 Mar 2019 19:15:01 +0100 Subject: [PATCH] Remove user mapping and the default superuser group, plus related changes --- README.md | 41 ++++-- ssoauth/__init__.py | 9 +- ssoauth/app_settings/defaults.py | 12 +- ssoauth/apps.py | 11 +- ssoauth/auth_utils.py | 73 ++++------- ssoauth/checks.py | 25 ++++ .../commands/create_compat_groups.py | 57 --------- .../commands/create_custom_groups.py | 51 -------- ssoauth/management/commands/group_mapping.py | 119 ------------------ .../ssoauth_setup_groups_and_perms.py | 62 +++++++++ ssoauth/migrations/0002_auto_20190306_1823.py | 20 +++ ssoauth/models.py | 9 -- ssoauth/views.py | 4 +- 13 files changed, 179 insertions(+), 314 deletions(-) delete mode 100644 ssoauth/management/commands/create_compat_groups.py delete mode 100644 ssoauth/management/commands/create_custom_groups.py delete mode 100644 ssoauth/management/commands/group_mapping.py create mode 100644 ssoauth/management/commands/ssoauth_setup_groups_and_perms.py create mode 100644 ssoauth/migrations/0002_auto_20190306_1823.py diff --git a/README.md b/README.md index 3d00f1d..4fb7d06 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,10 @@ ```python LOGIN_URL = urls.reverse_lazy("sso-dev") ``` -- If you want to debug `ssoauth` or need a fully functional SSO during development for some other reason, an example is below. For additional info see production setup chapter and `ssoauth.app_settings.defaults`. If you also want a working SLO during development you will need SSL for your localhost, `nginx` will be your best friend. +- If you want to debug `ssoauth` or need a fully functional SSO during development for some other reason, an example is below. For additional info see the production setup section and `ssoauth.app_settings.defaults`. If you also want a working SLO during development you will need SSL for your localhost, `nginx` will be your best friend. + +#### Advanced development setup +If you are not sure whether you need it: you don't need it. ```python """ settings/dev.py """ @@ -64,7 +67,11 @@ After logging out locally, user will be redirected to one of the following (with - `LOGOUT_REDIRECT_URL = urls.reverse_lazy("sso-logged-out-locally")` - Default: redirect to the view that asks users what to do next. Don't forget to override template `ssoauth/logged_out_locally.html` -**SLO**: SLO/SLS is disabled by default. If you want to try your luck with you should add to your project settings `SP_SLS_ENABLED = True` +#### Advanced Logging Out and SLO + +If unsure, ignore this section. + +**SLO**: SLO/SLS is disabled by default. If you want to try your luck with SLO you should add to your project settings `SP_SLS_ENABLED = True` Currently only IdP-initiated SLO is supported by this app. The only supported binding type is HTTP-Redirect due to the limitations of the underlying library used. @@ -73,13 +80,22 @@ If you have `nginx` serving pages to users, you might need to configure `x-frame #### Groups and Permissions -To receive groups over SSO you need a group mapping (and of course a properly configured IdP). You can manage group mapping with `group_mapping` management command: - - group_mapping add myproject_superusers "CN=MyProjectSuperusers,OU=Foo,OU=Bar,DC=fh-h,DC=de" -To generate a working mapping for `hshinfo` groups, use `ssoauth_group_mapping` management command in `syncds` (you can find one on the `sync` server). +Users receive groups using SSO. For this to work, you need: + - some groups in your django project (see `django.contrib.auth` groups) + - groups with exactly the same names provided by the IDP + - create a group in the IDM + - make sure IDM provides it to the IDP + - make sure IDP provides it to your SP + - you might want to predefine some groups in the project settings (see `ssoauth` default config for details) + - these groups will be created automatically (when migrating) and will receive the specified permissions + - e.g. you probably want a superuser group, see the example below -*Groups are not mapped automatically. The reason is that automatic mapping can pose security risks. Imagine auto-mapping that expects group with name "Superusers"; an intruder could create new group with this name under any path they own and/or create an alias/reference and receive superuser permissions in your project.* +```python +PREDEFINED_GROUPS = { + "my_project_superusers": [ssoauth.SUPERUSER_PERM_CODENAME], +} +``` #### Production Settings @@ -125,9 +141,8 @@ openssl req -newkey rsa:16384 -x509 -days 3650 -nodes -out sp.pem -keyout sp.key - create a new `<MetadataProvider .../>` element, your best guess is to copy an existing line that belongs to some existing Django project - `id` should be unique - `metadataFile` should point on your new metadata - - edit `./conf/attribute-filter.xml` - - add a new `<Rule .../>` element inside of `<AttributeFilterPolicy id="releaseToDjango">` - - `value` (looks like URI) should be the `entityID` of your SP (you find it in your meta) - - `systemctl restart tomcat8` (you now have some time to grab you a coffee) - - ensure the IdP works after restarting! - + - edit `./conf/attribute-filter.xml` + - use your head + - `systemctl restart tomcat8` + - make sure that IDP works + - if IDP does not work, rollback your changes and restart the IDP again diff --git a/ssoauth/__init__.py b/ssoauth/__init__.py index 76976b9..8c3c79b 100644 --- a/ssoauth/__init__.py +++ b/ssoauth/__init__.py @@ -1,10 +1,11 @@ import logging -from . import checks # As for Django 1.11 it still doesn't auto-import checks >.< -assert checks # must be imported - - logger = logging.getLogger("ssoauth") default_app_config = "ssoauth.apps.SSOAuthConfig" + +# permission codenames for builtin django features +STAFF_PERM_CODENAME = "staff" +SUPERUSER_PERM_CODENAME = "superuser" + diff --git a/ssoauth/app_settings/defaults.py b/ssoauth/app_settings/defaults.py index 5cf1fad..f6dd7f2 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_raw_saml2_values" # in case you want to override how groups are resolved for users +GROUP_RESOLVER = "ssoauth.auth_utils.groups_from_group_names" # 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 @@ -47,8 +47,8 @@ PREDEFINED_GROUPS = { # Give your local groups the same name as the AuthGroup they will be mapped to # (e.g. your local group for students will be named IDM_Studierende) # - # Example: - # {"IDM_Studierende": ["perm_codename", "another_perm_codename"]} + # {"example_group": ["perm_codename", "another_perm_codename"]} + # {"superusers": [ssoauth.SUPERUSER_PERM_CODENAME]} } @@ -70,12 +70,6 @@ SP_METADATA_LIFETIME_DAYS = 365 * 20 PROJECT_NAME = os.environ.get('DJANGO_SETTINGS_MODULE').split('.')[0] -# superusers is a group to have the best possible overview of them -SUPERUSER_GROUP_NAME = "{0}_superusers".format(PROJECT_NAME) - -# staff is a permission because a group would normally combine staff and permissions over some models -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 # the block below defines from which SAML2 attributes this SP receives user data diff --git a/ssoauth/apps.py b/ssoauth/apps.py index 12c3ed4..891a8cc 100644 --- a/ssoauth/apps.py +++ b/ssoauth/apps.py @@ -5,12 +5,15 @@ from django.db.models.signals import post_migrate from . import app_settings from . import logger from . import sso_utils +from . import checks # As for Django 1.11 it still doesn't auto-import checks >.< class SSOAuthConfig(AppConfig): name = "ssoauth" def ready(self, *args, **kwargs): + if not checks: + raise RuntimeError("Checks are not imported.") super().ready(*args, **kwargs) # OneLogin settings stuff try: @@ -26,8 +29,8 @@ class SSOAuthConfig(AppConfig): @staticmethod def post_migrate_callback(*args, **kwargs): - # compatibility groups and permissions - management.call_command("create_compat_groups") - # predefined groups and permissions + # first, let django create its own permissions create_permissions(*args, **kwargs) # calling create_permissions() before using the permissions - management.call_command("create_custom_groups") + # custom and compatibility groups and permissions + management.call_command("ssoauth_setup_groups_and_perms") + diff --git a/ssoauth/auth_utils.py b/ssoauth/auth_utils.py index 5a501cc..4e785f9 100644 --- a/ssoauth/auth_utils.py +++ b/ssoauth/auth_utils.py @@ -1,14 +1,12 @@ from django.db import transaction -from django.db.models import Q from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from uuid import UUID from . import models from . import logger from . import app_settings -import functools +from . import SUPERUSER_PERM_CODENAME, STAFF_PERM_CODENAME import importlib -import re def _validate_username(username): @@ -118,35 +116,18 @@ def set_user_groups(user, raw_values): 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))) + logger.info("User {user} is member of: {groups}".format(user=user, groups=[str(g) for g in groups])) -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 - - 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: - return from_group_name_list(raw_values) +def groups_from_group_names(user, saml2_raw_values): + """ This default group resolver returns list of Group objects based on group names as raw_values """ + groups = set(Group.objects.filter(name__in=saml2_raw_values)) + # perform some sanity checks + group_names_redundant = set(saml2_raw_values) - {g.name for g in groups} + if group_names_redundant: + logger.warning("Received redundant group names (clean up the configs): {0}".format(group_names_redundant)) + # done + return groups def cleanup_direct_permissions(user): @@ -155,41 +136,41 @@ def cleanup_direct_permissions(user): user.user_permissions.clear() -def set_user_compat_flags(user, check_login_perm=True): +def get_superuser_groups(): + """ :return: QuerySet with all groups that have superuser permission """ + return Group.objects.filter(permissions__codename=SUPERUSER_PERM_CODENAME) + + +def update_user_compat_flags(user, check_login_perm=True): def get_full_perm_name(codename): return "{app}.{codename}".format( app=get_user_model()._meta.app_label, codename=codename, ) - # defaults are restrictive user.is_staff = False user.is_superuser = False - # is_active must be initially set to True, otherwise has_perm() will not function + # is_active must be initially set to True, otherwise has_perm() will not function (e.g. for is_staff) user.is_active = True - # is_superuser - try: - group_su = Group.objects.get(name=app_settings.SUPERUSER_GROUP_NAME) - if group_su in user.groups.all(): - logger.info("User {user} is superuser".format(**locals())) - user.is_superuser = True - except Group.DoesNotExist as e: - logger.error("Could not fetch the superuser group. Forgot to migrate?") + # is_superuser (carefully resolving using groups, not using has_perm because checks is_superuser internally) + user_groups = user.groups.all() + su_groups = get_superuser_groups() + if bool(set(user_groups) & set(su_groups)): + user.is_superuser = True # is_staff - if user.has_perm(get_full_perm_name(app_settings.STAFF_PERM_CODENAME)): - logger.info("User {user} is staff (has admin access).".format(**locals())) + if user.has_perm(get_full_perm_name(STAFF_PERM_CODENAME)): + logger.info("User {0} is staff (has admin access).".format(user)) user.is_staff = True # is_active (actually represents the log in permission) if app_settings.LOGIN_PERM_CODENAME and check_login_perm: if user.has_perm(get_full_perm_name(app_settings.LOGIN_PERM_CODENAME)): - logger.info("User {user} is active.".format(**locals())) + logger.info("User {0} is active.".format(user)) user.is_active = True else: - logger.info("User {user} is inactive (does not have LOGIN_PERM_CODENAME).".format(**locals())) + logger.info("User {0} is inactive - does not have login permission {1}.".format(user, app_settings.LOGIN_PERM_CODENAME)) user.is_active = False else: user.is_active = True # without LOGIN_PERM_CODENAME every SSO user is considered active # done user.save() - diff --git a/ssoauth/checks.py b/ssoauth/checks.py index 93c054f..846eab9 100644 --- a/ssoauth/checks.py +++ b/ssoauth/checks.py @@ -1,9 +1,11 @@ from django.core.checks import Tags, Error, Warning, register from django.contrib.auth import get_user_model from django.db.utils import OperationalError, ProgrammingError +from django.apps import apps from django import conf from django import urls from . import app_settings +from . import SUPERUSER_PERM_CODENAME def _get_abstract_user(): @@ -35,6 +37,29 @@ def no_passwords_stored(app_configs, **kwargs): return errors +@register(Tags.security) +@_ignore_db_errors +def not_too_many_superuser_groups(app_configs, **kwargs): + errors = list() + group_model = apps.get_model("auth", "Group") + su_groups = group_model.objects.filter(permissions__codename=SUPERUSER_PERM_CODENAME) + if len(su_groups) > 1: + errors.append(Error("There are too many superuser groups: {0}".format(su_groups))) + for g in su_groups: + g.permissions.clear() + errors.append(Error("Purged permissions of group {0}".format(g), obj=group_model)) + return errors + + +@register(Tags.compatibility) +@_ignore_db_errors +def deprecated_su_group_name(app_configs, **kwargs): + errors = list() + if hasattr(conf.settings, "SSOAUTH_SUPERUSER_GROUP_NAME") or hasattr(conf.settings, "SUPERUSER_GROUP_NAME"): + errors.append(Warning("[SSOAUTH_]SUPERUSER_GROUP_NAME is deprecated and will be ignored. Create your own superuser group using [SSOAUTH_]PREDEFINED_GROUPS.", obj=conf.settings)) + return errors + + @register(Tags.compatibility) def compatible_user_model(app_configs, **kwargs): errors = list() diff --git a/ssoauth/management/commands/create_compat_groups.py b/ssoauth/management/commands/create_compat_groups.py deleted file mode 100644 index ce7cd0f..0000000 --- a/ssoauth/management/commands/create_compat_groups.py +++ /dev/null @@ -1,57 +0,0 @@ -from django.core.management.base import BaseCommand, CommandError -from django.contrib.auth.models import Group, Permission -from django.contrib.auth import get_user_model -from django.contrib.contenttypes.models import ContentType -from ... import app_settings -from ... import logger - - -class Command(BaseCommand): - help = "Creates groups and permissions for django.contrib.auth compatibility." - requires_migrations_checks = True - requires_system_checks = True - - def handle(self, *args, **options): - try: - self.ensure_group_exists(app_settings.SUPERUSER_GROUP_NAME) - self.ensure_permission_exists(app_settings.STAFF_PERM_CODENAME) - if app_settings.LOGIN_PERM_CODENAME: - self.ensure_permission_exists(app_settings.LOGIN_PERM_CODENAME) - except Exception as e: - raise CommandError("Could not ensure that compatibility groups and permissions exist. {0}".format(str(e))) - - @classmethod - def ensure_group_exists(cls, group_name, permission_names=list()): - """ - :return: group object - Ensures the group with the given permissions exists. - Creates the group and/or permissions if needed. - Adds permissions to the group if needed (but never removes any). - """ - group, created = Group.objects.get_or_create(name=group_name) - if created: - logger.info("Created group '{}'".format(group)) - for permission_name in permission_names: - permission = cls.ensure_permission_exists(permission_name) - if permission not in group.permissions.all(): - group.permissions.add(permission) - logger.info("Added permission '{1}' to group '{0}'".format(group, permission.codename)) - return group - - @staticmethod - def ensure_permission_exists(codename): - """ - :return: permission object - Ensures the permissions exists. Creates it if needed. - """ - user_content_type = ContentType.objects.get_for_model(get_user_model()) - permission, created = Permission.objects.get_or_create( - codename=codename, - content_type_id=user_content_type.pk, - ) - if created: - permission.name = "{0} (ssoauth)".format(codename) - permission.save() - logger.info("Created permission '{}'".format(permission)) - return permission - diff --git a/ssoauth/management/commands/create_custom_groups.py b/ssoauth/management/commands/create_custom_groups.py deleted file mode 100644 index 28eb0f6..0000000 --- a/ssoauth/management/commands/create_custom_groups.py +++ /dev/null @@ -1,51 +0,0 @@ -from django.core.management.base import BaseCommand, CommandError -from django.apps import apps -from django.contrib.auth import get_user_model - -from ... import app_settings -from ... import logger - - -def setup_groups(): - """ - Creates groups and permissions as specified in your project settings. - """ - # grab the required models - User = get_user_model() - Group = apps.get_model("auth", "Group") - ContentType = apps.get_model("contenttypes", "ContentType") - Permission = apps.get_model("auth", "Permission") - - for group_name, permission_names in app_settings.PREDEFINED_GROUPS.items(): - group, created_group = Group.objects.get_or_create(name=group_name) - if created_group: - logger.info("Created group \"{}\"".format(group_name)) - for perm_name in permission_names: - try: - perm = Permission.objects.get( - codename=perm_name, - content_type_id=ContentType.objects.get_for_model(User).id - ) - except Permission.DoesNotExist: - perm = Permission.objects.create( - codename=perm_name, - name=perm_name, - content_type_id=ContentType.objects.get_for_model(User).id - ) - logger.info("Created permission \"{}\"".format(perm_name)) - if perm not in group.permissions.all(): - group.permissions.add(perm) - logger.info("Added permission \"{}\" to group \"{}\"".format(perm_name, group_name)) - - -class Command(BaseCommand): - help = "Creates groups and permissions, predefined by user in project settings." - requires_migrations_checks = True - requires_system_checks = True - - def handle(self, *args, **options): - try: - setup_groups() - except Exception as e: - raise CommandError("Could not ensure that compatibility groups and permissions exist. {0}".format(str(e))) - diff --git a/ssoauth/management/commands/group_mapping.py b/ssoauth/management/commands/group_mapping.py deleted file mode 100644 index 9a85dde..0000000 --- a/ssoauth/management/commands/group_mapping.py +++ /dev/null @@ -1,119 +0,0 @@ -from django.core.management.base import BaseCommand, CommandError -from django.contrib.auth.models import Group, Permission -from ... models import GroupMapping -from django.contrib.contenttypes.models import ContentType -from django.contrib.auth import get_user_model -from ... import app_settings -from ... import logger - - -class Command(BaseCommand): - help = "Modify group mapping. You can also do it from your project admin." - requires_migrations_checks = True - requires_system_checks = True - - def add_arguments(self, parser): - parser.add_argument( - "action", - action="store", - choices=["list", "add", "del", "?"], - help="action", - ) - parser.add_argument( - "name", - action="store", - default=None, - nargs="?", - help="local group name (in this project)", - ) - parser.add_argument( - "dn", - action="store", - default=None, - nargs="?", - help="group DN in a directory as returned by Shibboleth", - ) - - def handle(self, *args, **options): - action, name, dn = options["action"], options["name"], options["dn"] - if action == "list": - self.print_mapping() - elif action == "add": - if not name or not dn: - return logger.error("Need group name and DN.") - self.add_mapping(name, dn) - self.print_mapping() - elif action == "del": - if not name: - return logger.error("Need group name.") - self.delete_mapping(name) - else: - self.print_help(str(), str()) - - def print_mapping(self): - groups = Group.objects.all() - mapped = groups.filter(sso_mapping__isnull=False) - unmapped = groups.filter(sso_mapping__isnull=True) - logger.info("There are {g} groups ({m} mapped, {u} unmapped):".format(g=len(groups), m=len(mapped), u=len(unmapped))) - for group in groups.order_by("-sso_mapping", "name"): - name = group.name - try: - dn = group.sso_mapping.dn - except GroupMapping.DoesNotExist: - dn = None - logger.info(" {name:<50} {dn}".format(**locals())) - - def delete_mapping(self, group_name): - mapping = self._get_mapping_for(group_name) - if mapping: - logger.info("Deleting {mapping}.".format(**locals())) - mapping.delete() - else: - logger.error("There is no mapping for {group_name}.".format(**locals())) - - def add_mapping(self, group_name, dn): - # group - try: - group = Group.objects.get(name__iexact=group_name) - logger.info("Existing group {group} will be used.".format(**locals())) - except Group.DoesNotExist: - logger.info("Group {group_name} will be created.".format(**locals())) - group = Group.objects.create(name=group_name) - # mapping - existing = self._get_mapping_for(group_name) - if existing: - logger.warning("Removing existing mapping {existing}".format(**locals())) - existing.delete() - new = GroupMapping.objects.create(group=group, dn=dn) - logger.info("Created {new}.".format(**locals())) - - def _get_mapping_for(self, group_name): - try: - return GroupMapping.objects.get(group__name__iexact=group_name) - except GroupMapping.DoesNotExist: - return None - - - ## seems like the following code can create groups and permissions as we had it - ## in the old There hshauth, based on the project settings - # - # @staticmethod - # def ensure_group_exists(group_name, permission_names=list()): - # """ - # Ensures the group with the given permissions exists. - # Creates the group and/or permissions if needed. - # Adds permissions to the group if needed (but never removes any). - # """ - # group, created = Group.objects.get_or_create(name=group_name) - # if created: - # logger.info("Created group '{}'".format(group)) - # for permission_name in permission_names: - # permission, created = Permission.objects.get_or_create( - # codename=permission_name, - # content_type_id=ContentType.objects.get_for_model(get_user_model()).id, - # ) - # if created: - # logger.info("Created permission '{}'".format(permission.codename)) - # if permission not in group.permissions.all(): - # group.permissions.add(permission) - # logger.info("Added permission '{1}' to group '{0}'".format(group, permission.codename)) diff --git a/ssoauth/management/commands/ssoauth_setup_groups_and_perms.py b/ssoauth/management/commands/ssoauth_setup_groups_and_perms.py new file mode 100644 index 0000000..4c74434 --- /dev/null +++ b/ssoauth/management/commands/ssoauth_setup_groups_and_perms.py @@ -0,0 +1,62 @@ +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth import get_user_model +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth.models import Group, Permission +from ... import app_settings +from ... import logger +from ... import SUPERUSER_PERM_CODENAME, STAFF_PERM_CODENAME + + +def get_user_content_type(): + return ContentType.objects.get_for_model(get_user_model()) + + +def get_or_create_permission(codename, create_with_name=None): + """ + :return: permission object + Ensures the permissions exists. Creates it if needed. + """ + try: + perm = Permission.objects.get(codename=codename) + except Permission.DoesNotExist: + perm = Permission.objects.create( + codename=codename, + name=create_with_name or codename, + content_type=get_user_content_type() + ) + logger.info("Created permission: {0}".format(perm)) + return perm + + +def get_or_create_group(name): + group, created = Group.objects.get_or_create(name=name) + if created: + logger.info("Created group: {0}".format(group)) + return group + + +def setup_predefined_groups(): + for group_name, perm_codename in app_settings.PREDEFINED_GROUPS.items(): + group = get_or_create_group(group_name) + for perm_codename in perm_codename: + perm = get_or_create_permission(perm_codename) + if perm not in group.permissions.all(): + group.permissions.add(perm) + logger.info("Added permission {0} to group {1}".format(perm, group)) + + +class Command(BaseCommand): + help = "Set up groups and permissions (both predefined and compatibility). Runs automatically after migrations." + requires_migrations_checks = True + requires_system_checks = True + + def handle(self, *args, **options): + try: + # compat permissions + for codename in [SUPERUSER_PERM_CODENAME, STAFF_PERM_CODENAME]: + get_or_create_permission(codename, "{0} (SSO)".format(codename)) + # predefined groups + setup_predefined_groups() + except Exception as e: + raise CommandError("Could not set up groups and permissions: {0}".format(e)) + diff --git a/ssoauth/migrations/0002_auto_20190306_1823.py b/ssoauth/migrations/0002_auto_20190306_1823.py new file mode 100644 index 0000000..3e589ea --- /dev/null +++ b/ssoauth/migrations/0002_auto_20190306_1823.py @@ -0,0 +1,20 @@ +# Generated by Django 2.1.7 on 2019-03-06 18:23 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ssoauth', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='groupmapping', + name='group', + ), + migrations.DeleteModel( + name='GroupMapping', + ), + ] diff --git a/ssoauth/models.py b/ssoauth/models.py index 30b0f66..5bc2b06 100644 --- a/ssoauth/models.py +++ b/ssoauth/models.py @@ -11,12 +11,3 @@ class UserMapping(models.Model): def __str__(self): return "{user} <-> {uuid}".format(user=self.user, uuid=self.uuid) - -class GroupMapping(models.Model): - group = models.OneToOneField("auth.Group", null=False, on_delete=models.CASCADE, related_name="sso_mapping") - dn = models.CharField(max_length=1000, null=False, blank=False) - created_on = models.DateField(null=False, auto_now_add=True) - - def __str__(self): - return "{group} <-> \"{dn}\"".format(group=self.group, dn=self.dn) - diff --git a/ssoauth/views.py b/ssoauth/views.py index fcab98b..d61f747 100644 --- a/ssoauth/views.py +++ b/ssoauth/views.py @@ -203,7 +203,7 @@ class ACSAuthNView(SAMLMixin, View): ) # other necessary steps auth_utils.cleanup_direct_permissions(user=user) # in case somebody was acting funny - auth_utils.set_user_compat_flags(user=user) # superuser, staff + auth_utils.update_user_compat_flags(user=user) # superuser, staff user.backend = app_settings.PRETEND_AUTH_BACKEND request.user = user if user.is_active: @@ -350,7 +350,7 @@ class DevView(FormView): contrib_auth.logout(self.request) # update the compat flags, might be needed when user or their groups change if self.request.user.is_authenticated: - auth_utils.set_user_compat_flags(self.request.user, False) + auth_utils.update_user_compat_flags(self.request.user, False) # redirect return http.HttpResponseRedirect(self.next_url or urls.reverse("sso-dev")) -- GitLab