mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 10:00:01 -03:30
Merge pull request #962 from anoek/rbac
"Completion" of RBAC migrations; resource_field elimination
This commit is contained in:
commit
380ba8a41d
@ -91,9 +91,8 @@ class ImplicitResourceField(models.ForeignKey):
|
||||
class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
|
||||
"""Descriptor Implict Role Fields. Auto-creates the appropriate role entry on first access"""
|
||||
|
||||
def __init__(self, role_name, resource_field, permissions, parent_role, *args, **kwargs):
|
||||
def __init__(self, role_name, permissions, parent_role, *args, **kwargs):
|
||||
self.role_name = role_name
|
||||
self.resource_field = resource_field
|
||||
self.permissions = permissions
|
||||
self.parent_role = parent_role
|
||||
|
||||
@ -143,10 +142,10 @@ class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
|
||||
setattr(instance, self.field.name, role)
|
||||
instance.save(update_fields=[self.field.name,])
|
||||
|
||||
if self.resource_field and self.permissions:
|
||||
if self.permissions is not None:
|
||||
permissions = RolePermission(
|
||||
role=role,
|
||||
resource=getattr(instance, self.resource_field)
|
||||
resource=instance.resource
|
||||
)
|
||||
|
||||
if 'all' in self.permissions and self.permissions['all']:
|
||||
@ -170,9 +169,8 @@ class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
|
||||
class ImplicitRoleField(models.ForeignKey):
|
||||
"""Implicitly creates a role entry for a resource"""
|
||||
|
||||
def __init__(self, role_name=None, resource_field=None, permissions=None, parent_role=None, *args, **kwargs):
|
||||
def __init__(self, role_name=None, permissions=None, parent_role=None, *args, **kwargs):
|
||||
self.role_name = role_name
|
||||
self.resource_field = resource_field
|
||||
self.permissions = permissions
|
||||
self.parent_role = parent_role
|
||||
|
||||
@ -187,7 +185,6 @@ class ImplicitRoleField(models.ForeignKey):
|
||||
self.name,
|
||||
ImplicitRoleDescriptor(
|
||||
self.role_name,
|
||||
self.resource_field,
|
||||
self.permissions,
|
||||
self.parent_role,
|
||||
self
|
||||
@ -211,7 +208,8 @@ class ImplicitRoleField(models.ForeignKey):
|
||||
first_field_name = field_name.split('.')[0]
|
||||
field = getattr(cls, first_field_name)
|
||||
|
||||
if type(field) is ReverseManyRelatedObjectsDescriptor:
|
||||
if type(field) is ReverseManyRelatedObjectsDescriptor or \
|
||||
type(field) is ManyRelatedObjectsDescriptor:
|
||||
if found_m2m_field:
|
||||
# This limitation is due to a lack of understanding on my part, the
|
||||
# trouble being that I can't seem to get m2m_changed to call anything that
|
||||
@ -227,14 +225,17 @@ class ImplicitRoleField(models.ForeignKey):
|
||||
found_m2m_field = True
|
||||
self.m2m_field_name = first_field_name
|
||||
self.m2m_field_attr = field_name.split('.',1)[1]
|
||||
m2m_changed.connect(self.m2m_update, field.through)
|
||||
|
||||
if type(field) is ManyRelatedObjectsDescriptor:
|
||||
raise Exception('ManyRelatedObjectsDescriptor references are currently unsupported ' +
|
||||
'(but the reverse is, so supporting this is probably easy to add)): %s.%s' %
|
||||
(cls.__name__, first_field_name))
|
||||
if type(field) is ReverseManyRelatedObjectsDescriptor:
|
||||
m2m_changed.connect(self.m2m_update, field.through)
|
||||
else:
|
||||
m2m_changed.connect(self.m2m_update_related, field.related.through)
|
||||
|
||||
|
||||
def m2m_update_related(self, **kwargs):
|
||||
kwargs['reverse'] = not kwargs['reverse']
|
||||
self.m2m_update(**kwargs)
|
||||
|
||||
def m2m_update(self, sender, instance, action, reverse, model, pk_set, **kwargs):
|
||||
if action == 'post_add' or action == 'pre_remove':
|
||||
if reverse:
|
||||
|
||||
1693
awx/main/migrations/_old_access.py
Normal file
1693
awx/main/migrations/_old_access.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
||||
from collections import defaultdict
|
||||
import _old_access as old_access
|
||||
|
||||
def migrate_users(apps, schema_editor):
|
||||
migrations = list()
|
||||
@ -52,7 +53,7 @@ def migrate_inventory(apps, schema_editor):
|
||||
|
||||
for inventory in Inventory.objects.all():
|
||||
teams, users = [], []
|
||||
for perm in Permission.objects.filter(inventory=inventory):
|
||||
for perm in Permission.objects.filter(inventory=inventory, active=True):
|
||||
role = None
|
||||
execrole = None
|
||||
if perm.permission_type == 'admin':
|
||||
@ -64,6 +65,10 @@ def migrate_inventory(apps, schema_editor):
|
||||
elif perm.permission_type == 'write':
|
||||
role = inventory.updater_role
|
||||
pass
|
||||
elif perm.permission_type == 'check':
|
||||
pass
|
||||
elif perm.permission_type == 'run':
|
||||
pass
|
||||
else:
|
||||
raise Exception('Unhandled permission type for inventory: %s' % perm.permission_type)
|
||||
if perm.run_ad_hoc_commands:
|
||||
@ -108,7 +113,7 @@ def migrate_projects(apps, schema_editor):
|
||||
Permission = apps.get_model('main', 'Permission')
|
||||
|
||||
for project in Project.objects.all():
|
||||
if project.organization is None and project.created_by is not None:
|
||||
if project.organizations.count() == 0 and project.created_by is not None:
|
||||
project.admin_role.members.add(project.created_by)
|
||||
migrations[project.name]['users'].add(project.created_by)
|
||||
|
||||
@ -116,19 +121,98 @@ def migrate_projects(apps, schema_editor):
|
||||
team.member_role.children.add(project.member_role)
|
||||
migrations[project.name]['teams'].add(team)
|
||||
|
||||
if project.organization is not None:
|
||||
for user in project.organization.users.all():
|
||||
project.member_role.members.add(user)
|
||||
migrations[project.name]['users'].add(user)
|
||||
if project.organizations.count() > 0:
|
||||
for org in project.organizations.all():
|
||||
for user in org.users.all():
|
||||
project.member_role.members.add(user)
|
||||
migrations[project.name]['users'].add(user)
|
||||
|
||||
for perm in Permission.objects.filter(project=project):
|
||||
for perm in Permission.objects.filter(project=project, active=True):
|
||||
# All perms at this level just imply a user or team can read
|
||||
if perm.team:
|
||||
team.member_role.children.add(project.member_role)
|
||||
migrations[project.name]['teams'].add(team)
|
||||
perm.team.member_role.children.add(project.member_role)
|
||||
migrations[project.name]['teams'].add(perm.team)
|
||||
|
||||
if perm.user:
|
||||
project.member_role.members.add(perm.user)
|
||||
migrations[project.name]['users'].add(perm.user)
|
||||
|
||||
return migrations
|
||||
|
||||
|
||||
|
||||
def migrate_job_templates(apps, schema_editor):
|
||||
'''
|
||||
NOTE: This must be run after orgs, inventory, projects, credential, and
|
||||
users have been migrated
|
||||
'''
|
||||
|
||||
|
||||
'''
|
||||
I can see job templates when:
|
||||
X I am a superuser.
|
||||
- I can read the inventory, project and credential (which means I am an
|
||||
org admin or member of a team with access to all of the above).
|
||||
- I have permission explicitly granted to check/deploy with the inventory
|
||||
and project.
|
||||
|
||||
|
||||
#This does not mean I would be able to launch a job from the template or
|
||||
#edit the template.
|
||||
- access.py can_read for JobTemplate enforces that you can only
|
||||
see it if you can launch it, so the above imply launch too
|
||||
'''
|
||||
|
||||
|
||||
'''
|
||||
Tower administrators, organization administrators, and project
|
||||
administrators, within a project under their purview, may create and modify
|
||||
new job templates for that project.
|
||||
|
||||
When editing a job template, they may select among the inventory groups and
|
||||
credentials in the organization for which they have usage permissions, or
|
||||
they may leave either blank to be selected at runtime.
|
||||
|
||||
Additionally, they may specify one or more users/teams that have execution
|
||||
permission for that job template, among the users/teams that are a member
|
||||
of that project.
|
||||
|
||||
That execution permission is valid irrespective of any explicit permissions
|
||||
the user has or has not been granted to the inventory group or credential
|
||||
specified in the job template.
|
||||
|
||||
'''
|
||||
|
||||
migrations = defaultdict(lambda: defaultdict(set))
|
||||
|
||||
User = apps.get_model('auth', 'User')
|
||||
JobTemplate = apps.get_model('main', 'JobTemplate')
|
||||
Team = apps.get_model('main', 'Team')
|
||||
Permission = apps.get_model('main', 'Permission')
|
||||
|
||||
for jt in JobTemplate.objects.all():
|
||||
for team in Team.objects.all():
|
||||
if Permission.objects.filter(
|
||||
team=team,
|
||||
inventory=jt.inventory,
|
||||
project=jt.project,
|
||||
active=True,
|
||||
permission_type__in=['create', 'check', 'run'] if jt.job_type == 'check' else ['create', 'run']
|
||||
):
|
||||
team.member_role.children.add(jt.executor_role);
|
||||
migrations[jt.name]['teams'].add(team)
|
||||
|
||||
|
||||
for user in User.objects.all():
|
||||
if jt.accessible_by(user, {'execute': True}):
|
||||
# If the job template is already accessible by the user, because they
|
||||
# are a sytem, organization, or project admin, then don't add an explicit
|
||||
# role entry for them
|
||||
continue
|
||||
|
||||
if old_access.check_user_access(user, jt.__class__, 'start', jt, False):
|
||||
jt.executor_role.members.add(user)
|
||||
migrations[jt.name]['users'].add(user)
|
||||
|
||||
|
||||
return migrations
|
||||
|
||||
@ -158,12 +158,10 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
||||
owner_role = ImplicitRoleField(
|
||||
role_name='Credential Owner',
|
||||
parent_role='team.admin_role',
|
||||
resource_field='resource',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
usage_role = ImplicitRoleField(
|
||||
role_name='Credential User',
|
||||
resource_field='resource',
|
||||
parent_role= 'team.member_role',
|
||||
permissions = {'use': True}
|
||||
)
|
||||
|
||||
@ -96,13 +96,11 @@ class Inventory(CommonModel, ResourceMixin):
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Inventory Administrator',
|
||||
parent_role='organization.admin_role',
|
||||
resource_field='resource',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Inventory Auditor',
|
||||
parent_role='organization.auditor_role',
|
||||
resource_field='resource',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
updater_role = ImplicitRoleField(
|
||||
@ -545,25 +543,21 @@ class Group(CommonModelNameNotUnique, ResourceMixin):
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Inventory Group Administrator',
|
||||
parent_role=['inventory.admin_role', 'parents.admin_role'],
|
||||
resource_field='resource',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Inventory Group Auditor',
|
||||
parent_role=['inventory.auditor_role', 'parents.auditor_role'],
|
||||
resource_field='resource',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
updater_role = ImplicitRoleField(
|
||||
role_name='Inventory Group Updater',
|
||||
parent_role=['inventory.updater_role', 'parents.updater_role'],
|
||||
resource_field='resource',
|
||||
permissions = {'read': True, 'write': True, 'create': True, 'use': True},
|
||||
)
|
||||
executor_role = ImplicitRoleField(
|
||||
role_name='Inventory Group Executor',
|
||||
parent_role=['inventory.executor_role', 'parents.executor_role'],
|
||||
resource_field='resource',
|
||||
permissions = {'read':True, 'execute':True},
|
||||
)
|
||||
|
||||
|
||||
@ -184,20 +184,16 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin):
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Job Template Administrator',
|
||||
parent_role='project.admin_role',
|
||||
resource_field='resource',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Job Template Auditor',
|
||||
parent_role='project.auditor_role',
|
||||
resource_field='resource',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
executor_role = ImplicitRoleField(
|
||||
role_name='Job Template Executor',
|
||||
parent_role='project.auditor_role',
|
||||
resource_field='resource',
|
||||
permissions = {'execute': True}
|
||||
permissions = {'read': True, 'execute': True}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -43,10 +43,6 @@ class Organization(CommonModel, ResourceMixin):
|
||||
blank=True,
|
||||
related_name='admin_of_organizations',
|
||||
)
|
||||
|
||||
# TODO: This field is deprecated. In 3.0 all projects will have exactly one
|
||||
# organization parent, the foreign key field representing that has been
|
||||
# moved to the Project model.
|
||||
projects = models.ManyToManyField(
|
||||
'Project',
|
||||
blank=True,
|
||||
@ -55,18 +51,15 @@ class Organization(CommonModel, ResourceMixin):
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Organization Administrator',
|
||||
parent_role='singleton:System Administrator',
|
||||
resource_field='resource',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Organization Auditor',
|
||||
parent_role='singleton:System Auditor',
|
||||
resource_field='resource',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
member_role = ImplicitRoleField(
|
||||
role_name='Organization Member',
|
||||
resource_field='resource',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
|
||||
@ -114,19 +107,16 @@ class Team(CommonModelNameNotUnique, ResourceMixin):
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Team Administrator',
|
||||
parent_role='organization.admin_role',
|
||||
resource_field='resource',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Team Auditor',
|
||||
parent_role='organization.auditor_role',
|
||||
resource_field='resource',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
member_role = ImplicitRoleField(
|
||||
role_name='Team Member',
|
||||
parent_role='admin_role',
|
||||
resource_field='resource',
|
||||
permissions = {'read':True},
|
||||
)
|
||||
|
||||
|
||||
@ -196,14 +196,6 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
||||
app_label = 'main'
|
||||
ordering = ('id',)
|
||||
|
||||
organization = models.ForeignKey(
|
||||
'Organization',
|
||||
blank=False,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='project_list', # TODO: this should eventually be refactored
|
||||
# back to 'projects' - anoek 2016-01-28
|
||||
)
|
||||
scm_delete_on_next_update = models.BooleanField(
|
||||
default=False,
|
||||
editable=False,
|
||||
@ -217,25 +209,21 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
|
||||
)
|
||||
admin_role = ImplicitRoleField(
|
||||
role_name='Project Administrator',
|
||||
parent_role='organization.admin_role',
|
||||
resource_field='resource',
|
||||
parent_role='organizations.admin_role',
|
||||
permissions = {'all': True}
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Project Auditor',
|
||||
parent_role='organization.auditor_role',
|
||||
resource_field='resource',
|
||||
parent_role='organizations.auditor_role',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
member_role = ImplicitRoleField(
|
||||
role_name='Project Member',
|
||||
resource_field='resource',
|
||||
permissions = {'read': True}
|
||||
)
|
||||
scm_update_role = ImplicitRoleField(
|
||||
role_name='Project Updater',
|
||||
parent_role='admin_role',
|
||||
resource_field='resource',
|
||||
permissions = {'scm_update': True}
|
||||
)
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ from awx.main.models.inventory import (
|
||||
Group,
|
||||
)
|
||||
from awx.main.models.projects import Project
|
||||
from awx.main.models.jobs import JobTemplate
|
||||
from awx.main.models.organization import (
|
||||
Organization,
|
||||
Team,
|
||||
@ -23,13 +24,37 @@ def user():
|
||||
return user
|
||||
return u
|
||||
|
||||
@pytest.fixture
|
||||
def check_jobtemplate(project, inventory, credential):
|
||||
return \
|
||||
JobTemplate.objects.create(
|
||||
job_type='check',
|
||||
project=project,
|
||||
inventory=inventory,
|
||||
credential=credential,
|
||||
name='check-job-template'
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def deploy_jobtemplate(project, inventory, credential):
|
||||
return \
|
||||
JobTemplate.objects.create(
|
||||
job_type='run',
|
||||
project=project,
|
||||
inventory=inventory,
|
||||
credential=credential,
|
||||
name='deploy-job-template'
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def team(organization):
|
||||
return Team.objects.create(organization=organization, name='test-team')
|
||||
|
||||
@pytest.fixture
|
||||
def project(organization):
|
||||
return Project.objects.create(name="test-project", organization=organization, description="test-project-desc")
|
||||
prj = Project.objects.create(name="test-project", description="test-project-desc")
|
||||
prj.organizations.add(organization)
|
||||
return prj
|
||||
|
||||
@pytest.fixture
|
||||
def user_project(user):
|
||||
|
||||
133
awx/main/tests/functional/test_rbac_job_templates.py
Normal file
133
awx/main/tests/functional/test_rbac_job_templates.py
Normal file
@ -0,0 +1,133 @@
|
||||
import pytest
|
||||
|
||||
from awx.main.migrations import _rbac as rbac
|
||||
from awx.main.models import Permission
|
||||
from django.apps import apps
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_template_migration_check(deploy_jobtemplate, check_jobtemplate, user):
|
||||
admin = user('admin', is_superuser=True)
|
||||
joe = user('joe')
|
||||
|
||||
|
||||
check_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||
|
||||
Permission(user=joe, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
||||
Permission(user=joe, inventory=check_jobtemplate.inventory,
|
||||
project=check_jobtemplate.project, permission_type='check').save()
|
||||
|
||||
rbac.migrate_users(apps, None)
|
||||
rbac.migrate_organization(apps, None)
|
||||
rbac.migrate_projects(apps, None)
|
||||
rbac.migrate_inventory(apps, None)
|
||||
|
||||
assert check_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||
|
||||
migrations = rbac.migrate_job_templates(apps, None)
|
||||
|
||||
assert len(migrations[check_jobtemplate.name]['users']) == 1
|
||||
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_template_migration_deploy(deploy_jobtemplate, check_jobtemplate, user):
|
||||
admin = user('admin', is_superuser=True)
|
||||
joe = user('joe')
|
||||
|
||||
|
||||
deploy_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||
|
||||
Permission(user=joe, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
||||
Permission(user=joe, inventory=deploy_jobtemplate.inventory,
|
||||
project=deploy_jobtemplate.project, permission_type='run').save()
|
||||
|
||||
rbac.migrate_users(apps, None)
|
||||
rbac.migrate_organization(apps, None)
|
||||
rbac.migrate_projects(apps, None)
|
||||
rbac.migrate_inventory(apps, None)
|
||||
|
||||
assert deploy_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||
|
||||
migrations = rbac.migrate_job_templates(apps, None)
|
||||
|
||||
assert len(migrations[deploy_jobtemplate.name]['users']) == 1
|
||||
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_template_team_migration_check(deploy_jobtemplate, check_jobtemplate, organization, team, user):
|
||||
admin = user('admin', is_superuser=True)
|
||||
joe = user('joe')
|
||||
team.users.add(joe)
|
||||
team.organization = organization
|
||||
team.save()
|
||||
|
||||
check_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||
|
||||
Permission(team=team, inventory=check_jobtemplate.inventory, permission_type='read').save()
|
||||
Permission(team=team, inventory=check_jobtemplate.inventory,
|
||||
project=check_jobtemplate.project, permission_type='check').save()
|
||||
|
||||
rbac.migrate_users(apps, None)
|
||||
rbac.migrate_team(apps, None)
|
||||
rbac.migrate_organization(apps, None)
|
||||
rbac.migrate_projects(apps, None)
|
||||
rbac.migrate_inventory(apps, None)
|
||||
|
||||
assert check_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||
|
||||
migrations = rbac.migrate_job_templates(apps, None)
|
||||
|
||||
assert len(migrations[check_jobtemplate.name]['users']) == 0
|
||||
assert len(migrations[check_jobtemplate.name]['teams']) == 1
|
||||
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||
|
||||
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_template_team_deploy_migration(deploy_jobtemplate, check_jobtemplate, organization, team, user):
|
||||
admin = user('admin', is_superuser=True)
|
||||
joe = user('joe')
|
||||
team.users.add(joe)
|
||||
team.organization = organization
|
||||
team.save()
|
||||
|
||||
deploy_jobtemplate.project.organizations.all()[0].users.add(joe)
|
||||
|
||||
Permission(team=team, inventory=deploy_jobtemplate.inventory, permission_type='read').save()
|
||||
Permission(team=team, inventory=deploy_jobtemplate.inventory,
|
||||
project=deploy_jobtemplate.project, permission_type='run').save()
|
||||
|
||||
rbac.migrate_users(apps, None)
|
||||
rbac.migrate_team(apps, None)
|
||||
rbac.migrate_organization(apps, None)
|
||||
rbac.migrate_projects(apps, None)
|
||||
rbac.migrate_inventory(apps, None)
|
||||
|
||||
assert deploy_jobtemplate.project.accessible_by(joe, {'read': True})
|
||||
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is False
|
||||
|
||||
migrations = rbac.migrate_job_templates(apps, None)
|
||||
|
||||
assert len(migrations[deploy_jobtemplate.name]['users']) == 0
|
||||
assert len(migrations[deploy_jobtemplate.name]['teams']) == 1
|
||||
assert deploy_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert deploy_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||
|
||||
assert check_jobtemplate.accessible_by(admin, {'execute': True}) is True
|
||||
assert check_jobtemplate.accessible_by(joe, {'execute': True}) is True
|
||||
@ -3,10 +3,16 @@ import pytest
|
||||
from awx.main.migrations import _rbac as rbac
|
||||
from awx.main.models import Permission
|
||||
from django.apps import apps
|
||||
from awx.main.migrations import _old_access as old_access
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_project_user_project(user_project, project, user):
|
||||
u = user('owner')
|
||||
|
||||
assert old_access.check_user_access(u, user_project.__class__, 'read', user_project)
|
||||
assert old_access.check_user_access(u, project.__class__, 'read', project) is False
|
||||
|
||||
assert user_project.accessible_by(u, {'read': True}) is False
|
||||
assert project.accessible_by(u, {'read': True}) is False
|
||||
migrations = rbac.migrate_projects(apps, None)
|
||||
@ -20,11 +26,14 @@ def test_project_accessible_by_sa(user, project):
|
||||
u = user('systemadmin', is_superuser=True)
|
||||
|
||||
assert project.accessible_by(u, {'read': True}) is False
|
||||
rbac.migrate_organization(apps, None)
|
||||
su_migrations = rbac.migrate_users(apps, None)
|
||||
migrations = rbac.migrate_projects(apps, None)
|
||||
assert len(su_migrations) == 1
|
||||
assert len(migrations[project.name]['users']) == 0
|
||||
assert len(migrations[project.name]['teams']) == 0
|
||||
print(project.admin_role.ancestors.all())
|
||||
print(project.admin_role.ancestors.all())
|
||||
assert project.accessible_by(u, {'read': True, 'write': True}) is True
|
||||
|
||||
@pytest.mark.django_db
|
||||
@ -58,6 +67,7 @@ def test_project_team(user, team, project):
|
||||
assert project.accessible_by(member, {'read': True}) is False
|
||||
|
||||
rbac.migrate_team(apps, None)
|
||||
rbac.migrate_organization(apps, None)
|
||||
migrations = rbac.migrate_projects(apps, None)
|
||||
|
||||
assert len(migrations[project.name]['users']) == 0
|
||||
@ -66,13 +76,18 @@ def test_project_team(user, team, project):
|
||||
assert project.accessible_by(nonmember, {'read': True}) is False
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_project_explicit_permission(user, team, project):
|
||||
u = user('user')
|
||||
p = Permission(user=u, project=project, permission_type='check')
|
||||
def test_project_explicit_permission(user, team, project, organization):
|
||||
u = user('prjuser')
|
||||
|
||||
assert old_access.check_user_access(u, project.__class__, 'read', project) is False
|
||||
|
||||
organization.users.add(u)
|
||||
p = Permission(user=u, project=project, permission_type='create', name='Perm name')
|
||||
p.save()
|
||||
|
||||
assert project.accessible_by(u, {'read': True}) is False
|
||||
|
||||
rbac.migrate_organization(apps, None)
|
||||
migrations = rbac.migrate_projects(apps, None)
|
||||
|
||||
assert len(migrations[project.name]['users']) == 1
|
||||
|
||||
11
docs/rbac.md
11
docs/rbac.md
@ -103,7 +103,16 @@ The `singleton` static method is a helper method on the `Role` model that helps
|
||||
|
||||
`role_name` is the display name of the role. This is useful when generating reports or looking the results of queries.
|
||||
|
||||
`permissions` is a dictionary of set permissions that a user with this role will gain to your `Resource`. A permission defaults to `False` if not explicitly provided. Below is a list of available permissions. The special permission `all` is a shortcut for generating a dict with all of the explicit permissions listed below set to `True`.
|
||||
`permissions` can be used when the model that contains the
|
||||
`ImplicitRoleField` utilizs the `ResourceMixin`. When present, a
|
||||
`RolePermission` entry will be automatically created to grant the specified
|
||||
permissions on the resource to the role defined by the `ImplicitRoleField`.
|
||||
|
||||
This field should be specified as a dictionary of permissions you wish to
|
||||
automatically grant. Below is a list of available permissions. The special
|
||||
permission `all` is a shortcut for generating a dict with all of the explicit
|
||||
permissions listed below set to `True`. Note that permissions default to
|
||||
`False` if not explicitly provided.
|
||||
|
||||
```python
|
||||
# Available Permissions
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user