From b3f9b4a2b0d17a22c3166eeea746c4b628ea6584 Mon Sep 17 00:00:00 2001 From: benshalev849 <87974262+benshalev849@users.noreply.github.com> Date: Fri, 19 Nov 2021 17:53:17 +0200 Subject: [PATCH] OIDC list accounts (#994) Added the function to use lists instead of a single string in account autoprovision. --- docs/oauth.md | 58 ++++++++++++++++++++++++++++++++- powerdnsadmin/models/setting.py | 1 + powerdnsadmin/routes/admin.py | 4 ++- powerdnsadmin/routes/index.py | 34 +++++++++++++++---- 4 files changed, 89 insertions(+), 8 deletions(-) diff --git a/docs/oauth.md b/docs/oauth.md index bc0e9af..df8dd2d 100644 --- a/docs/oauth.md +++ b/docs/oauth.md @@ -17,4 +17,60 @@ Now you can enable the OAuth in PowerDNS-Admin. * Replace the [tenantID] in the default URLs for authorize and token with your Tenant ID. * Restart PowerDNS-Admin -This should allow you to log in using OAuth. \ No newline at end of file +This should allow you to log in using OAuth. + +#### OpenID Connect OAuth +To link to oidc service for authenticationregister your PowerDNS-Admin in the OIDC Provider. This requires your PowerDNS-Admin web interface to use an HTTPS URL. + +Enable OpenID Connect OAuth option. +* Client key, The client ID +* Scope, The scope of the data. +* API URL, /auth (The ending can be different with each provider) +* Token URL, /token +* Authorize URL, /auth +* Logout URL, /logout + +* Username, This will be the claim that will be used as the username. (Usually preferred_username) +* First Name, This will be the firstname of the user. (Usually given_name) +* Last Name, This will be the lastname of the user. (Usually family_name) +* Email, This will be the email of the user. (Usually email) + +#### To create accounts on oidc login use the following properties: +* Autoprovision Account Name Property, This property will set the name of the created account. + This property can be a string or a list. +* Autoprovision Account Description Property, This property will set the description of the created account. + This property can be a string or a list. + +If we get a variable named "groups" and "groups_description" from our IdP. +This variable contains groups that the user is a part of. +We will put the variable name "groups" in the "Name Property" and "groups_description" in the "Description Property". +This will result in the following account being created: +Input we get from the Idp: + +``` +{ + "preferred_username": "example_username", + "given_name": "example_firstame", + "family_name": "example_lastname", + "email": "example_email", + "groups": ["github", "gitlab"] + "groups_description": ["github.com", "gitlab.com"] +} +``` + +The user properties will be: +``` +Username: customer_username +First Name: customer_firstame +Last Name: customer_lastname +Email: customer_email +Role: User +``` + +The groups properties will be: +``` +Name: github Description: github.com Members: example_username +Name: gitlab Description: gitlab.com Members: example_username +``` + +If the option "delete_sso_accounts" is turned on the user will only be apart of groups the IdP provided and removed from all other accoubnts. diff --git a/powerdnsadmin/models/setting.py b/powerdnsadmin/models/setting.py index 4a49a40..2a96349 100644 --- a/powerdnsadmin/models/setting.py +++ b/powerdnsadmin/models/setting.py @@ -28,6 +28,7 @@ class Setting(db.Model): 'allow_user_create_domain': False, 'allow_user_remove_domain': False, 'allow_user_view_history': False, + 'delete_sso_accounts': False, 'bg_domain_updates': False, 'enable_api_rr_history': True, 'site_name': 'PowerDNS-Admin', diff --git a/powerdnsadmin/routes/admin.py b/powerdnsadmin/routes/admin.py index d9b3dcc..6de8d19 100644 --- a/powerdnsadmin/routes/admin.py +++ b/powerdnsadmin/routes/admin.py @@ -644,7 +644,9 @@ def setting_basic(): 'pretty_ipv6_ptr', 'dnssec_admins_only', 'allow_user_create_domain', 'allow_user_remove_domain', 'allow_user_view_history', 'bg_domain_updates', 'site_name', 'session_timeout', 'warn_session_timeout', 'ttl_options', - 'pdns_api_timeout', 'verify_ssl_connections', 'verify_user_email', 'otp_field_enabled', 'custom_css', 'enable_api_rr_history' + + 'pdns_api_timeout', 'verify_ssl_connections', 'verify_user_email', + 'delete_sso_accounts', 'otp_field_enabled', 'custom_css', 'enable_api_rr_history' ] return render_template('admin_setting_basic.html', settings=settings) diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index 3d7a550..ccdcd6b 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -43,7 +43,6 @@ index_bp = Blueprint('index', template_folder='templates', url_prefix='/') - @index_bp.before_app_first_request def register_modules(): global google @@ -398,16 +397,39 @@ def login(): session.pop('oidc_token', None) return redirect(url_for('index.login')) + #This checks if the account_name_property and account_description property were included in settings. if Setting().get('oidc_oauth_account_name_property') and Setting().get('oidc_oauth_account_description_property'): + + #Gets the name_property and description_property. name_prop = Setting().get('oidc_oauth_account_name_property') desc_prop = Setting().get('oidc_oauth_account_description_property') + + account_to_add = [] + #If the name_property and desc_property exist in me (A variable that contains all the userinfo from the IdP). if name_prop in me and desc_prop in me: - account = handle_account(me[name_prop], me[desc_prop]) - account.add_user(user) + accounts_name_prop = [me[name_prop]] if type(me[name_prop]) is not list else me[name_prop] + accounts_desc_prop = [me[desc_prop]] if type(me[desc_prop]) is not list else me[desc_prop] + + #Run on all groups the user is in by the index num. + for i in range(len(accounts_name_prop)): + description = '' + if i < len(accounts_desc_prop): + description = accounts_desc_prop[i] + account = handle_account(accounts_name_prop[i], description) + + account_to_add.append(account) user_accounts = user.get_accounts() - for ua in user_accounts: - if ua.name != account.name: - ua.remove_user(user) + + # Add accounts + for account in account_to_add: + if account not in user_accounts: + account.add_user(user) + + # Remove accounts if the setting is enabled + if Setting().get('delete_sso_accounts'): + for account in user_accounts: + if account not in account_to_add: + account.remove_user(user) session['user_id'] = user.id session['authentication_type'] = 'OAuth'