From 45fc58fe757b53980848389d3b0bdfb2ec759ec8 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Sun, 18 Jul 2021 14:31:45 -0400 Subject: [PATCH] feat: Init credential library creation --- addons/api/addon/models/credential-library.js | 13 ++- addons/api/mirage/config.js | 10 +- addons/core/translations/form/en-us.yaml | 9 ++ addons/core/translations/resources/en-us.yaml | 2 + .../form/credential-library/index.hbs | 105 ++++++++++++++++++ .../form/credential-library/index.js | 20 ++++ ui/admin/app/router.js | 4 +- .../credential-stores/credential-store.js | 7 +- .../credential-store/credential-libraries.js | 37 ++++++ .../credential-libraries/new.js | 45 ++++++++ .../credential-stores/credential-store.hbs | 2 +- .../credential-store/-actions.hbs | 12 ++ .../credential-libraries/new.hbs | 7 ++ .../credential-libraries/new/-header.hbs | 3 + .../form/credential-library/index-test.js | 29 +++++ .../credential-libraries/new-test.js | 16 +++ 16 files changed, 316 insertions(+), 5 deletions(-) create mode 100644 ui/admin/app/components/form/credential-library/index.hbs create mode 100644 ui/admin/app/components/form/credential-library/index.js create mode 100644 ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js create mode 100644 ui/admin/app/templates/scopes/scope/credential-stores/credential-store/-actions.hbs create mode 100644 ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new.hbs create mode 100644 ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new/-header.hbs create mode 100644 ui/admin/tests/integration/components/form/credential-library/index-test.js create mode 100644 ui/admin/tests/unit/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new-test.js diff --git a/addons/api/addon/models/credential-library.js b/addons/api/addon/models/credential-library.js index c95b9f73e..4ca321a18 100644 --- a/addons/api/addon/models/credential-library.js +++ b/addons/api/addon/models/credential-library.js @@ -2,13 +2,24 @@ import GeneratedCredentialLibraryModel from '../generated/models/credential-libr import { attr } from '@ember-data/model'; import { fragment } from 'ember-data-model-fragments/attributes'; +/** + * Enum options for credential library. + */ +export const options = { + http_methods: ['GET', 'POST'], +}; + export default class CredentialLibraryModel extends GeneratedCredentialLibraryModel { // =error attributes // These attributes exist solely to capture errors on nested fields. // See the application adapter's error normalization method for // more information. - @attr('string', { readOnly: true }) attributes_http_method; + @attr('string', { + readOnly: true, + defaultValue: options.http_methods[0], + }) + attributes_http_method; @attr('string', { readOnly: true }) attributes_http_request_body; @attr('string', { readOnly: true }) attributes_path; diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 1fd364182..a45cd1bab 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -478,7 +478,15 @@ export default function () { ) => credentialLibraries.where({ credentialStoreId }) ); this.get('/credential-libraries/:id'); - this.post('/credential-libraries/:id'); + this.post( + '/credential-libraries', + function ({ credentialStores, credentialLibraries }) { + const attrs = this.normalizedRequestAttrs(); + const credentialStore = credentialStores.find(attrs.credentialStoreId); + attrs.scopeId = credentialStore.scope.id; + return credentialLibraries.create(attrs); + } + ); this.del('/credential-libraries/:id'); this.patch('/credential-libraries/:id'); diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index cb7b69832..b59b55b91 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -115,3 +115,12 @@ value: callback_url: label: Callback URL help: The URL to which the third-party provider sends the authentication response. Paste this into the provider configuration. +vault_path: + label: Vault Path + help: The path in Vault to request credentials from. +http_method: + label: HTTP Method + help: The HTTP method the library uses when requesting credentials from Vault. +http_request_body: + label: HTTP Request Body + help: The body of the HTTP request the library sends to Vault when requesting credentials. diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index cc9fed978..9468e3500 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -275,6 +275,8 @@ credential-library: title: Credential Library title_plural: Credential Libraries description: A credential library is a resource that provides credentials. + actions: + create: New Credential Library messages: none: title: No Credential Libraries Available diff --git a/ui/admin/app/components/form/credential-library/index.hbs b/ui/admin/app/components/form/credential-library/index.hbs new file mode 100644 index 000000000..f5bd6c4eb --- /dev/null +++ b/ui/admin/app/components/form/credential-library/index.hbs @@ -0,0 +1,105 @@ + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#each this.httpMethods as |httpMethod|}} + + {{/each}} + + + {{#if this.isHttpRequestBodyAllowed}} + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + {{/if}} + + + \ No newline at end of file diff --git a/ui/admin/app/components/form/credential-library/index.js b/ui/admin/app/components/form/credential-library/index.js new file mode 100644 index 000000000..69427c13b --- /dev/null +++ b/ui/admin/app/components/form/credential-library/index.js @@ -0,0 +1,20 @@ +import Component from '@glimmer/component'; +import { options } from 'api/models/credential-library'; +import { computed } from '@ember/object'; + +export default class FormCredentialLibraryIndexComponent extends Component { + // =properties + /** + * @type {object} + */ + httpMethods = options.http_methods; + + /** + * Only allow HTTP request body field if http_method is set to POST. + * @type {boolean} + */ + @computed('args.model.attributes_http_method') + get isHttpRequestBodyAllowed() { + return this.args.model.attributes_http_method?.match(/post/i); + } +} diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js index 4012791b2..3c359e268 100644 --- a/ui/admin/app/router.js +++ b/ui/admin/app/router.js @@ -91,7 +91,9 @@ Router.map(function () { 'credential-store', { path: ':credential_store_id' }, function () { - this.route('credential-libraries', function () {}); + this.route('credential-libraries', function () { + this.route('new'); + }); } ); this.route('new'); diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js index 307c11079..fa5b56baf 100644 --- a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js +++ b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js @@ -10,7 +10,7 @@ export default class ScopesScopeCredentialStoresCredentialStoreRoute extends Rou } /** - * Render credential store header and navigation page sections. + * Render credential store header, navigation, and actions page sections. * @override */ renderTemplate() { @@ -25,5 +25,10 @@ export default class ScopesScopeCredentialStoresCredentialStoreRoute extends Rou into: 'scopes/scope/credential-stores/credential-store', outlet: 'navigation', }); + + this.render('scopes/scope/credential-stores/credential-store/-actions', { + into: 'scopes/scope/credential-stores/credential-store', + outlet: 'actions', + }); } } diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries.js b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries.js index 7c8ade755..fa54e9691 100644 --- a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries.js +++ b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries.js @@ -1,4 +1,7 @@ import Route from '@ember/routing/route'; +import { action } from '@ember/object'; +import loading from 'ember-loading/decorator'; +import { notifySuccess, notifyError } from 'core/decorators/notify'; export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesRoute extends Route { // =methods @@ -13,4 +16,38 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari ); return this.store.query('credential-library', { credential_store_id }); } + + // =actions + + /** + * Rollback changes on a credential library. + * @param {CredentialLibraryModel} credentialLibrary + */ + @action + cancel(credentialLibrary) { + const { isNew } = credentialLibrary; + credentialLibrary.rollbackAttributes(); + if (isNew) + this.transitionTo( + 'scopes.scope.credential-stores.credential-store.credential-libraries' + ); + } + + /** + * Handle save of a credential library + * @param {CredentialLibraryModel} credentialLibrary + */ + @action + @loading + @notifyError(({ message }) => message) + @notifySuccess(({ isNew }) => + isNew ? 'notifications.create-success' : 'notifications.save-success' + ) + async save(credentialLibrary) { + await credentialLibrary.save(); + await this.transitionTo( + 'scopes.scope.credential-stores.credential-store.credential-libraries' + ); + this.refresh(); + } } diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js new file mode 100644 index 000000000..d83109dda --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new.js @@ -0,0 +1,45 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesNewRoute extends Route { + // =methods + + /** + * Creates a new unsaved credential library in current credential store. + * @return {CredentialLibraryModel} + */ + model() { + const { id: credential_store_id } = this.modelFor( + 'scopes.scope.credential-stores.credential-store' + ); + return this.store.createRecord('credential-library', { + credential_store_id, + }); + } + + /** + * Renders new credential library specific templates for header, navigation, + * and action page sections. + * @override + */ + renderTemplate() { + super.renderTemplate(...arguments); + + this.render( + 'scopes/scope/credential-stores/credential-store/credential-libraries/new/-header', + { + into: 'scopes/scope/credential-stores/credential-store', + outlet: 'header', + } + ); + + this.render('_empty', { + into: 'scopes/scope/credential-stores/credential-store', + outlet: 'navigation', + }); + + this.render('_empty', { + into: 'scopes/scope/credential-stores/credential-store', + outlet: 'actions', + }); + } +} diff --git a/ui/admin/app/templates/scopes/scope/credential-stores/credential-store.hbs b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store.hbs index a6ecd7f81..efc46641a 100644 --- a/ui/admin/app/templates/scopes/scope/credential-stores/credential-store.hbs +++ b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store.hbs @@ -15,7 +15,7 @@ - {{!-- {{outlet 'actions'}} --}} + {{outlet 'actions'}} diff --git a/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/-actions.hbs b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/-actions.hbs new file mode 100644 index 000000000..587d095db --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/-actions.hbs @@ -0,0 +1,12 @@ + + + {{t 'resources.credential-library.actions.create'}} + + \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new.hbs b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new.hbs new file mode 100644 index 000000000..5d03e5b9f --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new.hbs @@ -0,0 +1,7 @@ +{{page-title (t 'titles.new')}} + + \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new/-header.hbs b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new/-header.hbs new file mode 100644 index 000000000..51755b136 --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store/credential-libraries/new/-header.hbs @@ -0,0 +1,3 @@ +

+ {{t 'titles.new'}} +

\ No newline at end of file diff --git a/ui/admin/tests/integration/components/form/credential-library/index-test.js b/ui/admin/tests/integration/components/form/credential-library/index-test.js new file mode 100644 index 000000000..e0d24a8c2 --- /dev/null +++ b/ui/admin/tests/integration/components/form/credential-library/index-test.js @@ -0,0 +1,29 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module( + 'Integration | Component | form/credential-library/index', + function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.equal(this.element.textContent.trim(), ''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.equal(this.element.textContent.trim(), 'template block text'); + }); + } +); diff --git a/ui/admin/tests/unit/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new-test.js b/ui/admin/tests/unit/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new-test.js new file mode 100644 index 000000000..3c4efdd66 --- /dev/null +++ b/ui/admin/tests/unit/routes/scopes/scope/credential-stores/credential-store/credential-libraries/new-test.js @@ -0,0 +1,16 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Route | scopes/scope/credential-stores/credential-store/credential-libraries/new', + function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:scopes/scope/credential-stores/credential-store/credential-libraries/new' + ); + assert.ok(route); + }); + } +);