From fd0d8666ecf78173a5bed1ada2db1f9f30a49919 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Mon, 3 Oct 2022 12:19:38 +0200 Subject: [PATCH 4/8] misc: keep nameid attributes to rebuild it (#69740) Logout requests need a properly built NameID element, but we did not store enough information in models to do that, we uses the LassoSession dump from the session as a work-around. In order to have a session-less logout endpoint, we need to store those informations in the UserSAMLIdentifier model. --- mellon/adapters.py | 14 +++++++-- mellon/migrations/0006_nameid_attributes.py | 33 +++++++++++++++++++++ mellon/models.py | 5 ++++ mellon/views.py | 7 ++++- 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 mellon/migrations/0006_nameid_attributes.py diff --git a/mellon/adapters.py b/mellon/adapters.py index f4c089b..1778926 100644 --- a/mellon/adapters.py +++ b/mellon/adapters.py @@ -350,7 +350,7 @@ class DefaultAdapter: created = True user = self.create_user(User) - nameid_user = self._link_user(idp, saml_attributes, entity_id, name_id, user) + nameid_user = self._link_user(idp, saml_attributes, user) if user != nameid_user: logger.info( 'mellon: looked up user %s with name_id %s from issuer %s', nameid_user, name_id, entity_id @@ -458,9 +458,17 @@ class DefaultAdapter: ) return None - def _link_user(self, idp, saml_attributes, entity_id, name_id, user): + def _link_user(self, idp, saml_attributes, user): saml_id, created = models.UserSAMLIdentifier.objects.get_or_create( - name_id=name_id, issuer=models_utils.get_issuer(entity_id), defaults={'user': user} + name_id=saml_attributes['name_id_content'], + issuer=models_utils.get_issuer(saml_attributes['issuer']), + defaults={ + 'user': user, + 'nid_format': saml_attributes['name_id_format'], + 'nid_name_qualifier': saml_attributes.get('name_id_name_qualifier'), + 'nid_sp_name_qualifier': saml_attributes.get('name_id_sp_name_qualifier'), + 'nid_sp_provided_id': saml_attributes.get('name_id_sp_provided_id'), + }, ) if created: user.saml_identifier = saml_id diff --git a/mellon/migrations/0006_nameid_attributes.py b/mellon/migrations/0006_nameid_attributes.py new file mode 100644 index 0000000..607de0e --- /dev/null +++ b/mellon/migrations/0006_nameid_attributes.py @@ -0,0 +1,33 @@ +# Generated by Django 2.2.26 on 2022-10-03 10:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mellon', '0005_drop_rename_issuer'), + ] + + operations = [ + migrations.AddField( + model_name='usersamlidentifier', + name='nid_format', + field=models.TextField(null=True, verbose_name='NameID Format'), + ), + migrations.AddField( + model_name='usersamlidentifier', + name='nid_name_qualifier', + field=models.TextField(null=True, verbose_name='NameID NameQualifier'), + ), + migrations.AddField( + model_name='usersamlidentifier', + name='nid_sp_name_qualifier', + field=models.TextField(null=True, verbose_name='NameID SPNameQualifier'), + ), + migrations.AddField( + model_name='usersamlidentifier', + name='nid_sp_provided_id', + field=models.TextField(null=True, verbose_name='SAML NameID SPPRovidedID'), + ), + ] diff --git a/mellon/models.py b/mellon/models.py index fbb7b68..11b1c76 100644 --- a/mellon/models.py +++ b/mellon/models.py @@ -32,6 +32,11 @@ class UserSAMLIdentifier(models.Model): created = models.DateTimeField(verbose_name=_('created'), auto_now_add=True) issuer = models.ForeignKey('mellon.Issuer', verbose_name=_('Issuer'), null=True, on_delete=models.CASCADE) + nid_format = models.TextField(verbose_name=_('NameID Format'), null=True) + nid_name_qualifier = models.TextField(verbose_name=_('NameID NameQualifier'), null=True) + nid_sp_name_qualifier = models.TextField(verbose_name=_('NameID SPNameQualifier'), null=True) + nid_sp_provided_id = models.TextField(verbose_name=('SAML NameID SPPRovidedID'), null=True) + class Meta: verbose_name = _('user SAML identifier') verbose_name_plural = _('users SAML identifiers') diff --git a/mellon/views.py b/mellon/views.py index 3388ded..7946b9f 100644 --- a/mellon/views.py +++ b/mellon/views.py @@ -268,12 +268,17 @@ class LoginView(ProfileMixin, LogMixin, View): name_id = login.nameIdentifier name_id_format = force_str(name_id.format or lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED) attributes.update( - {'name_id_content': lasso_decode(name_id.content), 'name_id_format': name_id_format} + { + 'name_id_content': lasso_decode(name_id.content), + 'name_id_format': name_id_format, + } ) if name_id.nameQualifier: attributes['name_id_name_qualifier'] = force_str(name_id.nameQualifier) if name_id.spNameQualifier: attributes['name_id_sp_name_qualifier'] = force_str(name_id.spNameQualifier) + if name_id.spProvidedId: + attributes['name_id_provided_id'] = force_str(name_id.spProvidedId) authn_statement = login.assertion.authnStatement[0] if authn_statement.authnInstant: attributes['authn_instant'] = utils.iso8601_to_datetime(authn_statement.authnInstant) -- 2.37.2