From 0fb334e372277fdcdfb1a51116ad90f8c3e14067 Mon Sep 17 00:00:00 2001 From: sean-m-sullivan Date: Sun, 30 Apr 2023 15:06:47 -0400 Subject: [PATCH] collection, allow roles to be added to multiple teams and users --- .../plugins/module_utils/controller_api.py | 5 -- awx_collection/plugins/modules/role.py | 54 ++++++++------- .../integration/targets/role/tasks/main.yml | 67 ++++++++++++++++++- 3 files changed, 95 insertions(+), 31 deletions(-) diff --git a/awx_collection/plugins/module_utils/controller_api.py b/awx_collection/plugins/module_utils/controller_api.py index 92c36b5415..0a677be6eb 100644 --- a/awx_collection/plugins/module_utils/controller_api.py +++ b/awx_collection/plugins/module_utils/controller_api.py @@ -330,11 +330,6 @@ class ControllerAPIModule(ControllerModule): else: self.update_secrets = True - @staticmethod - def param_to_endpoint(name): - exceptions = {'inventory': 'inventories', 'target_team': 'teams', 'workflow': 'workflow_job_templates'} - return exceptions.get(name, '{0}s'.format(name)) - @staticmethod def get_name_field_from_endpoint(endpoint): return ControllerAPIModule.IDENTITY_FIELDS.get(endpoint, 'name') diff --git a/awx_collection/plugins/modules/role.py b/awx_collection/plugins/modules/role.py index bc7f9ea803..d98337cac4 100644 --- a/awx_collection/plugins/modules/role.py +++ b/awx_collection/plugins/modules/role.py @@ -24,11 +24,23 @@ options: user: description: - User that receives the permissions specified by the role. + - Deprecated, use 'users'. type: str + users: + description: + - Users that receive the permissions specified by the role. + type: list + elements: str team: description: - Team that receives the permissions specified by the role. + - Deprecated, use 'teams'. type: str + teams: + description: + - Teams that receive the permissions specified by the role. + type: list + elements: str role: description: - The role type to grant/revoke. @@ -161,7 +173,9 @@ def main(): argument_spec = dict( user=dict(), + users=dict(type='list', elements='str'), team=dict(), + teams=dict(type='list', elements='str'), role=dict( choices=[ "admin", @@ -219,9 +233,9 @@ def main(): 'projects': 'project', 'target_teams': 'target_team', 'workflows': 'workflow', + 'users': 'user', + 'teams': 'team', } - # Singular parameters - resource_param_keys = ('user', 'team', 'lookup_organization') resources = {} for resource_group, old_name in resource_list_param_keys.items(): @@ -229,9 +243,9 @@ def main(): resources.setdefault(resource_group, []).extend(module.params.get(resource_group)) if module.params.get(old_name) is not None: resources.setdefault(resource_group, []).append(module.params.get(old_name)) - for resource_group in resource_param_keys: - if module.params.get(resource_group) is not None: - resources[resource_group] = module.params.get(resource_group) + if module.params.get('lookup_organization') is not None: + resources['lookup_organization'] = module.params.get('lookup_organization') + # Change workflows and target_teams key to its endpoint name. if 'workflows' in resources: resources['workflow_job_templates'] = resources.pop('workflows') @@ -248,28 +262,13 @@ def main(): # separate actors from resources actor_data = {} missing_items = [] - for key in ('user', 'team'): - if key in resources: - if key == 'user': - lookup_data_populated = {} - else: - lookup_data_populated = lookup_data - # Attempt to look up project based on the provided name or ID and lookup data - data = module.get_one('{0}s'.format(key), name_or_id=resources[key], data=lookup_data_populated) - if data is None: - module.fail_json( - msg='Unable to find {0} with name: {1}'.format(key, resources[key]), changed=False - ) - else: - actor_data[key] = module.get_one('{0}s'.format(key), name_or_id=resources[key], data=lookup_data_populated) - resources.pop(key) # Lookup Resources resource_data = {} for key, value in resources.items(): for resource in value: # Attempt to look up project based on the provided name or ID and lookup data if key in resources: - if key == 'organizations': + if key == 'organizations' or key == 'users': lookup_data_populated = {} else: lookup_data_populated = lookup_data @@ -277,14 +276,18 @@ def main(): if data is None: missing_items.append(resource) else: - resource_data.setdefault(key, []).append(data) + if key == 'users' or key == 'teams': + actor_data.setdefault(key, []).append(data) + else: + resource_data.setdefault(key, []).append(data) if len(missing_items) > 0: module.fail_json( msg='There were {0} missing items, missing items: {1}'.format(len(missing_items), missing_items), changed=False ) + # build association agenda associations = {} - for actor_type, actor in actor_data.items(): + for actor_type, actors in actor_data.items(): for key, value in resource_data.items(): for resource in value: resource_roles = resource['summary_fields']['object_roles'] @@ -294,9 +297,10 @@ def main(): msg='Resource {0} has no role {1}, available roles: {2}'.format(resource['url'], role_field, available_roles), changed=False ) role_data = resource_roles[role_field] - endpoint = '/roles/{0}/{1}/'.format(role_data['id'], module.param_to_endpoint(actor_type)) + endpoint = '/roles/{0}/{1}/'.format(role_data['id'], actor_type) associations.setdefault(endpoint, []) - associations[endpoint].append(actor['id']) + for actor in actors: + associations[endpoint].append(actor['id']) # perform associations for association_endpoint, new_association_list in associations.items(): diff --git a/awx_collection/tests/integration/targets/role/tasks/main.yml b/awx_collection/tests/integration/targets/role/tasks/main.yml index 449b402321..e9ccc0db76 100644 --- a/awx_collection/tests/integration/targets/role/tasks/main.yml +++ b/awx_collection/tests/integration/targets/role/tasks/main.yml @@ -11,6 +11,8 @@ jt1: "AWX-Collection-tests-role-jt1-{{ test_id }}" jt2: "AWX-Collection-tests-role-jt2-{{ test_id }}" wfjt_name: "AWX-Collection-tests-role-project-wfjt-{{ test_id }}" + team_name: "AWX-Collection-tests-team-team-{{ test_id }}" + team2_name: "AWX-Collection-tests-team-team-{{ test_id }}2" - block: - name: Create a User @@ -27,6 +29,32 @@ that: - "result is changed" + - name: Create a 2nd User + user: + first_name: Joe + last_name: User + username: "{{ username }}2" + password: "{{ 65535 | random | to_uuid }}" + email: joe@example.org + state: present + register: result + + - assert: + that: + - "result is changed" + + - name: Create teams + team: + name: "{{ item }}" + organization: Default + register: result + loop: + - "{{ team_name }}" + - "{{ team2_name }}" + - assert: + that: + - "result is changed" + - name: Create a project project: name: "{{ project_name }}" @@ -55,9 +83,14 @@ that: - "result is changed" - - name: Add Joe to the update role of the default Project with lookup Organization + - name: Add Joe and teams to the update role of the default Project with lookup Organization role: user: "{{ username }}" + users: + - "{{ username }}2" + teams: + - "{{ team_name }}" + - "{{ team2_name }}" role: update lookup_organization: Default project: "Demo Project" @@ -74,6 +107,11 @@ - name: Add Joe to the new project by ID role: user: "{{ username }}" + users: + - "{{ username }}2" + teams: + - "{{ team_name }}" + - "{{ team2_name }}" role: update project: "{{ project_info['id'] }}" state: "{{ item }}" @@ -89,6 +127,8 @@ - name: Add Joe as execution admin to Default Org. role: user: "{{ username }}" + users: + - "{{ username }}2" role: execution_environment_admin organizations: Default state: "{{ item }}" @@ -110,6 +150,8 @@ - name: Add Joe to workflow execute role role: user: "{{ username }}" + users: + - "{{ username }}2" role: execute workflow: test-role-workflow job_templates: @@ -125,6 +167,8 @@ - name: Add Joe to nonexistant job template execute role role: user: "{{ username }}" + users: + - "{{ username }}2" role: execute workflow: test-role-workflow job_templates: @@ -141,6 +185,8 @@ - name: Add Joe to workflow execute role, no-op role: user: "{{ username }}" + users: + - "{{ username }}2" role: execute workflow: test-role-workflow state: present @@ -153,6 +199,8 @@ - name: Add Joe to workflow approve role role: user: "{{ username }}" + users: + - "{{ username }}2" role: approval workflow: test-role-workflow state: present @@ -170,6 +218,23 @@ state: absent register: result + - name: Delete a 2nd User + user: + username: "{{ username }}2" + email: joe@example.org + state: absent + register: result + + - name: Delete teams + team: + name: "{{ item }}" + organization: Default + state: absent + register: result + loop: + - "{{ team_name }}" + - "{{ team2_name }}" + - name: Delete job templates job_template: name: "{{ item }}"