From 25db119d02f2a0073d41b6cb451b416948e16cfa Mon Sep 17 00:00:00 2001 From: Erik Weber Date: Fri, 3 Jul 2020 08:55:31 +0200 Subject: [PATCH 1/3] Add Account creation/permission handling based on Azure oAuth group membership --- powerdnsadmin/models/setting.py | 5 ++ powerdnsadmin/routes/admin.py | 11 +++ powerdnsadmin/routes/index.py | 70 +++++++++++++++++++ .../admin_setting_authentication.html | 36 ++++++++++ 4 files changed, 122 insertions(+) diff --git a/powerdnsadmin/models/setting.py b/powerdnsadmin/models/setting.py index b225787..bd1066e 100644 --- a/powerdnsadmin/models/setting.py +++ b/powerdnsadmin/models/setting.py @@ -81,6 +81,11 @@ class Setting(db.Model): 'azure_admin_group': '', 'azure_operator_group': '', 'azure_user_group': '', + 'azure_group_accounts_enabled': False, + 'azure_group_accounts_name': 'displayName', + 'azure_group_accounts_name_re': '', + 'azure_group_accounts_description': 'description', + 'azure_group_accounts_description_re': '', 'oidc_oauth_enabled': False, 'oidc_oauth_key': '', 'oidc_oauth_secret': '', diff --git a/powerdnsadmin/routes/admin.py b/powerdnsadmin/routes/admin.py index 4ee8b58..d5557a1 100644 --- a/powerdnsadmin/routes/admin.py +++ b/powerdnsadmin/routes/admin.py @@ -780,6 +780,17 @@ def setting_authentication(): request.form.get('azure_operator_group')) Setting().set('azure_user_group', request.form.get('azure_user_group')) + Setting().set( + 'azure_group_accounts_enabled', True + if request.form.get('azure_group_accounts_enabled') == 'ON' else False) + Setting().set('azure_group_accounts_name', + request.form.get('azure_group_accounts_name')) + Setting().set('azure_group_accounts_name_re', + request.form.get('azure_group_accounts_name_re')) + Setting().set('azure_group_accounts_description', + request.form.get('azure_group_accounts_description')) + Setting().set('azure_group_accounts_description_re', + request.form.get('azure_group_accounts_description_re')) result = { 'status': True, 'msg': diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index 4df19e8..7013c01 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -279,6 +279,76 @@ def login(): error=('User ' + azure_username + ' is not in any authorised groups.')) + # Handle account/group creation, if enabled + if Setting().get('azure_group_accounts_enabled') and mygroups: + current_app.logger.info('Azure group account sync enabled') + for azure_group in mygroups: + + name_value = Setting().get('azure_group_accounts_name') + description_value = Setting().get('azure_group_accounts_description') + + select_values = name_value + if description_value != '': + select_values += ',' + description_value + azure_group_info = azure.get('groups/{}?$select={}'.format(azure_group, select_values)).text + current_app.logger.info('Group name for {}: {}'.format(azure_group, azure_group_info)) + group_info = json.loads(azure_group_info) + if name_value in group_info: + group_name = group_info[name_value] + group_description = '' + if description_value in group_info: + group_description = group_info[description_value] + + # Do regex search if enabled + pattern = Setting().get('azure_group_accounts_name_re') + if pattern != '': + current_app.logger.info('Matching group name {} against regex {}'.format(group_name, pattern)) + matches = re.match(pattern,group_name) + if matches: + current_app.logger.info('Group {} matched regexp'.format(group_name)) + group_name = matches.group(0) + else: + # Regexp didn't match, continue to next iteration + next + account = Account() + account_id = account.get_id_by_name(account_name=group_name) + + if account_id: + account = Account.query.get(account_id) + # check if user has permissions + account_users = account.get_user() + current_app.logger.info('Group: {} Users: {}'.format( + group_name, + account_users)) + if user.id in account_users: + current_app.logger.info('User id {} is already in account {}'.format( + user.id, group_name)) + else: + account.add_user(user) + history = History(msg='Update account {0}'.format( + account.name), + created_by='System') + history.add() + current_app.logger.info('User {} added to Account {}'.format( + user.username, account.name)) + else: + account.name = group_name + account.description = group_description + account.contact = '' + account.mail = '' + account.create_account() + history = History(msg='Create account {0}'.format( + account.name), + created_by='System') + history.add() + + account.add_user(user) + history = History(msg='Update account {0}'.format(account.name), + created_by='System') + history.add() + current_app.logger.warning('group info: {} '.format(account_id)) + + login_user(user, remember=False) signin_history(user.username, 'Azure OAuth', True) return redirect(url_for('index.index')) diff --git a/powerdnsadmin/templates/admin_setting_authentication.html b/powerdnsadmin/templates/admin_setting_authentication.html index 50c00ef..0bfeae9 100644 --- a/powerdnsadmin/templates/admin_setting_authentication.html +++ b/powerdnsadmin/templates/admin_setting_authentication.html @@ -456,6 +456,41 @@ +
+ AZURE GROUP ACCOUNT SYNC/CREATION +
+ +
+ +     + +
+
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
@@ -476,6 +511,7 @@
  • For the Scope, use User.Read openid mail profile
  • Replace the [tenantID] in the default URLs for authorize and token with your Tenant ID.
  • +

    If AZURE GROUP ACCOUNT SYNC/CREATION is enabled, Accounts will be created automatically based on group membership. If an Account exists, an authenticated user with group membership is added to the Account

    From e9934221062062b3f3d5fba556f3fe506d38006f Mon Sep 17 00:00:00 2001 From: Erik Weber Date: Fri, 3 Jul 2020 10:55:06 +0200 Subject: [PATCH 2/3] Add regex matching for group/account description --- powerdnsadmin/routes/index.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index 7013c01..4df4875 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -299,7 +299,19 @@ def login(): if description_value in group_info: group_description = group_info[description_value] - # Do regex search if enabled + # Do regex search if enabled for group description + description_pattern = Setting().get('azure_group_accounts_description_re') + if description_pattern != '': + current_app.logger.info('Matching group description {} against regex {}'.format(group_description, description_pattern)) + matches = re.match(description_pattern,group_description) + if matches: + current_app.logger.info('Group {} matched regexp'.format(group_description)) + group_description = matches.group(0) + else: + # Regexp didn't match, continue to next iteration + next + + # Do regex search if enabled for group name pattern = Setting().get('azure_group_accounts_name_re') if pattern != '': current_app.logger.info('Matching group name {} against regex {}'.format(group_name, pattern)) @@ -310,6 +322,7 @@ def login(): else: # Regexp didn't match, continue to next iteration next + account = Account() account_id = account.get_id_by_name(account_name=group_name) From 22eabef06ae1ca92ada38e6f681c3454f9e9041e Mon Sep 17 00:00:00 2001 From: Erik Weber Date: Fri, 3 Jul 2020 11:01:17 +0200 Subject: [PATCH 3/3] Use the correct matching group --- powerdnsadmin/routes/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerdnsadmin/routes/index.py b/powerdnsadmin/routes/index.py index 4df4875..30193b8 100644 --- a/powerdnsadmin/routes/index.py +++ b/powerdnsadmin/routes/index.py @@ -306,7 +306,7 @@ def login(): matches = re.match(description_pattern,group_description) if matches: current_app.logger.info('Group {} matched regexp'.format(group_description)) - group_description = matches.group(0) + group_description = matches.group(1) else: # Regexp didn't match, continue to next iteration next @@ -318,7 +318,7 @@ def login(): matches = re.match(pattern,group_name) if matches: current_app.logger.info('Group {} matched regexp'.format(group_name)) - group_name = matches.group(0) + group_name = matches.group(1) else: # Regexp didn't match, continue to next iteration next