diff --git a/ui/app/components/transform-create-form.js b/ui/app/components/transform-create-form.js index 548e2dd85c..12fadca2e7 100644 --- a/ui/app/components/transform-create-form.js +++ b/ui/app/components/transform-create-form.js @@ -1,3 +1,3 @@ -import TransformBase from './transform-edit-base'; +import TransformationEdit from './transformation-edit'; -export default TransformBase.extend({}); +export default TransformationEdit.extend({}); diff --git a/ui/app/components/transform-edit-base.js b/ui/app/components/transform-edit-base.js index 097c6ce691..17d879da9a 100644 --- a/ui/app/components/transform-edit-base.js +++ b/ui/app/components/transform-edit-base.js @@ -8,7 +8,23 @@ import FocusOnInsertMixin from 'vault/mixins/focus-on-insert'; const LIST_ROOT_ROUTE = 'vault.cluster.secrets.backend.list-root'; const SHOW_ROUTE = 'vault.cluster.secrets.backend.show'; +export const addToList = (list, itemToAdd) => { + if (!list || !Array.isArray(list)) return list; + list.push(itemToAdd); + return list.uniq(); +}; + +export const removeFromList = (list, itemToRemove) => { + if (!list) return list; + const index = list.indexOf(itemToRemove); + if (index < 0) return list; + const newList = list.removeAt(index, 1); + return newList.uniq(); +}; + export default Component.extend(FocusOnInsertMixin, { + store: service(), + flashMessages: service(), router: service(), mode: null, @@ -79,7 +95,7 @@ export default Component.extend(FocusOnInsertMixin, { delete() { this.persist('destroyRecord', () => { - this.hasDataChanges(); + this.onDataChange(); this.transitionToRoute(LIST_ROOT_ROUTE); }); }, diff --git a/ui/app/components/transform-edit-form.js b/ui/app/components/transform-edit-form.js index 548e2dd85c..12fadca2e7 100644 --- a/ui/app/components/transform-edit-form.js +++ b/ui/app/components/transform-edit-form.js @@ -1,3 +1,3 @@ -import TransformBase from './transform-edit-base'; +import TransformationEdit from './transformation-edit'; -export default TransformBase.extend({}); +export default TransformationEdit.extend({}); diff --git a/ui/app/components/transform-role-edit.js b/ui/app/components/transform-role-edit.js index fbf537aecb..187c331196 100644 --- a/ui/app/components/transform-role-edit.js +++ b/ui/app/components/transform-role-edit.js @@ -1,24 +1,6 @@ -import TransformBase from './transform-edit-base'; -import { inject as service } from '@ember/service'; - -const addToList = (list, itemToAdd) => { - if (!list || !Array.isArray(list)) return list; - list.push(itemToAdd); - return list.uniq(); -}; - -const removeFromList = (list, itemToRemove) => { - if (!list) return list; - const index = list.indexOf(itemToRemove); - if (index < 0) return list; - const newList = list.removeAt(index, 1); - return newList.uniq(); -}; +import TransformBase, { addToList, removeFromList } from './transform-edit-base'; export default TransformBase.extend({ - store: service(), - flashMessages: service(), - initialTransformations: null, init() { @@ -48,14 +30,9 @@ export default TransformBase.extend({ allowed_roles: roles, }); - return transformation - .save() - .then(() => { - return 'Successfully saved'; - }) - .catch(e => { - return { errorStatus: e.httpStatus, ...transform }; - }); + return transformation.save().catch(e => { + return { errorStatus: e.httpStatus, ...transform }; + }); }); }); diff --git a/ui/app/components/transformation-edit.js b/ui/app/components/transformation-edit.js index 548e2dd85c..2b18c4fd77 100644 --- a/ui/app/components/transformation-edit.js +++ b/ui/app/components/transformation-edit.js @@ -1,3 +1,112 @@ -import TransformBase from './transform-edit-base'; +import TransformBase, { addToList, removeFromList } from './transform-edit-base'; -export default TransformBase.extend({}); +export default TransformBase.extend({ + initialRoles: null, + + init() { + this._super(...arguments); + this.set('initialRoles', this.get('model.allowed_roles')); + }, + + updateOrCreateRole(role, transformationId, backend) { + return this.store + .queryRecord('transform/role', { + backend, + id: role.id, + }) + .then(roleStore => { + let transformations = roleStore.transformations; + if (role.action === 'ADD') { + transformations = addToList(transformations, transformationId); + } else if (role.action === 'REMOVE') { + transformations = removeFromList(transformations, transformationId); + } + roleStore.setProperties({ + backend, + transformations, + }); + return roleStore.save().catch(e => { + return { + errorStatus: e.httpStatus, + ...role, + }; + }); + }) + .catch(e => { + if (e.httpStatus !== 403 && role.action === 'ADD') { + // If role doesn't yet exist, create it with this transformation attached + var newRole = this.store.createRecord('transform/role', { + id: role.id, + name: role.id, + transformations: [transformationId], + backend, + }); + return newRole.save().catch(e => { + return { + errorStatus: e.httpStatus, + ...role, + action: 'CREATE', + }; + }); + } + + return { + ...role, + errorStatus: e.httpStatus, + }; + }); + }, + + handleUpdateRoles(updateRoles, transformationId) { + if (!updateRoles) return; + const backend = this.get('model.backend'); + const promises = updateRoles.map(r => this.updateOrCreateRole(r, transformationId, backend)); + + Promise.all(promises).then(results => { + let hasError = results.find(role => !!role.errorStatus); + + if (hasError) { + let message = + 'The edits to this transformation were successful, but transformations for its roles was not edited due to a lack of permissions.'; + if (results.find(e => !!e.errorStatus && e.errorStatus !== 403)) { + // if the errors weren't all due to permissions show generic message + // eg. trying to update a role with empty array as transformations + message = `You've edited the allowed_roles for this transformation. However, the corresponding edits to some roles' transformations were not made`; + } + this.get('flashMessages').stickyInfo(message); + } + }); + }, + + actions: { + createOrUpdate(type, event) { + event.preventDefault(); + + this.applyChanges('save', () => { + const transformationId = this.get('model.id'); + const newModelRoles = this.get('model.allowed_roles') || []; + const initialRoles = this.get('initialRoles') || []; + + const updateRoles = [...newModelRoles, ...initialRoles] + .filter(r => r.indexOf('*') < 0) // TODO: expand wildcards into included roles instead + .map(role => { + if (initialRoles.indexOf(role) < 0) { + return { + id: role, + action: 'ADD', + }; + } + if (newModelRoles.indexOf(role) < 0) { + return { + id: role, + action: 'REMOVE', + }; + } + return null; + }) + .filter(r => !!r); + this.handleUpdateRoles(updateRoles, transformationId); + }); + }, + }, +}); diff --git a/ui/app/serializers/transform/role.js b/ui/app/serializers/transform/role.js index 8a3d8f9eb3..ed04af7935 100644 --- a/ui/app/serializers/transform/role.js +++ b/ui/app/serializers/transform/role.js @@ -1,7 +1,6 @@ import ApplicationSerializer from '../application'; export default ApplicationSerializer.extend({ extractLazyPaginatedData(payload) { - // TODO: do this for transform too? let ret; ret = payload.data.keys.map(key => { let model = { diff --git a/ui/app/templates/components/transform-edit-form.hbs b/ui/app/templates/components/transform-edit-form.hbs index 3855980c7f..d2ef34999f 100644 --- a/ui/app/templates/components/transform-edit-form.hbs +++ b/ui/app/templates/components/transform-edit-form.hbs @@ -40,7 +40,7 @@ type="submit" disabled={{buttonDisabled}} class="button is-primary" - data-test-role-ssh-create=true + data-test-transformation-save-button=true > {{#if (eq mode 'create')}} Create transformation diff --git a/ui/app/templates/components/transformation-edit.hbs b/ui/app/templates/components/transformation-edit.hbs index 96f04e5c37..d177363c49 100644 --- a/ui/app/templates/components/transformation-edit.hbs +++ b/ui/app/templates/components/transformation-edit.hbs @@ -62,6 +62,7 @@ Edit transformation @@ -117,7 +118,12 @@