sources/saml: Add NameID Policy field, sent with AuthnRequest
This commit is contained in:
parent
d831599608
commit
0e3e73989d
|
@ -15,6 +15,7 @@ class SAMLSourceSerializer(ModelSerializer):
|
|||
fields = SOURCE_FORM_FIELDS + [
|
||||
"issuer",
|
||||
"sso_url",
|
||||
"name_id_policy",
|
||||
"binding_type",
|
||||
"slo_url",
|
||||
"temporary_user_delete_after",
|
||||
|
|
|
@ -23,6 +23,7 @@ class SAMLSourceForm(forms.ModelForm):
|
|||
fields = SOURCE_FORM_FIELDS + [
|
||||
"issuer",
|
||||
"sso_url",
|
||||
"name_id_policy",
|
||||
"binding_type",
|
||||
"slo_url",
|
||||
"temporary_user_delete_after",
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# Generated by Django 3.0.8 on 2020-07-08 13:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_sources_saml", "0004_auto_20200708_1207"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="samlsource",
|
||||
name="name_id_policy",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "Email"),
|
||||
(
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||
"Persistent",
|
||||
),
|
||||
(
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectName",
|
||||
"X509",
|
||||
),
|
||||
(
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName",
|
||||
"Windows",
|
||||
),
|
||||
(
|
||||
"urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
||||
"Transient",
|
||||
),
|
||||
],
|
||||
default="urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
||||
help_text="NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent.",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -7,6 +7,13 @@ from passbook.core.models import Source
|
|||
from passbook.core.types import UILoginButton
|
||||
from passbook.crypto.models import CertificateKeyPair
|
||||
from passbook.providers.saml.utils.time import timedelta_string_validator
|
||||
from passbook.sources.saml.processors.constants import (
|
||||
SAML_NAME_ID_FORMAT_EMAIL,
|
||||
SAML_NAME_ID_FORMAT_PRESISTENT,
|
||||
SAML_NAME_ID_FORMAT_TRANSIENT,
|
||||
SAML_NAME_ID_FORMAT_WINDOWS,
|
||||
SAML_NAME_ID_FORMAT_X509,
|
||||
)
|
||||
|
||||
|
||||
class SAMLBindingTypes(models.TextChoices):
|
||||
|
@ -17,6 +24,16 @@ class SAMLBindingTypes(models.TextChoices):
|
|||
POST_AUTO = "POST_AUTO", _("POST Binding with auto-confirmation")
|
||||
|
||||
|
||||
class SAMLNameIDPolicy(models.TextChoices):
|
||||
"""SAML NameID Policies"""
|
||||
|
||||
EMAIL = SAML_NAME_ID_FORMAT_EMAIL
|
||||
PERSISTENT = SAML_NAME_ID_FORMAT_PRESISTENT
|
||||
X509 = SAML_NAME_ID_FORMAT_X509
|
||||
WINDOWS = SAML_NAME_ID_FORMAT_WINDOWS
|
||||
TRANSIENT = SAML_NAME_ID_FORMAT_TRANSIENT
|
||||
|
||||
|
||||
class SAMLSource(Source):
|
||||
"""Authenticate using an external SAML Identity Provider."""
|
||||
|
||||
|
@ -31,6 +48,13 @@ class SAMLSource(Source):
|
|||
verbose_name=_("SSO URL"),
|
||||
help_text=_("URL that the initial Login request is sent to."),
|
||||
)
|
||||
name_id_policy = models.TextField(
|
||||
choices=SAMLNameIDPolicy.choices,
|
||||
default=SAMLNameIDPolicy.TRANSIENT,
|
||||
help_text=_(
|
||||
"NameID Policy sent to the IdP. Can be unset, in which case no Policy is sent."
|
||||
),
|
||||
)
|
||||
binding_type = models.CharField(
|
||||
max_length=100,
|
||||
choices=SAMLBindingTypes.choices,
|
||||
|
|
|
@ -127,6 +127,13 @@ class Processor:
|
|||
def prepare_flow(self, request: HttpRequest) -> HttpResponse:
|
||||
"""Prepare flow plan depending on whether or not the user exists"""
|
||||
name_id = self._get_name_id()
|
||||
# Sanity check, show a warning if NameIDPolicy doesn't match what we go
|
||||
if self._source.name_id_policy != name_id.attrib["Format"]:
|
||||
LOGGER.warning(
|
||||
"NameID from IdP doesn't match our policy",
|
||||
expected=self._source.name_id_policy,
|
||||
got=name_id.attrib["Format"],
|
||||
)
|
||||
# transient NameIDs are handeled seperately as they don't have to go through flows.
|
||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_TRANSIENT:
|
||||
return self._handle_name_id_transient(request)
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">{{ ISSUER }}</saml:Issuer>
|
||||
{{ AUTHN_REQUEST_SIGNATURE }}
|
||||
<samlp:NameIDPolicy Format="{{ NAME_ID_POLICY }}"></samlp:NameIDPolicy>
|
||||
</samlp:AuthnRequest>
|
||||
|
|
|
@ -41,6 +41,7 @@ class InitiateView(View):
|
|||
"AUTHN_REQUEST_ID": get_random_id(),
|
||||
"ISSUE_INSTANT": get_time_string(),
|
||||
"ISSUER": get_issuer(request, source),
|
||||
"NAME_ID_POLICY": source.name_id_policy,
|
||||
}
|
||||
authn_req = get_authnrequest_xml(parameters, signed=False)
|
||||
# If the source is configured for Redirect bindings, we can just redirect there
|
||||
|
|
11
swagger.yaml
11
swagger.yaml
|
@ -6505,6 +6505,17 @@ definitions:
|
|||
format: uri
|
||||
maxLength: 200
|
||||
minLength: 1
|
||||
name_id_policy:
|
||||
title: Name id policy
|
||||
description: NameID Policy sent to the IdP. Can be unset, in which case no
|
||||
Policy is sent.
|
||||
type: string
|
||||
enum:
|
||||
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:X509SubjectName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:WindowsDomainQualifiedName
|
||||
- urn:oasis:names:tc:SAML:2.0:nameid-format:transient
|
||||
binding_type:
|
||||
title: Binding type
|
||||
type: string
|
||||
|
|
Reference in New Issue