1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-28 02:25:27 +03:00

Merge pull request #5704 from mabashian/5677-rbac-org

Admin/non-org admin edit org in various resources bug fix
This commit is contained in:
Michael Abashian 2017-03-14 09:51:28 -04:00 committed by GitHub
commit d8592fc1f2
20 changed files with 142 additions and 36 deletions

View File

@ -288,7 +288,7 @@ CredentialsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location',
export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
$stateParams, CredentialForm, Rest, Alert, ProcessErrors, ClearScope, Prompt,
GetBasePath, GetChoices, KindChange, BecomeMethodChange, Empty, OwnerChange, FormSave, Wait,
$state, CreateSelect2, Authorization, i18n) {
$state, CreateSelect2, Authorization, i18n, OrgAdminLookup) {
ClearScope();
@ -499,6 +499,16 @@ export function CredentialsEdit($scope, $rootScope, $compile, $location, $log,
setAskCheckboxes();
if(data.organization) {
OrgAdminLookup.checkForAdminAccess({organization: data.organization})
.then(function(canEditOrg){
$scope.canEditOrg = canEditOrg;
});
}
else {
$scope.canEditOrg = true;
}
$scope.$emit('credentialLoaded');
Wait('stop');
})
@ -626,5 +636,5 @@ CredentialsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location',
'$log', '$stateParams', 'CredentialForm', 'Rest', 'Alert',
'ProcessErrors', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices',
'KindChange', 'BecomeMethodChange', 'Empty', 'OwnerChange',
'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n',
'FormSave', 'Wait', '$state', 'CreateSelect2', 'Authorization', 'i18n', 'OrgAdminLookup'
];

View File

@ -439,11 +439,11 @@ export function ProjectsAdd($scope, $rootScope, $compile, $location, $log,
'Do not put the username and key in the URL. ' +
'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '<strong>', '</strong>');
break;
case 'insights':
$scope.pathRequired = false;
$scope.scmRequired = false;
$scope.credentialLabel = "Red Hat Insights";
break;
case 'insights':
$scope.pathRequired = false;
$scope.scmRequired = false;
$scope.credentialLabel = "Red Hat Insights";
break;
default:
$scope.credentialLabel = "SCM Credential";
$scope.urlPopover = '<p> ' + i18n._('URL popover text');
@ -464,7 +464,7 @@ ProjectsAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
$stateParams, ProjectsForm, Rest, Alert, ProcessErrors, GenerateForm,
Prompt, ClearScope, GetBasePath, GetProjectPath, Authorization,
GetChoices, Empty, DebugForm, Wait, ProjectUpdate, $state, CreateSelect2, ToggleNotification, i18n) {
GetChoices, Empty, DebugForm, Wait, ProjectUpdate, $state, CreateSelect2, ToggleNotification, i18n, OrgAdminLookup) {
ClearScope('htmlTemplate');
@ -595,6 +595,11 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
$scope.scm_type_class = "btn-disabled";
}
OrgAdminLookup.checkForAdminAccess({organization: data.organization})
.then(function(canEditOrg){
$scope.canEditOrg = canEditOrg;
});
$scope.project_obj = data;
$scope.name = data.name;
$scope.$emit('projectLoaded');
@ -708,6 +713,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
if ($scope.scm_type.value) {
switch ($scope.scm_type.value) {
case 'git':
$scope.credentialLabel = "SCM Credential";
$scope.urlPopover = '<p>' + i18n._('Example URLs for GIT SCM include:') + '</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
'<li>git@github.com:ansible/ansible.git</li><li>git://servername.example.com/ansible.git</li></ul>' +
'<p>' + i18n.sprintf(i18n._('%sNote:%s When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' +
@ -715,11 +721,13 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
'SSH. GIT read only protocol (git://) does not use username or password information.'), '<strong>', '</strong>');
break;
case 'svn':
$scope.credentialLabel = "SCM Credential";
$scope.urlPopover = '<p>' + i18n._('Example URLs for Subversion SCM include:') + '</p>' +
'<ul class=\"no-bullets\"><li>https://github.com/ansible/ansible</li><li>svn://servername.example.com/path</li>' +
'<li>svn+ssh://servername.example.com/path</li></ul>';
break;
case 'hg':
$scope.credentialLabel = "SCM Credential";
$scope.urlPopover = '<p>' + i18n._('Example URLs for Mercurial SCM include:') + '</p>' +
'<ul class=\"no-bullets\"><li>https://bitbucket.org/username/project</li><li>ssh://hg@bitbucket.org/username/project</li>' +
'<li>ssh://server.example.com/path</li></ul>' +
@ -727,12 +735,13 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
'Do not put the username and key in the URL. ' +
'If using Bitbucket and SSH, do not supply your Bitbucket username.'), '<strong>', '</strong>');
break;
case 'insights':
$scope.pathRequired = false;
$scope.scmRequired = false;
$scope.credentialLabel = "Red Hat Insights";
case 'insights':
$scope.pathRequired = false;
$scope.scmRequired = false;
$scope.credentialLabel = "Red Hat Insights";
break;
default:
$scope.credentialLabel = "SCM Credential";
$scope.urlPopover = '<p> ' + i18n._('URL popover text');
}
}
@ -756,4 +765,4 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
ProjectsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
'$stateParams', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GenerateForm',
'Prompt', 'ClearScope', 'GetBasePath', 'GetProjectPath', 'Authorization', 'GetChoices', 'Empty',
'DebugForm', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', 'i18n'];
'DebugForm', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', 'i18n', 'OrgAdminLookup'];

View File

@ -154,7 +154,7 @@ TeamsAdd.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Generat
export function TeamsEdit($scope, $rootScope, $stateParams,
TeamForm, Rest, ProcessErrors, ClearScope, GetBasePath, Wait, $state) {
TeamForm, Rest, ProcessErrors, ClearScope, GetBasePath, Wait, $state, OrgAdminLookup) {
ClearScope();
@ -172,6 +172,11 @@ export function TeamsEdit($scope, $rootScope, $stateParams,
setScopeFields(data);
$scope.organization_name = data.summary_fields.organization.name;
OrgAdminLookup.checkForAdminAccess({organization: data.organization})
.then(function(canEditOrg){
$scope.canEditOrg = canEditOrg;
});
$scope.team_obj = data;
Wait('stop');
});
@ -243,5 +248,5 @@ export function TeamsEdit($scope, $rootScope, $stateParams,
}
TeamsEdit.$inject = ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest',
'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state'
'ProcessErrors', 'ClearScope', 'GetBasePath', 'Wait', '$state', 'OrgAdminLookup'
];

View File

@ -55,7 +55,8 @@ export default
dataTitle: i18n._('Organization') + ' ',
dataPlacement: 'bottom',
dataContainer: "body",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg',
awLookupWhen: '(credential_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg'
},
kind: {
label: i18n._('Type'),

View File

@ -49,7 +49,8 @@ angular.module('InventoryFormDefinition', [])
reqExpression: "organizationrequired",
init: "true"
},
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg',
awLookupWhen: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg'
},
variables: {
label: i18n._('Variables'),

View File

@ -50,7 +50,8 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
required: true,
dataContainer: 'body',
dataPlacement: 'right',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg',
awLookupWhen: '(project_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg'
},
scm_type: {
label: i18n._('SCM Type'),

View File

@ -42,7 +42,8 @@ export default
sourceModel: 'organization',
basePath: 'organizations',
sourceField: 'name',
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)',
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd) || !canEditOrg',
awLookupWhen: '(team_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg',
required: true,
}
},

View File

@ -54,7 +54,8 @@ export default
dataContainer: 'body',
dataPlacement: 'right',
column: 1,
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate) || !canEditOrg',
awLookupWhen: '(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAdd) && canEditOrg'
},
labels: {
label: i18n._('Labels'),

View File

@ -14,7 +14,7 @@ function InventoriesEdit($scope, $rootScope, $compile, $location,
$log, $stateParams, InventoryForm, Rest, Alert, ProcessErrors,
ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON,
ParseVariableString, Prompt, InitiatePlaybookRun,
TemplatesService, $state) {
TemplatesService, $state, OrgAdminLookup) {
// Inject dynamic view
var defaultUrl = GetBasePath('inventory'),
@ -77,6 +77,11 @@ function InventoriesEdit($scope, $rootScope, $compile, $location,
field_id: 'inventory_variables'
});
OrgAdminLookup.checkForAdminAccess({organization: data.organization})
.then(function(canEditOrg){
$scope.canEditOrg = canEditOrg;
});
$scope.inventory_obj = data;
$scope.name = data.name;
@ -132,5 +137,5 @@ export default ['$scope', '$rootScope', '$compile', '$location',
'$log', '$stateParams', 'InventoryForm', 'Rest', 'Alert',
'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', 'Wait',
'ToJSON', 'ParseVariableString', 'Prompt', 'InitiatePlaybookRun',
'TemplatesService', '$state', InventoriesEdit,
'TemplatesService', '$state', 'OrgAdminLookup', InventoriesEdit,
];

View File

@ -46,6 +46,9 @@ angular.module('inventory', [
data: {
activityStream: true,
activityStreamTarget: 'inventory'
},
ncyBreadcrumb: {
label: N_('INVENTORIES')
}
});

View File

@ -1,6 +1,6 @@
<div class="BreadCrumb InventoryManageBreadCrumbs">
<ol class="BreadCrumb-list">
<li class="BreadCrumb-item"><a ui-sref="inventories">Inventories</a></li>
<li class="BreadCrumb-item"><a ui-sref="inventories">INVENTORIES</a></li>
<li class="BreadCrumb-item BreadCrumb-invItem">
<a href ng-if="currentState !== 'inventoryManage' || groups.length > 0" ng-click="goToInventory()">{{inventory.name}}</a>
<span ng-if="currentState === 'inventoryManage' && groups.length === 0">{{inventory.name}}</span>

View File

@ -5,9 +5,9 @@
*************************************************/
export default
['$state', '$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$q',
['$state', '$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors',
'ConfigService',
function($state, $rootScope, Rest, GetBasePath, ProcessErrors, $q,
function($state, $rootScope, Rest, GetBasePath, ProcessErrors,
ConfigService){
return {
get: function() {
@ -29,7 +29,7 @@ export default
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
});
},
valid: function(license) {
if (!license.valid_key){
return false;

View File

@ -493,7 +493,8 @@ function(ConfigurationUtils, i18n, $rootScope) {
modelName = attrs.source,
lookupType = attrs.awlookuptype,
watcher = attrs.awRequiredWhen || undefined,
watchBasePath;
watchBasePath,
awLookupWhen = attrs.awLookupWhen;
if (attrs.autopopulatelookup !== undefined) {
autopopulateLookup = JSON.parse(attrs.autopopulatelookup);
@ -501,7 +502,6 @@ function(ConfigurationUtils, i18n, $rootScope) {
autopopulateLookup = true;
}
// The following block of code is for instances where the
// lookup field is reused by varying sub-forms. Example: The groups
// form will change it's credential lookup based on the
@ -602,7 +602,12 @@ function(ConfigurationUtils, i18n, $rootScope) {
// form.$pending will contain object reference to any ngModelControllers with outstanding requests
fieldCtrl.$asyncValidators.validResource = function(modelValue, viewValue) {
applyValidationStrategy(viewValue, fieldCtrl);
if(awLookupWhen === undefined || (awLookupWhen !== undefined && Boolean(scope.$eval(awLookupWhen)) === true)) {
applyValidationStrategy(viewValue, fieldCtrl);
}
else {
defer.resolve();
}
return defer.promise;
};

View File

@ -1388,6 +1388,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += (field.autopopulateLookup !== undefined) ? ` autopopulateLookup=${field.autopopulateLookup} ` : "";
html += (field.watchBasePath !== undefined) ? ` watchBasePath=${field.watchBasePath} ` : "";
html += `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 300, 'blur': 0 } }"`;
html += (field.awLookupWhen !== undefined) ? this.attr(field, 'awLookupWhen') : "";
html += " awlookup >\n";
html += "</div>\n";

View File

@ -86,6 +86,9 @@ angular.module('GeneratorHelpers', [systemStatus.name])
result += value;
result += '"';
break;
case 'awLookupWhen':
result = "ng-attr-awlookup=\"" + value + "\" ";
break;
default:
result = key + "=\"" + value + "\" ";
}

View File

@ -20,6 +20,7 @@ import templateUrl from './template-url/main';
import RestServices from '../rest/main';
import stateDefinitions from './stateDefinitions.factory';
import apiLoader from './api-loader';
import orgAdminLookup from './org-admin-lookup/main';
import 'angular-duration-format';
export default
@ -36,6 +37,7 @@ angular.module('shared', [listGenerator.name,
templateUrl.name,
RestServices.name,
apiLoader.name,
orgAdminLookup.name,
require('angular-cookies'),
'angular-duration-format'
])

View File

@ -0,0 +1,11 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import OrgAdminLookupFactory from './org-admin-lookup.factory';
export default
angular.module('orgAdminLookup', [])
.service('OrgAdminLookup', OrgAdminLookupFactory);

View File

@ -0,0 +1,35 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
['Rest', 'Authorization', 'GetBasePath', '$rootScope', '$q',
function(Rest, Authorization, GetBasePath, $rootScope, $q){
return {
checkForAdminAccess: function(params) {
// params.organization - id of the organization in question
var deferred = $q.defer();
if(Authorization.getUserInfo('is_superuser') !== true) {
Rest.setUrl(GetBasePath('users') + $rootScope.current_user.id + '/admin_of_organizations');
Rest.get({ params: { id: params.organization } })
.success(function(data) {
if(data.count && data.count > 0) {
deferred.resolve(true);
}
else {
deferred.resolve(false);
}
});
}
else {
deferred.resolve(true);
}
return deferred.promise;
}
};
}
];

View File

@ -150,7 +150,7 @@ export default ['$injector', '$stateExtender', '$log', 'i18n', function($injecto
url: url,
ncyBreadcrumb: {
[params.parent ? 'parent' : null]: `${params.parent}`,
label: i18n.sprintf(i18n._("CREATE %s"), i18n._(`${form.breadcrumbName || form.name}`))
label: i18n.sprintf(i18n._("CREATE %s"), i18n._(`${form.breadcrumbName || form.name.toUpperCase()}`))
},
views: {
'form': {
@ -376,14 +376,15 @@ export default ['$injector', '$stateExtender', '$log', 'i18n', function($injecto
function buildNotificationState(field) {
let state,
list = field.include ? $injector.get(field.include) : field;
list = field.include ? $injector.get(field.include) : field,
breadcrumbLabel = (field.iterator.replace('_', ' ') + 's').toUpperCase();
state = $stateExtender.buildDefinition({
searchPrefix: `${list.iterator}`,
name: `${formStateDefinition.name}.${list.iterator}s`,
url: `/${list.iterator}s`,
ncyBreadcrumb: {
parent: `${formStateDefinition.name}`,
label: `${field.iterator}s`
label: `${breadcrumbLabel}`
},
params: {
[list.iterator + '_search']: {
@ -571,14 +572,14 @@ export default ['$injector', '$stateExtender', '$log', 'i18n', function($injecto
list = field.include ? $injector.get(field.include) : field,
// Added this line specifically for Completed Jobs but should be OK
// for all the rest of the related tabs
breadcrumbLabel = field.iterator.replace('_', ' '),
breadcrumbLabel = (field.iterator.replace('_', ' ') + 's').toUpperCase(),
stateConfig = {
searchPrefix: `${list.iterator}`,
name: `${formStateDefinition.name}.${list.iterator}s`,
url: `/${list.iterator}s`,
ncyBreadcrumb: {
parent: `${formStateDefinition.name}`,
label: `${breadcrumbLabel}s`
label: `${breadcrumbLabel}`
},
params: {
[list.iterator + '_search']: {

View File

@ -8,12 +8,12 @@
[ '$scope', '$stateParams', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors',
'ClearScope', 'GetBasePath', '$q', 'ParseTypeChange', 'Wait', 'Empty',
'ToJSON', 'initSurvey', '$state', 'CreateSelect2', 'ParseVariableString',
'TemplatesService', 'OrganizationList', 'Rest', 'WorkflowService', 'ToggleNotification',
'TemplatesService', 'OrganizationList', 'Rest', 'WorkflowService', 'ToggleNotification', 'OrgAdminLookup',
function(
$scope, $stateParams, WorkflowForm, GenerateForm, Alert, ProcessErrors,
ClearScope, GetBasePath, $q, ParseTypeChange, Wait, Empty,
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
TemplatesService, OrganizationList, Rest, WorkflowService, ToggleNotification
TemplatesService, OrganizationList, Rest, WorkflowService, ToggleNotification, OrgAdminLookup
) {
ClearScope();
@ -145,6 +145,17 @@
workflowJobTemplateData.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
}
}
if(workflowJobTemplateData.organization) {
OrgAdminLookup.checkForAdminAccess({organization: workflowJobTemplateData.organization})
.then(function(canEditOrg){
$scope.canEditOrg = canEditOrg;
});
}
else {
$scope.canEditOrg = true;
}
Wait('stop');
$scope.url = workflowJobTemplateData.url;
$scope.survey_enabled = workflowJobTemplateData.survey_enabled;