Merge 1a0ff41e79
into 0b2ad520b7
This commit is contained in:
commit
991f45f3f8
|
@ -37,117 +37,6 @@ SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
|||
# MAIL_PASSWORD = None
|
||||
# MAIL_DEFAULT_SENDER = ('PowerDNS-Admin', 'noreply@domain.ltd')
|
||||
|
||||
# SAML Authnetication
|
||||
SAML_ENABLED = False
|
||||
# SAML_DEBUG = True
|
||||
# SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml')
|
||||
# ##Example for ADFS Metadata-URL
|
||||
# SAML_METADATA_URL = 'https://<hostname>/FederationMetadata/2007-06/FederationMetadata.xml'
|
||||
# #Cache Lifetime in Seconds
|
||||
# SAML_METADATA_CACHE_LIFETIME = 1
|
||||
|
||||
# # SAML SSO binding format to use
|
||||
# ## Default: library default (urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect)
|
||||
# #SAML_IDP_SSO_BINDING = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
||||
|
||||
# ## EntityID of the IdP to use. Only needed if more than one IdP is
|
||||
# ## in the SAML_METADATA_URL
|
||||
# ### Default: First (only) IdP in the SAML_METADATA_URL
|
||||
# ### Example: https://idp.example.edu/idp
|
||||
# #SAML_IDP_ENTITY_ID = 'https://idp.example.edu/idp'
|
||||
# ## NameID format to request
|
||||
# ### Default: The SAML NameID Format in the metadata if present,
|
||||
# ### otherwise urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
|
||||
# ### Example: urn:oid:0.9.2342.19200300.100.1.1
|
||||
# #SAML_NAMEID_FORMAT = 'urn:oid:0.9.2342.19200300.100.1.1'
|
||||
|
||||
# Following parameter defines RequestedAttributes section in SAML metadata
|
||||
# since certain iDPs require explicit attribute request. If not provided section
|
||||
# will not be available in metadata.
|
||||
#
|
||||
# Possible attributes:
|
||||
# name (mandatory), nameFormat, isRequired, friendlyName
|
||||
#
|
||||
# NOTE: This parameter requires to be entered in valid JSON format as displayed below
|
||||
# and multiple attributes can given
|
||||
#
|
||||
# Following example:
|
||||
#
|
||||
# SAML_SP_REQUESTED_ATTRIBUTES = '[ \
|
||||
# {"name": "urn:oid:0.9.2342.19200300.100.1.3", "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "isRequired": true, "friendlyName": "email"}, \
|
||||
# {"name": "mail", "isRequired": false, "friendlyName": "test-field"} \
|
||||
# ]'
|
||||
#
|
||||
# produces following metadata section:
|
||||
# <md:AttributeConsumingService index="1">
|
||||
# <md:RequestedAttribute Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" FriendlyName="email" isRequired="true"/>
|
||||
# <md:RequestedAttribute Name="mail" FriendlyName="test-field"/>
|
||||
# </md:AttributeConsumingService>
|
||||
|
||||
|
||||
# ## Attribute to use for Email address
|
||||
# ### Default: email
|
||||
# ### Example: urn:oid:0.9.2342.19200300.100.1.3
|
||||
# #SAML_ATTRIBUTE_EMAIL = 'urn:oid:0.9.2342.19200300.100.1.3'
|
||||
|
||||
# ## Attribute to use for Given name
|
||||
# ### Default: givenname
|
||||
# ### Example: urn:oid:2.5.4.42
|
||||
# #SAML_ATTRIBUTE_GIVENNAME = 'urn:oid:2.5.4.42'
|
||||
|
||||
# ## Attribute to use for Surname
|
||||
# ### Default: surname
|
||||
# ### Example: urn:oid:2.5.4.4
|
||||
# #SAML_ATTRIBUTE_SURNAME = 'urn:oid:2.5.4.4'
|
||||
|
||||
# ## Attribute to use for username
|
||||
# ### Default: Use NameID instead
|
||||
# ### Example: urn:oid:0.9.2342.19200300.100.1.1
|
||||
# #SAML_ATTRIBUTE_USERNAME = 'urn:oid:0.9.2342.19200300.100.1.1'
|
||||
|
||||
# ## Attribute to get admin status from
|
||||
# ### Default: Don't control admin with SAML attribute
|
||||
# ### Example: https://example.edu/pdns-admin
|
||||
# ### If set, look for the value 'true' to set a user as an administrator
|
||||
# ### If not included in assertion, or set to something other than 'true',
|
||||
# ### the user is set as a non-administrator user.
|
||||
# #SAML_ATTRIBUTE_ADMIN = 'https://example.edu/pdns-admin'
|
||||
|
||||
# ## Attribute to get account names from
|
||||
# ### Default: Don't control accounts with SAML attribute
|
||||
# ### If set, the user will be added and removed from accounts to match
|
||||
# ### what's in the login assertion. Accounts that don't exist will
|
||||
# ### be created and the user added to them.
|
||||
# SAML_ATTRIBUTE_ACCOUNT = 'https://example.edu/pdns-account'
|
||||
|
||||
# SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>'
|
||||
# SAML_SP_CONTACT_NAME = '<contact name>'
|
||||
# SAML_SP_CONTACT_MAIL = '<contact mail>'
|
||||
|
||||
# Configures the path to certificate file and it's respective private key file
|
||||
# This pair is used for signing metadata, encrypting tokens and all other signing/encryption
|
||||
# tasks during communication between iDP and SP
|
||||
# NOTE: if this two parameters aren't explicitly provided, self-signed certificate-key pair
|
||||
# will be generated in "PowerDNS-Admin" root directory
|
||||
# ###########################################################################################
|
||||
# CAUTION: For production use, usage of self-signed certificates it's highly discouraged.
|
||||
# Use certificates from trusted CA instead
|
||||
# ###########################################################################################
|
||||
# SAML_CERT_FILE = '/etc/pki/powerdns-admin/cert.crt'
|
||||
# SAML_CERT_KEY = '/etc/pki/powerdns-admin/key.pem'
|
||||
|
||||
# Configures if SAML tokens should be encrypted.
|
||||
# SAML_SIGN_REQUEST = False
|
||||
# #Use SAML standard logout mechanism retreived from idp metadata
|
||||
# #If configured false don't care about SAML session on logout.
|
||||
# #Logout from PowerDNS-Admin only and keep SAML session authenticated.
|
||||
# SAML_LOGOUT = False
|
||||
# #Configure to redirect to a different url then PowerDNS-Admin login after SAML logout
|
||||
# #for example redirect to google.com after successful saml logout
|
||||
# #SAML_LOGOUT_URL = 'https://google.com'
|
||||
|
||||
# #SAML_ASSERTION_ENCRYPTED = True
|
||||
|
||||
# Remote authentication settings
|
||||
|
||||
# Whether to enable remote user authentication or not
|
||||
|
|
|
@ -27,8 +27,4 @@ SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
|
|||
)
|
||||
|
||||
### DATABASE - SQLite
|
||||
# SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
||||
|
||||
# SAML Authnetication
|
||||
SAML_ENABLED = False
|
||||
SAML_ASSERTION_ENCRYPTED = True
|
||||
# SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
|
|
@ -110,6 +110,51 @@ class Setting(db.Model):
|
|||
'oidc_oauth_email': 'email',
|
||||
'oidc_oauth_account_name_property': '',
|
||||
'oidc_oauth_account_description_property': '',
|
||||
'saml_enabled': False,
|
||||
'saml_debug': True,
|
||||
'saml_metadata_url': 'https://example.com/metadata.xml',
|
||||
'saml_metadata_cache_lifetime': '15',
|
||||
'saml_idp_sso_binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||
'saml_idp_slo_binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||
'saml_sp_acs_binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
|
||||
'saml_sp_sls_binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
|
||||
'saml_idp_entity_id': 'https://idp.example.com/idp',
|
||||
'saml_nameid_format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
|
||||
'saml_sp_requested_attributes': '[ \
|
||||
{"name": "urn:oid:0.9.2342.19200300.100.1.1", "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "isRequired": true, "friendlyName": "username" }, \
|
||||
{"name": "urn:oid:0.9.2342.19200300.100.1.3", "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "isRequired": true, "friendlyName": "email"}, \
|
||||
{"name": "urn:oid:2.5.4.42", "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "isRequired": false, "friendlyName": "givenname"}, \
|
||||
{"name": "urn:oid:2.5.4.4", "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "isRequired": false, "friendlyName": "surname" } \
|
||||
]',
|
||||
'saml_attribute_email': 'urn:oid:0.9.2342.19200300.100.1.3',
|
||||
'saml_attribute_givenname': 'urn:oid:2.5.4.42',
|
||||
'saml_attribute_surname': 'urn:oid:2.5.4.4',
|
||||
'saml_attribute_username': 'urn:oid:0.9.2342.19200300.100.1.1',
|
||||
'saml_attribute_admin': 'https://example.edu/pdns-admin',
|
||||
'saml_attribute_account': 'https://example.edu/pdns-account',
|
||||
'saml_attribute_name': None,
|
||||
'saml_attribute_group': None,
|
||||
'saml_group_admin_name': None,
|
||||
'saml_group_to_account_mapping': None,
|
||||
'saml_sp_entity_id': 'https://pdnsa.example.com',
|
||||
'saml_sp_contact_name': 'admin',
|
||||
'saml_sp_contact_mail': 'powerdnsadmin@organization.com',
|
||||
'saml_cert_file': '/etc/pki/powerdns-admin/cert.crt',
|
||||
'saml_cert_key': '/etc/pki/powerdns-admin/key.pem',
|
||||
'saml_sign_authn_request': False,
|
||||
'saml_sign_logout_request_response': False,
|
||||
'saml_logout': False,
|
||||
'saml_logout_url': 'https://google.com',
|
||||
'saml_want_assertions_encrypted': False,
|
||||
'saml_digest_algorithm': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
|
||||
'saml_signature_algorithm': 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
|
||||
'saml_want_assertions_signed': False,
|
||||
'saml_sign_metadata': False,
|
||||
'saml_want_message_signed': False,
|
||||
'saml_nameid_encrypted': False,
|
||||
'saml_want_nameid_encrypted': False,
|
||||
'saml_metadata_cache_duration': None,
|
||||
'saml_metadata_valid_until': None,
|
||||
'forward_records_allow_edit': {
|
||||
'A': True,
|
||||
'AAAA': True,
|
||||
|
|
|
@ -669,11 +669,11 @@ class User(db.Model):
|
|||
current_app.logger.warning("Cannot apply autoprovisioning on user: {}".format(e))
|
||||
return entitlements
|
||||
|
||||
def updateUser(self, Entitlements):
|
||||
def updateUser(self, Entitlements, urn_value):
|
||||
"""
|
||||
Update user associations based on ldap attribute
|
||||
"""
|
||||
entitlements= getCorrectEntitlements(Entitlements)
|
||||
entitlements= getCorrectEntitlements(Entitlements, urn_value)
|
||||
if len(entitlements)!=0:
|
||||
self.revoke_privilege(True)
|
||||
for entitlement in entitlements:
|
||||
|
@ -712,12 +712,11 @@ class User(db.Model):
|
|||
if account!=None:
|
||||
account.add_user(user)
|
||||
|
||||
def getCorrectEntitlements(Entitlements):
|
||||
def getCorrectEntitlements(Entitlements, urn_value):
|
||||
"""
|
||||
Gather a list of valid records from the ldap attribute given
|
||||
"""
|
||||
from ..models.role import Role
|
||||
urn_value=Setting().get('urn_value')
|
||||
urnArgs=[x.lower() for x in urn_value.split(':')]
|
||||
entitlements=[]
|
||||
for Entitlement in Entitlements:
|
||||
|
|
|
@ -22,7 +22,6 @@ from ..models.domain_template import DomainTemplate
|
|||
from ..models.domain_template_record import DomainTemplateRecord
|
||||
from ..models.api_key import ApiKey
|
||||
from ..models.base import db
|
||||
|
||||
from ..lib.schema import ApiPlainKeySchema
|
||||
|
||||
apikey_plain_schema = ApiPlainKeySchema(many=True)
|
||||
|
@ -1367,7 +1366,8 @@ def has_an_auth_method(local_db_enabled=None,
|
|||
google_oauth_enabled=None,
|
||||
github_oauth_enabled=None,
|
||||
oidc_oauth_enabled=None,
|
||||
azure_oauth_enabled=None):
|
||||
azure_oauth_enabled=None,
|
||||
saml_enabled=None):
|
||||
if local_db_enabled is None:
|
||||
local_db_enabled = Setting().get('local_db_enabled')
|
||||
if ldap_enabled is None:
|
||||
|
@ -1380,6 +1380,8 @@ def has_an_auth_method(local_db_enabled=None,
|
|||
oidc_oauth_enabled = Setting().get('oidc_oauth_enabled')
|
||||
if azure_oauth_enabled is None:
|
||||
azure_oauth_enabled = Setting().get('azure_oauth_enabled')
|
||||
if saml_enabled is None:
|
||||
saml_enabled = Setting().get('saml_enabled')
|
||||
return local_db_enabled or ldap_enabled or google_oauth_enabled or github_oauth_enabled or oidc_oauth_enabled or azure_oauth_enabled
|
||||
|
||||
|
||||
|
@ -1621,6 +1623,115 @@ def setting_authentication():
|
|||
'msg':
|
||||
'Saved successfully. Please reload PDA to take effect.'
|
||||
}
|
||||
elif conf_type == 'saml':
|
||||
saml_enabled = True if request.form.get('saml_enabled') else False
|
||||
if not has_an_auth_method(saml_enabled=saml_enabled):
|
||||
result = {
|
||||
'status':
|
||||
False,
|
||||
'msg':
|
||||
'Must have at least one authentication method enabled.'
|
||||
}
|
||||
else:
|
||||
Setting().set(
|
||||
'saml_enabled',
|
||||
True if request.form.get('saml_enabled') else False)
|
||||
Setting().set('saml_metadata_url',
|
||||
request.form.get('saml_metadata_url'))
|
||||
if request.form.get('saml_metadata_cache_lifetime'):
|
||||
Setting().set('saml_metadata_cache_lifetime',
|
||||
request.form.get('saml_metadata_cache_lifetime'))
|
||||
else:
|
||||
Setting().set('saml_metadata_cache_lifetime',
|
||||
Setting().defaults['saml_metadata_cache_lifetime'])
|
||||
Setting().set('saml_idp_sso_binding',
|
||||
request.form.get('saml_idp_sso_binding'))
|
||||
Setting().set('saml_idp_slo_binding',
|
||||
request.form.get('saml_idp_slo_binding'))
|
||||
Setting().set('saml_idp_entity_id',
|
||||
request.form.get('saml_idp_entity_id'))
|
||||
Setting().set('saml_nameid_format',
|
||||
request.form.get('saml_nameid_format'))
|
||||
Setting().set('saml_sp_acs_binding',
|
||||
request.form.get('saml_sp_acs_binding'))
|
||||
Setting().set('saml_sp_sls_binding',
|
||||
request.form.get('saml_sp_sls_binding'))
|
||||
Setting().set('saml_sp_requested_attributes',
|
||||
request.form.get('saml_sp_requested_attributes'))
|
||||
Setting().set('saml_attribute_email',
|
||||
request.form.get('saml_attribute_email'))
|
||||
Setting().set('saml_attribute_givenname',
|
||||
request.form.get('saml_attribute_givenname'))
|
||||
Setting().set('saml_attribute_surname',
|
||||
request.form.get('saml_attribute_surname'))
|
||||
Setting().set('saml_attribute_username',
|
||||
request.form.get('saml_attribute_username'))
|
||||
Setting().set('saml_attribute_admin',
|
||||
request.form.get('saml_attribute_admin'))
|
||||
Setting().set('saml_attribute_account',
|
||||
request.form.get('saml_attribute_account'))
|
||||
Setting().set('saml_sp_entity_id',
|
||||
request.form.get('saml_sp_entity_id'))
|
||||
if request.form.get('saml_sp_contact_name'):
|
||||
Setting().set('saml_sp_contact_name',
|
||||
request.form.get('saml_sp_contact_name'))
|
||||
else:
|
||||
Setting().set('saml_sp_contact_name',
|
||||
Setting().defaults['saml_sp_contact_name'])
|
||||
if request.form.get('saml_sp_contact_mail'):
|
||||
Setting().set('saml_sp_contact_mail',
|
||||
request.form.get('saml_sp_contact_mail'))
|
||||
else:
|
||||
Setting().set('saml_sp_contact_mail',
|
||||
Setting().defaults['saml_sp_contact_mail'])
|
||||
Setting().set('saml_cert_file',
|
||||
request.form.get('saml_cert_file'))
|
||||
Setting().set('saml_cert_key',
|
||||
request.form.get('saml_cert_key'))
|
||||
Setting().set(
|
||||
'saml_sign_authn_request',
|
||||
True if request.form.get('saml_sign_authn_request') else False)
|
||||
Setting().set(
|
||||
'saml_sign_logout_request_response',
|
||||
True if request.form.get('saml_sign_logout_request_response') else False)
|
||||
Setting().set(
|
||||
'saml_logout',
|
||||
True if request.form.get('saml_logout') else False)
|
||||
if request.form.get('saml_logout_url'):
|
||||
Setting().set('saml_logout_url',
|
||||
request.form.get('saml_logout_url'))
|
||||
Setting().set(
|
||||
'saml_want_assertions_encrypted',
|
||||
True if request.form.get('saml_want_assertions_encrypted') else False)
|
||||
Setting().set(
|
||||
'saml_want_assertions_signed',
|
||||
True if request.form.get('saml_want_assertions_signed') else False)
|
||||
Setting().set(
|
||||
'saml_want_nameid_encrypted',
|
||||
True if request.form.get('saml_want_nameid_encrypted') else False)
|
||||
Setting().set(
|
||||
'saml_nameid_encrypted',
|
||||
True if request.form.get('saml_nameid_encrypted') else False)
|
||||
Setting().set('saml_digest_algorithm',
|
||||
request.form.get('saml_digest_algorithm'))
|
||||
Setting().set('saml_signature_algorithm',
|
||||
request.form.get('saml_signature_algorithm'))
|
||||
Setting().set(
|
||||
'saml_want_message_signed',
|
||||
True if request.form.get('saml_want_message_signed') else False)
|
||||
Setting().set(
|
||||
'saml_sign_metadata',
|
||||
True if request.form.get('saml_sign_metadata') else False)
|
||||
Setting().set('saml_metadata_cache_duration',
|
||||
request.form.get('saml_metadata_cache_duration'))
|
||||
Setting().set('saml_metadata_valid_until',
|
||||
request.form.get('saml_metadata_valid_until'))
|
||||
|
||||
result = {
|
||||
'status': True,
|
||||
'msg':
|
||||
'Saved successfully. Please reload PDA to take effect.'
|
||||
}
|
||||
else:
|
||||
return abort(400)
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ def oidc_login():
|
|||
|
||||
@index_bp.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
SAML_ENABLED = current_app.config.get('SAML_ENABLED')
|
||||
SAML_ENABLED = Setting().get('saml_enabled')
|
||||
|
||||
if g.user is not None and current_user.is_authenticated:
|
||||
return redirect(url_for('dashboard.dashboard'))
|
||||
|
@ -496,7 +496,7 @@ def login():
|
|||
|
||||
elif len(Entitlements)!=0:
|
||||
if checkForPDAEntries(Entitlements, urn_value):
|
||||
user.updateUser(Entitlements)
|
||||
user.updateUser(Entitlements, urn_value)
|
||||
else:
|
||||
current_app.logger.warning('Not a single powerdns-admin record was found, possibly a typo in the prefix')
|
||||
if Setting().get('purge'):
|
||||
|
@ -594,26 +594,33 @@ def prepare_welcome_user(user_id):
|
|||
|
||||
@index_bp.route('/logout')
|
||||
def logout():
|
||||
if current_app.config.get(
|
||||
'SAML_ENABLED'
|
||||
) and 'samlSessionIndex' in session and current_app.config.get(
|
||||
'SAML_LOGOUT'):
|
||||
if Setting().get('saml_enabled'
|
||||
) and 'samlSessionIndex' in session and Setting().get(
|
||||
'saml_logout'):
|
||||
req = saml.prepare_flask_request(request)
|
||||
auth = saml.init_saml_auth(req)
|
||||
if current_app.config.get('SAML_LOGOUT_URL'):
|
||||
if Setting().get('saml_logout_url'):
|
||||
try:
|
||||
return redirect(
|
||||
auth.logout(
|
||||
name_id_format=
|
||||
Setting().get('saml_nameid_format'),
|
||||
return_to=Setting().get('saml_logout_url'),
|
||||
session_index=session['samlSessionIndex'],
|
||||
name_id=session['samlNameId']))
|
||||
except:
|
||||
current_app.logger.info(
|
||||
"SAML: Your IDP does not support Single Logout.")
|
||||
try:
|
||||
return redirect(
|
||||
auth.logout(
|
||||
name_id_format=
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||
return_to=current_app.config.get('SAML_LOGOUT_URL'),
|
||||
Setting().get('saml_nameid_format'),
|
||||
session_index=session['samlSessionIndex'],
|
||||
name_id=session['samlNameId']))
|
||||
return redirect(
|
||||
auth.logout(
|
||||
name_id_format=
|
||||
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||
session_index=session['samlSessionIndex'],
|
||||
name_id=session['samlNameId']))
|
||||
except:
|
||||
current_app.logger.info(
|
||||
"SAML: Your IDP does not support Single Logout.")
|
||||
|
||||
redirect_uri = url_for('index.login')
|
||||
oidc_logout = Setting().get('oidc_oauth_logout_url')
|
||||
|
@ -930,25 +937,42 @@ def dyndns_update():
|
|||
### START SAML AUTHENTICATION ###
|
||||
@index_bp.route('/saml/login')
|
||||
def saml_login():
|
||||
if not current_app.config.get('SAML_ENABLED'):
|
||||
if not Setting().get('saml_enabled'):
|
||||
abort(400)
|
||||
global saml
|
||||
req = saml.prepare_flask_request(request)
|
||||
auth = saml.init_saml_auth(req)
|
||||
try:
|
||||
auth = saml.init_saml_auth(req)
|
||||
except:
|
||||
current_app.logger.info(
|
||||
"SAML: IDP Metadata were not successfully initialized. Reinitializing...")
|
||||
saml = SAML()
|
||||
req = saml.prepare_flask_request(request)
|
||||
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))
|
||||
|
||||
|
||||
@index_bp.route('/saml/metadata')
|
||||
def saml_metadata():
|
||||
if not current_app.config.get('SAML_ENABLED'):
|
||||
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')
|
||||
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:
|
||||
|
@ -958,15 +982,16 @@ def saml_metadata():
|
|||
resp = make_response(errors.join(', '), 500)
|
||||
return resp
|
||||
|
||||
|
||||
@index_bp.route('/saml/authorized', methods=['GET', 'POST'])
|
||||
def saml_authorized():
|
||||
errors = []
|
||||
if not current_app.config.get('SAML_ENABLED'):
|
||||
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')
|
||||
auth.process_response()
|
||||
current_app.logger.debug( auth.get_attributes() )
|
||||
errors = auth.get_errors()
|
||||
|
@ -979,9 +1004,9 @@ def saml_authorized():
|
|||
if 'RelayState' in request.form and self_url != request.form[
|
||||
'RelayState']:
|
||||
return redirect(auth.redirect_to(request.form['RelayState']))
|
||||
if current_app.config.get('SAML_ATTRIBUTE_USERNAME', False):
|
||||
if Setting().get('saml_attribute_username'):
|
||||
username = session['samlUserdata'][
|
||||
current_app.config['SAML_ATTRIBUTE_USERNAME']][0].lower()
|
||||
Setting().get('saml_attribute_username')][0].lower()
|
||||
else:
|
||||
username = session['samlNameId'].lower()
|
||||
user = User.query.filter_by(username=username).first()
|
||||
|
@ -992,22 +1017,38 @@ def saml_authorized():
|
|||
email=session['samlNameId'])
|
||||
user.create_local_user()
|
||||
session['user_id'] = user.id
|
||||
email_attribute_name = current_app.config.get('SAML_ATTRIBUTE_EMAIL',
|
||||
'email')
|
||||
givenname_attribute_name = current_app.config.get(
|
||||
'SAML_ATTRIBUTE_GIVENNAME', 'givenname')
|
||||
surname_attribute_name = current_app.config.get(
|
||||
'SAML_ATTRIBUTE_SURNAME', 'surname')
|
||||
name_attribute_name = current_app.config.get('SAML_ATTRIBUTE_NAME',
|
||||
None)
|
||||
account_attribute_name = current_app.config.get(
|
||||
'SAML_ATTRIBUTE_ACCOUNT', None)
|
||||
admin_attribute_name = current_app.config.get('SAML_ATTRIBUTE_ADMIN',
|
||||
None)
|
||||
group_attribute_name = current_app.config.get('SAML_ATTRIBUTE_GROUP',
|
||||
None)
|
||||
admin_group_name = current_app.config.get('SAML_GROUP_ADMIN_NAME',
|
||||
None)
|
||||
if Setting().get('saml_attribute_email'):
|
||||
email_attribute_name = Setting().get('saml_attribute_email')
|
||||
else:
|
||||
email_attribute_name = 'email'
|
||||
if Setting().get('saml_attribute_givenname'):
|
||||
givenname_attribute_name = Setting().get('saml_attribute_givenname')
|
||||
else:
|
||||
givenname_attribute_name = 'givenname'
|
||||
if Setting().get('saml_attribute_surname'):
|
||||
surname_attribute_name = Setting().get('saml_attribute_surname')
|
||||
else:
|
||||
surname_attribute_name = 'surname'
|
||||
if Setting().get('saml_attribute_name'):
|
||||
name_attribute_name = Setting().get('saml_attribute_name')
|
||||
else:
|
||||
name_attribute_name = None
|
||||
if Setting().get('saml_attribute_account'):
|
||||
account_attribute_name = Setting().get('saml_attribute_account')
|
||||
else:
|
||||
account_attribute_name = None
|
||||
if Setting().get('saml_attribute_admin'):
|
||||
admin_attribute_name = Setting().get('saml_attribute_admin')
|
||||
else:
|
||||
admin_attribute_name = None
|
||||
if Setting().get('saml_attribute_group'):
|
||||
group_attribute_name = Setting().get('saml_attribute_group')
|
||||
else:
|
||||
group_attribute_name = None
|
||||
if Setting().get('saml_group_admin_name'):
|
||||
admin_group_name = Setting().get('saml_group_admin_name')
|
||||
else:
|
||||
admin_group_name = None
|
||||
group_to_account_mapping = create_group_to_account_mapping()
|
||||
|
||||
if email_attribute_name in session['samlUserdata']:
|
||||
|
@ -1068,6 +1109,7 @@ def saml_authorized():
|
|||
user.username),
|
||||
created_by='SAML Assertion')
|
||||
history.add()
|
||||
|
||||
user.plain_text_password = None
|
||||
user.update_profile()
|
||||
session['authentication_type'] = 'SAML'
|
||||
|
@ -1077,8 +1119,7 @@ def saml_authorized():
|
|||
|
||||
|
||||
def create_group_to_account_mapping():
|
||||
group_to_account_mapping_string = current_app.config.get(
|
||||
'SAML_GROUP_TO_ACCOUNT_MAPPING', None)
|
||||
group_to_account_mapping_string = Setting().get('saml_group_to_account_mapping')
|
||||
if group_to_account_mapping_string and len(
|
||||
group_to_account_mapping_string.strip()) > 0:
|
||||
group_to_account_mapping = group_to_account_mapping_string.split(',')
|
||||
|
@ -1119,17 +1160,23 @@ def uplift_to_admin(user):
|
|||
|
||||
|
||||
@index_bp.route('/saml/sls')
|
||||
@login_required
|
||||
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:
|
||||
clear_session()
|
||||
if url is not None:
|
||||
return redirect(url)
|
||||
elif current_app.config.get('SAML_LOGOUT_URL') is not None:
|
||||
return redirect(current_app.config.get('SAML_LOGOUT_URL'))
|
||||
elif Setting().get('saml_logout_url') is not None:
|
||||
return redirect(Setting().get('saml_logout_url'))
|
||||
else:
|
||||
return redirect(url_for('login'))
|
||||
else:
|
||||
|
|
|
@ -6,11 +6,17 @@ import os
|
|||
|
||||
from ..lib.certutil import KEY_FILE, CERT_FILE, create_self_signed_cert
|
||||
from ..lib.utils import urlparse
|
||||
from ..models.setting import Setting
|
||||
|
||||
|
||||
# The python3-saml library currently supports only the Redirect binding for IDP endpoints.
|
||||
# For SP, the Assertion Consumer Service endpoint supports HTTP-POST binding,
|
||||
# while the Single Logout Service endpoint uses HTTP-Redirect.
|
||||
# Therefore, to protect users from using unsupported features, settings
|
||||
# 'saml_idp_sso_binding', 'saml_idp_slo_binding', 'saml_sp_acs_binding' and 'saml_sp_sls_binding'
|
||||
# are not exposed on the front end SAML interface.
|
||||
class SAML(object):
|
||||
def __init__(self):
|
||||
if current_app.config['SAML_ENABLED']:
|
||||
if Setting().get('saml_enabled'):
|
||||
from onelogin.saml2.auth import OneLogin_Saml2_Auth
|
||||
from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser
|
||||
|
||||
|
@ -19,29 +25,34 @@ class SAML(object):
|
|||
self.OneLogin_Saml2_IdPMetadataParser = OneLogin_Saml2_IdPMetadataParser
|
||||
self.idp_data = None
|
||||
|
||||
if 'SAML_IDP_ENTITY_ID' in current_app.config:
|
||||
self.idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(
|
||||
current_app.config['SAML_METADATA_URL'],
|
||||
entity_id=current_app.config.get('SAML_IDP_ENTITY_ID',
|
||||
None),
|
||||
required_sso_binding=current_app.
|
||||
config['SAML_IDP_SSO_BINDING'])
|
||||
if Setting().get('saml_idp_entity_id'):
|
||||
try:
|
||||
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'))
|
||||
except:
|
||||
self.idp_data = None
|
||||
else:
|
||||
self.idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(
|
||||
current_app.config['SAML_METADATA_URL'],
|
||||
entity_id=current_app.config.get('SAML_IDP_ENTITY_ID',
|
||||
None))
|
||||
try:
|
||||
self.idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(
|
||||
Setting().get('saml_metadata_url'),
|
||||
entity_id=None)
|
||||
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')
|
||||
exit(-1)
|
||||
Setting().set('saml_enabled', False)
|
||||
|
||||
|
||||
def get_idp_data(self):
|
||||
|
||||
lifetime = timedelta(
|
||||
minutes=current_app.config['SAML_METADATA_CACHE_LIFETIME'])
|
||||
|
||||
if self.idp_timestamp + lifetime < datetime.now():
|
||||
minutes=int(Setting().get('saml_metadata_cache_lifetime')))
|
||||
if not hasattr(self,'idp_timestamp'):
|
||||
self.retrieve_idp_data()
|
||||
elif self.idp_timestamp + lifetime < datetime.now():
|
||||
background_thread = Thread(target=self.retrieve_idp_data())
|
||||
background_thread.start()
|
||||
|
||||
|
@ -49,22 +60,27 @@ class SAML(object):
|
|||
|
||||
def retrieve_idp_data(self):
|
||||
|
||||
if 'SAML_IDP_SSO_BINDING' in current_app.config:
|
||||
new_idp_data = self.OneLogin_Saml2_IdPMetadataParser.parse_remote(
|
||||
current_app.config['SAML_METADATA_URL'],
|
||||
entity_id=current_app.config.get('SAML_IDP_ENTITY_ID', None),
|
||||
required_sso_binding=current_app.config['SAML_IDP_SSO_BINDING']
|
||||
)
|
||||
if Setting().get('saml_idp_sso_binding'):
|
||||
try:
|
||||
new_idp_data = self.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'))
|
||||
except:
|
||||
new_idp_data = None
|
||||
else:
|
||||
new_idp_data = self.OneLogin_Saml2_IdPMetadataParser.parse_remote(
|
||||
current_app.config['SAML_METADATA_URL'],
|
||||
entity_id=current_app.config.get('SAML_IDP_ENTITY_ID', None))
|
||||
try:
|
||||
new_idp_data = self.OneLogin_Saml2_IdPMetadataParser.parse_remote(
|
||||
Setting().get('saml_metadata_url'),
|
||||
entity_id=Setting().get('saml_idp_entity_id'))
|
||||
except:
|
||||
new_idp_data = None
|
||||
if new_idp_data is not None:
|
||||
self.idp_data = new_idp_data
|
||||
self.idp_timestamp = datetime.now()
|
||||
current_app.logger.info(
|
||||
"SAML: IDP Metadata successfully retrieved from: " +
|
||||
current_app.config['SAML_METADATA_URL'])
|
||||
Setting().get('saml_metadata_url'))
|
||||
else:
|
||||
current_app.logger.info(
|
||||
"SAML: IDP Metadata could not be retrieved")
|
||||
|
@ -94,20 +110,19 @@ class SAML(object):
|
|||
metadata = self.get_idp_data()
|
||||
settings = {}
|
||||
settings['sp'] = {}
|
||||
if 'SAML_NAMEID_FORMAT' in current_app.config:
|
||||
settings['sp']['NameIDFormat'] = current_app.config[
|
||||
'SAML_NAMEID_FORMAT']
|
||||
if Setting().get('saml_nameid_format'):
|
||||
settings['sp']['NameIDFormat'] = Setting().get('saml_nameid_format')
|
||||
else:
|
||||
settings['sp']['NameIDFormat'] = self.idp_data.get('sp', {}).get(
|
||||
'NameIDFormat',
|
||||
'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified')
|
||||
settings['sp']['entityId'] = current_app.config['SAML_SP_ENTITY_ID']
|
||||
settings['sp']['entityId'] = Setting().get('saml_sp_entity_id')
|
||||
|
||||
|
||||
if ('SAML_CERT' in current_app.config) and ('SAML_KEY' in current_app.config):
|
||||
if (Setting().get('saml_cert_file')) and (Setting().get('saml_cert_key')):
|
||||
|
||||
saml_cert_file = current_app.config['SAML_CERT']
|
||||
saml_key_file = current_app.config['SAML_KEY']
|
||||
saml_cert_file = Setting().get('saml_cert_file')
|
||||
saml_key_file = Setting().get('saml_cert_key')
|
||||
|
||||
if os.path.isfile(saml_cert_file):
|
||||
cert = open(saml_cert_file, "r").readlines()
|
||||
|
@ -130,8 +145,8 @@ class SAML(object):
|
|||
settings['sp']['privateKey'] = "".join(key)
|
||||
|
||||
|
||||
if 'SAML_SP_REQUESTED_ATTRIBUTES' in current_app.config:
|
||||
saml_req_attr = json.loads(current_app.config['SAML_SP_REQUESTED_ATTRIBUTES'])
|
||||
if Setting().get('saml_sp_requested_attributes'):
|
||||
saml_req_attr = json.loads(Setting().get('saml_sp_requested_attributes'))
|
||||
settings['sp']['attributeConsumingService'] = {
|
||||
"serviceName": "PowerDNSAdmin",
|
||||
"serviceDescription": "PowerDNS-Admin - PowerDNS administration utility",
|
||||
|
@ -143,55 +158,52 @@ class SAML(object):
|
|||
|
||||
settings['sp']['assertionConsumerService'] = {}
|
||||
settings['sp']['assertionConsumerService'][
|
||||
'binding'] = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
||||
'binding'] = Setting().get('saml_sp_acs_binding')#'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
||||
settings['sp']['assertionConsumerService'][
|
||||
'url'] = own_url + '/saml/authorized'
|
||||
settings['sp']['singleLogoutService'] = {}
|
||||
settings['sp']['singleLogoutService'][
|
||||
'binding'] = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
|
||||
'binding'] = Setting().get('saml_sp_sls_binding')#'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
|
||||
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'] = current_app.config['SAML_DEBUG']
|
||||
settings['debug'] = bool(Setting().get('saml_debug'))
|
||||
settings['security'] = {}
|
||||
settings['security'][
|
||||
'digestAlgorithm'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
|
||||
settings['security']['metadataCacheDuration'] = None
|
||||
settings['security']['metadataValidUntil'] = None
|
||||
'digestAlgorithm'] = Setting().get('saml_digest_algorithm')
|
||||
settings['security']['metadataCacheDuration'] = Setting().get('saml_metadata_cache_duration') if Setting().get('saml_metadata_cache_duration') else None
|
||||
settings['security']['metadataValidUntil'] = Setting().get('saml_metadata_valid_until') if Setting().get('saml_metadata_valid_until') else None
|
||||
settings['security']['requestedAuthnContext'] = True
|
||||
settings['security'][
|
||||
'signatureAlgorithm'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
|
||||
settings['security']['wantAssertionsEncrypted'] = current_app.config.get(
|
||||
'SAML_ASSERTION_ENCRYPTED', True)
|
||||
'signatureAlgorithm'] = Setting().get('saml_signature_algorithm')
|
||||
settings['security']['wantAssertionsEncrypted'] = bool(Setting().get('saml_want_assertions_encrypted'))
|
||||
settings['security']['wantAttributeStatement'] = True
|
||||
settings['security']['wantNameId'] = True
|
||||
settings['security']['authnRequestsSigned'] = current_app.config[
|
||||
'SAML_SIGN_REQUEST']
|
||||
settings['security']['logoutRequestSigned'] = current_app.config[
|
||||
'SAML_SIGN_REQUEST']
|
||||
settings['security']['logoutResponseSigned'] = current_app.config[
|
||||
'SAML_SIGN_REQUEST']
|
||||
settings['security']['nameIdEncrypted'] = False
|
||||
settings['security']['signMetadata'] = True
|
||||
settings['security']['wantAssertionsSigned'] = True
|
||||
settings['security']['wantMessagesSigned'] = current_app.config.get(
|
||||
'SAML_WANT_MESSAGE_SIGNED', True)
|
||||
settings['security']['wantNameIdEncrypted'] = False
|
||||
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'] = current_app.config['SAML_SP_CONTACT_NAME']
|
||||
settings['contactPerson']['support']['givenName'] = current_app.config[
|
||||
'SAML_SP_CONTACT_MAIL']
|
||||
settings['contactPerson']['support']['emailAddress'] = Setting().get('saml_sp_contact_mail')
|
||||
settings['contactPerson']['support']['givenName'] = Setting().get('saml_sp_contact_name')
|
||||
settings['contactPerson']['technical'] = {}
|
||||
settings['contactPerson']['technical'][
|
||||
'emailAddress'] = current_app.config['SAML_SP_CONTACT_MAIL']
|
||||
settings['contactPerson']['technical'][
|
||||
'givenName'] = current_app.config['SAML_SP_CONTACT_NAME']
|
||||
settings['contactPerson']['technical']['emailAddress'] = Setting().get('saml_sp_contact_mail')
|
||||
settings['contactPerson']['technical']['givenName'] = Setting().get('saml_sp_contact_name')
|
||||
settings['organization'] = {}
|
||||
settings['organization']['en-US'] = {}
|
||||
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
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
<li><a href="#tabs-github" data-toggle="tab">Github OAuth</a></li>
|
||||
<li><a href="#tabs-azure" data-toggle="tab">Microsoft OAuth</a></li>
|
||||
<li><a href="#tabs-oidc" data-toggle="tab">OpenID Connect OAuth</a></li>
|
||||
<li><a href="#tabs-saml" data-toggle="tab">SAML</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tabs-general">
|
||||
|
@ -677,6 +678,438 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="tabs-saml">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form role="form" method="post" data-toggle="validator">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" value="saml" name="config_tab" />
|
||||
<fieldset>
|
||||
<legend>GENERAL</legend>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="saml_enabled" name="saml_enabled" class="checkbox" {% if SETTING.get('saml_enabled') %}checked{% endif %}>
|
||||
<label for="saml_enabled">Enable SAML</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>IDP</legend>
|
||||
<div class="form-group">
|
||||
<label for="saml_idp_entity_id">IDP Entity ID</label>
|
||||
<input type="text" class="form-control" name="saml_idp_entity_id" id="saml_idp_entity_id" placeholder="e.g. https://idp.example.edu/idp" data-error="Please input SAML IDP Entity ID" value="{{ SETTING.get('saml_idp_entity_id') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_metadata_url">IDP Metadata URL</label>
|
||||
<input type="text" class="form-control" name="saml_metadata_url" id="saml_metadata_url" placeholder="SAML Metadata URL" data-error="Please input SAML Metadata URL" value="{{ SETTING.get('saml_metadata_url') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_metadata_cache_lifetime">IDP Metadata Cache Lifetime</label>
|
||||
<input type="text" class="form-control" name="saml_metadata_cache_lifetime" id="saml_metadata_cache_lifetime" placeholder="SAML Metadata Cache Lifetime" data-error="Please input SAML Metadata Cache Lifetime" value="{{ SETTING.get('saml_metadata_cache_lifetime') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_idp_sso_binding">IDP SSO Binding</label>
|
||||
<input type="text" class="form-control" name="saml_idp_sso_binding" id="saml_idp_sso_binding" placeholder="e.g. urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" data-error="Please input SAML IDP SSO Binding" value="{{ SETTING.get('saml_idp_sso_binding') }}" readonly>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_idp_slo_binding">IDP SLO Binding</label>
|
||||
<input type="text" class="form-control" name="saml_idp_slo_binding" id="saml_idp_slo_binding" placeholder="e.g. urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" data-error="Please input SAML IDP SLO Binding" value="{{ SETTING.get('saml_idp_slo_binding') }}" readonly>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>SP</legend>
|
||||
<div class="form-group">
|
||||
<label for="saml_sp_entity_id">SP Entity ID</label>
|
||||
<input type="text" class="form-control" name="saml_sp_entity_id" id="saml_sp_entity_id" placeholder="http://<SAML SP Entity ID>" data-error="Please input SAML SP Entity ID" value="{{ SETTING.get('saml_sp_entity_id') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_nameid_format">SP NameID Format</label>
|
||||
<input type="text" class="form-control" name="saml_nameid_format" id="saml_nameid_format" placeholder="e.g. urn:oid:0.9.2342.19200300.100.1.1" data-error="Please input NameID Format" value="{{ SETTING.get('saml_nameid_format') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_metadata_cache_duration">SP Metadata Cache Duration</label>
|
||||
<input type="text" class="form-control" name="saml_metadata_cache_duration" id="saml_metadata_cache_duration" placeholder="eg PT5M" data-error="Please input Metadata Cache Duration" value="{{ SETTING.get('saml_metadata_cache_duration') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_metadata_valid_until">SP Metadata Valid Until</label>
|
||||
<input type="text" class="form-control" name="saml_metadata_valid_until" id="saml_metadata_valid_until" placeholder="YYYY-MM-DDThh:mm:ssZ" data-error="Please input Metadata Expiration Date" value="{{ SETTING.get('saml_metadata_valid_until') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="saml_sign_metadata" name="saml_sign_metadata" class="checkbox" {% if SETTING.get('saml_sign_metadata') %}checked{% endif %}>
|
||||
<label for="saml_sign_metadata">Sign SP Metadata </label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_sp_acs_binding">SP ACS Binding</label>
|
||||
<input type="text" class="form-control" name="saml_sp_acs_binding" id="saml_sp_acs_binding" placeholder="e.g. urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" data-error="Please input SAML SP ACS Binding" value="{{ SETTING.get('saml_sp_acs_binding') }}" readonly>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_sp_acs_binding">SP SLS Binding</label>
|
||||
<input type="text" class="form-control" name="saml_sp_sls_binding" id="saml_sp_sls_binding" placeholder="e.g. urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" data-error="Please input SAML SP SLS Binding" value="{{ SETTING.get('saml_sp_sls_binding') }}" readonly>
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>SP ATTRIBUTES</legend>
|
||||
<div class="form-group">
|
||||
<label for="saml_sp_requested_attributes">Requested Attributes</label>
|
||||
<input type="text" class="form-control" name="saml_sp_requested_attributes" id="saml_sp_requested_attributes" placeholder="must be valid JSON" data-error="Plesae input Requested Attributes" value="{{ SETTING.get('saml_sp_requested_attributes') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_attribute_email">Email</label>
|
||||
<input type="text" class="form-control" name="saml_attribute_email" id="saml_attribute_email" placeholder="e.g. urn:oid:0.9.2342.19200300.100.1.3" data-error="Please input SAML Email Attribute" value="{{ SETTING.get('saml_attribute_email') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_attribute_givenname">Given Name</label>
|
||||
<input type="text" class="form-control" name="saml_attribute_givenname" id="saml_attribute_givenname" placeholder="e.g. urn:oid:2.5.4.42" data-error="Please input SAML Given Name Attribute" value="{{ SETTING.get('saml_attribute_givenname') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_attribute_surname">Surname</label>
|
||||
<input type="text" class="form-control" name="saml_attribute_surname" id="saml_attribute_surname" placeholder="e.g. urn:oid:2.5.4.4" data-error="Please input SAML Surname Attribute" value="{{ SETTING.get('saml_attribute_surname') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_attribute_username">Username</label>
|
||||
<input type="text" class="form-control" name="saml_attribute_username" id="saml_attribute_username" placeholder="e.g. urn:oid:0.9.2342.19200300.100.1.1" data-error="Please input SAML Username Attribute" value="{{ SETTING.get('saml_attribute_username') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_sp_contact_name">SP Contact Name</label>
|
||||
<input type="text" class="form-control" name="saml_sp_contact_name" id="saml_sp_contact_name" placeholder="<contact name>" data-error="Please input SAML SP contact name" value="{{ SETTING.get('saml_sp_contact_name') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_sp_contact_mail">SP Contact Mail</label>
|
||||
<input type="text" class="form-control" name="saml_sp_contact_mail" id="saml_sp_contact_mail" placeholder="<contact mail>" data-error="Please input SAML SP contact mail" value="{{ SETTING.get('saml_sp_contact_mail') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>SIGNING & ENCRYPTION</legend>
|
||||
<div class="form-group">
|
||||
<label for="saml_cert_file">Cert File</label>
|
||||
<input type="text" class="form-control" name="saml_cert_file" id="saml_cert_file" placeholder="e.g. opt/web/PowerDNS-Admin/cert.crt" data-error="Please input SAML cert file path" value="{{ SETTING.get('saml_cert_file') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_cert_key">Cert Key</label>
|
||||
<input type="text" class="form-control" name="saml_cert_key" id="saml_cert_key" placeholder="e.g. opt/web/PowerDNS-Admin/key.pem" data-error="Please input SAML key file path" value="{{ SETTING.get('saml_cert_key') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="saml_sign_authn_request" name="saml_sign_authn_request" class="checkbox" {% if SETTING.get('saml_sign_authn_request') %}checked{% endif %}>
|
||||
<label for="saml_sign_authn_request">Sign Authentication Request</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="saml_sign_logout_request_response" name="saml_sign_logout_request_response" class="checkbox" {% if SETTING.get('saml_sign_logout_request_response') %}checked{% endif %}>
|
||||
<label for="saml_sign_logout_request_response">Sign Logout Request & Response</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="saml_want_assertions_encrypted" name="saml_want_assertions_encrypted" class="checkbox" {% if SETTING.get('saml_want_assertions_encrypted') %}checked{% endif %}>
|
||||
<label for="saml_want_assertions_encrypted">Want Assertions Encrypted</label>
|
||||
</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_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 %}>
|
||||
<label for="saml_want_message_signed">Want Message Signed </label>
|
||||
</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>
|
||||
</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_nameid_encrypted">Want NameID Encrypted</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_digest_algorithm">Digest Algorithm</label>
|
||||
<input type="text" class="form-control" name="saml_digest_algorithm" id="saml_digest_algorithm" placeholder="must be a valid algorithm" data-error="Please input SAML digest algorithm" value="{{ SETTING.get('saml_digest_algorithm') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_signature_algorithm">Signature Algorithm</label>
|
||||
<input type="text" class="form-control" name="saml_signature_algorithm" id="saml_signature_algorithm" placeholder="must be a valid algorithm" data-error="Please input SAML signature algorithm" value="{{ SETTING.get('saml_signature_algorithm') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>LOGOUT</legend>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="saml_logout" name="saml_logout" class="checkbox" {% if SETTING.get('saml_logout') %}checked{% endif %}>
|
||||
<label for="saml_logout">SAML Logout</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_logout_url">Logout URL</label>
|
||||
<input type="text" class="form-control" name="saml_logout_url" id="saml_logout_url" placeholder="must be a valid logout URL" data-error="Please input SAML logout URL" value="{{ SETTING.get('saml_logout_url') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>AUTOPROVISION</legend>
|
||||
<div class="form-group">
|
||||
<label for="saml_attribute_admin">Admin SP Attribute</label>
|
||||
<input type="text" class="form-control" name="saml_attribute_admin" id="saml_attribute_admin" placeholder="e.g. https://example.edu/pdns-admin" data-error="Please input SAML Admin Attribute" value="{{ SETTING.get('saml_attribute_admin') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_attribute_account">Account SP Attribute</label>
|
||||
<input type="text" class="form-control" name="saml_attribute_account" id="saml_attribute_account" placeholder="e.g. https://example.edu/pdns-account" data-error="Please input SAML Account Attribute" value="{{ SETTING.get('saml_attribute_account') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<legend>Help</legend>
|
||||
<dl class="dl-horizontal">
|
||||
<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>IDP Entity ID</b>
|
||||
</li>
|
||||
<li>
|
||||
<b>IDP Metadata URL</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>SP Username Attribute</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> - Specify the EntityID of the IDP to use.<br>
|
||||
Only needed if more the XML provided in the SAML_METADATA_URL contains more than 1 IDP Entity.
|
||||
</li>
|
||||
<li>
|
||||
<b>IDP Metadata URL</b> - Url where the XML of the Identity Provider Metadata is published.
|
||||
</li>
|
||||
<li>
|
||||
<b>IDP Metadata Cache Lifetime</b> - Cache Lifetime in minutes before fresh metadata are fetched from the IDP Metadata URL
|
||||
</li>
|
||||
<li>
|
||||
<b>IDP SSO Binding</b> - SAML SSO binding format required for the IDP to use<br>
|
||||
</li>
|
||||
<li>
|
||||
<b>IDP SLO Binding</b> - SAML SLO binding format required for the IDP to use<br>
|
||||
</li>
|
||||
<b>NOTE:</b>:The Binding settings are currently disabled, as the underlying saml library currently supports only the Redirect binding for IDP endpoints.<br>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>SP</dt>
|
||||
<dd> <br>
|
||||
<ul>
|
||||
<li>
|
||||
<b>SP Entity ID</b> - Specify the EntityID of your Service Provider (SP).
|
||||
</li>
|
||||
<li>
|
||||
<b>SP NameID Format</b> - NameID format to request. This specifies the content of the NameID and any associated processing rules.
|
||||
</li>
|
||||
<li>
|
||||
<b>SP Metadata Cache Duration</b> - Set the cache duration of generated metadata.<br>
|
||||
Use PT5M to set cache duration to 5 minutes.
|
||||
</li>
|
||||
<li>
|
||||
<b>SP Metadata Valid Until</b> - Set the expiration date, in XML DateTime String format, for generated metadata.<br>
|
||||
XML DateTime String Format: "YYYY-MM-DDThh:mm:ssZ", Z can be Z for timezone 0 or "+-hh:mm" for other timezones.
|
||||
</li>
|
||||
<li>
|
||||
<b>Sign SP Metadata</b> - Choose whether metadata produced is signed.
|
||||
</li>
|
||||
<li>
|
||||
<b>SP ACS Binding</b> - SAML Assertion Consumer Service Binding Format for the SP to use on login.
|
||||
</li>
|
||||
<li>
|
||||
<b>SP SLS Binding</b> - SAML Single Logout Service Binding Format for the SP to use on logout.
|
||||
</li>
|
||||
<b>NOTE:</b>:The Binding settings are currently disabled, as in the underlying saml library, the ACS endpoint currently supports
|
||||
only the HTTP-POST binding, while the SLS endpoint supports only HTTP-Redirect.
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>SP ATTRIBUTES</dt>
|
||||
<dd> <br>
|
||||
<ul>
|
||||
<li>
|
||||
<b>Requested Attributes</b> - The following parameter defines RequestedAttributes section in SAML metadata
|
||||
since certain IDPs require explicitly requesting attributes.<br>
|
||||
If not provided, the Attribute Consuming Service Section will not be available in metadata.
|
||||
<br>
|
||||
<u>Possible attributes</u>:<br>
|
||||
<i>name (mandatory), nameFormat, isRequired, friendlyName</i>
|
||||
<br>
|
||||
<b>NOTE:</b> This parameter requires to be entered in valid JSON format as displayed below
|
||||
and multiple attributes can be given.
|
||||
<br>
|
||||
Following <i>example</i>:
|
||||
<br>
|
||||
<code>SAML_SP_REQUESTED_ATTRIBUTES = '[ <br>
|
||||
{"name": "urn:oid:0.9.2342.19200300.100.1.3", "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "isRequired": true, "friendlyName": "email"}, <br>
|
||||
{"name": "mail", "isRequired": false, "friendlyName": "test-field"} <br>
|
||||
]'</code>
|
||||
<br>
|
||||
produces following <i>metadata section</i>:
|
||||
<br><code>
|
||||
<md:AttributeConsumingService index="1"><br>
|
||||
<md:RequestedAttribute Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" FriendlyName="email" isRequired="true"/><br>
|
||||
<md:RequestedAttribute Name="mail" FriendlyName="test-field"/><br>
|
||||
</md:AttributeConsumingService></code>
|
||||
</li>
|
||||
</ul>
|
||||
The following attribute values must be <i>derived</i> from <b>Requested Attributes</b>, and must be in the form of a <b>valid URN</b> (e.g. <i>urn:oid:2.5.4.4</i>):
|
||||
<ul>
|
||||
<li>
|
||||
<b>Email</b> - Attribute to use for Email address.
|
||||
</li>
|
||||
<li>
|
||||
<b>Given Name</b> - Attribute to use for Given name.
|
||||
</li>
|
||||
<li>
|
||||
<b>Surname</b> - Attribute to use for Surname.
|
||||
</li>
|
||||
<li>
|
||||
<b>Username</b> - Attribute to use for username.
|
||||
</li>
|
||||
</ul>
|
||||
These may be generic strings containing your information:
|
||||
<ul>
|
||||
<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.<br>
|
||||
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.<br>
|
||||
</li>
|
||||
<li>
|
||||
<b>Sign Authentication Request</b> - Configures if the SP should sign <b>outgoing</b> authentication requests.
|
||||
</li>
|
||||
<li>
|
||||
<b>Sign Logout Request & Response</b> - Configures if the SP should sign <b>outgoing</b> Logout requests & Logout responses.
|
||||
</li>
|
||||
<li>
|
||||
<b>Want Assertions Encrypted</b> - Choose whether the SP expects <b>incoming</b> assertions received from the IDP to be encrypted.
|
||||
</li>
|
||||
<li>
|
||||
<b>Want Assertions Signed</b> - Choose whether the SP expects <b>incoming</b> assertions to be signed.
|
||||
</li>
|
||||
<li>
|
||||
<b>NameID Encrypted</b> - Indicates that the <b>outgoing</b> nameID of the logoutRequest sent by this SP will be encrypted.
|
||||
</li>
|
||||
<li>
|
||||
<b>Want NameID Encrypted</b> - Indicates a requirement for the <b>incoming</b> NameID received by this SP to be encrypted.
|
||||
</li>
|
||||
<li>
|
||||
<b>Want Message Signed</b> - Choose whether the SP expects <b>incoming</b> messages to be signed.
|
||||
</li>
|
||||
<li>
|
||||
<b>Digest Algorithm</b> - Encryption algorithm for the DigestValue, which is part of the validation process to ensure the integrity of the XML message.
|
||||
</li>
|
||||
<li>
|
||||
<b>Signature Algorithm</b> - Encryption algorithm for the message Signature.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt>LOGOUT</dt>
|
||||
<dd> <br>
|
||||
<ul>
|
||||
<li>
|
||||
<b>SAML Logout</b> - Choose whether user is logged out of the SAML session using SLO.
|
||||
<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> <br>
|
||||
Assert user Admin status and associated Accounts with 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>
|
||||
If not included in assertion, or set to something other than 'true',
|
||||
the user is set as a non-administrator user.
|
||||
</li>
|
||||
<li>
|
||||
<b>Account</b> - Attribute to get account names from.<br>
|
||||
If set, the user will be added and removed from accounts to match
|
||||
what's in the login assertion.<br>Accounts that don't exist will
|
||||
be created and the user added to them.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -691,6 +1124,13 @@
|
|||
{%- endassets %}
|
||||
|
||||
<script>
|
||||
|
||||
$(document).ready(function() {
|
||||
// Focus on text attribute of modal
|
||||
$("#modal_requested_attributes").on('shown.bs.modal', function() {
|
||||
$("#txt_req_attr").focus();
|
||||
});
|
||||
});
|
||||
|
||||
$(function() {
|
||||
$('#tabs').tabs({
|
||||
|
@ -1050,6 +1490,129 @@
|
|||
{% endif %}
|
||||
//END: OIDC Tab JS
|
||||
|
||||
// START: SAML tab js
|
||||
$('#saml_enabled').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
}).on('ifChanged', function(e) {
|
||||
var is_enabled = e.currentTarget.checked;
|
||||
if (is_enabled){
|
||||
$('#saml_metadata_url').prop('required', true);
|
||||
$('#saml_idp_sso_binding').prop('required', true);
|
||||
$('#saml_idp_slo_binding').prop('required', true);
|
||||
$('#saml_sp_entity_id').prop('required', true);
|
||||
$('#saml_nameid_format').prop('required', true);
|
||||
$('#saml_sp_acs_binding').prop('required', true);
|
||||
$('#saml_sp_sls_binding').prop('required', true);
|
||||
$('#saml_sp_requested_attributes').prop('required', true);
|
||||
$('#saml_attribute_username').prop('required', true);
|
||||
$('#saml_cert_file').prop('required', true);
|
||||
$('#saml_cert_key').prop('required', true);
|
||||
$('#saml_digest_algorithm').prop('required', true);
|
||||
$('#saml_signature_algorithm').prop('required', true);
|
||||
if ($('#saml_logout').is(":checked")) {
|
||||
$('#saml_logout_url').prop('required', true);
|
||||
}
|
||||
} else {
|
||||
$('#saml_metadata_url').prop('required', false);
|
||||
$('#saml_idp_sso_binding').prop('required', false);
|
||||
$('#saml_idp_slo_binding').prop('required', false);
|
||||
$('#saml_sp_entity_id').prop('required', false);
|
||||
$('#saml_nameid_format').prop('required', false);
|
||||
$('#saml_sp_acs_binding').prop('required', false);
|
||||
$('#saml_sp_sls_binding').prop('required', false);
|
||||
$('#saml_sp_requested_attributes').prop('required', false);
|
||||
$('#saml_attribute_username').prop('required', false);
|
||||
$('#saml_cert_file').prop('required', false);
|
||||
$('#saml_cert_key').prop('required', false);
|
||||
$('#saml_digest_algorithm').prop('required', false);
|
||||
$('#saml_signature_algorithm').prop('required', false);
|
||||
$('#saml_logout_url').prop('required', false);
|
||||
}
|
||||
});
|
||||
// init validation requirement at first time page load
|
||||
{% if SETTING.get('saml_enabled') %}
|
||||
$('#saml_metadata_url').prop('required', true);
|
||||
$('#saml_idp_sso_binding').prop('required', true);
|
||||
$('#saml_idp_slo_binding').prop('required', true);
|
||||
$('#saml_sp_entity_id').prop('required', true);
|
||||
$('#saml_nameid_format').prop('required', true);
|
||||
$('#saml_sp_acs_binding').prop('required', true);
|
||||
$('#saml_sp_sls_binding').prop('required', true);
|
||||
$('#saml_sp_requested_attributes').prop('required', true);
|
||||
$('#saml_attribute_username').prop('required', true);
|
||||
$('#saml_cert_file').prop('required', true);
|
||||
$('#saml_cert_key').prop('required', true);
|
||||
$('#saml_digest_algorithm').prop('required', true);
|
||||
$('#saml_signature_algorithm').prop('required', true);
|
||||
if ($('#saml_logout').is(":checked")) {
|
||||
$('#saml_logout_url').prop('required', true);
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
$(document.body).on("focus", "#saml_sp_requested_attributes", function (e) {
|
||||
var req_attr = $(this);
|
||||
req_attr.blur();
|
||||
var txt_data = req_attr.val();
|
||||
var modal = $("#modal_requested_attributes");
|
||||
var form = "<textarea spellcheck=\"false\" style=\"min-width: 100%;color: #333;\" placeholder=\"SP Requested Attributes in JSON format\" \
|
||||
rows=\"10\" id=\"txt_req_attr\" name=\"txt_req_attr\">" + txt_data + "</textarea>";
|
||||
modal.find('.modal-body p').html(form);
|
||||
modal.find('#button_save').click(function() {
|
||||
data = modal.find('#txt_req_attr').val();
|
||||
req_attr.val(data);
|
||||
modal.modal('hide');
|
||||
});
|
||||
modal.modal('show');
|
||||
});
|
||||
|
||||
$('#saml_logout').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
}).on('ifChanged', function(e) {
|
||||
var is_enabled = e.currentTarget.checked;
|
||||
if (is_enabled){
|
||||
$('#saml_logout_url').prop('required', true);
|
||||
}
|
||||
else{
|
||||
$('#saml_logout_url').prop('required', false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#saml_sign_authn_request').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
$('#saml_sign_logout_request_response').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
$('#saml_nameid_encrypted').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
$('#saml_want_nameid_encrypted').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
$('#saml_want_assertions_encrypted').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
$('#saml_want_assertions_signed').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
$('#saml_want_message_signed').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
$('#saml_sign_metadata').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
})
|
||||
// END: SAML Tab js
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -1098,5 +1661,26 @@
|
|||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
<div class="modal fade modal-primary" id="modal_requested_attributes">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">SP Requested Attributes</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-flat btn-primary" id="button_save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue