Added UI Interface for SAML Settings
This commit is contained in:
parent
3d839c92d2
commit
5d533173b1
|
@ -110,15 +110,15 @@ class Setting(db.Model):
|
|||
'oidc_oauth_email': 'email',
|
||||
'oidc_oauth_account_name_property': '',
|
||||
'oidc_oauth_account_description_property': '',
|
||||
'saml_enabled': True,
|
||||
'saml_enabled': False,
|
||||
'saml_debug': True,
|
||||
'saml_metadata_url': 'https://md.aai.grnet.gr/aggregates/grnet-metadata.xml',
|
||||
'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.uoa.gr/idp/shibboleth',
|
||||
'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" }, \
|
||||
|
@ -136,29 +136,25 @@ class Setting(db.Model):
|
|||
'saml_attribute_group': None,
|
||||
'saml_group_admin_name': None,
|
||||
'saml_group_to_account_mapping': None,
|
||||
'saml_sp_entity_id': 'https://dns.uoa.gr',
|
||||
'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': True,
|
||||
'saml_logout': False,
|
||||
'saml_logout_url': 'https://google.com',
|
||||
'saml_want_assertions_encrypted': False,
|
||||
'saml_digest_algorithm': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
|
||||
'saml_signature_algorithm': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
|
||||
'saml_want_assertions_signed': True,
|
||||
'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': 'PT5M',
|
||||
'saml_metadata_valid_until': '2021-12-31T00:00:00Z',
|
||||
'saml_autoprovisioning': True,
|
||||
'saml_urn_prefix': 'urn:mace:uoa.gr',
|
||||
'saml_autoprovisioning_attribute': 'urn:oid:1.3.6.1.4.1.5923.1.1.1.7',
|
||||
'saml_purge': False,
|
||||
'saml_metadata_cache_duration': None,
|
||||
'saml_metadata_valid_until': None,
|
||||
'forward_records_allow_edit': {
|
||||
'A': True,
|
||||
'AAAA': True,
|
||||
|
|
|
@ -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 ..services.saml import SAML
|
||||
from ..lib.schema import ApiPlainKeySchema
|
||||
|
||||
apikey_plain_schema = ApiPlainKeySchema(many=True)
|
||||
|
@ -1729,29 +1728,6 @@ def setting_authentication():
|
|||
Setting().set('saml_metadata_valid_until',
|
||||
request.form.get('saml_metadata_valid_until'))
|
||||
|
||||
Setting().set(
|
||||
'saml_autoprovisioning', True
|
||||
if request.form.get('saml_autoprovisioning') == 'ON' else False)
|
||||
if request.form.get('autoprovisioning')=='ON':
|
||||
Setting().set('saml_autoprovisioning_attribute',
|
||||
request.form.get('saml_autoprovisioning_attribute'))
|
||||
|
||||
if validateURN(request.form.get('saml_urn_value')):
|
||||
Setting().set('saml_urn_value',
|
||||
request.form.get('saml_urn_value'))
|
||||
else:
|
||||
return render_template('admin_setting_authentication.html',
|
||||
error="Invalid urn")
|
||||
else:
|
||||
if request.form.get('saml_autoprovisioning_attribute'):
|
||||
Setting().set('saml_autoprovisioning_attribute',
|
||||
request.form.get('saml_autoprovisioning_attribute'))
|
||||
if request.form.get('saml_urn_value'):
|
||||
Setting().set('saml_urn_value',
|
||||
request.form.get('saml_urn_value'))
|
||||
Setting().set('saml_purge', True
|
||||
if request.form.get('purge') == 'ON' else False)
|
||||
|
||||
result = {
|
||||
'status': True,
|
||||
'msg':
|
||||
|
|
|
@ -1030,80 +1030,51 @@ def saml_authorized():
|
|||
user.firstname = name[0]
|
||||
user.lastname = ' '.join(name[1:])
|
||||
|
||||
if not Setting().get('saml_autoprovisioning'):
|
||||
if group_attribute_name:
|
||||
user_groups = session['samlUserdata'].get(group_attribute_name, [])
|
||||
else:
|
||||
user_groups = []
|
||||
if admin_attribute_name or group_attribute_name:
|
||||
user_accounts = set(user.get_accounts())
|
||||
saml_accounts = []
|
||||
for group_mapping in group_to_account_mapping:
|
||||
mapping = group_mapping.split('=')
|
||||
group = mapping[0]
|
||||
account_name = mapping[1]
|
||||
if group_attribute_name:
|
||||
user_groups = session['samlUserdata'].get(group_attribute_name, [])
|
||||
else:
|
||||
user_groups = []
|
||||
if admin_attribute_name or group_attribute_name:
|
||||
user_accounts = set(user.get_accounts())
|
||||
saml_accounts = []
|
||||
for group_mapping in group_to_account_mapping:
|
||||
mapping = group_mapping.split('=')
|
||||
group = mapping[0]
|
||||
account_name = mapping[1]
|
||||
|
||||
if group in user_groups:
|
||||
account = handle_account(account_name)
|
||||
saml_accounts.append(account)
|
||||
|
||||
for account_name in session['samlUserdata'].get(
|
||||
account_attribute_name, []):
|
||||
if group in user_groups:
|
||||
account = handle_account(account_name)
|
||||
saml_accounts.append(account)
|
||||
saml_accounts = set(saml_accounts)
|
||||
for account in saml_accounts - user_accounts:
|
||||
account.add_user(user)
|
||||
history = History(msg='Adding {0} to account {1}'.format(
|
||||
user.username, account.name),
|
||||
created_by='SAML Assertion')
|
||||
history.add()
|
||||
for account in user_accounts - saml_accounts:
|
||||
account.remove_user(user)
|
||||
history = History(msg='Removing {0} from account {1}'.format(
|
||||
user.username, account.name),
|
||||
created_by='SAML Assertion')
|
||||
history.add()
|
||||
if admin_attribute_name and 'true' in session['samlUserdata'].get(
|
||||
admin_attribute_name, []):
|
||||
uplift_to_admin(user)
|
||||
elif admin_group_name in user_groups:
|
||||
uplift_to_admin(user)
|
||||
elif admin_attribute_name or group_attribute_name:
|
||||
if user.role.name != 'User':
|
||||
user.role_id = Role.query.filter_by(name='User').first().id
|
||||
history = History(msg='Demoting {0} to user'.format(
|
||||
user.username),
|
||||
created_by='SAML Assertion')
|
||||
history.add()
|
||||
elif Setting().get('saml_autoprovisioning'):
|
||||
urn_prefix = Setting().get('saml_urn_prefix')
|
||||
autoprovisioning_attribute = Setting().get('saml_autoprovisioning_attribute')
|
||||
Entitlements = []
|
||||
if autoprovisioning_attribute in session['samlUserdata']:
|
||||
for k in session['samlUserdata'][autoprovisioning_attribute]:
|
||||
Entitlements.append(k)
|
||||
|
||||
if len(Entitlements)==0 and Setting().get('saml_purge'):
|
||||
if user.role.name != 'User':
|
||||
user.role_id = Role.query.filter_by(name='User').first().id
|
||||
history = History(msg='Demoting {0} to user'.format(
|
||||
user.username),
|
||||
created_by='SAML Autoprovision')
|
||||
history.add()
|
||||
elif len(Entitlements)!=0:
|
||||
if checkForPDAEntries(Entitlements, urn_prefix):
|
||||
user.updateUser(Entitlements, urn_prefix)
|
||||
else:
|
||||
current_app.logger.warning('Not a single powerdns-admin record was found, possibly a typo in the prefix')
|
||||
if Setting().get('saml_purge'):
|
||||
current_app.logger.warning('Procceding to revoke every privilige from ' + user.username + '.' )
|
||||
if user.role.name != 'User':
|
||||
user.role_id = Role.query.filter_by(name='User').first().id
|
||||
history = History(msg='Demoting {0} to user'.format(
|
||||
user.username),
|
||||
created_by='SAML Autoprovision')
|
||||
history.add()
|
||||
|
||||
for account_name in session['samlUserdata'].get(
|
||||
account_attribute_name, []):
|
||||
account = handle_account(account_name)
|
||||
saml_accounts.append(account)
|
||||
saml_accounts = set(saml_accounts)
|
||||
for account in saml_accounts - user_accounts:
|
||||
account.add_user(user)
|
||||
history = History(msg='Adding {0} to account {1}'.format(
|
||||
user.username, account.name),
|
||||
created_by='SAML Assertion')
|
||||
history.add()
|
||||
for account in user_accounts - saml_accounts:
|
||||
account.remove_user(user)
|
||||
history = History(msg='Removing {0} from account {1}'.format(
|
||||
user.username, account.name),
|
||||
created_by='SAML Assertion')
|
||||
history.add()
|
||||
if admin_attribute_name and 'true' in session['samlUserdata'].get(
|
||||
admin_attribute_name, []):
|
||||
uplift_to_admin(user)
|
||||
elif admin_group_name in user_groups:
|
||||
uplift_to_admin(user)
|
||||
elif admin_attribute_name or group_attribute_name:
|
||||
if user.role.name != 'User':
|
||||
user.role_id = Role.query.filter_by(name='User').first().id
|
||||
history = History(msg='Demoting {0} to user'.format(
|
||||
user.username),
|
||||
created_by='SAML Assertion')
|
||||
history.add()
|
||||
|
||||
user.plain_text_password = None
|
||||
user.update_profile()
|
||||
|
|
|
@ -869,38 +869,6 @@
|
|||
<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>
|
||||
<div class="form-group">
|
||||
<label>Roles Autoprovisioning</label> <br />
|
||||
<label>
|
||||
<input type="radio" name="saml_autoprovisioning" id="saml_autoprovisioning_off" value="OFF"
|
||||
{% if not SETTING.get('saml_autoprovisioning') %}checked{% endif %}> OFF
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="saml_autoprovisioning" id="saml_autoprovisioning_on" value="ON"
|
||||
{% if SETTING.get('saml_autoprovisioning') %}checked{% endif %}> ON
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="saml_autoprovisioning_attribute">Roles provisioning field</label>
|
||||
<input type="text" class="form-control" name="saml_autoprovisioning_attribute" id="saml_autoprovisioning_attribute" placeholder="e.g. eduPersonEntitlement" data-error="Please input field responsible for autoprovisioning" value="{{ SETTING.get('saml_autoprovisioning_attribute') }}">
|
||||
<span class="help-block with-errors"></span>
|
||||
</div>
|
||||
<div class="form-group {% if error %}has-error{% endif %}">
|
||||
<label for="saml_urn_prefix">Urn prefix</label>
|
||||
<input type="text" class="form-control" name="saml_urn_prefix" id="saml_urn_prefix" placeholder="e.g. urn:mace:<yourOrganization>" data-error="Please fill this field" value="{{ SETTING.get('saml_urn_prefix') }}">
|
||||
{% if error %}
|
||||
<span class="help-block with-errors">Please input the correct prefix for your urn value</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Purge Roles If Empty</label> <br />
|
||||
<label>
|
||||
<input type="radio" name="saml_purge" id="saml_purge_off" value="OFF" {% if not SETTING.get('saml_purge') %}checked{% endif %}> OFF
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="radio" name="saml_purge" id="saml_purge_on" value="ON" {% if SETTING.get('saml_purge') %}checked{% endif %}> ON
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-flat btn-primary">Save</button>
|
||||
|
@ -1139,43 +1107,6 @@
|
|||
be created and the user added to them.
|
||||
</li>
|
||||
</ul>
|
||||
Provision PDA Role/Domains/Accounts based on urn SAML Attributes.<br>
|
||||
<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.<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.<br>
|
||||
e.g. <i>urn:oid:x.x.x.x.x</i><br>
|
||||
The allowed syntax for records inside this attribute in your SAML Token is: <br>
|
||||
<ul>if <i>PDA-Role∈[Administrator, Operator]</i>:
|
||||
<ul>
|
||||
<li>
|
||||
prefix:"powerdns-admin":PDA-Role
|
||||
</li>
|
||||
</ul>
|
||||
else (<i>if user</i>): <br>
|
||||
<ul>
|
||||
<li>
|
||||
prefix:"powerdns-admin":PDA-Role:<domain>:<account>
|
||||
</li>
|
||||
</ul>
|
||||
where <i>prefix</i> is given in the field "Urn prefix".
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<b>Urn prefix</b> - The prefix used before the static keyword "powerdns-admin" for your entitlements in the SAML token.
|
||||
Must comply with RFC no.8141.<br> e.g.<i>urn:mace:<your_organization></i>
|
||||
</li>
|
||||
<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.<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>
|
||||
</dl>
|
||||
</div>
|
||||
|
@ -1562,23 +1493,6 @@
|
|||
//END: OIDC Tab JS
|
||||
|
||||
// START: SAML tab js
|
||||
$('#saml_autoprovisioning_on').iCheck({
|
||||
radioClass: 'iradio_square-blue',
|
||||
increaseArea: '20%'
|
||||
});
|
||||
$('#saml_autoprovisioning_off').iCheck({
|
||||
radioClass: 'iradio_square-blue',
|
||||
increaseArea: '20%'
|
||||
});
|
||||
$('#saml_purge_on').iCheck({
|
||||
radioClass: 'iradio_square-blue',
|
||||
increaseArea: '20%'
|
||||
});
|
||||
$('#saml_purge_off').iCheck({
|
||||
radioClass: 'iradio_square-blue',
|
||||
increaseArea: '20%'
|
||||
});
|
||||
|
||||
$('#saml_enabled').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
|
@ -1601,10 +1515,6 @@
|
|||
if ($('#saml_logout').is(":checked")) {
|
||||
$('#saml_logout_url').prop('required', true);
|
||||
}
|
||||
if ($('#saml_autoprovisioning_on').is(":checked")) {
|
||||
$('#saml_autoprovisioning_attribute').prop('required', true);
|
||||
$('#saml_urn_prefix').prop('required', true);
|
||||
}
|
||||
} else {
|
||||
$('#saml_metadata_url').prop('required', false);
|
||||
$('#saml_idp_sso_binding').prop('required', false);
|
||||
|
@ -1620,8 +1530,6 @@
|
|||
$('#saml_digest_algorithm').prop('required', false);
|
||||
$('#saml_signature_algorithm').prop('required', false);
|
||||
$('#saml_logout_url').prop('required', false);
|
||||
$('#saml_autoprovisioning_attribute').prop('required', false);
|
||||
$('#saml_urn_prefix').prop('required', false);
|
||||
}
|
||||
});
|
||||
// init validation requirement at first time page load
|
||||
|
@ -1642,10 +1550,6 @@
|
|||
if ($('#saml_logout').is(":checked")) {
|
||||
$('#saml_logout_url').prop('required', true);
|
||||
}
|
||||
if ($('#saml_autoprovisioning_on').is(":checked")) {
|
||||
$('#saml_autoprovisioning_attribute').prop('required', true);
|
||||
$('#saml_urn_prefix').prop('required', true);
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
$(document.body).on("focus", "#saml_sp_requested_attributes", function (e) {
|
||||
|
@ -1664,37 +1568,6 @@
|
|||
modal.modal('show');
|
||||
});
|
||||
|
||||
$("#saml_autoprovisioning_on" ).on('ifChanged',function(){
|
||||
if ($('#saml_autoprovisioning_on').is(":checked") && $('#saml_enabled').is(":checked")) {
|
||||
$('#saml_autoprovisioning_attribute').prop('required', true);
|
||||
$('#saml_urn_prefix').prop('required', true);
|
||||
}
|
||||
else{
|
||||
$('#saml_autoprovisioning_attribute').prop('required', false);
|
||||
$('#saml_urn_prefix').prop('required', false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#saml_purge_on').on('ifClicked', function(event){
|
||||
if( !$(event.target).is(':checked')){
|
||||
var modal = $("#modal_confirm");
|
||||
var info = "Are you sure you want to do this? Users will lose their associated domains unless they already have their autoprovisioning field prepopulated." ;
|
||||
modal.find('.modal-body p').text(info);
|
||||
modal.find('#button_confirm').click(function () {
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.find('#button_cancel').click(function () {
|
||||
$('#saml_purge_off').iCheck('check');
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.find('#X').click(function () {
|
||||
$('#saml_purge_off').iCheck('check');
|
||||
modal.modal('hide');
|
||||
})
|
||||
modal.modal('show');
|
||||
}
|
||||
});
|
||||
|
||||
$('#saml_logout').iCheck({
|
||||
checkboxClass : 'icheckbox_square-blue',
|
||||
increaseArea : '20%'
|
||||
|
|
Loading…
Reference in a new issue