Add parent ORing for Role field, renamed permissions -> active_roles

This commit is contained in:
Wayne Witzel III 2016-04-21 15:54:36 -04:00
parent 718b5019b6
commit 0309757439
5 changed files with 52 additions and 13 deletions

View File

@ -319,7 +319,7 @@ class BaseSerializer(serializers.ModelSerializer):
# RBAC summary fields
request = self.context.get('request', None)
if request and isinstance(obj, ResourceMixin) and request.user.is_authenticated():
summary_fields['permissions'] = obj.get_permissions(request.user)
summary_fields['active_roles'] = obj.get_permissions(request.user)
roles = {}
for field in obj._meta.get_fields():
if type(field) is ImplicitRoleField:
@ -1479,7 +1479,7 @@ class ResourceAccessListElementSerializer(UserSerializer):
if 'summary_fields' not in ret:
ret['summary_fields'] = {}
ret['summary_fields']['permissions'] = get_roles_on_resource(obj, user)
ret['summary_fields']['active_roles'] = get_roles_on_resource(obj, user)
def format_role_perm(role):
role_dict = { 'id': role.id, 'name': role.name, 'description': role.description}
@ -1489,7 +1489,7 @@ class ResourceAccessListElementSerializer(UserSerializer):
role_dict['related'] = reverse_gfk(role.content_object)
except:
pass
return { 'role': role_dict, 'permissions': get_roles_on_resource(obj, role)}
return { 'role': role_dict, 'active_roles': get_roles_on_resource(obj, role)}
def format_team_role_perm(team_role, permissive_role_ids):
role = team_role.children.filter(id__in=permissive_role_ids)[0]
@ -1507,7 +1507,7 @@ class ResourceAccessListElementSerializer(UserSerializer):
role_dict['related'] = reverse_gfk(role.content_object)
except:
pass
return { 'role': role_dict, 'permissions': get_roles_on_resource(obj, team_role)}
return { 'role': role_dict, 'active_roles': get_roles_on_resource(obj, team_role)}
team_content_type = ContentType.objects.get_for_model(Team)
content_type = ContentType.objects.get_for_model(obj)

View File

@ -130,6 +130,10 @@ class ImplicitRoleField(models.ForeignKey):
field_names = [field_names]
for field_name in field_names:
# Handle the OR syntax for role parents
if type(field_name) == tuple:
continue
if field_name.startswith('singleton:'):
continue
@ -227,8 +231,16 @@ class ImplicitRoleField(models.ForeignKey):
paths = self.parent_role if type(self.parent_role) is list else [self.parent_role]
parent_roles = set()
for path in paths:
if path.startswith("singleton:"):
if type(path) == tuple:
for or_path in path:
if or_path.startswith("singleton:"):
raise Exception("Unable to use Singleton role in an OR context.")
parents = resolve_role_field(instance, or_path)
if len(parents) is not 0:
break
elif path.startswith("singleton:"):
singleton_name = path[10:]
Role_ = get_current_apps().get_model('main', 'Role')
qs = Role_.objects.filter(singleton_name=singleton_name)
@ -244,6 +256,7 @@ class ImplicitRoleField(models.ForeignKey):
parents = [role.id]
else:
parents = resolve_role_field(instance, path)
for parent in parents:
parent_roles.add(parent)
return parent_roles

View File

@ -228,12 +228,12 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin):
admin_role = ImplicitRoleField(
role_name='Job Template Administrator',
role_description='Full access to all settings',
parent_role='project.admin_role',
parent_role=[('project.admin_role', 'inventory.admin_role')]
)
auditor_role = ImplicitRoleField(
role_name='Job Template Auditor',
role_description='Read-only access to all settings',
parent_role='project.auditor_role',
parent_role=[('project.auditor_role', 'inventory.auditor_role')]
)
execute_role = ImplicitRoleField(
role_name='Job Template Runner',

View File

@ -333,15 +333,13 @@ class RoleAncestorEntry(models.Model):
descendent = models.ForeignKey(Role, null=False, on_delete=models.CASCADE, related_name='+')
ancestor = models.ForeignKey(Role, null=False, on_delete=models.CASCADE, related_name='+')
role_field = models.TextField(null=False)
#content_type_id = models.PositiveIntegerField(null=False)
#object_id = models.PositiveIntegerField(null=False)
content_type_id = models.PositiveIntegerField(null=False)
object_id = models.PositiveIntegerField(null=False)
def get_roles_on_resource(resource, accessor):
'''
Returns a dict (or None) of the roles a accessor has for a given resource.
Returns a string list of the roles a accessor has for a given resource.
An accessor can be either a User, Role, or an arbitrary resource that
contains one or more Roles associated with it.
'''
@ -355,11 +353,11 @@ def get_roles_on_resource(resource, accessor):
roles = Role.objects.filter(content_type__pk=accessor_type.id,
object_id=accessor.id)
return {
role_field: True for role_field in
return [
role_field for role_field in
RoleAncestorEntry.objects.filter(
ancestor__in=roles,
content_type_id=ContentType.objects.get_for_model(resource).id,
object_id=resource.id
).values_list('role_field', flat=True)
}
]

View File

@ -4,6 +4,8 @@ from awx.main.models import (
Role,
Organization,
Project,
JobTemplate,
Inventory,
)
@ -220,3 +222,29 @@ def test_auto_parenting():
assert org2.admin_role.is_ancestor_of(prj1.admin_role)
assert org2.admin_role.is_ancestor_of(prj2.admin_role)
@pytest.mark.django_db
def test_OR_parents(alice, bob):
org1 = Organization.objects.create(name="org1")
inv = Inventory.objects.create(name='inv1', organization=org1)
prj = Project.objects.create(name='prj1', organization=org1)
jt1 = JobTemplate.objects.create(name='jt1', inventory=inv)
jt2 = JobTemplate.objects.create(name='jt2', project=prj)
jt3 = JobTemplate.objects.create(name='jt3', inventory=inv, project=prj)
assert bob not in jt1.admin_role
assert alice not in jt2.admin_role
assert bob not in jt3.admin_role
assert alice not in jt3.admin_role
inv.admin_role.members.add(bob)
assert bob in jt1.admin_role
assert alice not in jt1.admin_role
prj.admin_role.members.add(alice)
assert alice in jt2.admin_role
assert bob not in jt2.admin_role
assert alice in jt3.admin_role
assert bob not in jt3.admin_role