SAML Settings Help, Now catching SAML Errors gracefully.

This commit is contained in:
vmarkop 2021-12-10 17:26:09 +02:00
parent d57a37e9c1
commit c4fde72296
5 changed files with 200 additions and 120 deletions

View file

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

View file

@ -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()
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:

View file

@ -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'
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
try:
auth = self.OneLogin_Saml2_Auth(req, settings)
except:
current_app.logger.error(
"SAML: SAML Authentication failed")
auth = None
return auth

View file

@ -810,7 +810,7 @@
</div>
<div class="form-group">
<input type="checkbox" id="saml_want_assertions_signed" name="saml_want_assertions_signed" class="checkbox" {% if SETTING.get('saml_want_assertions_signed') %}checked{% endif %}>
<label for="saml_want_assertions_encrypted">Want Assertions Signed</label>
<label for="saml_want_assertions_signed">Want Assertions Signed</label>
</div>
<div class="form-group">
<input type="checkbox" id="saml_want_message_signed" name="saml_want_message_signed" class="checkbox" {% if SETTING.get('saml_want_message_signed') %}checked{% endif %}>
@ -818,11 +818,11 @@
</div>
<div class="form-group">
<input type="checkbox" id="saml_nameid_encrypted" name="saml_nameid_encrypted" class="checkbox" {% if SETTING.get('saml_nameid_encrypted') %}checked{% endif %}>
<label for="saml_nameid_encrypted">NameID Encrypted </label>
<label for="saml_nameid_encrypted">NameID Encrypted</label>
</div>
<div class="form-group">
<input type="checkbox" id="saml_want_nameid_encrypted" name="saml_want_nameid_encrypted" class="checkbox" {% if SETTING.get('saml_want_nameid_encrypted') %}checked{% endif %}>
<label for="saml_want_message_signed">Want NameID Encrypted </label>
<label for="saml_want_nameid_encrypted">Want NameID Encrypted</label>
</div>
<div class="form-group">
<label for="saml_digest_algorithm">Digest Algorithm</label>
@ -900,32 +900,97 @@
<div class="col-md-8">
<legend>Help</legend>
<dl class="dl-horizontal">
<dt>METADATA</dt>
<dt>General</dt>
<dd> <br>
<b>Enable SAML</b> - Enables or disables SAML Authentication. <br>
If toggled on, the following fields must be filled in the left form:
<ul>
<li>
<b>Metadata URL</b> - URL to fetch metadata from
<b>IDP Entity ID</b>
</li>
<li>
<b>Metadata Cache Lifetime</b> - Cache Lifetime in minutes before fresh metadata are requested
<b>IDP Metadata URL</b>
</li>
<li>
<b>IDP SSO BINDING</b>
</li>
<li>
<b>IDP SLO BINDING</b>
</li>
<li>
<b>SP Entity ID</b>
</li>
<li>
<b>SP NameID Format</b>
</li>
<li>
<b>SP Requested Attributes</b>
</li>
<li>
<b>Cert File</b>
</li>
<li>
<b>Cert Key</b>
</li>
<li>
<b>Digest Algorithm</b>
</li>
<li>
<b>Signature Algorithm</b>
</li>
<li>
<b>Logout URL</b>, if <i>SAML Logout</i> is toggled on
</li>
<li>
<b>Roles Provisioning Field</b> and <b>Urn Prefix</b>, if <i>Roles Autoprovisioning</i> is toggled on.
</li>
</ul>
The rest can be filled, or left empty to revert to the Default settings.
</dd>
<dt>IDP</dt>
<dd> <br>
<ul>
<li>
<b>IDP Entity ID</b> - EntityID of the IDP to use. Only needed if more than one IDP is in the SAML_METADATA_URL
</li>
<li>
<b>IDP Metadata URL</b> - URL to fetch IDP metadata from
</li>
<li>
<b>IDP Metadata Cache Lifetime</b> - Cache Lifetime in minutes before fresh metadata are requested from the IDP Metadata URL
</li>
<li>
<b>IDP SSO Binding</b> - SAML SSO binding format to use
</li>
<li>
<b>IDP Entity ID</b> - EntityID of the IdP to use. Only needed if more than one IdP is in the SAML_METADATA_URL
<b>IDP SLO Binding</b> - SAML SLO binding format to use
</li>
<li>
<b>NameID Format</b> - NameID format to request
</li>
</ul>
</dd>
<dt>ATTRIBUTES</dt>
<dt>SP</dt>
<dd> <br>
<ul>
<li>
<b>SP Entity ID</b> - The entity ID of your Service Provider (SP).
</li>
<li>
<b>SP NameID Format</b> - NameID format to request
</li>
<li>
<b>SP Metadata Cache Duration</b> - Set the cache duration of generated metadata.
</li>
<li>
<b>SP Metadata Valid Until</b> - Set the expiration moment (in seconds) for generated metadata.
</li>
<li>
<b>Sign SP Metadata</b> - Choose whether metadata produced is signed.
</li>
</ul>
</dd>
<dt>SP ATTRIBUTES</dt>
<dd> <br>
<ul>
<li>
@ -965,6 +1030,79 @@
<li>
<b>Username</b> - Attribute to use for username.
</li>
<li>
<b>SP Entity Name</b> - Contact information about your SP, to be included in the generated metadata.
</li>
<li>
<b>SP Entity Mail</b> - Contact information about your SP, to be included in the generated metadata.
</li>
</ul>
</dd>
<dt>ENCRYPTION</dt>
<dd> <br>
<ul>
<li>
The <b>Cert File</b> - <b>Cert Key</b> pair configures the path
to certificate file and it's respective private key file.
It is used for signing metadata, encrypting tokens and all other
signing/encryption tasks during communication between iDP and SP.<br>
<b>NOTE</b>: If these two parameters aren't explicitly provided, a self-signed certificate-key pair
will be generated in "PowerDNS-Admin" root directory.<br>
<b style="color:red;">CAUTION</b>: For production use, usage of self-signed certificates is highly discouraged.
Use certificates from trusted CA instead.
</li>
<li>
<b>Sign Authentication Request</b> - Configures if the SP should sign outgoing authentication requests.
</li>
<li>
<b>Sign Logout Request & Response</b> - Configures if the SP should sign outgoing Logout requests & Logout responses.
</li>
<li>
<b>Want Assertions Encrypted</b> - Choose whether the SP expects assertions received from the IDP to be encrypted.
</li>
<li>
<b>Want Assertions Signed</b> - Choose whether the SP expects incoming assertions to be signed.
</li>
<li>
<b>NameID Encrypted</b> - Indicates that the nameID of the logoutRequest sent by this SP will be encrypted.
</li>
<li>
<b>Want NameID Encrypted</b> - Indicates a requirement for the NameID received by this SP to be encrypted.
</li>
<li>
<b>Want Message Signed</b> - Choose whether the SP expects incoming messages to be signed.
</li>
<li>
<b>Digest Algorithm</b> - Encryption algorithm to encode outgoing and decode incoming metadata.
</li>
<li>
<b>Signature Algorithm</b> - Encryption algorithm to encode/decode signatures.
</li>
</ul>
</dd>
<dt>LOGOUT</dt>
<dd> <br>
<ul>
<li>
<b>SAML Logout</b> - Choose whether user is logged out of SAML session and possibly redirect them elsewhere.
<ul>
<li>
If enabled, use SAML standard logout mechanism retreived from IDP metadata.
</li>
<li>
If disabled, don't care about SAML session on logout.<br>
Logout from PowerDNS-Admin only and keep SAML session authenticated.
</li>
</ul>
</li>
<li>
<b>Logout URL</b> - Configure to redirect to a url different than PowerDNS-Admin login after a successful SAML logout.
</li>
</ul>
</dd>
<dt>AUTOPROVISION</dt>
<dd> Provision PDA user privileges based on SAML Attributes.
<ul>
<li>
<b>Admin</b> - Attribute to get admin status from.<br>
If set, look for the value 'true' to set a user as an administrator.<br>
@ -978,87 +1116,9 @@
be created and the user added to them.
</li>
<li>
<b>SP Entity ID</b> - The entity ID of your Service Provider (SP).
</li>
<li>
<b>SP Entity Name</b> - Contact information about your SP, to be included in the generated metadata.
</li>
<li>
<b>SP Entity Mail</b> - Contact information about your SP, to be included in the generated metadata.
</li>
</ul>
</dd>
<dt>CERTIFICATE</dt>
<dd> Configures the path to certificate file and it's respective private key file.
<ul>
<li>
The <b>Cert File</b> - <b>Cert Key</b> pair is used for signing metadata, encrypting tokens and all other signing/encryption
tasks during communication between iDP and SP.<br>
<b>NOTE</b>: If these two parameters aren't explicitly provided, a self-signed certificate-key pair
will be generated in "PowerDNS-Admin" root directory.<br>
<b style="color:red;">CAUTION</b>: For production use, usage of self-signed certificates is highly discouraged.
Use certificates from trusted CA instead.
</li>
<li>
<b>Sign Request</b> - Configures if SAML tokens should be encrypted.<br>
If enabled a new app certificate will be generated on restart.
</li>
</ul>
</dd>
<dt>LOGOUT</dt>
<dd>
Choose whether user is logged out of SAML session and possibly redirect them elsewhere.
<ul>
<li>
<b>SAML Logout</b> - Use SAML standard logout mechanism retreived from IDP metadata.<br>
If disabled, don't care about SAML session on logout.<br>
Logout from PowerDNS-Admin only and keep SAML session authenticated.
</li>
<li>
<b>Logout URL</b> - Configure to redirect to a url different than PowerDNS-Admin login after a successful SAML logout.
</li>
</ul>
</dd>
<dt>ENCRYPTION</dt>
<dd> <br>
<ul>
<li>
<b>Encrypted Assertion</b> - Choose whether assertions are encrypted.
</li>
<li>
<b>Want Assertions Signed</b> - Expect incoming assertions to be signed.
</li>
<li>
<b>Digest Algorithm</b> - Encryption algorithm to encode outgoing and decode incoming metadata.
</li>
<li>
<b>Signature Algorithm</b> - Encryption algorithm to encode/decode signatures.
</li>
<li>
<b>Want Message Signed</b> - Expect incoming messages to be signed.
</li>
<li>
<b>Sign Metadata</b> - Choose whether metadata produced is signed.
</li>
</ul>
</dd>
<dt>DURATION</dt>
<dd> <br>
<ul>
<li>
<b>Metadata Cache Duration</b> - Set the cache duration of generated metadata.
</li>
<li>
<b>Metadata Valid Until</b> - Set the expiration moment (in seconds) for generated metadata.
</li>
</ul>
</dd>
<dt>AUTOPROVISION</dt>
<dd> Provision PDA user privileges based on SAML Token Attributes.
This feature and the "Admin / Account" attribute assertion are mutually exclusive.
<ul>
<li>
<b>Roles Autoprovisioning</b> - If toggled on, the PDA Role and the associations of users found in the local db, will be instantly updated from the SAML SP db every time they log in.
<b>Roles Autoprovisioning</b> - If toggled on, the PDA Role and the associations of users found in the local db, will be instantly updated from the SAML SP db every time they log in.<br>
<b style="color:red;">NOTE</b>:This feature and the assertion of "Admin / Account" attributes are mutually exclusive.<br>
If used, the values for Admin/Account given above will be ignored.
</li>
<li>
<b>Roles provisioning field</b> - The urn value of the attribute in the SAML Token where PDA will look for a new Role and/or new associations to domains/accounts.
@ -1084,7 +1144,8 @@
<li>
<b>Purge Roles If Empty</b> - If toggled on, SAML logins that have no valid "powerdns-admin" records to their autoprovisioning field,
will lose all their associations with any domain or account, also reverting to a User in the process, despite their current role in the local db.<br>
If toggled off, in the same scenario they get to keep their existing associations and their current Role.
If toggled off, in the same scenario they get to keep their existing associations and their current Role.<br>
<b style="color:red;">CAUTION</b>: Enabling this feature will revoke existing users' access to their associated domains unless they have their autoprovisioning field prepopulated.
</li>
</ul>
</dd>

View file

@ -26,14 +26,15 @@
<i class="fa fa-warning text-yellow"></i> Oops! Something went wrong
</h3><br>
<p>
Login failed.<br>
Error(s) when processing SAML Response:<br>
Login failed.
{% if error %}
<br>Error(s) when processing SAML Response:<br>
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
You may <a href="{{ url_for('dashboard.dashboard') }}">return to the dashboard</a>.
</p>
</div>