feat: Init credential library creation
This commit is contained in:
16 files changed
+316
-5
No files matched your search
@@ -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;
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
<form.input
|
||||
@name='name'
|
||||
@type='name'
|
||||
@value={{@model.name}}
|
||||
@label={{t 'form.name.label'}}
|
||||
@error={{@model.errors.name}}
|
||||
@helperText={{t 'form.name.help'}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.name}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.textarea
|
||||
@name='description'
|
||||
@type='description'
|
||||
@value={{@model.description}}
|
||||
@label={{t 'form.description.label'}}
|
||||
@error={{@model.errors.description}}
|
||||
@helperText={{t 'form.description.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.description}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
|
||||
<form.input
|
||||
@name='vault_path'
|
||||
@type='text'
|
||||
@value={{@model.attributes_path}}
|
||||
@label={{t 'form.vault_path.label'}}
|
||||
@error={{@model.errors.name}}
|
||||
@helperText={{t 'form.vault_path.help'}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.name}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.radioGroup
|
||||
@name='http_method'
|
||||
@selectedValue={{@model.attributes_http_method}}
|
||||
@changed={{fn (mut @model.attributes_http_method)}}
|
||||
as |radioGroup|
|
||||
>
|
||||
{{#each this.httpMethods as |httpMethod|}}
|
||||
<radioGroup.radio @label={{httpMethod}} @value={{httpMethod}} />
|
||||
{{/each}}
|
||||
</form.radioGroup>
|
||||
|
||||
{{#if this.isHttpRequestBodyAllowed}}
|
||||
<form.textarea
|
||||
@name='http_request_body'
|
||||
@type='text'
|
||||
@value={{@model.attributes_http_request_body}}
|
||||
@label={{t 'form.http_request_body.label'}}
|
||||
@error={{@model.errors.description}}
|
||||
@helperText={{t 'form.http_request_body.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.description}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
{{/if}}
|
||||
|
||||
<form.actions
|
||||
@disabled={{if @model.cannotSave @model.cannotSave}}
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
</Rose::Form>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
}
|
||||
+37
@@ -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();
|
||||
}
|
||||
}
|
||||
+45
@@ -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',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
</page.navigation>
|
||||
|
||||
<page.actions>
|
||||
{{!-- {{outlet 'actions'}} --}}
|
||||
{{outlet 'actions'}}
|
||||
</page.actions>
|
||||
|
||||
<page.body>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<Rose::Dropdown
|
||||
@text='{{t 'actions.manage'}}'
|
||||
@dropdownRight={{true}}
|
||||
as |dropdown|
|
||||
>
|
||||
<dropdown.link
|
||||
@route='scopes.scope.credential-stores.credential-store.credential-libraries.new'
|
||||
@model={{@model}}
|
||||
>
|
||||
{{t 'resources.credential-library.actions.create'}}
|
||||
</dropdown.link>
|
||||
</Rose::Dropdown>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{{page-title (t 'titles.new')}}
|
||||
|
||||
<Form::CredentialLibrary
|
||||
@model={{@model}}
|
||||
@submit={{route-action 'save' @model}}
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
/>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<h2>
|
||||
{{t 'titles.new'}}
|
||||
</h2>
|
||||
@@ -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`<Form::CredentialLibrary::Index />`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<Form::CredentialLibrary::Index>
|
||||
template block text
|
||||
</Form::CredentialLibrary::Index>
|
||||
`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), 'template block text');
|
||||
});
|
||||
}
|
||||
);
|
||||
+16
@@ -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);
|
||||
});
|
||||
}
|
||||
);
|
||||
Reference in new issue
Block a user