From ec4877f10b14990677318d7baf849aac30d4d87b Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Thu, 24 Aug 2017 17:26:56 -0400 Subject: [PATCH 1/2] adding org-admin check for projects and teams --- awx/ui/client/features/credentials/index.js | 11 ++++++++--- awx/ui/client/lib/models/OrgAdmin.js | 19 +++++++++++++++++++ awx/ui/client/lib/models/index.js | 5 +++-- .../projects/edit/projects-edit.controller.js | 10 ++++++++-- .../src/teams/edit/teams-edit.controller.js | 9 +++++++-- 5 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 awx/ui/client/lib/models/OrgAdmin.js diff --git a/awx/ui/client/features/credentials/index.js b/awx/ui/client/features/credentials/index.js index c11dc3e1d6..d24e5a0fba 100644 --- a/awx/ui/client/features/credentials/index.js +++ b/awx/ui/client/features/credentials/index.js @@ -3,7 +3,7 @@ import AddController from './add-credentials.controller'; import EditController from './edit-credentials.controller'; import CredentialsStrings from './credentials.strings' -function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, Organization) { +function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, Organization, OrgAdmin) { let id = $stateParams.credential_id; let promises = { @@ -14,6 +14,7 @@ function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, O promises.credential = new Credential('options'); promises.credentialType = new CredentialType(); promises.organization = new Organization(); + promises.orgAdmin = new OrgAdmin(); return $q.all(promises) } @@ -24,16 +25,19 @@ function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, O .then(models => { let typeId = models.credential.get('credential_type'); let orgId = models.credential.get('organization'); + let userId = models.me.get('results')[0].id; let dependents = { credentialType: new CredentialType('get', typeId), - organization: new Organization('get', orgId) + organization: new Organization('get', orgId), + orgAdmin: new OrgAdmin('get', userId) }; return $q.all(dependents) .then(related => { models.credentialType = related.credentialType; models.organization = related.organization; + models.is_org_admin = related.orgAdmin; return models; }); @@ -46,7 +50,8 @@ CredentialsResolve.$inject = [ 'MeModel', 'CredentialModel', 'CredentialTypeModel', - 'OrganizationModel' + 'OrganizationModel', + 'OrgAdminModel' ]; function CredentialsConfig ($stateExtenderProvider, legacyProvider, pathProvider, stringProvider) { diff --git a/awx/ui/client/lib/models/OrgAdmin.js b/awx/ui/client/lib/models/OrgAdmin.js new file mode 100644 index 0000000000..1f6dad80fe --- /dev/null +++ b/awx/ui/client/lib/models/OrgAdmin.js @@ -0,0 +1,19 @@ +let BaseModel; + +function OrgAdminModel (method, resource, graft) { + BaseModel.call(this, {path: 'users', subPath: 'admin_of_organizations'}); + + this.Constructor = OrgAdminModel; + + return this.create(method, resource, graft); +} + +function OrgAdminModelLoader (_BaseModel_) { + BaseModel = _BaseModel_; + + return OrgAdminModel; +} + +OrgAdminModelLoader.$inject = ['BaseModel']; + +export default OrgAdminModelLoader; diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index 0734340dae..32715697b5 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -4,6 +4,7 @@ import Credential from './Credential'; import CredentialType from './CredentialType'; import Me from './Me'; import Organization from './Organization'; +import OrgAdmin from './OrgAdmin'; angular .module('at.lib.models', []) @@ -12,5 +13,5 @@ angular .service('CredentialModel', Credential) .service('CredentialTypeModel', CredentialType) .service('MeModel', Me) - .service('OrganizationModel', Organization); - + .service('OrganizationModel', Organization) + .service('OrgAdminModel', OrgAdmin); 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 1ba9315dcc..4ac43a8160 100644 --- a/awx/ui/client/src/projects/edit/projects-edit.controller.js +++ b/awx/ui/client/src/projects/edit/projects-edit.controller.js @@ -8,11 +8,12 @@ export default ['$scope', '$rootScope', '$stateParams', 'ProjectsForm', 'Rest', 'Alert', 'ProcessErrors', 'GenerateForm', 'Prompt', 'GetBasePath', 'GetProjectPath', 'Authorization', 'GetChoices', 'Empty', 'Wait', 'ProjectUpdate', '$state', 'CreateSelect2', 'ToggleNotification', - 'i18n', 'CredentialTypes', + 'i18n', 'CredentialTypes', 'OrgAdminLookup', function($scope, $rootScope, $stateParams, ProjectsForm, Rest, Alert, ProcessErrors, GenerateForm, Prompt, GetBasePath, GetProjectPath, Authorization, GetChoices, Empty, Wait, ProjectUpdate, - $state, CreateSelect2, ToggleNotification, i18n, CredentialTypes) { + $state, CreateSelect2, ToggleNotification, i18n, CredentialTypes, + OrgAdminLookup) { var form = ProjectsForm(), defaultUrl = GetBasePath('projects') + $stateParams.project_id + '/', @@ -141,6 +142,11 @@ export default ['$scope', '$rootScope', '$stateParams', 'ProjectsForm', 'Rest', $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'); diff --git a/awx/ui/client/src/teams/edit/teams-edit.controller.js b/awx/ui/client/src/teams/edit/teams-edit.controller.js index d8b557817c..77166dfad5 100644 --- a/awx/ui/client/src/teams/edit/teams-edit.controller.js +++ b/awx/ui/client/src/teams/edit/teams-edit.controller.js @@ -5,9 +5,9 @@ *************************************************/ export default ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest', - 'ProcessErrors', 'GetBasePath', 'Wait', '$state', + 'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'OrgAdminLookup', function($scope, $rootScope, $stateParams, TeamForm, Rest, ProcessErrors, - GetBasePath, Wait, $state) { + GetBasePath, Wait, $state, OrgAdminLookup) { var form = TeamForm, id = $stateParams.team_id, @@ -23,6 +23,11 @@ export default ['$scope', '$rootScope', '$stateParams', 'TeamForm', 'Rest', 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'); }); From 39a4dee84f142c710df3649f35da0e60bc0822dc Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Wed, 30 Aug 2017 16:23:29 -0700 Subject: [PATCH 2/2] adding model for admin of orgs and disabling credential's org field when appropriate --- .../credentials/add-credentials.controller.js | 4 +-- .../edit-credentials.controller.js | 14 +++++++-- awx/ui/client/features/credentials/index.js | 15 ++++------ awx/ui/client/lib/models/Base.js | 29 ++++++++++++++++++- awx/ui/client/lib/models/Me.js | 15 ++++++---- awx/ui/client/lib/models/OrgAdmin.js | 19 ------------ awx/ui/client/lib/models/index.js | 4 +-- 7 files changed, 57 insertions(+), 43 deletions(-) delete mode 100644 awx/ui/client/lib/models/OrgAdmin.js diff --git a/awx/ui/client/features/credentials/add-credentials.controller.js b/awx/ui/client/features/credentials/add-credentials.controller.js index c991f62b0f..193c2cbb5b 100644 --- a/awx/ui/client/features/credentials/add-credentials.controller.js +++ b/awx/ui/client/features/credentials/add-credentials.controller.js @@ -43,8 +43,8 @@ function AddCredentialsController (models, $state, strings) { }; vm.form.save = data => { - data.user = me.getSelf().id; - + data.user = me.get('id'); + return credential.request('post', data); }; diff --git a/awx/ui/client/features/credentials/edit-credentials.controller.js b/awx/ui/client/features/credentials/edit-credentials.controller.js index 92a3dc357c..177dab5239 100644 --- a/awx/ui/client/features/credentials/edit-credentials.controller.js +++ b/awx/ui/client/features/credentials/edit-credentials.controller.js @@ -45,6 +45,14 @@ function EditCredentialsController (models, $state, $scope, strings) { vm.form.disabled = !isEditable; } + let isOrgAdmin = _.some(me.get('related.admin_of_organizations.results'), (org) => {return org.id === organization.get('id');}); + let isSuperuser = me.get('is_superuser'); + let isCurrentAuthor = Boolean(credential.get('summary_fields.created_by.id') === me.get('id')); + vm.form.organization._disabled = true; + if(isSuperuser || isOrgAdmin || (credential.get('organization') === null && isCurrentAuthor)){ + vm.form.organization._disabled = false; + } + vm.form.organization._resource = 'organization'; vm.form.organization._model = organization; vm.form.organization._route = 'credentials.edit.organization'; @@ -75,12 +83,12 @@ function EditCredentialsController (models, $state, $scope, strings) { }; /** - * If a credential's `credential_type` is changed while editing, the inputs associated with - * the old type need to be cleared before saving the inputs associated with the new type. + * If a credential's `credential_type` is changed while editing, the inputs associated with + * the old type need to be cleared before saving the inputs associated with the new type. * Otherwise inputs are merged together making the request invalid. */ vm.form.save = data => { - data.user = me.getSelf().id; + data.user = me.get('id'); credential.unset('inputs'); return credential.request('put', data); diff --git a/awx/ui/client/features/credentials/index.js b/awx/ui/client/features/credentials/index.js index d24e5a0fba..391e5acbb6 100644 --- a/awx/ui/client/features/credentials/index.js +++ b/awx/ui/client/features/credentials/index.js @@ -3,18 +3,19 @@ import AddController from './add-credentials.controller'; import EditController from './edit-credentials.controller'; import CredentialsStrings from './credentials.strings' -function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, Organization, OrgAdmin) { +function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, Organization) { let id = $stateParams.credential_id; let promises = { - me: new Me('get') + me: new Me('get').then((me) => { + return me.extend('get', 'admin_of_organizations'); + }) }; if (!id) { promises.credential = new Credential('options'); promises.credentialType = new CredentialType(); promises.organization = new Organization(); - promises.orgAdmin = new OrgAdmin(); return $q.all(promises) } @@ -25,19 +26,16 @@ function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, O .then(models => { let typeId = models.credential.get('credential_type'); let orgId = models.credential.get('organization'); - let userId = models.me.get('results')[0].id; let dependents = { credentialType: new CredentialType('get', typeId), - organization: new Organization('get', orgId), - orgAdmin: new OrgAdmin('get', userId) + organization: new Organization('get', orgId) }; return $q.all(dependents) .then(related => { models.credentialType = related.credentialType; models.organization = related.organization; - models.is_org_admin = related.orgAdmin; return models; }); @@ -50,8 +48,7 @@ CredentialsResolve.$inject = [ 'MeModel', 'CredentialModel', 'CredentialTypeModel', - 'OrganizationModel', - 'OrgAdminModel' + 'OrganizationModel' ]; function CredentialsConfig ($stateExtenderProvider, legacyProvider, pathProvider, stringProvider) { diff --git a/awx/ui/client/lib/models/Base.js b/awx/ui/client/lib/models/Base.js index db2851bc53..a1c60e1bd9 100644 --- a/awx/ui/client/lib/models/Base.js +++ b/awx/ui/client/lib/models/Base.js @@ -277,9 +277,35 @@ function has (method, keys) { return value !== undefined && value !== null; } +function extend (method, related) { + if (!related) { + related = method + method = 'GET' + } else { + method = method.toUpperCase() + } + + if (this.has(method, `related.${related}`)) { + let id = this.get('id') + + let req = { + method, + url: this.get(`related.${related}`) + }; + + return $http(req) + .then(({data}) => { + this.set(method, `related.${related}`, data); + return this; + }) + } + + return Promise.reject(new Error(`No related property, ${related}, exists`)); +} + function normalizePath (resource) { let version = '/api/v2/'; - + return `${version}${resource}/`; } @@ -383,6 +409,7 @@ function BaseModel (path, settings) { this.search = search; this.set = set; this.unset = unset; + this.extend = extend; this.http = { get: httpGet.bind(this), diff --git a/awx/ui/client/lib/models/Me.js b/awx/ui/client/lib/models/Me.js index 9393132e8e..a606db9731 100644 --- a/awx/ui/client/lib/models/Me.js +++ b/awx/ui/client/lib/models/Me.js @@ -1,16 +1,19 @@ let BaseModel; -function getSelf () { - return this.get('results[0]'); -} - function MeModel (method, resource, graft) { BaseModel.call(this, 'me'); this.Constructor = MeModel; - this.getSelf = getSelf.bind(this); - return this.create(method, resource, graft); + return this.create(method, resource, graft) + .then(() => { + if (this.has('results')) { + _.merge(this.model.GET, this.get('results[0]')); + this.unset('results'); + } + + return this; + }); } function MeModelLoader (_BaseModel_) { diff --git a/awx/ui/client/lib/models/OrgAdmin.js b/awx/ui/client/lib/models/OrgAdmin.js deleted file mode 100644 index 1f6dad80fe..0000000000 --- a/awx/ui/client/lib/models/OrgAdmin.js +++ /dev/null @@ -1,19 +0,0 @@ -let BaseModel; - -function OrgAdminModel (method, resource, graft) { - BaseModel.call(this, {path: 'users', subPath: 'admin_of_organizations'}); - - this.Constructor = OrgAdminModel; - - return this.create(method, resource, graft); -} - -function OrgAdminModelLoader (_BaseModel_) { - BaseModel = _BaseModel_; - - return OrgAdminModel; -} - -OrgAdminModelLoader.$inject = ['BaseModel']; - -export default OrgAdminModelLoader; diff --git a/awx/ui/client/lib/models/index.js b/awx/ui/client/lib/models/index.js index 32715697b5..c40f9e156d 100644 --- a/awx/ui/client/lib/models/index.js +++ b/awx/ui/client/lib/models/index.js @@ -4,7 +4,6 @@ import Credential from './Credential'; import CredentialType from './CredentialType'; import Me from './Me'; import Organization from './Organization'; -import OrgAdmin from './OrgAdmin'; angular .module('at.lib.models', []) @@ -13,5 +12,4 @@ angular .service('CredentialModel', Credential) .service('CredentialTypeModel', CredentialType) .service('MeModel', Me) - .service('OrganizationModel', Organization) - .service('OrgAdminModel', OrgAdmin); + .service('OrganizationModel', Organization);