diff --git a/awx/api/views.py b/awx/api/views.py index 4ad0a9cc58..d452758ce6 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1401,7 +1401,7 @@ class OrganizationCredentialList(SubListCreateAPIView): user_visible = Credential.accessible_objects(self.request.user, 'read_role').all() org_set = Credential.accessible_objects(organization.admin_role, 'read_role').all() - if self.request.user.is_superuser: + if self.request.user.is_superuser or self.request.user.is_system_auditor: return org_set return org_set & user_visible @@ -2591,7 +2591,7 @@ class SystemJobTemplateList(ListAPIView): serializer_class = SystemJobTemplateSerializer def get(self, request, *args, **kwargs): - if not request.user.is_superuser: + if not request.user.is_superuser and not request.user.is_system_auditor: raise PermissionDenied("Superuser privileges needed.") return super(SystemJobTemplateList, self).get(request, *args, **kwargs) @@ -3321,7 +3321,7 @@ class SystemJobList(ListCreateAPIView): serializer_class = SystemJobListSerializer def get(self, request, *args, **kwargs): - if not request.user.is_superuser: + if not request.user.is_superuser and not request.user.is_system_auditor: raise PermissionDenied("Superuser privileges needed.") return super(SystemJobList, self).get(request, *args, **kwargs) @@ -3625,8 +3625,6 @@ class RoleList(ListAPIView): new_in_300 = True def get_queryset(self): - if self.request.user.is_superuser: - return Role.objects.all() return Role.visible_roles(self.request.user) diff --git a/awx/main/access.py b/awx/main/access.py index f47198e4b0..85d4a9d980 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -139,7 +139,7 @@ class BaseAccess(object): self.user = user def get_queryset(self): - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return self.model.objects.all() else: return self.model.objects.none() @@ -221,7 +221,7 @@ class UserAccess(BaseAccess): model = User def get_queryset(self): - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return User.objects.all() if tower_settings.ORG_ADMINS_CAN_SEE_ALL_USERS and \ @@ -718,7 +718,7 @@ class ProjectAccess(BaseAccess): model = Project def get_queryset(self): - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return self.model.objects.all() qs = self.model.accessible_objects(self.user, 'read_role') return qs.select_related('modified_by', 'credential', 'current_job', 'last_job').all() @@ -752,7 +752,7 @@ class ProjectUpdateAccess(BaseAccess): model = ProjectUpdate def get_queryset(self): - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return self.model.objects.all() qs = ProjectUpdate.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'project') @@ -788,7 +788,7 @@ class JobTemplateAccess(BaseAccess): model = JobTemplate def get_queryset(self): - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: qs = self.model.objects.all() else: qs = self.model.accessible_objects(self.user, 'read_role') @@ -979,7 +979,7 @@ class JobAccess(BaseAccess): qs = qs.select_related('created_by', 'modified_by', 'job_template', 'inventory', 'project', 'credential', 'cloud_credential', 'job_template') qs = qs.prefetch_related('unified_job_template') - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs.all() qs_jt = qs.filter( @@ -1086,7 +1086,7 @@ class AdHocCommandAccess(BaseAccess): qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory', 'credential') - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs.all() inventory_qs = Inventory.accessible_objects(self.user, 'read_role') @@ -1147,7 +1147,7 @@ class AdHocCommandEventAccess(BaseAccess): qs = self.model.objects.distinct() qs = qs.select_related('ad_hoc_command', 'host') - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs.all() ad_hoc_command_qs = self.user.get_queryset(AdHocCommand) host_qs = self.user.get_queryset(Host) @@ -1173,7 +1173,7 @@ class JobHostSummaryAccess(BaseAccess): def get_queryset(self): qs = self.model.objects qs = qs.select_related('job', 'job__job_template', 'host') - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs.all() job_qs = self.user.get_queryset(Job) host_qs = self.user.get_queryset(Host) @@ -1205,7 +1205,7 @@ class JobEventAccess(BaseAccess): event_data__icontains='"ansible_job_id": "', event_data__contains='"module_name": "async_status"') - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs.all() job_qs = self.user.get_queryset(Job) @@ -1318,7 +1318,7 @@ class ScheduleAccess(BaseAccess): qs = self.model.objects.all() qs = qs.select_related('created_by', 'modified_by') qs = qs.prefetch_related('unified_job_template') - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs.all() job_template_qs = self.user.get_queryset(JobTemplate) inventory_source_qs = self.user.get_queryset(InventorySource) @@ -1369,12 +1369,13 @@ class NotificationTemplateAccess(BaseAccess): def get_queryset(self): qs = self.model.objects.all() - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs return self.model.objects.filter(organization__in=Organization.accessible_objects(self.user, 'admin_role').all()) - @check_superuser def can_read(self, obj): + if self.user.is_superuser or self.user.is_system_auditor: + return True if obj.organization is not None: return self.user in obj.organization.admin_role return False @@ -1413,7 +1414,7 @@ class NotificationAccess(BaseAccess): def get_queryset(self): qs = self.model.objects.all() - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return qs return self.model.objects.filter(notification_template__organization__in=Organization.accessible_objects(self.user, 'admin_role')) @@ -1430,7 +1431,7 @@ class LabelAccess(BaseAccess): model = Label def get_queryset(self): - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return self.model.objects.all() return self.model.objects.filter( organization__in=Organization.accessible_objects(self.user, 'read_role') @@ -1493,9 +1494,7 @@ class ActivityStreamAccess(BaseAccess): 'inventory_update', 'credential', 'team', 'project', 'project_update', 'permission', 'job_template', 'job', 'ad_hoc_command', 'notification_template', 'notification', 'label', 'role') - if self.user.is_superuser: - return qs.all() - if self.user in Role.singleton('system_auditor'): + if self.user.is_superuser or self.user.is_system_auditor: return qs.all() inventory_set = Inventory.accessible_objects(self.user, 'read_role') @@ -1543,7 +1542,7 @@ class CustomInventoryScriptAccess(BaseAccess): model = CustomInventoryScript def get_queryset(self): - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return self.model.objects.distinct().all() return self.model.accessible_objects(self.user, 'read_role').all() @@ -1599,7 +1598,7 @@ class RoleAccess(BaseAccess): def can_read(self, obj): if not obj: return False - if self.user.is_superuser: + if self.user.is_superuser or self.user.is_system_auditor: return True if obj.object_id: diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index b6f43018fc..e25d53ac51 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -881,6 +881,7 @@ var tower = angular.module('Tower', [ } // If browser refresh, set the user_is_superuser value $rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser'); + $rootScope.user_is_system_auditor = Authorization.getUserInfo('is_system_auditor'); // state the user refreshes we want to open the socket, except if the user is on the login page, which should happen after the user logs in (see the AuthService module for that call to OpenSocket) if(!_.contains($location.$$url, '/login')){ ConfigService.getConfig().then(function(){ diff --git a/awx/ui/client/src/login/loginModal/loginModal.controller.js b/awx/ui/client/src/login/loginModal/loginModal.controller.js index c87aafba41..9bd5132ab1 100644 --- a/awx/ui/client/src/login/loginModal/loginModal.controller.js +++ b/awx/ui/client/src/login/loginModal/loginModal.controller.js @@ -137,6 +137,7 @@ export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope', $rootScope.sessionTimer = timer; $rootScope.$emit('OpenSocket'); $rootScope.user_is_superuser = data.results[0].is_superuser; + $rootScope.user_is_system_auditor = data.results[0].is_system_auditor; scope.$emit('AuthorizationGetLicense'); }); }) diff --git a/awx/ui/client/src/setup-menu/setup-menu.partial.html b/awx/ui/client/src/setup-menu/setup-menu.partial.html index ae1cdb0ee2..fc9a04d4ed 100644 --- a/awx/ui/client/src/setup-menu/setup-menu.partial.html +++ b/awx/ui/client/src/setup-menu/setup-menu.partial.html @@ -24,7 +24,7 @@ Add passwords, SSH keys, etc. for Tower to use when launching jobs against machines, or when syncing inventories or projects.

- +

Management Jobs

Manage the cleanup of old job history, activity streams, data marked for deletion, and system tracking info. @@ -37,7 +37,7 @@

+ ng-if="orgAdmin || user_is_system_auditor || user_is_superuser">

Notifications

Create templates for sending notifications with Email, HipChat, Slack, and SMS.