diff --git a/awx/ui/client/legacy-styles/ansible-ui.less b/awx/ui/client/legacy-styles/ansible-ui.less index b341f2d21e..2dfc7682e6 100644 --- a/awx/ui/client/legacy-styles/ansible-ui.less +++ b/awx/ui/client/legacy-styles/ansible-ui.less @@ -2026,3 +2026,8 @@ tr td button i { margin-bottom: 15px; } } + +button.dropdown-toggle, +.input-group-btn { + z-index: 1; +} diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 362e89770a..d5fc78bfb2 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -30,6 +30,7 @@ import {JobsListController} from './controllers/Jobs'; import {PortalController} from './controllers/Portal'; import systemTracking from './system-tracking/main'; import inventoryScripts from './inventory-scripts/main'; +import permissions from './permissions/main'; import managementJobs from './management-jobs/main'; import routeExtensions from './shared/route-extensions/main'; import breadcrumbs from './shared/breadcrumbs/main'; @@ -55,7 +56,6 @@ import {AdhocCtrl} from './controllers/Adhoc'; import {AdminsList} from './controllers/Admins'; import {UsersList, UsersAdd, UsersEdit} from './controllers/Users'; import {TeamsList, TeamsAdd, TeamsEdit} from './controllers/Teams'; -import {PermissionsAdd, PermissionsList, PermissionsEdit} from './controllers/Permissions'; import './shared/RestServices'; import './shared/api-loader'; import './shared/form-generator'; @@ -86,6 +86,7 @@ var tower = angular.module('Tower', [ breadcrumbs.name, systemTracking.name, inventoryScripts.name, + permissions.name, managementJobs.name, setupMenu.name, mainMenu.name, @@ -140,9 +141,6 @@ var tower = angular.module('Tower', [ 'ProjectFormDefinition', 'ProjectStatusDefinition', 'ProjectsHelper', - 'PermissionFormDefinition', - 'PermissionListDefinition', - 'PermissionsHelper', 'CompletedJobsDefinition', 'AllJobsDefinition', 'JobFormDefinition', @@ -591,39 +589,6 @@ var tower = angular.module('Tower', [ } }). - when('/teams/:team_id/permissions/add', { - name: 'teamPermissionAdd', - templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsAdd, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - - when('/teams/:team_id/permissions', { - name: 'teamPermissions', - templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsList, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - - when('/teams/:team_id/permissions/:permission_id', { - name: 'teamPermissionEdit', - templateUrl: urlPrefix + 'partials/teams.html', - controller: PermissionsEdit, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - when('/teams/:team_id/users', { name: 'teamUsers', templateUrl: urlPrefix + 'partials/teams.html', @@ -789,39 +754,6 @@ var tower = angular.module('Tower', [ } }). - when('/users/:user_id/permissions/add', { - name: 'userPermissionAdd', - templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsAdd, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - - when('/users/:user_id/permissions', { - name: 'userPermissions', - templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsList, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - - when('/users/:user_id/permissions/:permission_id', { - name: 'userPermissionEdit', - templateUrl: urlPrefix + 'partials/users.html', - controller: PermissionsEdit, - resolve: { - features: ['FeaturesService', function(FeaturesService) { - return FeaturesService.get(); - }] - } - }). - when('/users/:user_id/credentials/add', { name: 'userCredentialAdd', templateUrl: urlPrefix + 'partials/teams.html', @@ -896,7 +828,7 @@ var tower = angular.module('Tower', [ }] } }). - + when('/license', { name: 'license', templateUrl: urlPrefix + 'partials/license.html', diff --git a/awx/ui/client/src/controllers/Permissions.js b/awx/ui/client/src/controllers/Permissions.js deleted file mode 100644 index 82c05cdff0..0000000000 --- a/awx/ui/client/src/controllers/Permissions.js +++ /dev/null @@ -1,409 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:Permissions - * @description This controller's for permissions -*/ - - -export function PermissionsList($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, PermissionList, - GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, - GetBasePath, CheckAccess, Wait) { - - ClearScope(); - - var list = PermissionList, - base = $location.path().replace(/^\//, '').split('/')[0], - defaultUrl = GetBasePath(base), - generator = GenerateList; - - generator.inject(list, { mode: 'edit', scope: $scope, breadCrumbs: true }); - defaultUrl += ($routeParams.user_id !== undefined) ? $routeParams.user_id : $routeParams.team_id; - defaultUrl += '/permissions/'; - - $scope.selected = []; - - CheckAccess({ - scope: $scope - }); - - if ($scope.removePostRefresh) { - $scope.removePostRefresh(); - } - $scope.removePostRefresh = $scope.$on('PostRefresh', function () { - // Cleanup after a delete - Wait('stop'); - $('#prompt-modal').modal('hide'); - }); - - SearchInit({ - scope: $scope, - set: 'permissions', - list: list, - url: defaultUrl - }); - PaginateInit({ - scope: $scope, - list: list, - url: defaultUrl - }); - $scope.search(list.iterator); - - LoadBreadCrumbs(); - - $scope.addPermission = function () { - if ($scope.PermissionAddAllowed) { - $location.path($location.path() + '/add'); - } - }; - - // if the permission includes adhoc (and is not admin), display that - $scope.getPermissionText = function () { - if (this.permission.permission_type !== "admin" && this.permission.run_ad_hoc_commands) { - return this.permission.permission_type + " + run commands"; - } else { - return this.permission.permission_type; - } - }; - - $scope.editPermission = function (id) { - $location.path($location.path() + '/' + id); - }; - - $scope.deletePermission = function (id, name) { - var action = function () { - $('#prompt-modal').modal('hide'); - Wait('start'); - var url = GetBasePath('base') + 'permissions/' + id + '/'; - Rest.setUrl(url); - Rest.destroy() - .success(function () { - $scope.search(list.iterator); - }) - .error(function (data, status) { - Wait('stop'); - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); - }); - }; - - if ($scope.PermissionAddAllowed) { - Prompt({ - hdr: 'Delete', - body: 'Are you sure you want to delete ' + name + '?', - action: action - }); - } - }; -} - -PermissionsList.$inject = ['$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'PermissionList', - 'generateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', - 'ClearScope', 'ProcessErrors', 'GetBasePath', 'CheckAccess', 'Wait' -]; - - -export function PermissionsAdd($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm, - GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, - GetBasePath, ReturnToCaller, InventoryList, ProjectList, LookUpInit, CheckAccess, - Wait, PermissionCategoryChange) { - - ClearScope(); - - // Inject dynamic view - var form = PermissionsForm, - generator = GenerateForm, - id = ($routeParams.user_id !== undefined) ? $routeParams.user_id : $routeParams.team_id, - base = $location.path().replace(/^\//, '').split('/')[0], - master = {}; - - generator.inject(form, { mode: 'add', related: false, scope: $scope }); - CheckAccess({ scope: $scope }); - generator.reset(); - LoadBreadCrumbs(); - - $scope.inventoryrequired = true; - $scope.projectrequired = false; - $scope.category = 'Inventory'; - master.category = 'Inventory'; - master.inventoryrequired = true; - master.projectrequired = false; - $scope.run_ad_hoc_commands = false; - - LookUpInit({ - scope: $scope, - form: form, - current_item: null, - list: InventoryList, - field: 'inventory', - input_type: 'radio' - }); - - LookUpInit({ - scope: $scope, - form: form, - current_item: null, - list: ProjectList, - field: 'project', - input_type: 'radio' - }); - - $scope.$watch("category", function(val) { - if (val === 'Deploy') { - $scope.projectrequired = true; - LookUpInit({ - scope: $scope, - form: form, - current_item: null, - list: ProjectList, - field: 'project', - input_type: 'radio' - }); - } else { - $scope.projectrequired = false; - } - }); - - $scope.changeAdhocCommandCheckbox = function () { - if ($scope.category === 'Deploy') { - $scope.run_ad_hoc_command = false; - } else { - if ($scope.permission_type === 'admin') { - $scope.run_ad_hoc_commands = true; - $("#permission_run_ad_hoc_commands_chbox").attr("disabled", true); - } else { - if (!$scope.run_ad_hoc_commands) { - $scope.run_ad_hoc_commands = false; - } - $("#permission_run_ad_hoc_commands_chbox").attr("disabled", false); - } - } - }; - - // Save - $scope.formSave = function () { - var fld, url, data = {}; - generator.clearApiErrors(); - Wait('start'); - if ($scope.PermissionAddAllowed) { - data = {}; - for (fld in form.fields) { - data[fld] = $scope[fld]; - } - // job template (or deploy) based permissions do not have the run - // ad hoc commands parameter - if (data.category === "Deploy") { - data.run_ad_hoc_commands = false; - } else { - delete data.project; - } - - url = (base === 'teams') ? GetBasePath('teams') + id + '/permissions/' : GetBasePath('users') + id + '/permissions/'; - Rest.setUrl(url); - Rest.post(data) - .success(function () { - Wait('stop'); - ReturnToCaller(1); - }) - .error(function (data, status) { - Wait('stop'); - ProcessErrors($scope, data, status, PermissionsForm, { hdr: 'Error!', - msg: 'Failed to create new permission. Post returned status: ' + status }); - }); - } else { - Alert('Access Denied', 'You do not have access to create new permission objects. Please contact a system administrator.', - 'alert-danger'); - } - }; - - // Cancel - $scope.formReset = function () { - $rootScope.flashMessage = null; - generator.reset(); - for (var fld in master) { - $scope[fld] = master[fld]; - } - $scope.selectCategory(); - }; - - $scope.selectCategory = function () { - PermissionCategoryChange({ scope: $scope, reset: true }); - }; - - - $scope.selectCategory(); - -} - -PermissionsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm', - 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'ReturnToCaller', - 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess', 'Wait', 'PermissionCategoryChange' -]; - - -export function PermissionsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm, - GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, Prompt, GetBasePath, - InventoryList, ProjectList, LookUpInit, CheckAccess, Wait, PermissionCategoryChange) { - - ClearScope(); - - var generator = GenerateForm, - form = PermissionsForm, - base_id = ($routeParams.user_id !== undefined) ? $routeParams.user_id : $routeParams.team_id, - id = $routeParams.permission_id, - defaultUrl = GetBasePath('base') + 'permissions/' + id + '/', - master = {}; - - $scope.changeAdhocCommandCheckbox = function () { - if ($scope.category === 'Deploy') { - $scope.run_ad_hoc_command = false; - } else { - if ($scope.permission_type === 'admin') { - $scope.run_ad_hoc_commands = true; - $("#permission_run_ad_hoc_commands_chbox").attr("disabled", true); - } else { - if (!$scope.run_ad_hoc_commands) { - $scope.run_ad_hoc_commands = false; - } - $("#permission_run_ad_hoc_commands_chbox").attr("disabled", false); - } - } - }; - - generator.inject(form, { mode: 'edit', related: true, scope: $scope }); - generator.reset(); - - $scope.selectCategory = function (resetIn) { - var reset = (resetIn === false) ? false : true; - PermissionCategoryChange({ scope: $scope, reset: reset }); - }; - if ($scope.removeFillForm) { - $scope.removeFillForm(); - } - $scope.removeFillForm = $scope.$on('FillForm', function () { - // Retrieve detail record and prepopulate the form - Wait('start'); - Rest.setUrl(defaultUrl); - Rest.get() - .success(function (data) { - var fld, sourceModel, sourceField; - LoadBreadCrumbs({ path: '/users/' + base_id + '/permissions/' + id, title: data.name }); - for (fld in form.fields) { - if (data[fld]) { - if (form.fields[fld].sourceModel) { - sourceModel = form.fields[fld].sourceModel; - sourceField = form.fields[fld].sourceField; - $scope[sourceModel + '_' + sourceField] = data.summary_fields[sourceModel][sourceField]; - master[sourceModel + '_' + sourceField] = data.summary_fields[sourceModel][sourceField]; - } - $scope[fld] = data[fld]; - master[fld] = $scope[fld]; - } - } - - $scope.category = 'Deploy'; - if (data.permission_type !== 'run' && data.permission_type !== 'check' && data.permission_type !== 'create') { - $scope.category = 'Inventory'; - } - master.category = $scope.category; - $scope.selectCategory(false); //call without resetting $scope.category value - - LookUpInit({ - scope: $scope, - form: form, - current_item: data.inventory, - list: InventoryList, - field: 'inventory', - input_type: "radio" - }); - - LookUpInit({ - scope: $scope, - form: form, - current_item: data.project, - list: ProjectList, - field: 'project', - input_type: 'radio' - }); - - $scope.changeAdhocCommandCheckbox(); - - if (!$scope.PermissionAddAllowed) { - // If not a privileged user, disable access - $('form[name="permission_form"]').find('select, input, button').each(function () { - if ($(this).is('input') || $(this).is('select')) { - $(this).attr('readonly', 'readonly'); - } - if ($(this).is('input[type="checkbox"]') || - $(this).is('input[type="radio"]') || - $(this).is('button')) { - $(this).attr('disabled', 'disabled'); - } - }); - } - Wait('stop'); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, form, { hdr: 'Error!', - msg: 'Failed to retrieve Permission: ' + id + '. GET status: ' + status }); - }); - }); - - CheckAccess({ - scope: $scope, - callback: 'FillForm' - }); - - // Save changes to the parent - $scope.formSave = function () { - var fld, data = {}; - generator.clearApiErrors(); - Wait('start'); - for (fld in form.fields) { - data[fld] = $scope[fld]; - } - // job template (or deploy) based permissions do not have the run - // ad hoc commands parameter - if (data.category === "Deploy") { - data.run_ad_hoc_commands = false; - } else { - delete data.project; - } - - Rest.setUrl(defaultUrl); - if($scope.category === "Inventory"){ - delete data.project; - } - Rest.put(data) - .success(function () { - Wait('stop'); - ReturnToCaller(1); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update Permission: ' + - $routeParams.id + '. PUT status: ' + status }); - }); - }; - - - // Cancel - $scope.formReset = function () { - generator.reset(); - for (var fld in master) { - $scope[fld] = master[fld]; - } - $scope.selectCategory(false); - }; - -} - -PermissionsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', - '$log', '$routeParams', 'PermissionsForm', 'GenerateForm', 'Rest', 'Alert', - 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'Prompt', - 'GetBasePath', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess', - 'Wait', 'PermissionCategoryChange']; diff --git a/awx/ui/client/src/controllers/Teams.js b/awx/ui/client/src/controllers/Teams.js index cd60103dbe..ab5dc9298f 100644 --- a/awx/ui/client/src/controllers/Teams.js +++ b/awx/ui/client/src/controllers/Teams.js @@ -176,7 +176,7 @@ TeamsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$r export function TeamsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, GetBasePath, CheckAccess, - OrganizationList, Wait, Stream) { + OrganizationList, Wait, Stream, permissionsChoices, permissionsLabel, permissionsSearchSelect) { ClearScope(); @@ -188,10 +188,40 @@ export function TeamsEdit($scope, $rootScope, $compile, $location, $log, $routeP id = $routeParams.team_id, relatedSets = {}; + $scope.permission_label = {}; + $scope.permission_search_select = []; + + // return a promise from the options request with the permission type choices (including adhoc) as a param + var permissionsChoice = permissionsChoices({ + scope: $scope, + url: 'api/v1/' + base + '/' + id + '/permissions/' + }); + + // manipulate the choices from the options request to be set on + // scope and be usable by the list form + permissionsChoice.then(function (choices) { + choices = + permissionsLabel({ + choices: choices + }); + _.map(choices, function(n, key) { + $scope.permission_label[key] = n; + }); + }); + $scope.team_id = id; - generator.inject(form, { mode: 'edit', related: true, scope: $scope }); - generator.reset(); + // manipulate the choices from the options request to be usable + // by the search option for permission_type, you can't inject the + // list until this is done! + permissionsChoice.then(function (choices) { + form.related.permissions.fields.permission_type.searchOptions = + permissionsSearchSelect({ + choices: choices + }); + generator.inject(form, { mode: 'edit', related: true, scope: $scope }); + generator.reset(); + }); $scope.PermissionAddAllowed = false; @@ -277,6 +307,15 @@ export function TeamsEdit($scope, $rootScope, $compile, $location, $log, $routeP '. GET status: ' + status }); }); + $scope.getPermissionText = function () { + if (this.permission.permission_type !== "admin" && this.permission.run_ad_hoc_commands) { + return $scope.permission_label[this.permission.permission_type] + + " and " + $scope.permission_label.adhoc; + } else { + return $scope.permission_label[this.permission.permission_type]; + } + }; + $scope.showActivity = function () { Stream({ scope: $scope }); }; @@ -392,5 +431,5 @@ export function TeamsEdit($scope, $rootScope, $compile, $location, $log, $routeP TeamsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'TeamForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', - 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', 'GetBasePath', 'CheckAccess', 'OrganizationList', 'Wait', 'Stream' + 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', 'GetBasePath', 'CheckAccess', 'OrganizationList', 'Wait', 'Stream', 'permissionsChoices', 'permissionsLabel', 'permissionsSearchSelect' ]; diff --git a/awx/ui/client/src/controllers/Users.js b/awx/ui/client/src/controllers/Users.js index b2b9794378..2a37115a16 100644 --- a/awx/ui/client/src/controllers/Users.js +++ b/awx/ui/client/src/controllers/Users.js @@ -208,7 +208,7 @@ UsersAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$r export function UsersEdit($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, GetBasePath, - Prompt, CheckAccess, ResetForm, Wait, Stream) { + Prompt, CheckAccess, ResetForm, Wait, Stream, permissionsChoices, permissionsLabel, permissionsSearchSelect) { ClearScope(); @@ -220,12 +220,42 @@ export function UsersEdit($scope, $rootScope, $compile, $location, $log, $routeP id = $routeParams.user_id, relatedSets = {}; + $scope.permission_label = {}; + $scope.permission_search_select = []; + + // return a promise from the options request with the permission type choices (including adhoc) as a param + var permissionsChoice = permissionsChoices({ + scope: $scope, + url: 'api/v1/' + base + '/' + id + '/permissions/' + }); + + // manipulate the choices from the options request to be set on + // scope and be usable by the list form + permissionsChoice.then(function (choices) { + choices = + permissionsLabel({ + choices: choices + }); + _.map(choices, function(n, key) { + $scope.permission_label[key] = n; + }); + }); + if ($scope.removeFormReady) { $scope.removeFormReady(); } $scope.removeFormReady = $scope.$on('formReady', function () { - generator.inject(form, { mode: 'edit', related: true, scope: $scope }); - generator.reset(); + // manipulate the choices from the options request to be usable + // by the search option for permission_type, you can't inject the + // list until this is done! + permissionsChoice.then(function (choices) { + form.related.permissions.fields.permission_type.searchOptions = + permissionsSearchSelect({ + choices: choices + }); + generator.inject(form, { mode: 'edit', related: true, scope: $scope }); + generator.reset(); + }); if ($scope.removePostRefresh) { $scope.removePostRefresh(); @@ -293,12 +323,12 @@ export function UsersEdit($scope, $rootScope, $compile, $location, $log, $routeP $routeParams.id + '. GET status: ' + status }); }); - // if the permission includes adhoc (and is not admin), display that $scope.getPermissionText = function () { if (this.permission.permission_type !== "admin" && this.permission.run_ad_hoc_commands) { - return this.permission.permission_type + " + run commands"; + return $scope.permission_label[this.permission.permission_type] + + " and " + $scope.permission_label.adhoc; } else { - return this.permission.permission_type; + return $scope.permission_label[this.permission.permission_type]; } }; @@ -489,5 +519,5 @@ export function UsersEdit($scope, $rootScope, $compile, $location, $log, $routeP UsersEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'UserForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', - 'GetBasePath', 'Prompt', 'CheckAccess', 'ResetForm', 'Wait', 'Stream' + 'GetBasePath', 'Prompt', 'CheckAccess', 'ResetForm', 'Wait', 'Stream', 'permissionsChoices', 'permissionsLabel', 'permissionsSearchSelect' ]; diff --git a/awx/ui/client/src/forms.js b/awx/ui/client/src/forms.js index 5ac8de52d0..3bd8170ee7 100644 --- a/awx/ui/client/src/forms.js +++ b/awx/ui/client/src/forms.js @@ -23,7 +23,6 @@ import LicenseUpdate from "./forms/LicenseUpdate"; import LogViewerOptions from "./forms/LogViewerOptions"; import LogViewerStatus from "./forms/LogViewerStatus"; import Organizations from "./forms/Organizations"; -import Permissions from "./forms/Permissions"; import ProjectStatus from "./forms/ProjectStatus"; import Projects from "./forms/Projects"; import Source from "./forms/Source"; @@ -51,7 +50,6 @@ export LogViewerOptions, LogViewerStatus, Organizations, - Permissions, ProjectStatus, Projects, Source, diff --git a/awx/ui/client/src/forms/Permissions.js b/awx/ui/client/src/forms/Permissions.js deleted file mode 100644 index 3072366981..0000000000 --- a/awx/ui/client/src/forms/Permissions.js +++ /dev/null @@ -1,159 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - /** - * @ngdoc function - * @name forms.function:Permissions - * @description This form is for adding/editing persmissions -*/ - -export default - angular.module('PermissionFormDefinition', []) - .value('PermissionsForm', { - - addTitle: 'Add Permission', //Title in add mode - editTitle: '{{ name }}', //Title in edit mode - name: 'permission', //entity or model name in singular form - well: true, //Wrap the form with TB well - forceListeners: true, - - stream: { - 'class': "btn-primary btn-xs activity-btn", - ngClick: "showActivity()", - awToolTip: "View Activity Stream", - awFeature: 'activity_streams', - dataPlacement: "top", - icon: "icon-comments-alt", - mode: 'edit', - iconSize: 'large' - }, - - fields: { - category: { - label: 'Permission Type', - labelClass: 'prepend-asterisk', - type: 'radio_group', - options: [{ - label: 'Inventory', - value: 'Inventory', - selected: true - }, { - label: 'Job Template', - value: 'Deploy' - }], - ngChange: 'selectCategory()' - }, - name: { - label: 'Name', - type: 'text', - addRequired: true, - editRequired: true, - capitalize: false - }, - description: { - label: 'Description', - type: 'text', - addRequired: false, - editRequired: false - }, - user: { - label: 'User', - type: 'hidden' - }, - team: { - label: 'Team', - type: 'hidden' - }, - project: { - label: 'Project', - type: 'lookup', - sourceModel: 'project', - sourceField: 'name', - ngShow: "category == 'Deploy'", - ngClick: 'lookUpProject()', - awRequiredWhen: { - variable: "projectrequired", - init: "false" - } - }, - inventory: { - label: 'Inventory', - type: 'lookup', - sourceModel: 'inventory', - sourceField: 'name', - ngClick: 'lookUpInventory()', - awRequiredWhen: { - variable: "inventoryrequired", - init: "true" - } - }, - permission_type: { - label: 'Permission', - labelClass: 'prepend-asterisk', - type: 'radio_group', - class: 'squeeze', - ngChange: 'changeAdhocCommandCheckbox()', - options: [{ - label: 'Read', - value: 'read', - ngShow: "category == 'Inventory'" - }, { - label: 'Write', - value: 'write', - ngShow: "category == 'Inventory'" - }, { - label: 'Admin', - value: 'admin', - ngShow: "category == 'Inventory'" - }, { - label: 'Create', - value: 'create', - ngShow: "category == 'Deploy'" - }, { - label: 'Run', - value: 'run', - ngShow: "category == 'Deploy'" - }, { - label: 'Check', - value: 'check', - ngShow: "category == 'Deploy'" - }], - // hack: attach helpCollapse here if the permissions - // category is deploy - helpCollapse: [{ - hdr: 'Permission', - ngBind: 'permissionTypeHelp', - ngHide: "category == 'Inventory'" - }] - }, - run_ad_hoc_commands: { - label: 'Execute commands', - type: 'checkbox', - // hack: attach helpCollapse here if the permissions - // category is inventory - helpCollapse: [{ - hdr: 'Permission', - ngBind: 'permissionTypeHelp' - }], - ngShow: "category == 'Inventory'", - associated: 'permission_type' - }, - }, - - buttons: { - save: { - ngClick: 'formSave()', - ngDisabled: true - }, - reset: { - ngClick: 'formReset()', - ngDisabled: true - } - }, - - related: { } - - }); // Form diff --git a/awx/ui/client/src/forms/Teams.js b/awx/ui/client/src/forms/Teams.js index 7da930b9db..195f783ede 100644 --- a/awx/ui/client/src/forms/Teams.js +++ b/awx/ui/client/src/forms/Teams.js @@ -156,7 +156,9 @@ export default ngBind: 'permission.summary_fields.project.name' }, permission_type: { - label: 'Permission' + label: 'Permission', + ngBind: 'getPermissionText()', + searchType: 'select' } }, diff --git a/awx/ui/client/src/forms/Users.js b/awx/ui/client/src/forms/Users.js index 1f7ff32694..1f2ebef3aa 100644 --- a/awx/ui/client/src/forms/Users.js +++ b/awx/ui/client/src/forms/Users.js @@ -212,7 +212,8 @@ export default }, permission_type: { label: 'Permission', - ngBind: 'getPermissionText()' + ngBind: 'getPermissionText()', + searchType: 'select' } }, diff --git a/awx/ui/client/src/helpers.js b/awx/ui/client/src/helpers.js index be783b45a9..e5477a186c 100644 --- a/awx/ui/client/src/helpers.js +++ b/awx/ui/client/src/helpers.js @@ -26,7 +26,6 @@ import LogViewer from "./helpers/LogViewer"; import Lookup from "./helpers/Lookup"; import PaginationHelpers from "./helpers/PaginationHelpers"; import Parse from "./helpers/Parse"; -import Permissions from "./helpers/Permissions"; import ProjectPath from "./helpers/ProjectPath"; import Projects from "./helpers/Projects"; import Schedules from "./helpers/Schedules"; @@ -64,7 +63,6 @@ export Lookup, PaginationHelpers, Parse, - Permissions, ProjectPath, Projects, Schedules, diff --git a/awx/ui/client/src/helpers/Permissions.js b/awx/ui/client/src/helpers/Permissions.js deleted file mode 100644 index 565b755b0a..0000000000 --- a/awx/ui/client/src/helpers/Permissions.js +++ /dev/null @@ -1,79 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - /** - * @ngdoc function - * @name helpers.function:Permissions - * @description - * Functions shared amongst Permission related controllers - * - */ - -export default - angular.module('PermissionsHelper', []) - - // Handle category change event - .factory('PermissionCategoryChange', ['$sce', - function ($sce) { - return function (params) { - var scope = params.scope, - reset = params.reset, - html; - - if (scope.category === 'Inventory') { - scope.projectrequired = false; - html = "
\n" + - "
Read
\n" + - "
Only allow the user or team to view the inventory." + - "
\n" + - "
Write
\n" + - "
Allow the user or team to modify hosts and groups " + - "contained in the inventory, add new hosts and groups" + - ", and perform inventory sync operations.\n" + - "
Admin
\n" + - "
Allow the user or team full access to the " + - "inventory. This includes reading, writing, deletion " + - "of the inventory, inventory sync operations, and " + - "the ability to execute commands on the inventory." + - "
\n" + - "
Execute commands
\n" + - "
Allow the user to execute commands on the " + - "inventory.
\n" + - "
\n"; - scope.permissionTypeHelp = $sce.trustAsHtml(html); - } else { - scope.projectrequired = true; - html = "
\n" + - "
Create
\n" + - "
Allow the user or team to create job templates. " + - "This implies that they have the Run and Check " + - "permissions.
\n" + - "
Run
\n" + - "
Allow the user or team to run a job template from " + - "the project against the inventory. In Run mode " + - "modules will " + - "be executed, and changes to the inventory will occur." + - "
\n" + - "
Check
\n" + - "
Only allow the user or team to run the project " + - "against the inventory as a dry-run operation. In " + - "Check mode, module operations " + - "will only be simulated. No changes will occur." + - "
\n" + - "
\n"; - scope.permissionTypeHelp = $sce.trustAsHtml(html); - } - - if (reset) { - if (scope.category === "Inventory") { - scope.permission_type = "read"; - } else { - scope.permission_type = "run"; - } - } - }; - } - ]); diff --git a/awx/ui/client/src/lists.js b/awx/ui/client/src/lists.js index ecef643812..da4aa67f7b 100644 --- a/awx/ui/client/src/lists.js +++ b/awx/ui/client/src/lists.js @@ -21,7 +21,6 @@ import JobHosts from "./lists/JobHosts"; import JobTemplates from "./lists/JobTemplates"; import Jobs from "./lists/Jobs"; import Organizations from "./lists/Organizations"; -import Permissions from "./lists/Permissions"; import PortalJobTemplates from "./lists/PortalJobTemplates"; import PortalJobs from "./lists/PortalJobs"; import Projects from "./lists/Projects"; @@ -50,7 +49,6 @@ export JobTemplates, Jobs, Organizations, - Permissions, PortalJobTemplates, PortalJobs, Projects, diff --git a/awx/ui/client/src/permissions/add/add.controller.js b/awx/ui/client/src/permissions/add/add.controller.js new file mode 100644 index 0000000000..0dad88f9db --- /dev/null +++ b/awx/ui/client/src/permissions/add/add.controller.js @@ -0,0 +1,157 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +/** + * @ngdoc function + * @name controllers.function:Permissions + * @description This controller for permissions add +*/ +export default + ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'permissionsForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath', 'ReturnToCaller', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess', 'Wait', 'permissionsCategoryChange', 'permissionsChoices', 'permissionsLabel', + function($scope, $rootScope, $compile, $location, $log, $routeParams, permissionsForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope, GetBasePath, ReturnToCaller, InventoryList, ProjectList, LookUpInit, CheckAccess, Wait, permissionsCategoryChange, permissionsChoices, permissionsLabel) { + + ClearScope(); + + // Inject dynamic view + var form = permissionsForm, + generator = GenerateForm, + id = ($routeParams.user_id !== undefined) ? $routeParams.user_id : $routeParams.team_id, + base = $location.path().replace(/^\//, '').split('/')[0], + master = {}; + + var permissionsChoice = permissionsChoices({ + scope: $scope, + url: 'api/v1/' + base + '/' + id + '/permissions/' + }); + + permissionsChoice.then(function (choices) { + return permissionsLabel({ + choices: choices + }); + }).then(function (choices) { + _.map(choices, function(n, key) { + $scope.permission_label[key] = n; + }); + }); + + generator.inject(form, { mode: 'add', related: false, scope: $scope }); + CheckAccess({ scope: $scope }); + generator.reset(); + LoadBreadCrumbs(); + + $scope.inventoryrequired = true; + $scope.projectrequired = false; + $scope.category = 'Inventory'; + master.category = 'Inventory'; + master.inventoryrequired = true; + master.projectrequired = false; + $scope.run_ad_hoc_commands = false; + $scope.permission_label = {}; + + LookUpInit({ + scope: $scope, + form: form, + current_item: null, + list: InventoryList, + field: 'inventory', + input_type: 'radio' + }); + + LookUpInit({ + scope: $scope, + form: form, + current_item: null, + list: ProjectList, + field: 'project', + input_type: 'radio' + }); + + $scope.$watch("category", function(val) { + if (val === 'Deploy') { + $scope.projectrequired = true; + LookUpInit({ + scope: $scope, + form: form, + current_item: null, + list: ProjectList, + field: 'project', + input_type: 'radio' + }); + } else { + $scope.projectrequired = false; + } + }); + + $scope.changeAdhocCommandCheckbox = function () { + if ($scope.category === 'Deploy') { + $scope.run_ad_hoc_command = false; + } else { + if ($scope.permission_type === 'admin') { + $scope.run_ad_hoc_commands = true; + $("#permission_run_ad_hoc_commands_chbox").attr("disabled", true); + } else { + if (!$scope.run_ad_hoc_commands) { + $scope.run_ad_hoc_commands = false; + } + $("#permission_run_ad_hoc_commands_chbox").attr("disabled", false); + } + } + }; + + // Save + $scope.formSave = function () { + var fld, url, data = {}; + generator.clearApiErrors(); + Wait('start'); + if ($scope.PermissionAddAllowed) { + data = {}; + for (fld in form.fields) { + data[fld] = $scope[fld]; + } + // job template (or deploy) based permissions do not have the run + // ad hoc commands parameter + if (data.category === "Deploy") { + data.run_ad_hoc_commands = false; + } else { + delete data.project; + } + + url = (base === 'teams') ? GetBasePath('teams') + id + '/permissions/' : GetBasePath('users') + id + '/permissions/'; + Rest.setUrl(url); + Rest.post(data) + .success(function () { + Wait('stop'); + ReturnToCaller(1); + }) + .error(function (data, status) { + Wait('stop'); + ProcessErrors($scope, data, status, permissionsForm, { hdr: 'Error!', + msg: 'Failed to create new permission. Post returned status: ' + status }); + }); + } else { + Alert('Access Denied', 'You do not have access to create new permission objects. Please contact a system administrator.', + 'alert-danger'); + } + }; + + // Cancel + $scope.formReset = function () { + $rootScope.flashMessage = null; + generator.reset(); + for (var fld in master) { + $scope[fld] = master[fld]; + } + $scope.selectCategory(); + }; + + $scope.selectCategory = function () { + permissionsCategoryChange({ scope: $scope, reset: true }); + }; + + + $scope.selectCategory(); + + }]; diff --git a/awx/ui/client/src/permissions/add/main.js b/awx/ui/client/src/permissions/add/main.js new file mode 100644 index 0000000000..ad86ab1801 --- /dev/null +++ b/awx/ui/client/src/permissions/add/main.js @@ -0,0 +1,21 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import userRoute from './user-add.route'; +import teamRoute from './team-add.route'; +import controller from './add.controller'; + +export default + angular.module('permissionsAdd', []) + .controller('addController', controller) + .config(['$routeProvider', function($routeProvider) { + var url = userRoute.route; + delete userRoute.route; + $routeProvider.when(url, userRoute); + url = teamRoute.route; + delete teamRoute.route; + $routeProvider.when(url, teamRoute); + }]); diff --git a/awx/ui/client/src/permissions/add/team-add.route.js b/awx/ui/client/src/permissions/add/team-add.route.js new file mode 100644 index 0000000000..706d273f79 --- /dev/null +++ b/awx/ui/client/src/permissions/add/team-add.route.js @@ -0,0 +1,19 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {templateUrl} from '../../shared/template-url/template-url.factory'; + +export default { + name: 'teamPermissionsAdd', + route: '/teams/:team_id/permissions/add', + templateUrl: templateUrl('permissions/shared/team-permissions'), + controller: 'addController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } +}; diff --git a/awx/ui/client/src/permissions/add/user-add.route.js b/awx/ui/client/src/permissions/add/user-add.route.js new file mode 100644 index 0000000000..877be6c20c --- /dev/null +++ b/awx/ui/client/src/permissions/add/user-add.route.js @@ -0,0 +1,19 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {templateUrl} from '../../shared/template-url/template-url.factory'; + +export default { + name: 'userPermissionsAdd', + route: '/users/:user_id/permissions/add', + templateUrl: templateUrl('permissions/shared/user-permissions'), + controller: 'addController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } +}; diff --git a/awx/ui/client/src/permissions/edit/edit.controller.js b/awx/ui/client/src/permissions/edit/edit.controller.js new file mode 100644 index 0000000000..1b1c6a552b --- /dev/null +++ b/awx/ui/client/src/permissions/edit/edit.controller.js @@ -0,0 +1,184 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +/** + * @ngdoc function + * @name controllers.function:Permissions + * @description This controller for permissions edit +*/ +export default + ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'permissionsForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'Prompt', 'GetBasePath', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess', 'Wait', 'permissionsCategoryChange', 'permissionsChoices', 'permissionsLabel', + function($scope, $rootScope, $compile, $location, $log, $routeParams, permissionsForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, Prompt, GetBasePath, InventoryList, ProjectList, LookUpInit, CheckAccess, Wait, permissionsCategoryChange, permissionsChoices, permissionsLabel) { + + ClearScope(); + + var generator = GenerateForm, + form = permissionsForm, + base_id = ($routeParams.user_id !== undefined) ? $routeParams.user_id : $routeParams.team_id, + id = $routeParams.permission_id, + defaultUrl = GetBasePath('base') + 'permissions/' + id + '/', + base = $location.path().replace(/^\//, '').split('/')[0], + master = {}; + + $scope.permission_label = {}; + + var permissionsChoice = permissionsChoices({ + scope: $scope, + url: 'api/v1/' + base + '/' + base_id + '/permissions/' + }); + + permissionsChoice.then(function (choices) { + return permissionsLabel({ + choices: choices + }); + }).then(function (choices) { + _.map(choices, function(n, key) { + $scope.permission_label[key] = n; + }); + }); + + $scope.changeAdhocCommandCheckbox = function () { + if ($scope.category === 'Deploy') { + $scope.run_ad_hoc_command = false; + } else { + if ($scope.permission_type === 'admin') { + $scope.run_ad_hoc_commands = true; + $("#permission_run_ad_hoc_commands_chbox").attr("disabled", true); + } else { + if (!$scope.run_ad_hoc_commands) { + $scope.run_ad_hoc_commands = false; + } + $("#permission_run_ad_hoc_commands_chbox").attr("disabled", false); + } + } + }; + + generator.inject(form, { mode: 'edit', related: true, scope: $scope }); + generator.reset(); + + $scope.selectCategory = function (resetIn) { + var reset = (resetIn === false) ? false : true; + permissionsCategoryChange({ scope: $scope, reset: reset }); + }; + if ($scope.removeFillForm) { + $scope.removeFillForm(); + } + $scope.removeFillForm = $scope.$on('FillForm', function () { + // Retrieve detail record and prepopulate the form + Wait('start'); + Rest.setUrl(defaultUrl); + Rest.get() + .success(function (data) { + var fld, sourceModel, sourceField; + LoadBreadCrumbs({ path: '/users/' + base_id + '/permissions/' + id, title: data.name }); + for (fld in form.fields) { + if (data[fld]) { + if (form.fields[fld].sourceModel) { + sourceModel = form.fields[fld].sourceModel; + sourceField = form.fields[fld].sourceField; + $scope[sourceModel + '_' + sourceField] = data.summary_fields[sourceModel][sourceField]; + master[sourceModel + '_' + sourceField] = data.summary_fields[sourceModel][sourceField]; + } + $scope[fld] = data[fld]; + master[fld] = $scope[fld]; + } + } + + $scope.category = 'Deploy'; + if (data.permission_type !== 'run' && data.permission_type !== 'check' && data.permission_type !== 'create') { + $scope.category = 'Inventory'; + } + master.category = $scope.category; + $scope.selectCategory(false); //call without resetting $scope.category value + + LookUpInit({ + scope: $scope, + form: form, + current_item: data.inventory, + list: InventoryList, + field: 'inventory', + input_type: "radio" + }); + + LookUpInit({ + scope: $scope, + form: form, + current_item: data.project, + list: ProjectList, + field: 'project', + input_type: 'radio' + }); + + $scope.changeAdhocCommandCheckbox(); + + if (!$scope.PermissionAddAllowed) { + // If not a privileged user, disable access + $('form[name="permission_form"]').find('select, input, button').each(function () { + if ($(this).is('input') || $(this).is('select')) { + $(this).attr('readonly', 'readonly'); + } + if ($(this).is('input[type="checkbox"]') || + $(this).is('input[type="radio"]') || + $(this).is('button')) { + $(this).attr('disabled', 'disabled'); + } + }); + } + Wait('stop'); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, form, { hdr: 'Error!', + msg: 'Failed to retrieve Permission: ' + id + '. GET status: ' + status }); + }); + }); + + CheckAccess({ + scope: $scope, + callback: 'FillForm' + }); + + // Save changes to the parent + $scope.formSave = function () { + var fld, data = {}; + generator.clearApiErrors(); + Wait('start'); + for (fld in form.fields) { + data[fld] = $scope[fld]; + } + // job template (or deploy) based permissions do not have the run + // ad hoc commands parameter + if (data.category === "Deploy") { + data.run_ad_hoc_commands = false; + } else { + delete data.project; + } + + Rest.setUrl(defaultUrl); + if($scope.category === "Inventory"){ + delete data.project; + } + Rest.put(data) + .success(function () { + Wait('stop'); + ReturnToCaller(1); + }) + .error(function (data, status) { + ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update Permission: ' + + $routeParams.id + '. PUT status: ' + status }); + }); + }; + + + // Cancel + $scope.formReset = function () { + generator.reset(); + for (var fld in master) { + $scope[fld] = master[fld]; + } + $scope.selectCategory(false); + }; + + }]; diff --git a/awx/ui/client/src/permissions/edit/main.js b/awx/ui/client/src/permissions/edit/main.js new file mode 100644 index 0000000000..08e4aa2b85 --- /dev/null +++ b/awx/ui/client/src/permissions/edit/main.js @@ -0,0 +1,21 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import userRoute from './user-edit.route'; +import teamRoute from './team-edit.route'; +import controller from './edit.controller'; + +export default + angular.module('permissionsEdit', []) + .controller('permissionsEditController', controller) + .config(['$routeProvider', function($routeProvider) { + var url = userRoute.route; + delete userRoute.route; + $routeProvider.when(url, userRoute); + url = teamRoute.route; + delete teamRoute.route; + $routeProvider.when(url, teamRoute); + }]); diff --git a/awx/ui/client/src/permissions/edit/team-edit.route.js b/awx/ui/client/src/permissions/edit/team-edit.route.js new file mode 100644 index 0000000000..8122b7fcf5 --- /dev/null +++ b/awx/ui/client/src/permissions/edit/team-edit.route.js @@ -0,0 +1,19 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {templateUrl} from '../../shared/template-url/template-url.factory'; + +export default { + name: 'teamPermissionsEdit', + route: '/teams/:team_id/permissions/:permission_id', + templateUrl: templateUrl('permissions/shared/team-permissions'), + controller: 'permissionsEditController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } +}; diff --git a/awx/ui/client/src/permissions/edit/user-edit.route.js b/awx/ui/client/src/permissions/edit/user-edit.route.js new file mode 100644 index 0000000000..3d42d5827e --- /dev/null +++ b/awx/ui/client/src/permissions/edit/user-edit.route.js @@ -0,0 +1,19 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {templateUrl} from '../../shared/template-url/template-url.factory'; + +export default { + name: 'userPermissionsEdit', + route: '/users/:user_id/permissions/:permission_id', + templateUrl: templateUrl('permissions/shared/user-permissions'), + controller: 'permissionsEditController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } +}; diff --git a/awx/ui/client/src/permissions/list/list.controller.js b/awx/ui/client/src/permissions/list/list.controller.js new file mode 100644 index 0000000000..4e9ea4c716 --- /dev/null +++ b/awx/ui/client/src/permissions/list/list.controller.js @@ -0,0 +1,136 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +/** + * @ngdoc function + * @name controllers.function:Permissions + * @description This controller for permissions list +*/ + + +export default + ['$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'permissionsList', 'generateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', 'GetBasePath', 'CheckAccess', 'Wait', 'permissionsChoices', 'permissionsLabel', 'permissionsSearchSelect', + function ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, permissionsList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, CheckAccess, Wait, permissionsChoices, permissionsLabel, permissionsSearchSelect) { + + ClearScope(); + + var list = permissionsList, + base = $location.path().replace(/^\//, '').split('/')[0], + base_id = ($routeParams.user_id !== undefined) ? $routeParams.user_id : $routeParams.team_id, + defaultUrl = GetBasePath(base), + generator = GenerateList; + + $scope.permission_label = {}; + $scope.permission_search_select = []; + + // return a promise from the options request with the permission type choices (including adhoc) as a param + var permissionsChoice = permissionsChoices({ + scope: $scope, + url: 'api/v1/' + base + '/' + base_id + '/permissions/' + }); + + // manipulate the choices from the options request to be set on + // scope and be usable by the list form + permissionsChoice.then(function (choices) { + choices = + permissionsLabel({ + choices: choices + }); + _.map(choices, function(n, key) { + $scope.permission_label[key] = n; + }); + }); + + // manipulate the choices from the options request to be usable + // by the search option for permission_type, you can't inject the + // list until this is done! + permissionsChoice.then(function (choices) { + list.fields.permission_type.searchOptions = + permissionsSearchSelect({ + choices: choices + }); + generator.inject(list, { mode: 'edit', scope: $scope, breadCrumbs: true }); + }); + + defaultUrl += ($routeParams.user_id !== undefined) ? $routeParams.user_id : $routeParams.team_id; + defaultUrl += '/permissions/'; + + $scope.selected = []; + + CheckAccess({ + scope: $scope + }); + + if ($scope.removePostRefresh) { + $scope.removePostRefresh(); + } + $scope.removePostRefresh = $scope.$on('PostRefresh', function () { + // Cleanup after a delete + Wait('stop'); + $('#prompt-modal').modal('hide'); + }); + + SearchInit({ + scope: $scope, + set: 'permissions', + list: list, + url: defaultUrl + }); + PaginateInit({ + scope: $scope, + list: list, + url: defaultUrl + }); + $scope.search(list.iterator); + + LoadBreadCrumbs(); + + $scope.addPermission = function () { + if ($scope.PermissionAddAllowed) { + $location.path($location.path() + '/add'); + } + }; + + // if the permission includes adhoc (and is not admin), display that + $scope.getPermissionText = function () { + if (this.permission.permission_type !== "admin" && this.permission.run_ad_hoc_commands) { + return $scope.permission_label[this.permission.permission_type] + + " and " + $scope.permission_label.adhoc; + } else { + return $scope.permission_label[this.permission.permission_type]; + } + }; + + $scope.editPermission = function (id) { + $location.path($location.path() + '/' + id); + }; + + $scope.deletePermission = function (id, name) { + var action = function () { + $('#prompt-modal').modal('hide'); + Wait('start'); + var url = GetBasePath('base') + 'permissions/' + id + '/'; + Rest.setUrl(url); + Rest.destroy() + .success(function () { + $scope.search(list.iterator); + }) + .error(function (data, status) { + Wait('stop'); + ProcessErrors($scope, data, status, null, { hdr: 'Error!', + msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); + }); + }; + + if ($scope.PermissionAddAllowed) { + Prompt({ + hdr: 'Delete', + body: 'Are you sure you want to delete ' + name + '?', + action: action + }); + } + }; + }]; diff --git a/awx/ui/client/src/permissions/list/main.js b/awx/ui/client/src/permissions/list/main.js new file mode 100644 index 0000000000..beed341e5b --- /dev/null +++ b/awx/ui/client/src/permissions/list/main.js @@ -0,0 +1,21 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import userRoute from './user-list.route'; +import teamRoute from './team-list.route'; +import controller from './list.controller'; + +export default + angular.module('permissionsList', []) + .controller('permissionsListController', controller) + .config(['$routeProvider', function($routeProvider) { + var url = userRoute.route; + delete userRoute.route; + $routeProvider.when(url, userRoute); + url = teamRoute.route; + delete teamRoute.route; + $routeProvider.when(url, teamRoute); + }]); diff --git a/awx/ui/client/src/permissions/list/team-list.route.js b/awx/ui/client/src/permissions/list/team-list.route.js new file mode 100644 index 0000000000..bd93ebf2e5 --- /dev/null +++ b/awx/ui/client/src/permissions/list/team-list.route.js @@ -0,0 +1,19 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {templateUrl} from '../../shared/template-url/template-url.factory'; + +export default { + name: 'teamPermissionsList', + route: '/teams/:team_id/permissions', + templateUrl: templateUrl('permissions/shared/team-permissions'), + controller: 'permissionsListController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } +}; diff --git a/awx/ui/client/src/permissions/list/user-list.route.js b/awx/ui/client/src/permissions/list/user-list.route.js new file mode 100644 index 0000000000..586b000d69 --- /dev/null +++ b/awx/ui/client/src/permissions/list/user-list.route.js @@ -0,0 +1,19 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import {templateUrl} from '../../shared/template-url/template-url.factory'; + +export default { + name: 'userPermissionsList', + route: '/users/:user_id/permissions', + templateUrl: templateUrl('permissions/shared/user-permissions'), + controller: 'permissionsListController', + resolve: { + features: ['FeaturesService', function(FeaturesService) { + return FeaturesService.get(); + }] + } +}; diff --git a/awx/ui/client/src/permissions/main.js b/awx/ui/client/src/permissions/main.js new file mode 100644 index 0000000000..fef273cfa6 --- /dev/null +++ b/awx/ui/client/src/permissions/main.js @@ -0,0 +1,30 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + +import permissionsList from './list/main'; +import permissionsAdd from './add/main'; +import permissionsEdit from './edit/main'; + +import list from './shared/permissions.list'; +import form from './shared/permissions.form'; + +import permissionsCategoryChange from './shared/category-change.factory'; +import permissionsChoices from './shared/get-choices.factory'; +import permissionsLabel from './shared/get-labels.factory'; +import permissionsSearchSelect from './shared/get-search-select.factory'; + +export default + angular.module('permissions', [ + permissionsList.name, + permissionsAdd.name, + permissionsEdit.name + ]) + .factory('permissionsList', list) + .factory('permissionsForm', form) + .factory('permissionsCategoryChange', permissionsCategoryChange) + .factory('permissionsChoices', permissionsChoices) + .factory('permissionsLabel', permissionsLabel) + .factory('permissionsSearchSelect', permissionsSearchSelect); diff --git a/awx/ui/client/src/permissions/shared/category-change.factory.js b/awx/ui/client/src/permissions/shared/category-change.factory.js new file mode 100644 index 0000000000..0de5c7f2ea --- /dev/null +++ b/awx/ui/client/src/permissions/shared/category-change.factory.js @@ -0,0 +1,74 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + /** + * @ngdoc function + * @name helpers.function:Permissions + * @description + * Functions shared amongst Permission related controllers + * + */ + + export default + ['$sce', function($sce) { + return function (params) { + var scope = params.scope, + reset = params.reset, + html; + + if (scope.category === 'Inventory') { + scope.projectrequired = false; + html = "
\n" + + "
Read Inventory
\n" + + "
Only allow the user or team to view the inventory." + + "
\n" + + "
Edit Inventory
\n" + + "
Allow the user or team to modify hosts and groups " + + "contained in the inventory, add new hosts and groups" + + ", and perform inventory sync operations.\n" + + "
Administrate Inventory
\n" + + "
Allow the user or team full access to the " + + "inventory. This includes reading, writing, deletion " + + "of the inventory, inventory sync operations, and " + + "the ability to execute commands on the inventory." + + "
\n" + + "
Execute Commands
\n" + + "
Allow the user to execute commands on the " + + "inventory.
\n" + + "
\n"; + scope.permissionTypeHelp = $sce.trustAsHtml(html); + } else { + scope.projectrequired = true; + html = "
\n" + + "
Create a Job Template
\n" + + "
Allow the user or team to create job templates. " + + "This implies that they have the Run and Check " + + "permissions.
\n" + + "
Deploy To Inventory
\n" + + "
Allow the user or team to run a job template from " + + "the project against the inventory. In Run mode " + + "modules will " + + "be executed, and changes to the inventory will occur." + + "
\n" + + "
Deploy to Inventory (Dry Run)
\n" + + "
Only allow the user or team to run the project " + + "against the inventory as a dry-run operation. In " + + "Check mode, module operations " + + "will only be simulated. No changes will occur." + + "
\n" + + "
\n"; + scope.permissionTypeHelp = $sce.trustAsHtml(html); + } + + if (reset) { + if (scope.category === "Inventory") { + scope.permission_type = "read"; + } else { + scope.permission_type = "run"; + } + } + }; + }]; diff --git a/awx/ui/client/src/permissions/shared/get-choices.factory.js b/awx/ui/client/src/permissions/shared/get-choices.factory.js new file mode 100644 index 0000000000..57e14433f4 --- /dev/null +++ b/awx/ui/client/src/permissions/shared/get-choices.factory.js @@ -0,0 +1,39 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + /** + * @ngdoc function + * @name helpers.function:Permissions + * @description + * Gets permission type labels from the API and sets them as the permissions labels on the relevant radio buttons + * + */ + + export default + ['Rest', 'ProcessErrors', function(Rest, ProcessErrors) { + return function (params) { + var scope = params.scope, + url = params.url; + + // Auto populate the field if there is only one result + Rest.setUrl(url); + return Rest.options() + .then(function (data) { + data = data.data; + var choices = data.actions.GET.permission_type.choices; + + // manually add the adhoc label to the choices object + choices.push(["adhoc", + data.actions.GET.run_ad_hoc_commands.label]); + + return choices; + }) + .catch(function (data, status) { + ProcessErrors(scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to get permission type labels. Options requrest returned status: ' + status }); + }); + }; + }]; diff --git a/awx/ui/client/src/permissions/shared/get-labels.factory.js b/awx/ui/client/src/permissions/shared/get-labels.factory.js new file mode 100644 index 0000000000..2b20ca70bb --- /dev/null +++ b/awx/ui/client/src/permissions/shared/get-labels.factory.js @@ -0,0 +1,26 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + /** + * @ngdoc function + * @name helpers.function:Permissions + * @description + * Gets permission type labels from the API and sets them as the permissions labels on the relevant radio buttons + * + */ + + export default + [function() { + return function (params) { + // convert the choices from the API from the format + // [["read", "Read Inventory"], ...] to + // {read: "Read Inventory", ...} + return params.choices.reduce(function(obj, kvp) { + obj[kvp[0]] = kvp[1]; + return obj; + }, {}); + }; + }]; diff --git a/awx/ui/client/src/permissions/shared/get-search-select.factory.js b/awx/ui/client/src/permissions/shared/get-search-select.factory.js new file mode 100644 index 0000000000..110663cd7b --- /dev/null +++ b/awx/ui/client/src/permissions/shared/get-search-select.factory.js @@ -0,0 +1,29 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + /** + * @ngdoc function + * @name helpers.function:Permissions + * @description + * Gets permission type labels from the API and sets them as the permissions labels on the relevant radio buttons + * + */ + + export default + [function() { + return function (params) { + // convert the choices from the API from the format + // [["read", "Read Inventory"], ...] to + // [{name: "read", value: "Read Inventory"}, ...] + return params.choices.filter(function (kvp) { + return (kvp[0] !== "adhoc"); + }).map(function (kvp) { + if (kvp[0] !== "adhoc") { + return {name: kvp[1], value: kvp[0]}; + } + }); + }; + }]; diff --git a/awx/ui/client/src/permissions/shared/permissions.form.js b/awx/ui/client/src/permissions/shared/permissions.form.js new file mode 100644 index 0000000000..de97aa01ad --- /dev/null +++ b/awx/ui/client/src/permissions/shared/permissions.form.js @@ -0,0 +1,158 @@ +/************************************************* + * Copyright (c) 2015 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + /** + * @ngdoc function + * @name forms.function:Permissions + * @description This form is for adding/editing persmissions +*/ + +export default function() { + return { + addTitle: 'Add Permission', //Title in add mode + editTitle: '{{ name }}', //Title in edit mode + name: 'permission', //entity or model name in singular form + well: true, //Wrap the form with TB well + forceListeners: true, + + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + awFeature: 'activity_streams', + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + }, + + fields: { + category: { + label: 'Permission Type', + labelClass: 'prepend-asterisk', + type: 'radio_group', + options: [{ + label: 'Inventory', + value: 'Inventory', + selected: true + }, { + label: 'Job Template', + value: 'Deploy' + }], + ngChange: 'selectCategory()' + }, + name: { + label: 'Name', + type: 'text', + addRequired: true, + editRequired: true, + capitalize: false + }, + description: { + label: 'Description', + type: 'text', + addRequired: false, + editRequired: false + }, + user: { + label: 'User', + type: 'hidden' + }, + team: { + label: 'Team', + type: 'hidden' + }, + project: { + label: 'Project', + type: 'lookup', + sourceModel: 'project', + sourceField: 'name', + ngShow: "category == 'Deploy'", + ngClick: 'lookUpProject()', + awRequiredWhen: { + variable: "projectrequired", + init: "false" + } + }, + inventory: { + label: 'Inventory', + type: 'lookup', + sourceModel: 'inventory', + sourceField: 'name', + ngClick: 'lookUpInventory()', + awRequiredWhen: { + variable: "inventoryrequired", + init: "true" + } + }, + permission_type: { + label: 'Permission', + labelClass: 'prepend-asterisk', + type: 'radio_group', + class: 'squeeze', + ngChange: 'changeAdhocCommandCheckbox()', + options: [{ + label: '{{ permission_label.read }}', + value: 'read', + ngShow: "category == 'Inventory'" + }, { + label: '{{ permission_label.write }}', + value: 'write', + ngShow: "category == 'Inventory'" + }, { + label: '{{ permission_label.admin }}', + value: 'admin', + ngShow: "category == 'Inventory'" + }, { + label: '{{ permission_label.create }}', + value: 'create', + ngShow: "category == 'Deploy'" + }, { + label: '{{ permission_label.run }}', + value: 'run', + ngShow: "category == 'Deploy'" + }, { + label: '{{ permission_label.check }}', + value: 'check', + ngShow: "category == 'Deploy'" + }], + // hack: attach helpCollapse here if the permissions + // category is deploy + helpCollapse: [{ + hdr: 'Permission', + ngBind: 'permissionTypeHelp', + ngHide: "category == 'Inventory'" + }] + }, + run_ad_hoc_commands: { + label: '{{ permission_label.adhoc }}', + type: 'checkbox', + // hack: attach helpCollapse here if the permissions + // category is inventory + helpCollapse: [{ + hdr: 'Permission', + ngBind: 'permissionTypeHelp' + }], + ngShow: "category == 'Inventory'", + associated: 'permission_type' + }, + }, + + buttons: { + save: { + ngClick: 'formSave()', + ngDisabled: true + }, + reset: { + ngClick: 'formReset()', + ngDisabled: true + } + }, + + related: { } + + }; +} diff --git a/awx/ui/client/src/lists/Permissions.js b/awx/ui/client/src/permissions/shared/permissions.list.js similarity index 93% rename from awx/ui/client/src/lists/Permissions.js rename to awx/ui/client/src/permissions/shared/permissions.list.js index 9938c6fdc7..f9c50c7818 100644 --- a/awx/ui/client/src/lists/Permissions.js +++ b/awx/ui/client/src/permissions/shared/permissions.list.js @@ -3,11 +3,10 @@ * * All Rights Reserved *************************************************/ - -export default - angular.module('PermissionListDefinition', []) - .value('PermissionList', { + + export default function() { + return { name: 'permissions', iterator: 'permission', @@ -38,7 +37,8 @@ export default }, permission_type: { label: 'Permission', - ngBind: 'getPermissionText()' + ngBind: 'getPermissionText()', + searchType: 'select' } }, @@ -77,4 +77,5 @@ export default dataPlacement: 'top' } } - }); + }; +} diff --git a/awx/ui/client/src/permissions/shared/team-permissions.partial.html b/awx/ui/client/src/permissions/shared/team-permissions.partial.html new file mode 100644 index 0000000000..e8c4dad50b --- /dev/null +++ b/awx/ui/client/src/permissions/shared/team-permissions.partial.html @@ -0,0 +1,18 @@ + + + + + + + + + +
+
+
diff --git a/awx/ui/client/src/permissions/shared/user-permissions.partial.html b/awx/ui/client/src/permissions/shared/user-permissions.partial.html new file mode 100644 index 0000000000..271a15d748 --- /dev/null +++ b/awx/ui/client/src/permissions/shared/user-permissions.partial.html @@ -0,0 +1,18 @@ + + + + + + + + + +
+
+