mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
Merge pull request #1639 from wwitzel3/bug-1428
Add parent ORing for Role field, renamed permissions -> active_roles
This commit is contained in:
@@ -319,7 +319,7 @@ class BaseSerializer(serializers.ModelSerializer):
|
|||||||
# RBAC summary fields
|
# RBAC summary fields
|
||||||
request = self.context.get('request', None)
|
request = self.context.get('request', None)
|
||||||
if request and isinstance(obj, ResourceMixin) and request.user.is_authenticated():
|
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 = {}
|
roles = {}
|
||||||
for field in obj._meta.get_fields():
|
for field in obj._meta.get_fields():
|
||||||
if type(field) is ImplicitRoleField:
|
if type(field) is ImplicitRoleField:
|
||||||
@@ -1479,7 +1479,7 @@ class ResourceAccessListElementSerializer(UserSerializer):
|
|||||||
|
|
||||||
if 'summary_fields' not in ret:
|
if 'summary_fields' not in ret:
|
||||||
ret['summary_fields'] = {}
|
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):
|
def format_role_perm(role):
|
||||||
role_dict = { 'id': role.id, 'name': role.name, 'description': role.description}
|
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)
|
role_dict['related'] = reverse_gfk(role.content_object)
|
||||||
except:
|
except:
|
||||||
pass
|
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):
|
def format_team_role_perm(team_role, permissive_role_ids):
|
||||||
role = team_role.children.filter(id__in=permissive_role_ids)[0]
|
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)
|
role_dict['related'] = reverse_gfk(role.content_object)
|
||||||
except:
|
except:
|
||||||
pass
|
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)
|
team_content_type = ContentType.objects.get_for_model(Team)
|
||||||
content_type = ContentType.objects.get_for_model(obj)
|
content_type = ContentType.objects.get_for_model(obj)
|
||||||
|
|||||||
@@ -130,6 +130,10 @@ class ImplicitRoleField(models.ForeignKey):
|
|||||||
field_names = [field_names]
|
field_names = [field_names]
|
||||||
|
|
||||||
for field_name in 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:'):
|
if field_name.startswith('singleton:'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -227,8 +231,16 @@ class ImplicitRoleField(models.ForeignKey):
|
|||||||
|
|
||||||
paths = self.parent_role if type(self.parent_role) is list else [self.parent_role]
|
paths = self.parent_role if type(self.parent_role) is list else [self.parent_role]
|
||||||
parent_roles = set()
|
parent_roles = set()
|
||||||
|
|
||||||
for path in paths:
|
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:]
|
singleton_name = path[10:]
|
||||||
Role_ = get_current_apps().get_model('main', 'Role')
|
Role_ = get_current_apps().get_model('main', 'Role')
|
||||||
qs = Role_.objects.filter(singleton_name=singleton_name)
|
qs = Role_.objects.filter(singleton_name=singleton_name)
|
||||||
@@ -244,6 +256,7 @@ class ImplicitRoleField(models.ForeignKey):
|
|||||||
parents = [role.id]
|
parents = [role.id]
|
||||||
else:
|
else:
|
||||||
parents = resolve_role_field(instance, path)
|
parents = resolve_role_field(instance, path)
|
||||||
|
|
||||||
for parent in parents:
|
for parent in parents:
|
||||||
parent_roles.add(parent)
|
parent_roles.add(parent)
|
||||||
return parent_roles
|
return parent_roles
|
||||||
|
|||||||
@@ -175,15 +175,15 @@ class Migration(migrations.Migration):
|
|||||||
name='use_role',
|
name='use_role',
|
||||||
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May use this inventory, but not read sensitive portions or modify it', parent_role=None, to='main.Role', role_name=b'Inventory User', null=b'True'),
|
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'May use this inventory, but not read sensitive portions or modify it', parent_role=None, to='main.Role', role_name=b'Inventory User', null=b'True'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AlterField(
|
||||||
model_name='jobtemplate',
|
model_name='jobtemplate',
|
||||||
name='admin_role',
|
name='admin_role',
|
||||||
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Full access to all settings', parent_role=b'project.admin_role', to='main.Role', role_name=b'Job Template Administrator', null=b'True'),
|
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Full access to all settings', parent_role=[(b'project.admin_role', b'inventory.admin_role')], to='main.Role', role_name=b'Job Template Administrator', null=b'True'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AlterField(
|
||||||
model_name='jobtemplate',
|
model_name='jobtemplate',
|
||||||
name='auditor_role',
|
name='auditor_role',
|
||||||
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Read-only access to all settings', parent_role=b'project.auditor_role', to='main.Role', role_name=b'Job Template Auditor', null=b'True'),
|
field=awx.main.fields.ImplicitRoleField(related_name='+', role_description=b'Read-only access to all settings', parent_role=[(b'project.auditor_role', b'inventory.auditor_role')], to='main.Role', role_name=b'Job Template Auditor', null=b'True'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='jobtemplate',
|
model_name='jobtemplate',
|
||||||
|
|||||||
@@ -228,12 +228,12 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin):
|
|||||||
admin_role = ImplicitRoleField(
|
admin_role = ImplicitRoleField(
|
||||||
role_name='Job Template Administrator',
|
role_name='Job Template Administrator',
|
||||||
role_description='Full access to all settings',
|
role_description='Full access to all settings',
|
||||||
parent_role='project.admin_role',
|
parent_role=[('project.admin_role', 'inventory.admin_role')]
|
||||||
)
|
)
|
||||||
auditor_role = ImplicitRoleField(
|
auditor_role = ImplicitRoleField(
|
||||||
role_name='Job Template Auditor',
|
role_name='Job Template Auditor',
|
||||||
role_description='Read-only access to all settings',
|
role_description='Read-only access to all settings',
|
||||||
parent_role='project.auditor_role',
|
parent_role=[('project.auditor_role', 'inventory.auditor_role')]
|
||||||
)
|
)
|
||||||
execute_role = ImplicitRoleField(
|
execute_role = ImplicitRoleField(
|
||||||
role_name='Job Template Runner',
|
role_name='Job Template Runner',
|
||||||
|
|||||||
@@ -333,15 +333,13 @@ class RoleAncestorEntry(models.Model):
|
|||||||
descendent = models.ForeignKey(Role, null=False, on_delete=models.CASCADE, related_name='+')
|
descendent = models.ForeignKey(Role, null=False, on_delete=models.CASCADE, related_name='+')
|
||||||
ancestor = 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)
|
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)
|
content_type_id = models.PositiveIntegerField(null=False)
|
||||||
object_id = models.PositiveIntegerField(null=False)
|
object_id = models.PositiveIntegerField(null=False)
|
||||||
|
|
||||||
|
|
||||||
def get_roles_on_resource(resource, accessor):
|
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
|
An accessor can be either a User, Role, or an arbitrary resource that
|
||||||
contains one or more Roles associated with it.
|
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,
|
roles = Role.objects.filter(content_type__pk=accessor_type.id,
|
||||||
object_id=accessor.id)
|
object_id=accessor.id)
|
||||||
|
|
||||||
return {
|
return [
|
||||||
role_field: True for role_field in
|
role_field for role_field in
|
||||||
RoleAncestorEntry.objects.filter(
|
RoleAncestorEntry.objects.filter(
|
||||||
ancestor__in=roles,
|
ancestor__in=roles,
|
||||||
content_type_id=ContentType.objects.get_for_model(resource).id,
|
content_type_id=ContentType.objects.get_for_model(resource).id,
|
||||||
object_id=resource.id
|
object_id=resource.id
|
||||||
).values_list('role_field', flat=True)
|
).values_list('role_field', flat=True)
|
||||||
}
|
]
|
||||||
|
|||||||
@@ -420,8 +420,8 @@ def test_ensure_permissions_is_present(organization, get, user):
|
|||||||
org = response.data
|
org = response.data
|
||||||
|
|
||||||
assert 'summary_fields' in org
|
assert 'summary_fields' in org
|
||||||
assert 'permissions' in org['summary_fields']
|
assert 'active_roles' in org['summary_fields']
|
||||||
assert org['summary_fields']['permissions']['read_role'] > 0
|
assert 'read_role' in org['summary_fields']['active_roles']
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_ensure_role_summary_is_present(organization, get, user):
|
def test_ensure_role_summary_is_present(organization, get, user):
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from awx.main.models import (
|
|||||||
Role,
|
Role,
|
||||||
Organization,
|
Organization,
|
||||||
Project,
|
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(prj1.admin_role)
|
||||||
assert org2.admin_role.is_ancestor_of(prj2.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
|
||||||
|
|||||||
Reference in New Issue
Block a user