diff --git a/awx/api/filters.py b/awx/api/filters.py index 59d85b785d..fbfd325b81 100644 --- a/awx/api/filters.py +++ b/awx/api/filters.py @@ -49,7 +49,18 @@ class FieldLookupBackend(BaseFilterBackend): # FIXME: Could build up a list of models used across relationships, use # those lookups combined with request.user.get_queryset(Model) to make # sure user cannot query using objects he could not view. + new_parts = [] for n, name in enumerate(parts[:-1]): + + # HACK: Make project and inventory source filtering by old field names work for backwards compatibility. + if model._meta.object_name in ('Project', 'InventorySource'): + name = { + 'current_update': 'current_job', + 'last_update': 'last_job', + 'last_update_failed': 'last_job_failed', + 'last_updated': 'last_job_run', + }.get(name, name) + if name == 'pk': field = model._meta.pk else: @@ -59,7 +70,11 @@ class FieldLookupBackend(BaseFilterBackend): model = field.rel.to else: model = field.model - return field + new_parts.append(name) + if parts: + new_parts.append(parts[-1]) + new_lookup = '__'.join(new_parts) + return field, new_lookup def to_python_boolean(self, value, allow_none=False): value = unicode(value) @@ -90,7 +105,7 @@ class FieldLookupBackend(BaseFilterBackend): return field.to_python(value) def value_to_python(self, model, lookup, value): - field = self.get_field_from_lookup(model, lookup) + field, new_lookup = self.get_field_from_lookup(model, lookup) if lookup.endswith('__isnull'): value = self.to_python_boolean(value) elif lookup.endswith('__in'): @@ -106,7 +121,7 @@ class FieldLookupBackend(BaseFilterBackend): return value else: value = self.value_to_python_for_field(field, value) - return value + return value, new_lookup def filter_queryset(self, request, queryset, view): try: @@ -152,13 +167,13 @@ class FieldLookupBackend(BaseFilterBackend): for value in values: if q_int: value = int(value) - value = self.value_to_python(queryset.model, key, value) + value, new_key = self.value_to_python(queryset.model, key, value) if q_chain: - chain_filters.append((q_not, key, value)) + chain_filters.append((q_not, new_key, value)) elif q_or: - or_filters.append((q_not, key, value)) + or_filters.append((q_not, new_key, value)) else: - and_filters.append((q_not, key, value)) + 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: diff --git a/awx/api/views.py b/awx/api/views.py index 3bbe2b5f3e..b01026911a 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -168,7 +168,7 @@ class DashboardView(APIView): user_groups = get_user_queryset(request.user, Group) groups_job_failed = (Group.objects.filter(hosts_with_active_failures__gt=0) | Group.objects.filter(groups_with_active_failures__gt=0)).count() - groups_inventory_failed = Group.objects.filter(inventory_sources__last_update_failed=True).count() + groups_inventory_failed = Group.objects.filter(inventory_sources__last_job_failed=True).count() data['groups'] = {'url': reverse('api:group_list'), 'failures_url': reverse('api:group_list') + "?has_active_failures=True", 'total': user_groups.count(), @@ -183,32 +183,32 @@ class DashboardView(APIView): 'failed': user_hosts_failed.count()} user_projects = get_user_queryset(request.user, Project) - user_projects_failed = user_projects.filter(last_update_failed=True) + user_projects_failed = user_projects.filter(last_job_failed=True) data['projects'] = {'url': reverse('api:project_list'), - 'failures_url': reverse('api:project_list') + "?last_update_failed=True", + 'failures_url': reverse('api:project_list') + "?last_job_failed=True", 'total': user_projects.count(), 'failed': user_projects_failed.count()} git_projects = user_projects.filter(scm_type='git') - git_failed_projects = git_projects.filter(last_update_failed=True) + git_failed_projects = git_projects.filter(last_job_failed=True) svn_projects = user_projects.filter(scm_type='svn') - svn_failed_projects = svn_projects.filter(last_update_failed=True) + svn_failed_projects = svn_projects.filter(last_job_failed=True) hg_projects = user_projects.filter(scm_type='hg') - hg_failed_projects = hg_projects.filter(last_update_failed=True) + hg_failed_projects = hg_projects.filter(last_job_failed=True) data['scm_types'] = {} data['scm_types']['git'] = {'url': reverse('api:project_list') + "?scm_type=git", 'label': 'Git', - 'failures_url': reverse('api:project_list') + "?scm_type=git&last_update_failed=True", + 'failures_url': reverse('api:project_list') + "?scm_type=git&last_job_failed=True", 'total': git_projects.count(), 'failed': git_failed_projects.count()} data['scm_types']['svn'] = {'url': reverse('api:project_list') + "?scm_type=svn", 'label': 'Subversion', - 'failures_url': reverse('api:project_list') + "?scm_type=svn&last_update_failed=True", + 'failures_url': reverse('api:project_list') + "?scm_type=svn&last_job_failed=True", 'total': svn_projects.count(), 'failed': svn_failed_projects.count()} data['scm_types']['hg'] = {'url': reverse('api:project_list') + "?scm_type=hg", 'label': 'Mercurial', - 'failures_url': reverse('api:project_list') + "?scm_type=hg&last_update_failed=True", + 'failures_url': reverse('api:project_list') + "?scm_type=hg&last_job_failed=True", 'total': hg_projects.count(), 'failed': hg_failed_projects.count()} diff --git a/awx/main/tests/projects.py b/awx/main/tests/projects.py index acc79ed664..dfb609d8f0 100644 --- a/awx/main/tests/projects.py +++ b/awx/main/tests/projects.py @@ -183,6 +183,19 @@ class ProjectsTest(BaseTest): self.get(url, expect=401) self.get(url, expect=401, auth=self.get_invalid_credentials()) + def test_dashboard(self): + url = reverse('api:dashboard_view') + # superuser can read dashboard. + response = self.get(url, expect=200, auth=self.get_super_credentials()) + # org admin can read dashboard. + response = self.get(url, expect=200, auth=self.get_normal_credentials()) + # regular user can read dashboard. + response = self.get(url, expect=200, auth=self.get_nobody_credentials()) + # anonymous/invalid user can't access dashboard. + self.get(url, expect=401) + self.get(url, expect=401, auth=self.get_invalid_credentials()) + # FIXME: Test that data on dashboard is filtered based on RBAC. + def test_mainline(self): # =====================================================================