From adebe00ca500043b6c88dbde5669200e7b65332f Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 6 Dec 2016 20:43:15 -0500 Subject: [PATCH 1/2] filter.py implementation of role level filtering --- awx/api/filters.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/awx/api/filters.py b/awx/api/filters.py index d861303f1e..5c987dc440 100644 --- a/awx/api/filters.py +++ b/awx/api/filters.py @@ -19,6 +19,7 @@ from rest_framework.filters import BaseFilterBackend # Ansible Tower from awx.main.utils import get_type_for_model, to_python_boolean +from awx.main.models.rbac import RoleAncestorEntry class MongoFilterBackend(BaseFilterBackend): @@ -158,6 +159,7 @@ class FieldLookupBackend(BaseFilterBackend): and_filters = [] or_filters = [] chain_filters = [] + role_filters = [] for key, values in request.query_params.lists(): if key in self.RESERVED_NAMES: continue @@ -174,6 +176,18 @@ class FieldLookupBackend(BaseFilterBackend): key = key[:-5] q_int = True + # RBAC filtering + if key == 'role_level': + model = queryset.model + role_filters.append( + Q(pk__in=RoleAncestorEntry.objects.filter( + ancestor__in=request.user.roles.all(), + content_type_id=ContentType.objects.get_for_model(model).id, + role_field=values[0] + ).values_list('object_id').distinct()) + ) + continue + # Custom chain__ and or__ filters, mutually exclusive (both can # precede not__). q_chain = False @@ -204,13 +218,15 @@ class FieldLookupBackend(BaseFilterBackend): and_filters.append((q_not, new_key, value)) # Now build Q objects for database query filter. - if and_filters or or_filters or chain_filters: + if and_filters or or_filters or chain_filters or role_filters: args = [] for n, k, v in and_filters: if n: args.append(~Q(**{k:v})) else: args.append(Q(**{k:v})) + for q in role_filters: + args.append(q) if or_filters: q = Q() for n,k,v in or_filters: From 2092e67f6f9cdbc674032cb662b9641e88644551 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 8 Dec 2016 12:28:46 -0500 Subject: [PATCH 2/2] docs and lazy eval for role_level filter --- awx/api/filters.py | 19 +++++++++---------- awx/api/templates/api/_list_common.md | 5 +++++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/awx/api/filters.py b/awx/api/filters.py index 5c987dc440..5146ff0cd2 100644 --- a/awx/api/filters.py +++ b/awx/api/filters.py @@ -178,14 +178,7 @@ class FieldLookupBackend(BaseFilterBackend): # RBAC filtering if key == 'role_level': - model = queryset.model - role_filters.append( - Q(pk__in=RoleAncestorEntry.objects.filter( - ancestor__in=request.user.roles.all(), - content_type_id=ContentType.objects.get_for_model(model).id, - role_field=values[0] - ).values_list('object_id').distinct()) - ) + role_filters.append(values[0]) continue # Custom chain__ and or__ filters, mutually exclusive (both can @@ -225,8 +218,14 @@ class FieldLookupBackend(BaseFilterBackend): args.append(~Q(**{k:v})) else: args.append(Q(**{k:v})) - for q in role_filters: - args.append(q) + for role_name in role_filters: + args.append( + Q(pk__in=RoleAncestorEntry.objects.filter( + ancestor__in=request.user.roles.all(), + content_type_id=ContentType.objects.get_for_model(queryset.model).id, + role_field=role_name + ).values_list('object_id').distinct()) + ) if or_filters: q = Q() for n,k,v in or_filters: diff --git a/awx/api/templates/api/_list_common.md b/awx/api/templates/api/_list_common.md index e355421de3..36e6819276 100644 --- a/awx/api/templates/api/_list_common.md +++ b/awx/api/templates/api/_list_common.md @@ -132,3 +132,8 @@ values. Lists (for the `in` lookup) may be specified as a comma-separated list of values. + +(_Added in Ansible Tower 3.1.0_) Filtering based on the requesting user's +level of access by query string parameter. + +* `role_level`: Level of role to filter on, such as `admin_role`