provisionning: factorize user creation (#59135)
This commit is contained in:
parent
2daa7b3170
commit
8dc951282a
|
@ -17,10 +17,11 @@
|
|||
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, UserExtraAttributes
|
||||
from hobo.multitenant.utils import provision_user_groups
|
||||
|
@ -50,6 +51,64 @@ def user_str(user):
|
|||
return s
|
||||
|
||||
|
||||
def get_issuer(entity_id):
|
||||
issuer, _ = Issuer.objects.get_or_create(entity_id=entity_id)
|
||||
return issuer
|
||||
|
||||
|
||||
def provision_user(entity_id, o, tries=0):
|
||||
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),
|
||||
}
|
||||
|
||||
excluded_attrs = ['roles', 'password']
|
||||
extra_attributes = {k: v for k, v in o.items() if k not in excluded_attrs}
|
||||
|
||||
User = get_user_model()
|
||||
try:
|
||||
user = User.objects.get(
|
||||
saml_identifiers__name_id=o['uuid'], saml_identifiers__issuer__entity_id=entity_id
|
||||
)
|
||||
user_extra_attributes = UserExtraAttributes.objects.get(user=user)
|
||||
except User.DoesNotExist:
|
||||
user = User()
|
||||
user_extra_attributes = UserExtraAttributes(user=user, data=extra_attributes)
|
||||
|
||||
for key, value in attributes.items():
|
||||
if getattr(user, key) != value:
|
||||
setattr(user, key, value)
|
||||
updated.add(key)
|
||||
|
||||
for key in extra_attributes:
|
||||
if extra_attributes[key] != user_extra_attributes.data.get(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'])
|
||||
user_extra_attributes.save()
|
||||
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()
|
||||
user_extra_attributes.save()
|
||||
logger.info('updated user %s(%s)', user_str(user), updated)
|
||||
return user
|
||||
|
||||
|
||||
class NotificationProcessing:
|
||||
@classmethod
|
||||
def check_valid_notification(cls, notification):
|
||||
|
@ -80,71 +139,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 UserSAMLIdentifier
|
||||
from mellon.models_utils import get_issuer
|
||||
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 = get_issuer(issuer)
|
||||
mellon_user = UserSAMLIdentifier.objects.create(
|
||||
user=user, issuer=saml_issuer, name_id=o['uuid']
|
||||
)
|
||||
excluded_attrs = ['roles', 'password']
|
||||
|
||||
UserExtraAttributes.objects.update_or_create(
|
||||
user=user,
|
||||
defaults={'data': {k: v for k, v in o.items() if k not in excluded_attrs}},
|
||||
)
|
||||
if new:
|
||||
logger.info('provisionned new user %s', user_str(user))
|
||||
else:
|
||||
for key, value in attributes.items():
|
||||
if getattr(user, key) != value:
|
||||
setattr(user, key, value)
|
||||
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
|
||||
for o in data:
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue