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.
This commit is contained in:
Benjamin Dauvergne 2022-10-03 12:19:38 +02:00
parent e98308d45c
commit 600c8cfbc0
4 changed files with 55 additions and 4 deletions

View File

@ -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

View File

@ -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'),
),
]

View File

@ -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')

View File

@ -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)