diff --git a/hobo/agent/common/migrations/0005_userextraattributes.py b/hobo/agent/common/migrations/0005_userextraattributes.py new file mode 100644 index 0000000..5df2128 --- /dev/null +++ b/hobo/agent/common/migrations/0005_userextraattributes.py @@ -0,0 +1,35 @@ +# Generated by Django 2.2.26 on 2023-01-03 09:30 + +import django.contrib.postgres.fields.jsonb +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('common', '0004_alter_role_uuid'), + ] + + operations = [ + migrations.CreateModel( + name='UserExtraAttributes', + fields=[ + ( + 'id', + models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ('data', django.contrib.postgres.fields.jsonb.JSONField(default=dict)), + ( + 'user', + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name='extra_attributes', + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/hobo/agent/common/models.py b/hobo/agent/common/models.py index 132d036..de69f7d 100644 --- a/hobo/agent/common/models.py +++ b/hobo/agent/common/models.py @@ -1,5 +1,6 @@ +from django.conf import settings from django.contrib.auth.models import Group -from django.contrib.postgres.fields import ArrayField +from django.contrib.postgres.fields import ArrayField, JSONField from django.db import models @@ -11,3 +12,12 @@ class Role(Group): emails_to_members = models.BooleanField(default=True) objects = models.Manager() + + +class UserExtraAttributes(models.Model): + user = models.OneToOneField( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name='extra_attributes', + ) + data = JSONField(default=dict) diff --git a/hobo/provisionning/utils.py b/hobo/provisionning/utils.py index 1177cfc..9c80441 100644 --- a/hobo/provisionning/utils.py +++ b/hobo/provisionning/utils.py @@ -22,7 +22,7 @@ from django.db import IntegrityError from django.db.models.query import Q from django.db.transaction import atomic -from hobo.agent.common.models import Role +from hobo.agent.common.models import Role, UserExtraAttributes from hobo.multitenant.utils import provision_user_groups logger = logging.getLogger(__name__) @@ -122,6 +122,12 @@ class NotificationProcessing: mellon_user = UserSAMLIdentifier.objects.create( user=user, issuer=saml_issuer, name_id=o['uuid'] ) + excluded_attrs = ['roles', 'password'] + + extra_attributes = 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: diff --git a/tests/test_provisionning_middleware.py b/tests/test_provisionning_middleware.py index 4f9d9cd..4ca7efe 100644 --- a/tests/test_provisionning_middleware.py +++ b/tests/test_provisionning_middleware.py @@ -32,12 +32,43 @@ def test_provisionning(app, db, settings): '@type': 'user', 'data': [ { + 'id': 1, 'uuid': 'a' * 32, + 'username': 'johndoe', 'first_name': 'John', 'last_name': 'Doe', 'email': 'john.doe@example.net', 'is_superuser': True, + 'is_staff': True, + 'is_active': True, 'roles': [], + 'ou': {'foo': 'bar'}, + 'date_joined': '2022-07-19T15:07:54.649675+02:00', + 'last_login': '2023-01-03T10:56:11.552280+01:00', + 'password': 'pbkdf2_sha256$150000$afVbUpBWl3v7$6D9xCdIf0lnjSGHm+BxPmkWvRvIq0vvP4c/FTFhZnkY=', + 'email_verified': False, + 'email_verified_date': None, + 'phone': '123456', + 'phone_verified_on': None, + 'modified': '2023-01-03T10:59:45.103274+01:00', + 'last_account_deletion_alert': None, + 'deactivation': None, + 'deactivation_reason': None, + 'first_name_verified': False, + 'last_name_verified': False, + 'address': 'Somewhere', + 'address_verified': False, + 'zipcode': '13333', + 'zipcode_verified': False, + 'city': 'Marseille', + 'city_verified': False, + 'phone_verified': False, + 'mobile': '56789', + 'mobile_verified': False, + 'preferred_username': 'Blue', + 'preferred_username_verified': False, + 'custom_attr': 'foo', + 'custom_attr_verified': False, } ], }, @@ -50,4 +81,10 @@ def test_provisionning(app, db, settings): sign_url('/__provision__/?orig=%s' % 'hobo.example.invalid', 'xxx'), notification, status=200 ) assert User.objects.count() == 1 - assert User.objects.get(email='john.doe@example.net') + + user = User.objects.latest('pk') + assert user.email == 'john.doe@example.net' + + excluded_attrs = ['password', 'roles'] + expected_data = {k: v for k, v in notification['objects']['data'][0].items() if k not in excluded_attrs} + assert user.extra_attributes.data == expected_data