AWX Collections for DAB RBAC

Adds new modules for CRUD operations on the
following endpoints:

- api/v2/role_definitions
- api/v2/role_user_assignments
- api/v2/role_team_assignments

Note: assignment is Create or Delete only

Additional changes:
- Currently DAB endpoints do not have "type"
field on the resource list items. So this modifies
the create_or_update_if_needed to allow manually
specifying item type.

Signed-off-by: Seth Foster <fosterbseth@gmail.com>
This commit is contained in:
Seth Foster
2024-04-02 15:26:07 -04:00
committed by Alan Rominger
parent 389a729b75
commit 3bb559dd09
13 changed files with 807 additions and 6 deletions

View File

@@ -652,7 +652,7 @@ class ControllerAPIModule(ControllerModule):
# If we have neither of these, then we can try un-authenticated access
self.authenticated = True
def delete_if_needed(self, existing_item, on_delete=None, auto_exit=True):
def delete_if_needed(self, existing_item, item_type=None, on_delete=None, auto_exit=True):
# This will exit from the module on its own.
# If the method successfully deletes an item and on_delete param is defined,
# the on_delete parameter will be called as a method pasing in this object and the json from the response
@@ -664,8 +664,9 @@ class ControllerAPIModule(ControllerModule):
# If we have an item, we can try to delete it
try:
item_url = existing_item['url']
item_type = existing_item['type']
item_id = existing_item['id']
if not item_type:
item_type = existing_item['type']
item_name = self.get_item_name(existing_item, allow_unknown=True)
except KeyError as ke:
self.fail_json(msg="Unable to process delete of item due to missing data {0}".format(ke))
@@ -907,7 +908,7 @@ class ControllerAPIModule(ControllerModule):
return True
return False
def update_if_needed(self, existing_item, new_item, on_update=None, auto_exit=True, associations=None):
def update_if_needed(self, existing_item, new_item, item_type=None, on_update=None, auto_exit=True, associations=None):
# This will exit from the module on its own
# If the method successfully updates an item and on_update param is defined,
# the on_update parameter will be called as a method pasing in this object and the json from the response
@@ -921,7 +922,8 @@ class ControllerAPIModule(ControllerModule):
# If we have an item, we can see if it needs an update
try:
item_url = existing_item['url']
item_type = existing_item['type']
if not item_type:
item_type = existing_item['type']
if item_type == 'user':
item_name = existing_item['username']
elif item_type == 'workflow_job_template_node':
@@ -990,7 +992,7 @@ class ControllerAPIModule(ControllerModule):
new_item.pop(key)
if existing_item:
return self.update_if_needed(existing_item, new_item, on_update=on_update, auto_exit=auto_exit, associations=associations)
return self.update_if_needed(existing_item, new_item, item_type=item_type, on_update=on_update, auto_exit=auto_exit, associations=associations)
else:
return self.create_if_needed(
existing_item, new_item, endpoint, on_create=on_create, item_type=item_type, auto_exit=auto_exit, associations=associations

View File

@@ -0,0 +1,114 @@
#!/usr/bin/python
# coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'}
DOCUMENTATION = '''
---
module: role_definition
author: "Seth Foster (@fosterseth)"
short_description: Add role definition to Automation Platform Controller
description:
- Contains a list of permissions and a resource type that can then be assigned to users or teams.
options:
name:
description:
- Name of this role definition.
required: True
type: str
permissions:
description:
- List of permissions to include in the role definition.
required: True
type: list
elements: str
content_type:
description:
- The type of resource this applies to.
required: True
type: str
description:
description:
- Optional description of this role definition.
type: str
state:
description:
- The desired state of the role definition.
default: present
choices:
- present
- absent
type: str
extends_documentation_fragment: awx.awx.auth
'''
EXAMPLES = '''
- name: Create Role Definition
role_definition:
name: test_view_jt
permissions:
- awx.view_jobtemplate
- awx.execute_jobtemplate
content_type: awx.jobtemplate
description: role definition to view and execute jt
state: present
'''
from ..module_utils.controller_api import ControllerAPIModule
def main():
# Any additional arguments that are not fields of the item can be added here
argument_spec = dict(
name=dict(required=True, type='str'),
permissions=dict(required=True, type='list', elements='str'),
content_type=dict(required=True, type='str'),
description=dict(required=False, type='str'),
state=dict(default='present', choices=['present', 'absent']),
)
module = ControllerAPIModule(argument_spec=argument_spec)
name = module.params.get('name')
permissions = module.params.get('permissions')
content_type = module.params.get('content_type')
description = module.params.get('description')
state = module.params.get('state')
if description is None:
description = ''
role_definition = module.get_one('role_definitions', name_or_id=name)
if state == 'absent':
module.delete_if_needed(
role_definition,
item_type='role_definition',
)
post_kwargs = {
'name': name,
'permissions': permissions,
'content_type': content_type,
'description': description
}
if state == 'present':
module.create_or_update_if_needed(
role_definition,
post_kwargs,
endpoint='role_definitions',
item_type='role_definition',
)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,123 @@
#!/usr/bin/python
# coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'}
DOCUMENTATION = '''
---
module: role_team_assignment
author: "Seth Foster (@fosterseth)"
short_description: Gives a team permission to a resource or an organization.
description:
- Use this endpoint to give a team permission to a resource or an organization.
- After creation, the assignment cannot be edited, but can be deleted to remove those permissions.
options:
role_definition:
description:
- The name or id of the role definition to assign to the team.
required: True
type: str
object_id:
description:
- Primary key of the object this assignment applies to.
required: True
type: int
team:
description:
- The name or id of the team to assign to the object.
required: False
type: str
object_ansible_id:
description:
- Resource id of the object this role applies to. Alternative to the object_id field.
required: False
type: int
team_ansible_id:
description:
- Resource id of the team who will receive permissions from this assignment. Alternative to team field.
required: False
type: int
state:
description:
- The desired state of the role definition.
default: present
choices:
- present
- absent
type: str
extends_documentation_fragment: awx.awx.auth
'''
EXAMPLES = '''
- name: Give Team A JT permissions
role_team_assignment:
role_definition: launch JT
object_id: 1
team: Team A
state: present
'''
from ..module_utils.controller_api import ControllerAPIModule
def main():
# Any additional arguments that are not fields of the item can be added here
argument_spec = dict(
team=dict(required=False, type='str'),
object_id=dict(required=True, type='int'),
role_definition=dict(required=True, type='str'),
object_ansible_id=dict(required=False, type='int'),
team_ansible_id=dict(required=False, type='int'),
state=dict(default='present', choices=['present', 'absent']),
)
module = ControllerAPIModule(argument_spec=argument_spec)
team = module.params.get('team')
object_id = module.params.get('object_id')
role_definition_str = module.params.get('role_definition')
object_ansible_id = module.params.get('object_ansible_id')
team_ansible_id = module.params.get('team_ansible_id')
state = module.params.get('state')
role_definition = module.get_one('role_definitions', allow_none=False, name_or_id=role_definition_str)
team = module.get_one('teams', allow_none=False, name_or_id=team)
kwargs = {
'role_definition': role_definition['id'],
'object_id': object_id,
'team': team['id'],
'object_ansible_id': object_ansible_id,
'team_ansible_id': team_ansible_id,
}
# get rid of None type values
kwargs = {k: v for k, v in kwargs.items() if v is not None}
role_team_assignment = module.get_one('role_team_assignments', **{'data': kwargs})
if state == 'absent':
module.delete_if_needed(
role_team_assignment,
item_type='role_team_assignment',
)
if state == 'present':
module.create_if_needed(
role_team_assignment,
kwargs,
endpoint='role_team_assignments',
item_type='role_team_assignment',
)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,124 @@
#!/usr/bin/python
# coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'}
DOCUMENTATION = '''
---
module: role_user_assignment
author: "Seth Foster (@fosterseth)"
short_description: Gives a user permission to a resource or an organization.
description:
- Use this endpoint to give a user permission to a resource or an organization.
- After creation, the assignment cannot be edited, but can be deleted to remove those permissions.
options:
role_definition:
description:
- The name or id of the role definition to assign to the user.
required: True
type: str
object_id:
description:
- Primary key of the object this assignment applies to.
required: True
type: int
user:
description:
- The name or id of the user to assign to the object.
required: False
type: str
object_ansible_id:
description:
- Resource id of the object this role applies to. Alternative to the object_id field.
required: False
type: int
user_ansible_id:
description:
- Resource id of the user who will receive permissions from this assignment. Alternative to user field.
required: False
type: int
state:
description:
- The desired state of the role definition.
default: present
choices:
- present
- absent
type: str
extends_documentation_fragment: awx.awx.auth
'''
EXAMPLES = '''
- name: Give Bob JT permissions
role_user_assignment:
role_definition: launch JT
object_id: 1
user: bob
state: present
'''
from ..module_utils.controller_api import ControllerAPIModule
def main():
# Any additional arguments that are not fields of the item can be added here
argument_spec = dict(
user=dict(required=False, type='str'),
object_id=dict(required=True, type='int'),
role_definition=dict(required=True, type='str'),
object_ansible_id=dict(required=False, type='int'),
user_ansible_id=dict(required=False, type='int'),
state=dict(default='present', choices=['present', 'absent']),
)
module = ControllerAPIModule(argument_spec=argument_spec)
user = module.params.get('user')
object_id = module.params.get('object_id')
role_definition_str = module.params.get('role_definition')
object_ansible_id = module.params.get('object_ansible_id')
user_ansible_id = module.params.get('user_ansible_id')
state = module.params.get('state')
role_definition = module.get_one('role_definitions', allow_none=False, name_or_id=role_definition_str)
user = module.get_one('users', allow_none=False, name_or_id=user)
kwargs = {
'role_definition': role_definition['id'],
'object_id': object_id,
'user': user['id'],
'object_ansible_id': object_ansible_id,
'user_ansible_id': user_ansible_id,
}
# get rid of None type values
kwargs = {k: v for k, v in kwargs.items() if v is not None}
role_user_assignment = module.get_one('role_user_assignments', **{'data': kwargs})
if state == 'absent':
module.delete_if_needed(
role_user_assignment,
item_type='role_user_assignment',
)
if state == 'present':
module.create_if_needed(
role_user_assignment,
kwargs,
endpoint='role_user_assignments',
item_type='role_user_assignment',
)
if __name__ == '__main__':
main()