diff --git a/passbook/providers/saml/api.py b/passbook/providers/saml/api.py index 38dc57545..8378391f0 100644 --- a/passbook/providers/saml/api.py +++ b/passbook/providers/saml/api.py @@ -14,12 +14,16 @@ class SAMLProviderSerializer(ModelSerializer): fields = [ "pk", "name", - "property_mappings", + "processor_path", "acs_url", "audience", - "processor_path", "issuer", - "assertion_valid_for", + "assertion_valid_not_before", + "assertion_valid_not_on_or_after", + "session_valid_not_on_or_after", + "property_mappings", + "digest_algorithm", + "signature_algorithm", "signing", "signing_cert", "signing_key", @@ -39,7 +43,7 @@ class SAMLPropertyMappingSerializer(ModelSerializer): class Meta: model = SAMLPropertyMapping - fields = ["pk", "name", "saml_name", "friendly_name", "values"] + fields = ["pk", "name", "saml_name", "friendly_name", "template"] class SAMLPropertyMappingViewSet(ModelViewSet): diff --git a/passbook/providers/saml/forms.py b/passbook/providers/saml/forms.py index ed7f79d6e..7c305187a 100644 --- a/passbook/providers/saml/forms.py +++ b/passbook/providers/saml/forms.py @@ -4,7 +4,6 @@ from django import forms from django.contrib.admin.widgets import FilteredSelectMultiple from django.utils.translation import gettext as _ -from passbook.lib.fields import DynamicArrayField from passbook.providers.saml.models import ( SAMLPropertyMapping, SAMLProvider, @@ -63,10 +62,9 @@ class SAMLPropertyMappingForm(forms.ModelForm): class Meta: model = SAMLPropertyMapping - fields = ["name", "saml_name", "friendly_name", "values"] + fields = ["name", "saml_name", "friendly_name", "template"] widgets = { "name": forms.TextInput(), "saml_name": forms.TextInput(), "friendly_name": forms.TextInput(), } - field_classes = {"values": DynamicArrayField} diff --git a/passbook/providers/saml/migrations/0003_auto_20200216_1109.py b/passbook/providers/saml/migrations/0003_auto_20200216_1109.py index a1076185b..a1cd56c65 100644 --- a/passbook/providers/saml/migrations/0003_auto_20200216_1109.py +++ b/passbook/providers/saml/migrations/0003_auto_20200216_1109.py @@ -4,46 +4,6 @@ import django.contrib.postgres.fields from django.db import migrations, models -def create_default_property_mappings(apps, schema_editor): - """Create default SAML Property Mappings""" - SAMLPropertyMapping = apps.get_model( - "passbook_providers_saml", "SAMLPropertyMapping" - ) - db_alias = schema_editor.connection.alias - defaults = [ - { - "FriendlyName": "eduPersonPrincipalName", - "Name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", - "Value": "{user.email}", - }, - {"FriendlyName": "cn", "Name": "urn:oid:2.5.4.3", "Value": "{user.name}",}, - { - "FriendlyName": "mail", - "Name": "urn:oid:0.9.2342.19200300.100.1.3", - "Value": "{user.email}", - }, - { - "FriendlyName": "displayName", - "Name": "urn:oid:2.16.840.1.113730.3.1.241", - "Value": "{user.username}", - }, - { - "FriendlyName": "uid", - "Name": "urn:oid:0.9.2342.19200300.100.1.1", - "Value": "{user.pk}", - }, - ] - for default in defaults: - SAMLPropertyMapping.objects.using(db_alias).get_or_create( - saml_name=default["Name"], - friendly_name=default["FriendlyName"], - values=[default["Value"]], - defaults={ - "name": f"Autogenerated SAML Mapping: {default['FriendlyName']} -> {default['Value']}" - }, - ) - - class Migration(migrations.Migration): dependencies = [ @@ -75,5 +35,4 @@ class Migration(migrations.Migration): name="signing_cert", field=models.TextField(verbose_name="Singing Certificate"), ), - migrations.RunPython(create_default_property_mappings), ] diff --git a/passbook/providers/saml/migrations/0005_remove_samlpropertymapping_values.py b/passbook/providers/saml/migrations/0005_remove_samlpropertymapping_values.py new file mode 100644 index 000000000..1e41fe964 --- /dev/null +++ b/passbook/providers/saml/migrations/0005_remove_samlpropertymapping_values.py @@ -0,0 +1,75 @@ +# Generated by Django 3.0.3 on 2020-02-17 16:15 + +from django.db import migrations + + +def cleanup_old_autogenerated(apps, schema_editor): + SAMLPropertyMapping = apps.get_model( + "passbook_providers_saml", "SAMLPropertyMapping" + ) + db_alias = schema_editor.connection.alias + SAMLPropertyMapping.objects.using(db_alias).filter( + name__startswith="Autogenerated" + ).delete() + + +def create_default_property_mappings(apps, schema_editor): + """Create default SAML Property Mappings""" + SAMLPropertyMapping = apps.get_model( + "passbook_providers_saml", "SAMLPropertyMapping" + ) + db_alias = schema_editor.connection.alias + defaults = [ + { + "FriendlyName": "eduPersonPrincipalName", + "Name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", + "Template": "{{ user.email }}", + }, + { + "FriendlyName": "cn", + "Name": "urn:oid:2.5.4.3", + "Template": "{{ user.name }}", + }, + { + "FriendlyName": "mail", + "Name": "urn:oid:0.9.2342.19200300.100.1.3", + "Template": "{{ user.email }}", + }, + { + "FriendlyName": "displayName", + "Name": "urn:oid:2.16.840.1.113730.3.1.241", + "Template": "{{ user.username }}", + }, + { + "FriendlyName": "uid", + "Name": "urn:oid:0.9.2342.19200300.100.1.1", + "Template": "{{ user.pk }}", + }, + { + "FriendlyName": "member-of", + "Name": "member-of", + "Template": "[{% for group in user.groups.all() %}'{{ group.name }}',{% endfor %}]", + }, + ] + for default in defaults: + SAMLPropertyMapping.objects.using(db_alias).get_or_create( + saml_name=default["Name"], + friendly_name=default["FriendlyName"], + template=default["Template"], + defaults={ + "name": f"Autogenerated SAML Mapping: {default['FriendlyName']} -> {default['Template']}" + }, + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_providers_saml", "0004_auto_20200217_1526"), + ] + + operations = [ + migrations.RunPython(cleanup_old_autogenerated), + migrations.RemoveField(model_name="samlpropertymapping", name="values",), + migrations.RunPython(create_default_property_mappings), + ] diff --git a/passbook/providers/saml/models.py b/passbook/providers/saml/models.py index 0b0d66870..bee0dd4ac 100644 --- a/passbook/providers/saml/models.py +++ b/passbook/providers/saml/models.py @@ -1,5 +1,4 @@ """passbook saml_idp Models""" -from django.contrib.postgres.fields import ArrayField from django.db import models from django.shortcuts import reverse from django.utils.translation import ugettext_lazy as _ @@ -118,15 +117,6 @@ class SAMLPropertyMapping(PropertyMapping): saml_name = models.TextField(verbose_name="SAML Name") friendly_name = models.TextField(default=None, blank=True, null=True) - values = ArrayField( - models.TextField(), - help_text=_( - ( - "This string can contain string substitutions delimited by {}." - " The following Variables are available: user, request" - ) - ), - ) form = "passbook.providers.saml.forms.SAMLPropertyMappingForm" diff --git a/passbook/providers/saml/processors/base.py b/passbook/providers/saml/processors/base.py index 067195ae0..a90cb7803 100644 --- a/passbook/providers/saml/processors/base.py +++ b/passbook/providers/saml/processors/base.py @@ -98,17 +98,19 @@ class Processor: for mapping in self._remote.property_mappings.all().select_subclasses(): if isinstance(mapping, SAMLPropertyMapping): + value = mapping.render( + user=self._http_request.user, + request=self._http_request, + provider=self._remote, + ) mapping_payload = { "Name": mapping.saml_name, - "ValueArray": [], "FriendlyName": mapping.friendly_name, } - for value in mapping.values: - mapping_payload["ValueArray"].append( - value.format( - user=self._http_request.user, request=self._http_request - ) - ) + if isinstance(value, list): + mapping_payload["ValueArray"] = value + else: + mapping_payload["Value"] = value attributes.append(mapping_payload) self._assertion_params["ATTRIBUTES"] = attributes self._assertion_xml = get_assertion_xml(