diff --git a/powerdnsadmin/routes/admin.py b/powerdnsadmin/routes/admin.py index 86f068c..70fa563 100644 --- a/powerdnsadmin/routes/admin.py +++ b/powerdnsadmin/routes/admin.py @@ -1759,8 +1759,8 @@ def setting_authentication(): 'msg': 'Saved successfully. Please reload PDA to take effect.' } - global saml - saml = SAML() + # # Attempt to reinitialize SAML. If attempt fails, setting will be automatically disabled. + # SAML() else: return abort(400) diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index bc128d2..32534c8 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -917,6 +917,8 @@ def saml_login(): auth = saml.init_saml_auth(req) redirect_url = OneLogin_Saml2_Utils.get_self_url(req) + url_for( 'index.saml_authorized') + if auth is None: + return render_template('errors/SAML.html') return redirect(auth.login(return_to=redirect_url)) @@ -928,8 +930,15 @@ def saml_metadata(): req = saml.prepare_flask_request(request) auth = saml.init_saml_auth(req) + if auth is None: + return render_template('errors/SAML.html') settings = auth.get_settings() - metadata = settings.get_sp_metadata() + try: + metadata = settings.get_sp_metadata() + except: + current_app.logger.error( + "SAML: Error fetching SP Metadata") + return render_template('errors/SAML.html') errors = settings.validate_metadata(metadata) if len(errors) == 0: @@ -947,6 +956,8 @@ def saml_authorized(): abort(400) req = saml.prepare_flask_request(request) auth = saml.init_saml_auth(req) + if auth is None: + return render_template('errors/SAML.html') auth.process_response() current_app.logger.debug( auth.get_attributes() ) errors = auth.get_errors() @@ -1147,8 +1158,13 @@ def uplift_to_admin(user): @index_bp.route('/saml/sls') def saml_logout(): + if not Setting().get('saml_enabled'): + current_app.logger.error("SAML authentication is disabled.") + abort(400) req = saml.prepare_flask_request(request) auth = saml.init_saml_auth(req) + if auth is None: + return render_template('errors/SAML.html') url = auth.process_slo() errors = auth.get_errors() if len(errors) == 0: diff --git a/powerdnsadmin/services/saml.py b/powerdnsadmin/services/saml.py index 3c8e658..22d8f1d 100644 --- a/powerdnsadmin/services/saml.py +++ b/powerdnsadmin/services/saml.py @@ -25,7 +25,8 @@ class SAML(object): self.idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( Setting().get('saml_metadata_url'), entity_id=Setting().get('saml_idp_entity_id'), - required_sso_binding=Setting().get('saml_idp_sso_binding')) + required_sso_binding=Setting().get('saml_idp_sso_binding'), + required_slo_binding=Setting().get('saml_idp_slo_binding')) except: self.idp_data = None else: @@ -36,23 +37,18 @@ class SAML(object): except: self.idp_data = None if self.idp_data is None: - current_app.logger.info( + current_app.logger.error( 'SAML: IDP Metadata initial load failed') Setting().set('saml_enabled', False) - print("SAML EN1 ", Setting().get('saml_enabled')) - # exit(-1) + def get_idp_data(self): lifetime = timedelta( - minutes=int(Setting().get('saml_metadata_cache_lifetime'))) # should be seconds instead of minutes? - # Since SAML is now user-configurable, idp_data may change before the lifetime has ended, - # so metadata should not be cached at all, or outdated settings may be used. - try: + minutes=int(Setting().get('saml_metadata_cache_lifetime'))) + if not hasattr(self,'idp_timestamp'): self.retrieve_idp_data() - except: - return None - if self.idp_timestamp + lifetime < datetime.now(): + elif self.idp_timestamp + lifetime < datetime.now(): background_thread = Thread(target=self.retrieve_idp_data()) background_thread.start() @@ -165,9 +161,10 @@ class SAML(object): settings['sp']['singleLogoutService'][ 'binding'] = Setting().get('saml_idp_slo_binding') settings['sp']['singleLogoutService']['url'] = own_url + '/saml/sls' - settings['idp'] = metadata['idp'] + if metadata is not None and 'idp' in metadata: + settings['idp'] = metadata['idp'] settings['strict'] = True - settings['debug'] = Setting().get('saml_debug') + settings['debug'] = bool(Setting().get('saml_debug')) settings['security'] = {} settings['security'][ 'digestAlgorithm'] = Setting().get('saml_digest_algorithm') @@ -176,17 +173,17 @@ class SAML(object): settings['security']['requestedAuthnContext'] = True settings['security'][ 'signatureAlgorithm'] = Setting().get('saml_signature_algorithm') - settings['security']['wantAssertionsEncrypted'] = Setting().get('saml_want_assertions_encrypted') + settings['security']['wantAssertionsEncrypted'] = bool(Setting().get('saml_want_assertions_encrypted')) settings['security']['wantAttributeStatement'] = True settings['security']['wantNameId'] = True - settings['security']['authnRequestsSigned'] = Setting().get('saml_sign_authn_request') - settings['security']['logoutRequestSigned'] = Setting().get('saml_sign_logout_request_response') - settings['security']['logoutResponseSigned'] = Setting().get('saml_sign_logout_request_response') - settings['security']['nameIdEncrypted'] = Setting().get('saml_nameid_encrypted') - settings['security']['signMetadata'] = Setting().get('saml_sign_metadata') - settings['security']['wantAssertionsSigned'] = Setting().get('saml_want_assertions_signed') - settings['security']['wantMessagesSigned'] = Setting().get('saml_want_message_signed') - settings['security']['wantNameIdEncrypted'] = Setting().get('saml_want_nameid_encrypted') + settings['security']['authnRequestsSigned'] = bool(Setting().get('saml_sign_authn_request')) + settings['security']['logoutRequestSigned'] = bool(Setting().get('saml_sign_logout_request_response')) + settings['security']['logoutResponseSigned'] = bool(Setting().get('saml_sign_logout_request_response')) + settings['security']['nameIdEncrypted'] = bool(Setting().get('saml_nameid_encrypted')) + settings['security']['signMetadata'] = bool(Setting().get('saml_sign_metadata')) + settings['security']['wantAssertionsSigned'] = bool(Setting().get('saml_want_assertions_signed')) + settings['security']['wantMessagesSigned'] = bool(Setting().get('saml_want_message_signed')) + settings['security']['wantNameIdEncrypted'] = bool(Setting().get('saml_want_nameid_encrypted')) settings['contactPerson'] = {} settings['contactPerson']['support'] = {} settings['contactPerson']['support']['emailAddress'] = Setting().get('saml_sp_contact_mail') @@ -199,5 +196,10 @@ class SAML(object): settings['organization']['en-US']['displayname'] = 'PowerDNS-Admin' settings['organization']['en-US']['name'] = 'PowerDNS-Admin' settings['organization']['en-US']['url'] = own_url - auth = self.OneLogin_Saml2_Auth(req, settings) + try: + auth = self.OneLogin_Saml2_Auth(req, settings) + except: + current_app.logger.error( + "SAML: SAML Authentication failed") + auth = None return auth diff --git a/powerdnsadmin/templates/admin_setting_authentication.html b/powerdnsadmin/templates/admin_setting_authentication.html index 044c588..210a50b 100644 --- a/powerdnsadmin/templates/admin_setting_authentication.html +++ b/powerdnsadmin/templates/admin_setting_authentication.html @@ -810,7 +810,7 @@
- Login failed.
- Error(s) when processing SAML Response:
+ Login failed.
+ {% if error %}
+
Error(s) when processing SAML Response: