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