diff --git a/ci/roles/role_assignment/tasks/main.yml b/ci/roles/role_assignment/tasks/main.yml index cff79e4d..e0e59815 100644 --- a/ci/roles/role_assignment/tasks/main.yml +++ b/ci/roles/role_assignment/tasks/main.yml @@ -45,12 +45,6 @@ state: absent user: admin -- name: Delete project - openstack.cloud.project: - cloud: "{{ cloud }}" - state: absent - name: ansible_project - - name: Create domain openstack.cloud.identity_domain: cloud: "{{ cloud }}" @@ -78,6 +72,7 @@ state: present name: ansible_user domain: default + register: specific_user - name: Create user in specific domain openstack.cloud.identity_user: @@ -138,6 +133,45 @@ that: - role_assignment is changed +- name: Assign role to user in specific domain on default domain project + openstack.cloud.role_assignment: + cloud: "{{ cloud }}" + role: anotherrole + user: "{{ specific_user.user.id }}" + domain: default + project: ansible_project + register: role_assignment + +- name: Assert role assignment + assert: + that: + - role_assignment is changed + +- name: Revoke role to user in specific domain + openstack.cloud.role_assignment: + cloud: "{{ cloud }}" + role: anotherrole + user: "{{ specific_user.user.id }}" + domain: default + project: ansible_project + state: absent + register: role_assignment + +- name: Assert role assignment revoked + assert: + that: + - role_assignment is changed + +- name: Assign role to user in specific domain on default domain project + openstack.cloud.role_assignment: + cloud: "{{ cloud }}" + role: anotherrole + user: ansible_user + user_domain: "{{ specific_user.user.domain_id }}" + project: ansible_project + project_domain: default + register: role_assignment + - name: Delete group in default domain openstack.cloud.identity_group: cloud: "{{ cloud }}" @@ -171,3 +205,10 @@ cloud: "{{ cloud }}" state: absent name: ansible_domain + +- name: Delete project + openstack.cloud.project: + cloud: "{{ cloud }}" + state: absent + name: ansible_project + diff --git a/plugins/modules/role_assignment.py b/plugins/modules/role_assignment.py index 5ec69923..b97d80fb 100644 --- a/plugins/modules/role_assignment.py +++ b/plugins/modules/role_assignment.py @@ -19,7 +19,9 @@ options: - Valid only with keystone version 3. - Required if I(project) is not specified. - When I(project) is specified, then I(domain) will not be used for - scoping the role association, only for finding resources. + scoping the role association, only for finding resources. Deprecated + for finding resources, please use I(group_domain), I(project_domain), + I(role_domain), or I(user_domain). - "When scoping the role association, I(project) has precedence over I(domain) and I(domain) has precedence over I(system): When I(project) is specified, then I(domain) and I(system) are not used for role @@ -32,24 +34,45 @@ options: - Valid only with keystone version 3. - If I(group) is not specified, then I(user) is required. Both may not be specified at the same time. + - You can supply I(group_domain) or the deprecated usage of I(domain) to + find group resources. + type: str + group_domain: + description: + - Name or ID for the domain. + - Valid only with keystone version 3. + - Only valid for finding group resources. type: str project: description: - Name or ID of the project to scope the role association to. - If you are using keystone version 2, then this value is required. - When I(project) is specified, then I(domain) will not be used for - scoping the role association, only for finding resources. + scoping the role association, only for finding resources. Prefer + I(group_domain) over I(domain). - "When scoping the role association, I(project) has precedence over I(domain) and I(domain) has precedence over I(system): When I(project) is specified, then I(domain) and I(system) are not used for role association. When I(domain) is specified, then I(system) will not be used for role association." type: str + project_domain: + description: + - Name or ID for the domain. + - Valid only with keystone version 3. + - Only valid for finding project resources. + type: str role: description: - Name or ID for the role. required: true type: str + role_domain: + description: + - Name or ID for the domain. + - Valid only with keystone version 3. + - Only valid for finding role resources. + type: str state: description: - Should the roles be present or absent on the user. @@ -73,6 +96,12 @@ options: - If I(user) is not specified, then I(group) is required. Both may not be specified at the same time. type: str + user_domain: + description: + - Name or ID for the domain. + - Valid only with keystone version 3. + - Only valid for finding user resources. + type: str extends_documentation_fragment: - openstack.cloud.openstack ''' @@ -101,11 +130,15 @@ class IdentityRoleAssignmentModule(OpenStackModule): argument_spec = dict( domain=dict(), group=dict(), + group_domain=dict(type='str'), project=dict(), + project_domain=dict(type='str'), role=dict(required=True), + role_domain=dict(type='str'), state=dict(default='present', choices=['absent', 'present']), system=dict(), user=dict(), + user_domain=dict(type='str'), ) module_kwargs = dict( @@ -113,17 +146,33 @@ class IdentityRoleAssignmentModule(OpenStackModule): ('user', 'group'), ('domain', 'project', 'system'), ], + mutually_exclusive=[ + ('user', 'group'), + ('project', 'system'), # domain should be part of this + ], supports_check_mode=True ) + def _find_domain_id(self, domain): + if domain is not None: + domain = self.conn.identity.find_domain(domain, + ignore_missing=False) + return dict(domain_id=domain['id']) + return dict() + def run(self): filters = {} - find_filters = {} - kwargs = {} + group_find_filters = {} + project_find_filters = {} + role_find_filters = {} + user_find_filters = {} + role_find_filters.update(self._find_domain_id( + self.params['role_domain'])) role_name_or_id = self.params['role'] role = self.conn.identity.find_role(role_name_or_id, - ignore_missing=False) + ignore_missing=False, + **role_find_filters) filters['role_id'] = role['id'] domain_name_or_id = self.params['domain'] @@ -131,22 +180,31 @@ class IdentityRoleAssignmentModule(OpenStackModule): domain = self.conn.identity.find_domain( domain_name_or_id, ignore_missing=False) filters['scope_domain_id'] = domain['id'] - find_filters['domain_id'] = domain['id'] - kwargs['domain'] = domain['id'] + group_find_filters['domain_id'] = domain['id'] + project_find_filters['domain_id'] = domain['id'] + user_find_filters['domain_id'] = domain['id'] user_name_or_id = self.params['user'] if user_name_or_id is not None: + user_find_filters.update(self._find_domain_id( + self.params['user_domain'])) user = self.conn.identity.find_user( - user_name_or_id, ignore_missing=False, **find_filters) + user_name_or_id, ignore_missing=False, + **user_find_filters) filters['user_id'] = user['id'] - kwargs['user'] = user['id'] + else: + user = None group_name_or_id = self.params['group'] if group_name_or_id is not None: + group_find_filters.update(self._find_domain_id( + self.params['group_domain'])) group = self.conn.identity.find_group( - group_name_or_id, ignore_missing=False, **find_filters) + group_name_or_id, ignore_missing=False, + **group_find_filters) filters['group_id'] = group['id'] - kwargs['group'] = group['id'] + else: + group = None system_name = self.params['system'] if system_name is not None: @@ -154,14 +212,14 @@ class IdentityRoleAssignmentModule(OpenStackModule): if 'scope_domain_id' not in filters: filters['scope.system'] = system_name - kwargs['system'] = system_name - project_name_or_id = self.params['project'] if project_name_or_id is not None: + project_find_filters.update(self._find_domain_id( + self.params['project_domain'])) project = self.conn.identity.find_project( - project_name_or_id, ignore_missing=False, **find_filters) + project_name_or_id, ignore_missing=False, + **project_find_filters) filters['scope_project_id'] = project['id'] - kwargs['project'] = project['id'] # project has precedence over domain and system filters.pop('scope_domain_id', None) @@ -176,10 +234,50 @@ class IdentityRoleAssignmentModule(OpenStackModule): or (state == 'absent' and role_assignments))) if state == 'present' and not role_assignments: - self.conn.grant_role(role['id'], **kwargs) + if 'scope_domain_id' in filters: + if user is not None: + self.conn.identity.assign_domain_role_to_user( + filters['scope_domain_id'], user, role) + else: + self.conn.identity.assign_domain_role_to_group( + filters['scope_domain_id'], group, role) + elif 'scope_project_id' in filters: + if user is not None: + self.conn.identity.assign_project_role_to_user( + filters['scope_project_id'], user, role) + else: + self.conn.identity.assign_project_role_to_group( + filters['scope_project_id'], group, role) + elif 'scope.system' in filters: + if user is not None: + self.conn.identity.assign_system_role_to_user( + user, role, filters['scope.system']) + else: + self.conn.identity.assign_system_role_to_group( + group, role, filters['scope.system']) self.exit_json(changed=True) elif state == 'absent' and role_assignments: - self.conn.revoke_role(role['id'], **kwargs) + if 'scope_domain_id' in filters: + if user is not None: + self.conn.identity.unassign_domain_role_from_user( + filters['scope_domain_id'], user, role) + else: + self.conn.identity.unassign_domain_role_from_group( + filters['scope_domain_id'], group, role) + elif 'scope_project_id' in filters: + if user is not None: + self.conn.identity.unassign_project_role_from_user( + filters['scope_project_id'], user, role) + else: + self.conn.identity.unassign_project_role_from_group( + filters['scope_project_id'], group, role) + elif 'scope.system' in filters: + if user is not None: + self.conn.identity.unassign_system_role_from_user( + user, role, filters['scope.system']) + else: + self.conn.identity.unassign_system_role_from_group( + group, role, filters['scope.system']) self.exit_json(changed=True) else: self.exit_json(changed=False)