1818
1919from onelogin .saml2 .constants import OneLogin_Saml2_Constants
2020from onelogin .saml2 .xml_utils import OneLogin_Saml2_XML
21- from onelogin .saml2 .utils import OneLogin_Saml2_Utils
2221
2322
2423class OneLogin_Saml2_IdPMetadataParser (object ):
@@ -89,8 +88,7 @@ def parse(
8988 idp_metadata ,
9089 required_sso_binding = OneLogin_Saml2_Constants .BINDING_HTTP_REDIRECT ,
9190 required_slo_binding = OneLogin_Saml2_Constants .BINDING_HTTP_REDIRECT ,
92- entity_id = None ,
93- index = 0 ):
91+ entity_id = None ):
9492 """
9593 Parses the Identity Provider metadata and return a dict with extracted data.
9694
@@ -121,99 +119,97 @@ def parse(
121119 that contains multiple EntityDescriptor.
122120 :type entity_id: string
123121
124- :param index: If the metadata contains more than 1 certificate, use index to get the right certificate.
125- :type index: number
126-
127122 :returns: settings dict with extracted data
128123 :rtype: dict
129124 """
130125 data = {}
131126
132127 dom = OneLogin_Saml2_XML .to_etree (idp_metadata )
133- idp_entity_id = want_authn_requests_signed = idp_name_id_format = idp_sso_url = idp_slo_url = idp_x509_cert = None
128+ idp_entity_id = want_authn_requests_signed = idp_name_id_format = idp_sso_url = idp_slo_url = certs = None
134129
135130 entity_desc_path = '//md:EntityDescriptor'
136131 if entity_id :
137132 entity_desc_path += "[@entityID='%s']" % entity_id
138133 entity_descriptor_nodes = OneLogin_Saml2_XML .query (dom , entity_desc_path )
139134
140135 if len (entity_descriptor_nodes ) > 0 :
141- for entity_descriptor_node in entity_descriptor_nodes :
142- idp_descriptor_nodes = OneLogin_Saml2_XML .query (entity_descriptor_node , './md:IDPSSODescriptor' )
143- if len (idp_descriptor_nodes ) > 0 :
144- idp_descriptor_node = idp_descriptor_nodes [0 ]
145-
146- idp_entity_id = entity_descriptor_node .get ('entityID' , None )
147-
148- want_authn_requests_signed = entity_descriptor_node .get ('WantAuthnRequestsSigned' , None )
149-
150- name_id_format_nodes = OneLogin_Saml2_XML .query (idp_descriptor_node , './md:NameIDFormat' )
151- if len (name_id_format_nodes ) > 0 :
152- idp_name_id_format = name_id_format_nodes [0 ].text
153-
154- sso_nodes = OneLogin_Saml2_XML .query (
155- idp_descriptor_node ,
156- "./md:SingleSignOnService[@Binding='%s']" % required_sso_binding
157- )
158-
159- if len (sso_nodes ) > 0 :
160- idp_sso_url = sso_nodes [0 ].get ('Location' , None )
161-
162- slo_nodes = OneLogin_Saml2_XML .query (
163- idp_descriptor_node ,
164- "./md:SingleLogoutService[@Binding='%s']" % required_slo_binding
165- )
166-
167- if len (slo_nodes ) > 0 :
168- idp_slo_url = slo_nodes [0 ].get ('Location' , None )
169-
170- # Attempt to extract the cert/public key to be used for
171- # verifying signatures (as opposed to extracing a key to be
172- # used for encryption), by specifying `use=signing` in the
173- # XPath expression. If that does not yield a cert, retry
174- # using a more relaxed XPath expression (the `use` attribute
175- # is optional according to the saml-metadata-2.0-os spec).
176- cert_nodes = OneLogin_Saml2_XML .query (
177- idp_descriptor_node ,
178- "./md:KeyDescriptor[@use='signing']/ds:KeyInfo/ds:X509Data/ds:X509Certificate"
179- )
180-
181- if not cert_nodes :
182- cert_nodes = OneLogin_Saml2_XML .query (
183- idp_descriptor_node ,
184- "./md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate"
185- )
186-
187- if len (cert_nodes ) > 0 :
188- idp_x509_cert = OneLogin_Saml2_Utils .format_cert (cert_nodes [index ].text , False )
189-
190- data ['idp' ] = {}
191-
192- if idp_entity_id is not None :
193- data ['idp' ]['entityId' ] = idp_entity_id
194-
195- if idp_sso_url is not None :
196- data ['idp' ]['singleSignOnService' ] = {}
197- data ['idp' ]['singleSignOnService' ]['url' ] = idp_sso_url
198- data ['idp' ]['singleSignOnService' ]['binding' ] = required_sso_binding
199-
200- if idp_slo_url is not None :
201- data ['idp' ]['singleLogoutService' ] = {}
202- data ['idp' ]['singleLogoutService' ]['url' ] = idp_slo_url
203- data ['idp' ]['singleLogoutService' ]['binding' ] = required_slo_binding
204-
205- if idp_x509_cert is not None :
206- data ['idp' ]['x509cert' ] = idp_x509_cert
207-
208- if want_authn_requests_signed is not None :
209- data ['security' ] = {}
210- data ['security' ]['authnRequestsSigned' ] = want_authn_requests_signed
211-
212- if idp_name_id_format :
213- data ['sp' ] = {}
214- data ['sp' ]['NameIDFormat' ] = idp_name_id_format
215-
216- break
136+ entity_descriptor_node = entity_descriptor_nodes [0 ]
137+ idp_descriptor_nodes = OneLogin_Saml2_XML .query (entity_descriptor_node , './md:IDPSSODescriptor' )
138+ if len (idp_descriptor_nodes ) > 0 :
139+ idp_descriptor_node = idp_descriptor_nodes [0 ]
140+
141+ idp_entity_id = entity_descriptor_node .get ('entityID' , None )
142+
143+ want_authn_requests_signed = entity_descriptor_node .get ('WantAuthnRequestsSigned' , None )
144+
145+ name_id_format_nodes = OneLogin_Saml2_XML .query (idp_descriptor_node , './md:NameIDFormat' )
146+ if len (name_id_format_nodes ) > 0 :
147+ idp_name_id_format = name_id_format_nodes [0 ].text
148+
149+ sso_nodes = OneLogin_Saml2_XML .query (
150+ idp_descriptor_node ,
151+ "./md:SingleSignOnService[@Binding='%s']" % required_sso_binding
152+ )
153+
154+ if len (sso_nodes ) > 0 :
155+ idp_sso_url = sso_nodes [0 ].get ('Location' , None )
156+
157+ slo_nodes = OneLogin_Saml2_XML .query (
158+ idp_descriptor_node ,
159+ "./md:SingleLogoutService[@Binding='%s']" % required_slo_binding
160+ )
161+
162+ if len (slo_nodes ) > 0 :
163+ idp_slo_url = slo_nodes [0 ].get ('Location' , None )
164+
165+ signing_nodes = OneLogin_Saml2_XML .query (idp_descriptor_node , "./md:KeyDescriptor[not(contains(@use, 'encryption'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate" )
166+ encryption_nodes = OneLogin_Saml2_XML .query (idp_descriptor_node , "./md:KeyDescriptor[not(contains(@use, 'signing'))]/ds:KeyInfo/ds:X509Data/ds:X509Certificate" )
167+
168+ if len (signing_nodes ) > 0 or len (encryption_nodes ) > 0 :
169+ certs = {}
170+ if len (signing_nodes ) > 0 :
171+ certs ['signing' ] = []
172+ for cert_node in signing_nodes :
173+ certs ['signing' ].append ('' .join (cert_node .text .split ()))
174+ if len (encryption_nodes ) > 0 :
175+ certs ['encryption' ] = []
176+ for cert_node in encryption_nodes :
177+ certs ['encryption' ].append ('' .join (cert_node .text .split ()))
178+
179+ data ['idp' ] = {}
180+
181+ if idp_entity_id is not None :
182+ data ['idp' ]['entityId' ] = idp_entity_id
183+
184+ if idp_sso_url is not None :
185+ data ['idp' ]['singleSignOnService' ] = {}
186+ data ['idp' ]['singleSignOnService' ]['url' ] = idp_sso_url
187+ data ['idp' ]['singleSignOnService' ]['binding' ] = required_sso_binding
188+
189+ if idp_slo_url is not None :
190+ data ['idp' ]['singleLogoutService' ] = {}
191+ data ['idp' ]['singleLogoutService' ]['url' ] = idp_slo_url
192+ data ['idp' ]['singleLogoutService' ]['binding' ] = required_slo_binding
193+
194+ if want_authn_requests_signed is not None :
195+ data ['security' ] = {}
196+ data ['security' ]['authnRequestsSigned' ] = want_authn_requests_signed
197+
198+ if idp_name_id_format :
199+ data ['sp' ] = {}
200+ data ['sp' ]['NameIDFormat' ] = idp_name_id_format
201+
202+ if certs is not None :
203+ if len (certs ) == 1 or \
204+ (('signing' in certs and len (certs ['signing' ]) == 1 ) and
205+ ('encryption' in certs and len (certs ['encryption' ]) == 1 and
206+ certs ['signing' ][0 ] == certs ['encryption' ][0 ])):
207+ if 'signing' in certs :
208+ data ['idp' ]['x509cert' ] = certs ['signing' ][0 ]
209+ else :
210+ data ['idp' ]['x509cert' ] = certs ['encryption' ][0 ]
211+ else :
212+ data ['idp' ]['x509certMulti' ] = certs
217213 return data
218214
219215 @staticmethod
@@ -234,6 +230,14 @@ def merge_settings(settings, new_metadata_settings):
234230 # Guarantee to not modify original data (`settings.copy()` would not
235231 # be sufficient, as it's just a shallow copy).
236232 result_settings = deepcopy (settings )
233+
234+ # previously I will take care of cert stuff
235+ if 'idp' in new_metadata_settings and 'idp' in result_settings :
236+ if new_metadata_settings ['idp' ].get ('x509cert' , None ) and result_settings ['idp' ].get ('x509certMulti' , None ):
237+ del result_settings ['idp' ]['x509certMulti' ]
238+ if new_metadata_settings ['idp' ].get ('x509certMulti' , None ) and result_settings ['idp' ].get ('x509cert' , None ):
239+ del result_settings ['idp' ]['x509cert' ]
240+
237241 # Merge `new_metadata_settings` into `result_settings`.
238242 dict_deep_merge (result_settings , new_metadata_settings )
239243 return result_settings
0 commit comments