Merge pull request #962 from anoek/rbac

"Completion" of RBAC migrations; resource_field elimination
This commit is contained in:
Akita Noek
2016-02-15 13:36:31 -05:00
12 changed files with 1990 additions and 64 deletions

View File

@@ -91,9 +91,8 @@ class ImplicitResourceField(models.ForeignKey):
class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor): class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
"""Descriptor Implict Role Fields. Auto-creates the appropriate role entry on first access""" """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.role_name = role_name
self.resource_field = resource_field
self.permissions = permissions self.permissions = permissions
self.parent_role = parent_role self.parent_role = parent_role
@@ -143,10 +142,10 @@ class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
setattr(instance, self.field.name, role) setattr(instance, self.field.name, role)
instance.save(update_fields=[self.field.name,]) instance.save(update_fields=[self.field.name,])
if self.resource_field and self.permissions: if self.permissions is not None:
permissions = RolePermission( permissions = RolePermission(
role=role, role=role,
resource=getattr(instance, self.resource_field) resource=instance.resource
) )
if 'all' in self.permissions and self.permissions['all']: if 'all' in self.permissions and self.permissions['all']:
@@ -170,9 +169,8 @@ class ImplicitRoleDescriptor(ReverseSingleRelatedObjectDescriptor):
class ImplicitRoleField(models.ForeignKey): class ImplicitRoleField(models.ForeignKey):
"""Implicitly creates a role entry for a resource""" """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.role_name = role_name
self.resource_field = resource_field
self.permissions = permissions self.permissions = permissions
self.parent_role = parent_role self.parent_role = parent_role
@@ -187,7 +185,6 @@ class ImplicitRoleField(models.ForeignKey):
self.name, self.name,
ImplicitRoleDescriptor( ImplicitRoleDescriptor(
self.role_name, self.role_name,
self.resource_field,
self.permissions, self.permissions,
self.parent_role, self.parent_role,
self self
@@ -211,7 +208,8 @@ class ImplicitRoleField(models.ForeignKey):
first_field_name = field_name.split('.')[0] first_field_name = field_name.split('.')[0]
field = getattr(cls, first_field_name) 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: if found_m2m_field:
# This limitation is due to a lack of understanding on my part, the # 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 # 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 found_m2m_field = True
self.m2m_field_name = first_field_name self.m2m_field_name = first_field_name
self.m2m_field_attr = field_name.split('.',1)[1] self.m2m_field_attr = field_name.split('.',1)[1]
m2m_changed.connect(self.m2m_update, field.through)
if type(field) is ManyRelatedObjectsDescriptor: if type(field) is ReverseManyRelatedObjectsDescriptor:
raise Exception('ManyRelatedObjectsDescriptor references are currently unsupported ' + m2m_changed.connect(self.m2m_update, field.through)
'(but the reverse is, so supporting this is probably easy to add)): %s.%s' % else:
(cls.__name__, first_field_name)) 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): def m2m_update(self, sender, instance, action, reverse, model, pk_set, **kwargs):
if action == 'post_add' or action == 'pre_remove': if action == 'post_add' or action == 'pre_remove':
if reverse: if reverse:

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
from collections import defaultdict from collections import defaultdict
import _old_access as old_access
def migrate_users(apps, schema_editor): def migrate_users(apps, schema_editor):
migrations = list() migrations = list()
@@ -52,7 +53,7 @@ def migrate_inventory(apps, schema_editor):
for inventory in Inventory.objects.all(): for inventory in Inventory.objects.all():
teams, users = [], [] teams, users = [], []
for perm in Permission.objects.filter(inventory=inventory): for perm in Permission.objects.filter(inventory=inventory, active=True):
role = None role = None
execrole = None execrole = None
if perm.permission_type == 'admin': if perm.permission_type == 'admin':
@@ -64,6 +65,10 @@ def migrate_inventory(apps, schema_editor):
elif perm.permission_type == 'write': elif perm.permission_type == 'write':
role = inventory.updater_role role = inventory.updater_role
pass pass
elif perm.permission_type == 'check':
pass
elif perm.permission_type == 'run':
pass
else: else:
raise Exception('Unhandled permission type for inventory: %s' % perm.permission_type) raise Exception('Unhandled permission type for inventory: %s' % perm.permission_type)
if perm.run_ad_hoc_commands: if perm.run_ad_hoc_commands:
@@ -108,7 +113,7 @@ def migrate_projects(apps, schema_editor):
Permission = apps.get_model('main', 'Permission') Permission = apps.get_model('main', 'Permission')
for project in Project.objects.all(): 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) project.admin_role.members.add(project.created_by)
migrations[project.name]['users'].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) team.member_role.children.add(project.member_role)
migrations[project.name]['teams'].add(team) migrations[project.name]['teams'].add(team)
if project.organization is not None: if project.organizations.count() > 0:
for user in project.organization.users.all(): for org in project.organizations.all():
project.member_role.members.add(user) for user in org.users.all():
migrations[project.name]['users'].add(user) 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 # All perms at this level just imply a user or team can read
if perm.team: if perm.team:
team.member_role.children.add(project.member_role) perm.team.member_role.children.add(project.member_role)
migrations[project.name]['teams'].add(team) migrations[project.name]['teams'].add(perm.team)
if perm.user: if perm.user:
project.member_role.members.add(perm.user) project.member_role.members.add(perm.user)
migrations[project.name]['users'].add(perm.user) migrations[project.name]['users'].add(perm.user)
return migrations 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

View File

@@ -158,12 +158,10 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
owner_role = ImplicitRoleField( owner_role = ImplicitRoleField(
role_name='Credential Owner', role_name='Credential Owner',
parent_role='team.admin_role', parent_role='team.admin_role',
resource_field='resource',
permissions = {'all': True} permissions = {'all': True}
) )
usage_role = ImplicitRoleField( usage_role = ImplicitRoleField(
role_name='Credential User', role_name='Credential User',
resource_field='resource',
parent_role= 'team.member_role', parent_role= 'team.member_role',
permissions = {'use': True} permissions = {'use': True}
) )

View File

@@ -96,13 +96,11 @@ class Inventory(CommonModel, ResourceMixin):
admin_role = ImplicitRoleField( admin_role = ImplicitRoleField(
role_name='Inventory Administrator', role_name='Inventory Administrator',
parent_role='organization.admin_role', parent_role='organization.admin_role',
resource_field='resource',
permissions = {'all': True} permissions = {'all': True}
) )
auditor_role = ImplicitRoleField( auditor_role = ImplicitRoleField(
role_name='Inventory Auditor', role_name='Inventory Auditor',
parent_role='organization.auditor_role', parent_role='organization.auditor_role',
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
updater_role = ImplicitRoleField( updater_role = ImplicitRoleField(
@@ -545,25 +543,21 @@ class Group(CommonModelNameNotUnique, ResourceMixin):
admin_role = ImplicitRoleField( admin_role = ImplicitRoleField(
role_name='Inventory Group Administrator', role_name='Inventory Group Administrator',
parent_role=['inventory.admin_role', 'parents.admin_role'], parent_role=['inventory.admin_role', 'parents.admin_role'],
resource_field='resource',
permissions = {'all': True} permissions = {'all': True}
) )
auditor_role = ImplicitRoleField( auditor_role = ImplicitRoleField(
role_name='Inventory Group Auditor', role_name='Inventory Group Auditor',
parent_role=['inventory.auditor_role', 'parents.auditor_role'], parent_role=['inventory.auditor_role', 'parents.auditor_role'],
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
updater_role = ImplicitRoleField( updater_role = ImplicitRoleField(
role_name='Inventory Group Updater', role_name='Inventory Group Updater',
parent_role=['inventory.updater_role', 'parents.updater_role'], parent_role=['inventory.updater_role', 'parents.updater_role'],
resource_field='resource',
permissions = {'read': True, 'write': True, 'create': True, 'use': True}, permissions = {'read': True, 'write': True, 'create': True, 'use': True},
) )
executor_role = ImplicitRoleField( executor_role = ImplicitRoleField(
role_name='Inventory Group Executor', role_name='Inventory Group Executor',
parent_role=['inventory.executor_role', 'parents.executor_role'], parent_role=['inventory.executor_role', 'parents.executor_role'],
resource_field='resource',
permissions = {'read':True, 'execute':True}, permissions = {'read':True, 'execute':True},
) )

View File

@@ -184,20 +184,16 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin):
admin_role = ImplicitRoleField( admin_role = ImplicitRoleField(
role_name='Job Template Administrator', role_name='Job Template Administrator',
parent_role='project.admin_role', parent_role='project.admin_role',
resource_field='resource',
permissions = {'all': True} permissions = {'all': True}
) )
auditor_role = ImplicitRoleField( auditor_role = ImplicitRoleField(
role_name='Job Template Auditor', role_name='Job Template Auditor',
parent_role='project.auditor_role', parent_role='project.auditor_role',
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
executor_role = ImplicitRoleField( executor_role = ImplicitRoleField(
role_name='Job Template Executor', role_name='Job Template Executor',
parent_role='project.auditor_role', permissions = {'read': True, 'execute': True}
resource_field='resource',
permissions = {'execute': True}
) )
@classmethod @classmethod

View File

@@ -43,10 +43,6 @@ class Organization(CommonModel, ResourceMixin):
blank=True, blank=True,
related_name='admin_of_organizations', 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( projects = models.ManyToManyField(
'Project', 'Project',
blank=True, blank=True,
@@ -55,18 +51,15 @@ class Organization(CommonModel, ResourceMixin):
admin_role = ImplicitRoleField( admin_role = ImplicitRoleField(
role_name='Organization Administrator', role_name='Organization Administrator',
parent_role='singleton:System Administrator', parent_role='singleton:System Administrator',
resource_field='resource',
permissions = {'all': True} permissions = {'all': True}
) )
auditor_role = ImplicitRoleField( auditor_role = ImplicitRoleField(
role_name='Organization Auditor', role_name='Organization Auditor',
parent_role='singleton:System Auditor', parent_role='singleton:System Auditor',
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
member_role = ImplicitRoleField( member_role = ImplicitRoleField(
role_name='Organization Member', role_name='Organization Member',
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
@@ -114,19 +107,16 @@ class Team(CommonModelNameNotUnique, ResourceMixin):
admin_role = ImplicitRoleField( admin_role = ImplicitRoleField(
role_name='Team Administrator', role_name='Team Administrator',
parent_role='organization.admin_role', parent_role='organization.admin_role',
resource_field='resource',
permissions = {'all': True} permissions = {'all': True}
) )
auditor_role = ImplicitRoleField( auditor_role = ImplicitRoleField(
role_name='Team Auditor', role_name='Team Auditor',
parent_role='organization.auditor_role', parent_role='organization.auditor_role',
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
member_role = ImplicitRoleField( member_role = ImplicitRoleField(
role_name='Team Member', role_name='Team Member',
parent_role='admin_role', parent_role='admin_role',
resource_field='resource',
permissions = {'read':True}, permissions = {'read':True},
) )

View File

@@ -196,14 +196,6 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
app_label = 'main' app_label = 'main'
ordering = ('id',) 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( scm_delete_on_next_update = models.BooleanField(
default=False, default=False,
editable=False, editable=False,
@@ -217,25 +209,21 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
) )
admin_role = ImplicitRoleField( admin_role = ImplicitRoleField(
role_name='Project Administrator', role_name='Project Administrator',
parent_role='organization.admin_role', parent_role='organizations.admin_role',
resource_field='resource',
permissions = {'all': True} permissions = {'all': True}
) )
auditor_role = ImplicitRoleField( auditor_role = ImplicitRoleField(
role_name='Project Auditor', role_name='Project Auditor',
parent_role='organization.auditor_role', parent_role='organizations.auditor_role',
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
member_role = ImplicitRoleField( member_role = ImplicitRoleField(
role_name='Project Member', role_name='Project Member',
resource_field='resource',
permissions = {'read': True} permissions = {'read': True}
) )
scm_update_role = ImplicitRoleField( scm_update_role = ImplicitRoleField(
role_name='Project Updater', role_name='Project Updater',
parent_role='admin_role', parent_role='admin_role',
resource_field='resource',
permissions = {'scm_update': True} permissions = {'scm_update': True}
) )

View File

@@ -6,6 +6,7 @@ from awx.main.models.inventory import (
Group, Group,
) )
from awx.main.models.projects import Project from awx.main.models.projects import Project
from awx.main.models.jobs import JobTemplate
from awx.main.models.organization import ( from awx.main.models.organization import (
Organization, Organization,
Team, Team,
@@ -23,13 +24,37 @@ def user():
return user return user
return u 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 @pytest.fixture
def team(organization): def team(organization):
return Team.objects.create(organization=organization, name='test-team') return Team.objects.create(organization=organization, name='test-team')
@pytest.fixture @pytest.fixture
def project(organization): 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 @pytest.fixture
def user_project(user): def user_project(user):

View 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

View File

@@ -3,10 +3,16 @@ import pytest
from awx.main.migrations import _rbac as rbac from awx.main.migrations import _rbac as rbac
from awx.main.models import Permission from awx.main.models import Permission
from django.apps import apps from django.apps import apps
from awx.main.migrations import _old_access as old_access
@pytest.mark.django_db @pytest.mark.django_db
def test_project_user_project(user_project, project, user): def test_project_user_project(user_project, project, user):
u = user('owner') 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 user_project.accessible_by(u, {'read': True}) is False
assert project.accessible_by(u, {'read': True}) is False assert project.accessible_by(u, {'read': True}) is False
migrations = rbac.migrate_projects(apps, None) migrations = rbac.migrate_projects(apps, None)
@@ -20,11 +26,14 @@ def test_project_accessible_by_sa(user, project):
u = user('systemadmin', is_superuser=True) u = user('systemadmin', is_superuser=True)
assert project.accessible_by(u, {'read': True}) is False assert project.accessible_by(u, {'read': True}) is False
rbac.migrate_organization(apps, None)
su_migrations = rbac.migrate_users(apps, None) su_migrations = rbac.migrate_users(apps, None)
migrations = rbac.migrate_projects(apps, None) migrations = rbac.migrate_projects(apps, None)
assert len(su_migrations) == 1 assert len(su_migrations) == 1
assert len(migrations[project.name]['users']) == 0 assert len(migrations[project.name]['users']) == 0
assert len(migrations[project.name]['teams']) == 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 assert project.accessible_by(u, {'read': True, 'write': True}) is True
@pytest.mark.django_db @pytest.mark.django_db
@@ -58,6 +67,7 @@ def test_project_team(user, team, project):
assert project.accessible_by(member, {'read': True}) is False assert project.accessible_by(member, {'read': True}) is False
rbac.migrate_team(apps, None) rbac.migrate_team(apps, None)
rbac.migrate_organization(apps, None)
migrations = rbac.migrate_projects(apps, None) migrations = rbac.migrate_projects(apps, None)
assert len(migrations[project.name]['users']) == 0 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 assert project.accessible_by(nonmember, {'read': True}) is False
@pytest.mark.django_db @pytest.mark.django_db
def test_project_explicit_permission(user, team, project): def test_project_explicit_permission(user, team, project, organization):
u = user('user') u = user('prjuser')
p = Permission(user=u, project=project, permission_type='check')
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() p.save()
assert project.accessible_by(u, {'read': True}) is False assert project.accessible_by(u, {'read': True}) is False
rbac.migrate_organization(apps, None)
migrations = rbac.migrate_projects(apps, None) migrations = rbac.migrate_projects(apps, None)
assert len(migrations[project.name]['users']) == 1 assert len(migrations[project.name]['users']) == 1

View File

@@ -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. `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 ```python
# Available Permissions # Available Permissions