Skip to content

Commit a24f585

Browse files
committed
Merge pull request #24 from onelogin/refactor-settings
Refactor settings
2 parents e815da3 + 6d35b8e commit a24f585

3 files changed

Lines changed: 87 additions & 125 deletions

File tree

src/onelogin/saml2/settings.py

Lines changed: 73 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -206,22 +206,12 @@ def __load_settings_from_dict(self, settings):
206206
if len(errors) == 0:
207207
self.__errors = []
208208
self.__sp = settings['sp']
209-
210-
if 'idp' in settings:
211-
self.__idp = settings['idp']
212-
213-
if 'strict' in settings:
214-
self.__strict = settings['strict']
215-
if 'debug' in settings:
216-
self.__debug = settings['debug']
217-
if 'security' in settings:
218-
self.__security = settings['security']
219-
else:
220-
self.__security = {}
221-
if 'contactPerson' in settings:
222-
self.__contacts = settings['contactPerson']
223-
if 'organization' in settings:
224-
self.__organization = settings['organization']
209+
self.__idp = settings.get('idp', {})
210+
self.__strict = settings.get('strict', False)
211+
self.__debug = settings.get('debug', False)
212+
self.__security = settings.get('security', {})
213+
self.__contacts = settings.get('contactPerson', {})
214+
self.__organization = settings.get('organization', {})
225215

226216
self.__add_default_values()
227217
return True
@@ -261,79 +251,55 @@ def __add_default_values(self):
261251
"""
262252
Add default values if the settings info is not complete
263253
"""
264-
if 'assertionConsumerService' not in self.__sp:
265-
self.__sp['assertionConsumerService'] = {}
266-
if 'binding' not in self.__sp['assertionConsumerService']:
267-
self.__sp['assertionConsumerService']['binding'] = OneLogin_Saml2_Constants.BINDING_HTTP_POST
254+
self.__sp.setdefault('assertionConsumerService', {})
255+
self.__sp['assertionConsumerService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_POST)
268256

269-
if 'singleLogoutService' not in self.__sp:
270-
self.__sp['singleLogoutService'] = {}
271-
if 'binding' not in self.__sp['singleLogoutService']:
272-
self.__sp['singleLogoutService']['binding'] = OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT
257+
self.__sp.setdefault('attributeConsumingService', {})
273258

274-
if 'singleLogoutService' not in self.__idp:
275-
self.__idp['singleLogoutService'] = {}
259+
self.__sp.setdefault('singleLogoutService', {})
260+
self.__sp['singleLogoutService'].setdefault('binding', OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT)
276261

277-
# Related to nameID
278-
if 'NameIDFormat' not in self.__sp:
279-
self.__sp['NameIDFormat'] = OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED
280-
if 'nameIdEncrypted' not in self.__security:
281-
self.__security['nameIdEncrypted'] = False
262+
self.__idp.setdefault('singleLogoutService', {})
282263

283-
# Sign provided
284-
if 'authnRequestsSigned' not in self.__security:
285-
self.__security['authnRequestsSigned'] = False
286-
if 'logoutRequestSigned' not in self.__security:
287-
self.__security['logoutRequestSigned'] = False
288-
if 'logoutResponseSigned' not in self.__security:
289-
self.__security['logoutResponseSigned'] = False
290-
if 'signMetadata' not in self.__security:
291-
self.__security['signMetadata'] = False
264+
# Related to nameID
265+
self.__sp.setdefault('NameIDFormat', OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED)
266+
self.__security.setdefault('nameIdEncrypted', False)
292267

293268
# Metadata format
294-
if 'metadataValidUntil' not in self.__security.keys():
295-
self.__security['metadataValidUntil'] = None # None means use default
296-
if 'metadataCacheDuration' not in self.__security.keys():
297-
self.__security['metadataCacheDuration'] = None # None means use default
269+
self.__security.setdefault('metadataValidUntil', None) # None means use default
270+
self.__security.setdefault('metadataCacheDuration', None) # None means use default
271+
272+
# Sign provided
273+
self.__security.setdefault('authnRequestsSigned', False)
274+
self.__security.setdefault('logoutRequestSigned', False)
275+
self.__security.setdefault('logoutResponseSigned', False)
276+
self.__security.setdefault('signMetadata', False)
298277

299278
# Sign expected
300-
if 'wantMessagesSigned' not in self.__security:
301-
self.__security['wantMessagesSigned'] = False
302-
if 'wantAssertionsSigned' not in self.__security:
303-
self.__security['wantAssertionsSigned'] = False
279+
self.__security.setdefault('wantMessagesSigned', False)
280+
self.__security.setdefault('wantAssertionsSigned', False)
304281

305282
# NameID element expected
306-
if 'wantNameId' not in self.__security.keys():
307-
self.__security['wantNameId'] = True
283+
self.__security.setdefault('wantNameId', True)
308284

309285
# Encrypt expected
310-
if 'wantAssertionsEncrypted' not in self.__security:
311-
self.__security['wantAssertionsEncrypted'] = False
312-
if 'wantNameIdEncrypted' not in self.__security:
313-
self.__security['wantNameIdEncrypted'] = False
286+
self.__security.setdefault('wantAssertionsEncrypted', False)
287+
self.__security.setdefault('wantNameIdEncrypted', False)
314288

315289
# Signature Algorithm
316-
if 'signatureAlgorithm' not in self.__security.keys():
317-
self.__security['signatureAlgorithm'] = OneLogin_Saml2_Constants.RSA_SHA1
290+
self.__security.setdefault('signatureAlgorithm', OneLogin_Saml2_Constants.RSA_SHA1)
318291

319292
# AttributeStatement required by default
320-
if 'wantAttributeStatement' not in self.__security.keys():
321-
self.__security['wantAttributeStatement'] = True
293+
self.__security.setdefault('wantAttributeStatement', True)
322294

323-
if 'x509cert' not in self.__idp:
324-
self.__idp['x509cert'] = ''
325-
if 'certFingerprint' not in self.__idp:
326-
self.__idp['certFingerprint'] = ''
327-
if 'certFingerprintAlgorithm' not in self.__idp:
328-
self.__idp['certFingerprintAlgorithm'] = 'sha1'
295+
self.__idp.setdefault('x509cert', '')
296+
self.__idp.setdefault('certFingerprint', '')
297+
self.__idp.setdefault('certFingerprintAlgorithm', 'sha1')
329298

330-
if 'x509cert' not in self.__sp:
331-
self.__sp['x509cert'] = ''
332-
if 'privateKey' not in self.__sp:
333-
self.__sp['privateKey'] = ''
299+
self.__sp.setdefault('x509cert', '')
300+
self.__sp.setdefault('privateKey', '')
334301

335-
if 'requestedAuthnContext' not in self.__security:
336-
self.__security['requestedAuthnContext'] = True
302+
self.__security.setdefault('requestedAuthnContext', True)
337303

338304
def check_settings(self, settings):
339305
"""
@@ -372,37 +338,31 @@ def check_idp_settings(self, settings):
372338
if not isinstance(settings, dict) or len(settings) == 0:
373339
errors.append('invalid_syntax')
374340
else:
375-
if 'idp' not in settings or len(settings['idp']) == 0:
341+
if not settings.get('idp'):
376342
errors.append('idp_not_found')
377343
else:
378344
idp = settings['idp']
379-
if 'entityId' not in idp or len(idp['entityId']) == 0:
345+
if not idp.get('entityId'):
380346
errors.append('idp_entityId_not_found')
381347

382-
if 'singleSignOnService' not in idp or \
383-
'url' not in idp['singleSignOnService'] or \
384-
len(idp['singleSignOnService']['url']) == 0:
348+
if not idp.get('singleSignOnService', {}).get('url'):
385349
errors.append('idp_sso_not_found')
386350
elif not validate_url(idp['singleSignOnService']['url']):
387351
errors.append('idp_sso_url_invalid')
388352

389-
if 'singleLogoutService' in idp and \
390-
'url' in idp['singleLogoutService'] and \
391-
len(idp['singleLogoutService']['url']) > 0 and \
392-
not validate_url(idp['singleLogoutService']['url']):
353+
slo_url = idp.get('singleLogoutService', {}).get('url')
354+
if slo_url and not validate_url(slo_url):
393355
errors.append('idp_slo_url_invalid')
394356

395357
if 'security' in settings:
396358
security = settings['security']
397359

398-
exists_x509 = ('x509cert' in idp and
399-
len(idp['x509cert']) > 0)
400-
exists_fingerprint = ('certFingerprint' in idp and
401-
len(idp['certFingerprint']) > 0)
360+
exists_x509 = bool(idp.get('x509cert'))
361+
exists_fingerprint = bool(idp.get('certFingerprint'))
402362

403-
want_assert_sign = 'wantAssertionsSigned' in security.keys() and security['wantAssertionsSigned']
404-
want_mes_signed = 'wantMessagesSigned' in security.keys() and security['wantMessagesSigned']
405-
nameid_enc = 'nameIdEncrypted' in security.keys() and security['nameIdEncrypted']
363+
want_assert_sign = bool(security.get('wantAssertionsSigned'))
364+
want_mes_signed = bool(security.get('wantMessagesSigned'))
365+
nameid_enc = bool(security.get('nameIdEncrypted'))
406366

407367
if (want_assert_sign or want_mes_signed) and \
408368
not(exists_x509 or exists_fingerprint):
@@ -422,32 +382,28 @@ def check_sp_settings(self, settings):
422382
assert isinstance(settings, dict)
423383

424384
errors = []
425-
if not isinstance(settings, dict) or len(settings) == 0:
385+
if not isinstance(settings, dict) or not settings:
426386
errors.append('invalid_syntax')
427387
else:
428-
if 'sp' not in settings or len(settings['sp']) == 0:
388+
if not settings.get('sp'):
429389
errors.append('sp_not_found')
430390
else:
431391
# check_sp_certs uses self.__sp so I add it
432392
old_sp = self.__sp
433393
self.__sp = settings['sp']
434394

435395
sp = settings['sp']
436-
security = {}
437-
if 'security' in settings:
438-
security = settings['security']
396+
security = settings.get('security', {})
439397

440-
if 'entityId' not in sp or len(sp['entityId']) == 0:
398+
if not sp.get('entityId'):
441399
errors.append('sp_entityId_not_found')
442400

443-
if 'assertionConsumerService' not in sp or \
444-
'url' not in sp['assertionConsumerService'] or \
445-
len(sp['assertionConsumerService']['url']) == 0:
401+
if not sp.get('assertionConsumerService', {}).get('url'):
446402
errors.append('sp_acs_not_found')
447403
elif not validate_url(sp['assertionConsumerService']['url']):
448404
errors.append('sp_acs_url_invalid')
449405

450-
if 'attributeConsumingService' in sp and len(sp['attributeConsumingService']):
406+
if sp.get('attributeConsumingService'):
451407
attributeConsumingService = sp['attributeConsumingService']
452408
if 'serviceName' not in attributeConsumingService:
453409
errors.append('sp_attributeConsumingService_serviceName_not_found')
@@ -472,22 +428,20 @@ def check_sp_settings(self, settings):
472428
if "serviceDescription" in attributeConsumingService and not isinstance(attributeConsumingService['serviceDescription'], basestring):
473429
errors.append('sp_attributeConsumingService_serviceDescription_type_invalid')
474430

475-
if 'singleLogoutService' in sp and \
476-
'url' in sp['singleLogoutService'] and \
477-
len(sp['singleLogoutService']['url']) > 0 and \
478-
not validate_url(sp['singleLogoutService']['url']):
431+
slo_url = sp.get('singleLogoutService', {}).get('url')
432+
if slo_url and not validate_url(slo_url):
479433
errors.append('sp_sls_url_invalid')
480434

481435
if 'signMetadata' in security and isinstance(security['signMetadata'], dict):
482436
if 'keyFileName' not in security['signMetadata'] or \
483437
'certFileName' not in security['signMetadata']:
484438
errors.append('sp_signMetadata_invalid')
485439

486-
authn_sign = 'authnRequestsSigned' in security and security['authnRequestsSigned']
487-
logout_req_sign = 'logoutRequestSigned' in security and security['logoutRequestSigned']
488-
logout_res_sign = 'logoutResponseSigned' in security and security['logoutResponseSigned']
489-
want_assert_enc = 'wantAssertionsEncrypted' in security and security['wantAssertionsEncrypted']
490-
want_nameid_enc = 'wantNameIdEncrypted' in security and security['wantNameIdEncrypted']
440+
authn_sign = bool(security.get('authnRequestsSigned'))
441+
logout_req_sign = bool(security.get('logoutRequestSigned'))
442+
logout_res_sign = bool(security.get('logoutResponseSigned'))
443+
want_assert_enc = bool(security.get('wantAssertionsEncrypted'))
444+
want_nameid_enc = bool(security.get('wantNameIdEncrypted'))
491445

492446
if not self.check_sp_certs():
493447
if authn_sign or logout_req_sign or logout_res_sign or \
@@ -526,7 +480,6 @@ def check_sp_settings(self, settings):
526480
def check_sp_certs(self):
527481
"""
528482
Checks if the x509 certs of the SP exists and are valid.
529-
530483
:returns: If the x509 certs of the SP exists and are valid
531484
:rtype: boolean
532485
"""
@@ -537,42 +490,40 @@ def check_sp_certs(self):
537490
def get_sp_key(self):
538491
"""
539492
Returns the x509 private key of the SP.
540-
541493
:returns: SP private key
542-
:rtype: string
494+
:rtype: string or None
543495
"""
544496
key = self.__sp.get('privateKey')
545-
if not key:
546-
key_file_name = self.__paths['cert'] + 'sp.key'
497+
key_file_name = self.__paths['cert'] + 'sp.key'
498+
499+
if not key and exists(key_file_name):
500+
with open(key_file_name) as f:
501+
key = f.read()
547502

548-
if exists(key_file_name):
549-
with open(key_file_name, 'r') as f_key:
550-
key = self.__sp['privateKey'] = f_key.read()
551503
return key or None
552504

553505
def get_sp_cert(self):
554506
"""
555507
Returns the x509 public cert of the SP.
556-
557508
:returns: SP public cert
558-
:rtype: string
509+
:rtype: string or None
559510
"""
560511
cert = self.__sp.get('x509cert')
561-
if not cert:
562-
cert_file_name = self.__paths['cert'] + 'sp.crt'
563-
if exists(cert_file_name):
564-
with open(cert_file_name, 'r') as f_cert:
565-
cert = self.__sp['x509cert'] = f_cert.read()
512+
cert_file_name = self.__paths['cert'] + 'sp.crt'
513+
514+
if not cert and exists(cert_file_name):
515+
with open(cert_file_name) as f:
516+
cert = f.read()
517+
566518
return cert or None
567519

568520
def get_idp_cert(self):
569521
"""
570522
Returns the x509 public cert of the IdP.
571-
572523
:returns: IdP public cert
573524
:rtype: string
574525
"""
575-
return self.__idp['x509cert'] or None
526+
return self.__idp.get('x509cert')
576527

577528
def get_idp_data(self):
578529
"""

tests/src/OneLogin/saml2_tests/auth_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ def testIsInValidLogoutResponseSign(self):
986986

987987
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2)
988988
auth.process_slo()
989-
self.assertIn('In order to validate the sign on the SAMLResponse, the x509cert of the IdP is required', auth.get_errors())
989+
self.assertIn('Signature validation failed. Logout Response rejected', auth.get_errors())
990990

991991
def testIsValidLogoutRequestSign(self):
992992
"""
@@ -1074,4 +1074,4 @@ def testIsValidLogoutRequestSign(self):
10741074
settings_2 = OneLogin_Saml2_Settings(settings_info)
10751075
auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2)
10761076
auth.process_slo()
1077-
self.assertIn('In order to validate the sign on the SAMLRequest, the x509cert of the IdP is required', auth.get_errors())
1077+
self.assertIn('Signature validation failed. Logout Request rejected', auth.get_errors())

tests/src/OneLogin/saml2_tests/settings_test.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,12 +404,23 @@ def testGetSPMetadataSigned(self):
404404
settings_info = self.loadSettingsJSON()
405405
if 'security' not in settings_info:
406406
settings_info['security'] = {}
407+
408+
settings_info['security']['signMetadata'] = {}
409+
410+
try:
411+
OneLogin_Saml2_Settings(settings_info)
412+
self.assertTrue(False)
413+
except Exception as e:
414+
self.assertIn('sp_signMetadata_invalid', str(e))
415+
416+
# Default cert/key
407417
settings_info['security']['signMetadata'] = True
408418
self.generateAndCheckMetadata(settings_info)
409419

410420
# Now try again with SP keys set directly in settings and not from files:
411421
del settings_info['custom_base_path']
412-
self.generateAndCheckMetadata(settings_info)
422+
with self.assertRaises(OneLogin_Saml2_Error):
423+
OneLogin_Saml2_Settings(settings_info).get_sp_metadata()
413424

414425
# Now the keys should not be found, so metadata generation won't work:
415426
del settings_info['sp']['x509cert']

0 commit comments

Comments
 (0)