From 602d14e9816eaba6fca78a5268c56293ba396c29 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Thu, 26 Aug 2021 16:31:39 +0200 Subject: [PATCH 1/3] provisionning: factorize user creation (#59135) --- hobo/provisionning/utils.py | 109 ++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/hobo/provisionning/utils.py b/hobo/provisionning/utils.py index dc59e6c..414dc14 100644 --- a/hobo/provisionning/utils.py +++ b/hobo/provisionning/utils.py @@ -17,10 +17,12 @@ import hashlib import logging +from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from django.db import IntegrityError from django.db.models.query import Q from django.db.transaction import atomic +from mellon.models import Issuer from hobo.agent.common.models import Role from hobo.multitenant.utils import provision_user_groups @@ -50,6 +52,48 @@ def user_str(user): return s +def get_issuer(entity_id): + issuer, created = Issuer.objects.get_or_create(entity_id=entity_id) + return issuer + + +def provision_user(entity_id, o, tries=0): + updated = set() + attributes = { + 'username': o['uuid'][:150], + 'first_name': o['first_name'][:30], + 'last_name': o['last_name'][:150], + 'email': o['email'][:254], + 'is_superuser': o['is_superuser'], + 'is_staff': o['is_superuser'], + 'is_active': o.get('is_active', True), + } + + User = get_user_model() + user = get_user_from_name_id(name_id=o['uuid'], entity_id=entity_id) or User() + + for key in attributes: + if getattr(user, key) != attributes[key]: + setattr(user, key, attributes[key]) + updated.add(key) + + if not user.id: # user is new + issuer = get_issuer(entity_id) + try: + with atomic(savepoint=False): + user.save() + user.saml_identifiers.create(issuer=issuer, name_id=o['uuid']) + logger.info('provisionned new user %s', user_str(user)) + except IntegrityError: + if tries > 0: + raise + return provision_user(user, o, tries=tries + 1) + elif updated: + user.save() + logger.info('updated user %s(%s)', user_str(user), updated) + return user + + class NotificationProcessing: @classmethod def check_valid_notification(cls, notification): @@ -80,64 +124,21 @@ class NotificationProcessing: @classmethod def provision_user(cls, issuer, action, data, full=False): - from django.contrib.auth import get_user_model - from mellon.models import Issuer, UserSAMLIdentifier + assert not full # provisionning all users is dangerous, we prefer deprovision User = get_user_model() - - assert not full # provisionning all users is dangerous, we prefer deprovision uuids = set() + for o in data: - try: - with atomic(): - if action == 'provision': - new = False - updated = set() - attributes = { - 'first_name': o['first_name'][:30], - 'last_name': o['last_name'][:150], - 'email': o['email'][:254], - 'username': o['uuid'][:150], - 'is_superuser': o['is_superuser'], - 'is_staff': o['is_superuser'], - 'is_active': o.get('is_active', True), - } - assert cls.check_valid_user(o) - try: - mellon_user = UserSAMLIdentifier.objects.get( - issuer__entity_id=issuer, name_id=o['uuid'] - ) - user = mellon_user.user - except UserSAMLIdentifier.DoesNotExist: - try: - user = User.objects.get( - Q(username=o['uuid'][:30]) | Q(username=o['uuid'][:150]) - ) - except User.DoesNotExist: - # temp user object - user = User.objects.create(**attributes) - new = True - saml_issuer, created = Issuer.objects.get_or_create(entity_id=issuer) - mellon_user = UserSAMLIdentifier.objects.create( - user=user, issuer=saml_issuer, name_id=o['uuid'] - ) - if new: - logger.info('provisionned new user %s', user_str(user)) - else: - for key in attributes: - if getattr(user, key) != attributes[key]: - setattr(user, key, attributes[key]) - updated.add(key) - if updated: - user.save() - logger.info('updated user %s(%s)', user_str(user), updated) - role_uuids = [role['uuid'] for role in o.get('roles', [])] - provision_user_groups(user, role_uuids) - elif action == 'deprovision': - assert 'uuid' in o - uuids.add(o['uuid']) - except IntegrityError: - raise TryAgain + if action == 'provision': + assert cls.check_valid_user(o) + user = provision_user(issuer, o) + role_uuids = [role['uuid'] for role in o.get('roles', [])] + provision_user_groups(user, role_uuids) + elif action == 'deprovision': + assert 'uuid' in o + uuids.add(o['uuid']) + if (full and action == 'provision') or (action == 'deprovision'): if action == 'deprovision': qs = User.objects.filter(saml_identifiers__name_id__in=uuids) -- 2.35.1