From c91cd606edae0e33d09d396977cd525f430cced9 Mon Sep 17 00:00:00 2001 From: mabashian Date: Mon, 8 Apr 2019 17:22:38 -0400 Subject: [PATCH] Corresponding UI changes for notifications tab and toggle permissions --- .../sources/edit/sources-edit.controller.js | 7 ++- .../related/sources/sources.form.js | 2 +- .../src/notifications/notifications.list.js | 8 +-- .../shared/notification-list-init.factory.js | 8 --- .../edit/organizations-edit.controller.js | 59 +++++++++---------- awx/ui/client/src/organizations/main.js | 44 +++++++++++++- .../projects/edit/projects-edit.controller.js | 19 +++--- .../job-template-edit.controller.js | 7 ++- .../edit-workflow/workflow-edit.controller.js | 8 ++- 9 files changed, 104 insertions(+), 58 deletions(-) diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js index 8a60e5fea3..33fa44f301 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/edit/sources-edit.controller.js @@ -17,9 +17,14 @@ export default ['$state', '$scope', 'ParseVariableString', 'ParseTypeChange', const inventorySourceData = inventorySource.get(); + // To toggle notifications a user needs to have a read role on the inventory + // _and_ have at least a notification template admin role on an org. + // If the user has gotten this far it's safe to say they have + // at least read access to the inventory + $scope.sufficientRoleForNotifToggle = isNotificationAdmin; + $scope.sufficientRoleForNotif = isNotificationAdmin || $scope.user_is_system_auditor; $scope.projectBasePath = GetBasePath('projects') + '?not__status=never updated'; $scope.canAdd = inventorySourcesOptions.actions.POST; - $scope.isNotificationAdmin = isNotificationAdmin || false; const virtualEnvs = ConfigData.custom_virtualenvs || []; $scope.custom_virtualenvs_options = virtualEnvs; // instantiate expected $scope values from inventorySourceData diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/sources/sources.form.js b/awx/ui/client/src/inventories-hosts/inventories/related/sources/sources.form.js index 132403527e..33336160dc 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/sources/sources.form.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/sources/sources.form.js @@ -9,7 +9,7 @@ export default ['NotificationsList', 'i18n', function(NotificationsList, i18n){ var notifications_object = { generateList: true, include: "NotificationsList", - ngIf: "(current_user.is_superuser || isOrgAdmin || isNotificationAdmin) && !(inventory_source_obj.source === undefined || inventory_source_obj.source === '')", + ngIf: "(sufficientRoleForNotif) && !(inventory_source_obj.source === undefined || inventory_source_obj.source === '')", ngClick: "$state.go('inventories.edit.inventory_sources.edit.notifications')" }; let clone = _.clone(NotificationsList); diff --git a/awx/ui/client/src/notifications/notifications.list.js b/awx/ui/client/src/notifications/notifications.list.js index c323194de9..fb781e693b 100644 --- a/awx/ui/client/src/notifications/notifications.list.js +++ b/awx/ui/client/src/notifications/notifications.list.js @@ -20,7 +20,7 @@ export default ['i18n', 'templateUrl', function(i18n, templateUrl){ hover: false, emptyListText: i18n.sprintf(i18n._("This list is populated by notification templates added from the %sNotifications%s section"), " ", " "), basePath: 'notification_templates', - ngIf: 'current_user.is_superuser || isOrgAdmin || isNotificationAdmin', + ngIf: 'sufficientRoleForNotif', fields: { name: { key: true, @@ -40,7 +40,7 @@ export default ['i18n', 'templateUrl', function(i18n, templateUrl){ flag: 'notification_templates_success', type: "toggle", ngClick: "toggleNotification($event, notification.id, \"notification_templates_success\")", - ngDisabled: "!(current_user.is_superuser || isOrgAdmin)", + ngDisabled: "!sufficientRoleForNotifToggle", awToolTip: "{{ schedule.play_tip }}", dataTipWatch: "schedule.play_tip", dataPlacement: "right", @@ -53,7 +53,7 @@ export default ['i18n', 'templateUrl', function(i18n, templateUrl){ flag: 'notification_templates_error', type: "toggle", ngClick: "toggleNotification($event, notification.id, \"notification_templates_error\")", - ngDisabled: "!(current_user.is_superuser || isOrgAdmin)", + ngDisabled: "!sufficientRoleForNotifToggle", awToolTip: "{{ schedule.play_tip }}", dataTipWatch: "schedule.play_tip", dataPlacement: "right", @@ -64,7 +64,7 @@ export default ['i18n', 'templateUrl', function(i18n, templateUrl){ add: { type: 'template', template: templateUrl('notifications/notification-templates-list/add-notifications-action'), - ngShow: 'current_user.is_superuser || (current_user_admin_orgs && current_user_admin_orgs.length > 0)' + ngShow: 'isNotificationAdmin' } } diff --git a/awx/ui/client/src/notifications/shared/notification-list-init.factory.js b/awx/ui/client/src/notifications/shared/notification-list-init.factory.js index 9f9c5ea543..101519640a 100644 --- a/awx/ui/client/src/notifications/shared/notification-list-init.factory.js +++ b/awx/ui/client/src/notifications/shared/notification-list-init.factory.js @@ -22,14 +22,6 @@ export default ['Wait', 'GetBasePath', 'ProcessErrors', 'Rest', 'GetChoices', url = params.url, id = params.id; - scope.current_user_admin_orgs = []; - - Rest.setUrl($rootScope.current_user.related.admin_of_organizations); - Rest.get() - .then(({data}) => { - scope.current_user_admin_orgs = data.results.map(i => i.name); - }); - scope.addNotificationTemplate = function() { var org_id; if($stateParams.hasOwnProperty('project_id')){ diff --git a/awx/ui/client/src/organizations/edit/organizations-edit.controller.js b/awx/ui/client/src/organizations/edit/organizations-edit.controller.js index a847068d49..f0e018c4b7 100644 --- a/awx/ui/client/src/organizations/edit/organizations-edit.controller.js +++ b/awx/ui/client/src/organizations/edit/organizations-edit.controller.js @@ -4,11 +4,11 @@ * All Rights Reserved *************************************************/ -export default ['$scope', '$location', '$stateParams', 'OrgAdminLookup', - 'OrganizationForm', 'Rest', 'ProcessErrors', 'Prompt', '$rootScope', 'i18n', +export default ['$scope', '$location', '$stateParams', 'isOrgAdmin', 'isNotificationAdmin', + 'OrganizationForm', 'Rest', 'ProcessErrors', 'Prompt', 'i18n', 'isOrgAuditor', 'GetBasePath', 'Wait', '$state', 'ToggleNotification', 'CreateSelect2', 'InstanceGroupsService', 'InstanceGroupsData', 'ConfigData', - function($scope, $location, $stateParams, OrgAdminLookup, - OrganizationForm, Rest, ProcessErrors, Prompt, $rootScope, i18n, + function($scope, $location, $stateParams, isOrgAdmin, isNotificationAdmin, + OrganizationForm, Rest, ProcessErrors, Prompt, i18n, isOrgAuditor, GetBasePath, Wait, $state, ToggleNotification, CreateSelect2, InstanceGroupsService, InstanceGroupsData, ConfigData) { let form = OrganizationForm(), @@ -18,34 +18,22 @@ export default ['$scope', '$location', '$stateParams', 'OrgAdminLookup', id = $stateParams.organization_id, instance_group_url = defaultUrl + id + '/instance_groups/'; - init(); + $scope.isOrgAuditor = isOrgAuditor; + $scope.isOrgAdmin = isOrgAdmin; + $scope.isNotificationAdmin = isNotificationAdmin; - function init() { - OrgAdminLookup.checkForAdminAccess({organization: id}) - .then(function(isOrgAdmin){ - $scope.isOrgAdmin = isOrgAdmin; - }); - - Rest.setUrl(GetBasePath('users') + $rootScope.current_user.id + '/roles/?role_field=notification_admin_role'); - Rest.get() - .then(({data}) => { - $scope.isNotificationAdmin = (data.count && data.count > 0); - }); - - $scope.$watch('organization_obj.summary_fields.user_capabilities.edit', function(val) { - if (val === false) { - $scope.canAdd = false; - } - }); - - $scope.instance_groups = InstanceGroupsData; - const virtualEnvs = ConfigData.custom_virtualenvs || []; - $scope.custom_virtualenvs_visible = virtualEnvs.length > 1; - $scope.custom_virtualenvs_options = virtualEnvs.filter( - v => !/\/ansible\/$/.test(v) - ); - } + $scope.$watch('organization_obj.summary_fields.user_capabilities.edit', function(val) { + if (val === false) { + $scope.canAdd = false; + } + }); + $scope.instance_groups = InstanceGroupsData; + const virtualEnvs = ConfigData.custom_virtualenvs || []; + $scope.custom_virtualenvs_visible = virtualEnvs.length > 1; + $scope.custom_virtualenvs_options = virtualEnvs.filter( + v => !/\/ansible\/$/.test(v) + ); // Retrieve detail record and prepopulate the form Wait('start'); @@ -54,6 +42,15 @@ export default ['$scope', '$location', '$stateParams', 'OrgAdminLookup', .then(({data}) => { let fld; + $scope.sufficientRoleForNotifToggle = + isNotificationAdmin && ( + $scope.is_system_auditor || + isOrgAuditor || + isOrgAdmin + ); + + $scope.sufficientRoleForNotif = isNotificationAdmin || isOrgAuditor || $scope.user_is_system_auditor; + $scope.organization_name = data.name; for (fld in form.fields) { if (typeof data[fld] !== 'undefined') { @@ -169,4 +166,4 @@ export default ['$scope', '$location', '$stateParams', 'OrgAdminLookup', }; } -]; +]; \ No newline at end of file diff --git a/awx/ui/client/src/organizations/main.js b/awx/ui/client/src/organizations/main.js index a0b179261f..8540261ede 100644 --- a/awx/ui/client/src/organizations/main.js +++ b/awx/ui/client/src/organizations/main.js @@ -98,7 +98,49 @@ angular.module('Organizations', [ 'status: ' + status }); }); - }] + }], + isOrgAuditor: ['Rest', 'ProcessErrors', 'GetBasePath', 'i18n', '$stateParams', + function(Rest, ProcessErrors, GetBasePath, i18n, $stateParams) { + Rest.setUrl(`${GetBasePath('organizations')}?role_level=auditor_role&id=${$stateParams.organization_id}`); + return Rest.get() + .then(({data}) => { + return data.count > 0; + }) + .catch(({data, status}) => { + ProcessErrors(null, data, status, null, { + hdr: i18n._('Error!'), + msg: i18n._('Failed while checking to see if user is a notification administrator of this organization. GET returned ') + status + }); + }); + }], + isOrgAdmin: ['ProcessErrors', 'i18n', '$stateParams', 'OrgAdminLookup', + function(ProcessErrors, i18n, $stateParams, OrgAdminLookup) { + return OrgAdminLookup.checkForAdminAccess({organization: $stateParams.organization_id}) + .then(function(isOrgAdmin){ + return isOrgAdmin; + }) + .catch(({data, status}) => { + ProcessErrors(null, data, status, null, { + hdr: i18n._('Error!'), + msg: i18n._('Failed while checking to see if user is an administrator of this organization. GET returned ') + status + }); + }); + + }], + isNotificationAdmin: ['Rest', 'ProcessErrors', 'GetBasePath', 'i18n', + function(Rest, ProcessErrors, GetBasePath, i18n) { + Rest.setUrl(`${GetBasePath('organizations')}?role_level=notification_admin_role&page_size=1`); + return Rest.get() + .then(({data}) => { + return data.count > 0; + }) + .catch(({data, status}) => { + ProcessErrors(null, data, status, null, { + hdr: i18n._('Error!'), + msg: i18n._('Failed to get organizations for which this user is a notification admin. GET returned ') + status + }); + }); + }], } } // concat manually-defined state definitions with generated defintions diff --git a/awx/ui/client/src/projects/edit/projects-edit.controller.js b/awx/ui/client/src/projects/edit/projects-edit.controller.js index 54f6493326..c113dc46fb 100644 --- a/awx/ui/client/src/projects/edit/projects-edit.controller.js +++ b/awx/ui/client/src/projects/edit/projects-edit.controller.js @@ -20,15 +20,10 @@ export default ['$scope', '$rootScope', '$stateParams', 'ProjectsForm', 'Rest', master = {}, id = $stateParams.project_id; - init(); - - function init() { - $scope.project_local_paths = []; - $scope.base_dir = ''; - const virtualEnvs = ConfigData.custom_virtualenvs || []; - $scope.custom_virtualenvs_options = virtualEnvs; - $scope.isNotificationAdmin = isNotificationAdmin || false; - } + $scope.project_local_paths = []; + $scope.base_dir = ''; + const virtualEnvs = ConfigData.custom_virtualenvs || []; + $scope.custom_virtualenvs_options = virtualEnvs; $scope.$watch('project_obj.summary_fields.user_capabilities.edit', function(val) { if (val === false) { @@ -157,6 +152,12 @@ export default ['$scope', '$rootScope', '$stateParams', 'ProjectsForm', 'Rest', }); $scope.project_obj = data; + // To toggle notifications a user needs to have an admin role on the project + // _and_ have at least a notification template admin role on an org. + // Only users with admin role on the project can edit it which is why we + // look at that user_capability + $scope.sufficientRoleForNotifToggle = isNotificationAdmin && data.summary_fields.user_capabilities.edit; + $scope.sufficientRoleForNotif = isNotificationAdmin || $scope.user_is_system_auditor; $scope.name = data.name; $scope.breadcrumb.project_name = data.name; $scope.$emit('projectLoaded'); diff --git a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js index 7cbde90b9d..e56dd91393 100644 --- a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js +++ b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js @@ -54,6 +54,12 @@ export default CallbackHelpInit({ scope: $scope }); + // To toggle notifications a user needs to have a read role on the JT + // _and_ have at least a notification template admin role on an org. + // If the user has gotten this far it's safe to say they have + // at least read access to the JT + $scope.sufficientRoleForNotifToggle = isNotificationAdmin; + $scope.sufficientRoleForNotif = isNotificationAdmin || $scope.user_is_system_auditor; $scope.playbook_options = null; $scope.playbook = null; $scope.mode = 'edit'; @@ -66,7 +72,6 @@ export default $scope.skip_tag_options = []; const virtualEnvs = ConfigData.custom_virtualenvs || []; $scope.custom_virtualenvs_options = virtualEnvs; - $scope.isNotificationAdmin = isNotificationAdmin || false; SurveyControllerInit({ scope: $scope, diff --git a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js index 0f0916b4fe..5f9d48534b 100644 --- a/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js +++ b/awx/ui/client/src/templates/workflows/edit-workflow/workflow-edit.controller.js @@ -18,6 +18,12 @@ export default [ workflowLaunch, $transitions, WorkflowJobTemplate, Inventory, isNotificationAdmin ) { + // To toggle notifications a user needs to have a read role on the WFJT + // _and_ have at least a notification template admin role on an org. + // If the user has gotten this far it's safe to say they have + // at least read access to the WFJT + $scope.sufficientRoleForNotifToggle = isNotificationAdmin; + $scope.sufficientRoleForNotif = isNotificationAdmin || $scope.user_is_system_auditor; $scope.missingTemplates = _.has(workflowLaunch, 'node_templates_missing') && workflowLaunch.node_templates_missing.length > 0 ? true : false; $scope.$watch('workflow_job_template_obj.summary_fields.user_capabilities.edit', function(val) { @@ -26,8 +32,6 @@ export default [ } }); - $scope.isNotificationAdmin = isNotificationAdmin || false; - const criteriaObj = { from: (state) => state.name === 'templates.editWorkflowJobTemplate.workflowMaker', to: (state) => state.name === 'templates.editWorkflowJobTemplate'