Skip to content

Commit 49a9cbf

Browse files
authored
providers/saml: update metadata parser for single logout and encryption certificate (#20031)
providers/saml: update metadata parser for single logout
1 parent 56361c2 commit 49a9cbf

File tree

1 file changed

+46
-1
lines changed

1 file changed

+46
-1
lines changed

authentik/providers/saml/processors/metadata_parser.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class ServiceProviderMetadata:
3737
name_id_policy: SAMLNameIDPolicy
3838

3939
signing_keypair: CertificateKeyPair | None = None
40+
encryption_keypair: CertificateKeyPair | None = None
41+
42+
# Single Logout Service (optional)
43+
sls_binding: str | None = None
44+
sls_location: str | None = None
4045

4146
def to_provider(
4247
self, name: str, authorization_flow: Flow, invalidation_flow: Flow
@@ -50,10 +55,19 @@ def to_provider(
5055
provider.sp_binding = self.acs_binding
5156
provider.acs_url = self.acs_location
5257
provider.default_name_id_policy = self.name_id_policy
58+
# Single Logout Service
59+
if self.sls_location:
60+
provider.sls_url = self.sls_location
61+
if self.sls_binding:
62+
provider.sls_binding = self.sls_binding
5363
if self.signing_keypair and self.auth_n_request_signed:
5464
self.signing_keypair.name = f"Provider {name} - SAML Signing Certificate"
5565
self.signing_keypair.save()
5666
provider.verification_kp = self.signing_keypair
67+
if self.encryption_keypair:
68+
self.encryption_keypair.name = f"Provider {name} - SAML Encryption Certificate"
69+
self.encryption_keypair.save()
70+
provider.encryption_kp = self.encryption_keypair
5771
if self.assertion_signed:
5872
provider.signing_kp = CertificateKeyPair.objects.exclude(key_data__iexact="").first()
5973
# Set all auto-generated Property-mappings as defaults
@@ -67,7 +81,7 @@ class ServiceProviderMetadataParser:
6781
"""Service-Provider Metadata Parser"""
6882

6983
def get_signing_cert(self, root: etree.Element) -> CertificateKeyPair | None:
70-
"""Extract X509Certificate from metadata, when given."""
84+
"""Extract signing X509Certificate from metadata, when given."""
7185
signing_certs = root.xpath(
7286
'//md:SPSSODescriptor/md:KeyDescriptor[@use="signing"]//ds:X509Certificate/text()',
7387
namespaces=NS_MAP,
@@ -81,6 +95,21 @@ def get_signing_cert(self, root: etree.Element) -> CertificateKeyPair | None:
8195
certificate_data=raw_cert,
8296
)
8397

98+
def get_encryption_cert(self, root: etree.Element) -> CertificateKeyPair | None:
99+
"""Extract encryption X509Certificate from metadata, when given."""
100+
encryption_certs = root.xpath(
101+
'//md:SPSSODescriptor/md:KeyDescriptor[@use="encryption"]//ds:X509Certificate/text()',
102+
namespaces=NS_MAP,
103+
)
104+
if len(encryption_certs) < 1:
105+
return None
106+
raw_cert = format_cert(encryption_certs[0])
107+
# sanity check, make sure the certificate is valid.
108+
load_pem_x509_certificate(raw_cert.encode("utf-8"), default_backend())
109+
return CertificateKeyPair(
110+
certificate_data=raw_cert,
111+
)
112+
84113
def check_signature(self, root: etree.Element, keypair: CertificateKeyPair):
85114
"""If Metadata is signed, check validity of signature"""
86115
xmlsec.tree.add_ids(root, ["ID"])
@@ -137,18 +166,34 @@ def parse(self, raw_xml: str) -> ServiceProviderMetadata:
137166
signing_keypair = self.get_signing_cert(root)
138167
if signing_keypair:
139168
self.check_signature(root, signing_keypair)
169+
encryption_keypair = self.get_encryption_cert(root)
140170

141171
name_id_format = descriptor.findall(f"{{{NS_SAML_METADATA}}}NameIDFormat")
142172
name_id_policy = SAMLNameIDPolicy.UNSPECIFIED
143173
if len(name_id_format) > 0:
144174
name_id_policy = SAMLNameIDPolicy(name_id_format[0].text)
145175

176+
# Parse SingleLogoutService (optional)
177+
sls_binding = None
178+
sls_location = None
179+
sls_services = descriptor.findall(f"{{{NS_SAML_METADATA}}}SingleLogoutService")
180+
if len(sls_services) > 0:
181+
sls_service = sls_services[0]
182+
sls_binding = {
183+
SAML_BINDING_REDIRECT: SAMLBindings.REDIRECT,
184+
SAML_BINDING_POST: SAMLBindings.POST,
185+
}.get(sls_service.attrib.get("Binding"))
186+
sls_location = sls_service.attrib.get("Location")
187+
146188
return ServiceProviderMetadata(
147189
entity_id=entity_id,
148190
acs_binding=acs_binding,
149191
acs_location=acs_location,
150192
auth_n_request_signed=auth_n_request_signed,
151193
assertion_signed=assertion_signed,
152194
signing_keypair=signing_keypair,
195+
encryption_keypair=encryption_keypair,
153196
name_id_policy=name_id_policy,
197+
sls_binding=sls_binding,
198+
sls_location=sls_location,
154199
)

0 commit comments

Comments
 (0)