Icu 5651 create spk type (#1266)
Add functionality for creating a ssh private key credential and add corresponding acceptance tests. ✅ Closes: ICU-5651 Co-authored-by: Cameron Perera <cameron.perera@hashicorp.com> Co-authored-by: Randall Morey <randall@randallmorey.com> Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
This commit is contained in:
9 files changed
+423
-129
No files matched your search
@@ -26,15 +26,19 @@ export default class CredentialSerializer extends ApplicationSerializer {
|
||||
serializeUsernamePassword() {
|
||||
const serialized = super.serialize(...arguments);
|
||||
// Remove non-username_password type attributes
|
||||
delete serialized['attributes']['private_key'];
|
||||
delete serialized['attributes']['passphrase'];
|
||||
delete serialized.attributes.private_key;
|
||||
delete serialized.attributes.passphrase;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
serializeSSHPrivateKey() {
|
||||
const serialized = super.serialize(...arguments);
|
||||
// Remove non-ssh_private_key type attributes
|
||||
delete serialized['attributes']['password'];
|
||||
delete serialized.attributes.password;
|
||||
if (!serialized?.attributes?.passphrase)
|
||||
delete serialized.attributes.passphrase;
|
||||
if (!serialized?.attributes?.private_key)
|
||||
delete serialized.attributes.private_key;
|
||||
return serialized;
|
||||
}
|
||||
|
||||
@@ -42,7 +46,9 @@ export default class CredentialSerializer extends ApplicationSerializer {
|
||||
const normalizedHash = copy(hash, true);
|
||||
const normalized = super.normalize(typeClass, normalizedHash, ...rest);
|
||||
// Remove passphrase as we don't track it after being created/updated
|
||||
delete normalized['data']['attributes']['passphrase'];
|
||||
normalized.data.attributes.passphrase = '';
|
||||
normalized.data.attributes.password = '';
|
||||
normalized.data.attributes.private_key = '';
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,9 @@ module('Unit | Serializer | credential', function (hooks) {
|
||||
type: 'username_password',
|
||||
username: 'username',
|
||||
version: 1,
|
||||
passphrase: '',
|
||||
private_key: '',
|
||||
password: '',
|
||||
},
|
||||
type: 'credential',
|
||||
id: 'credup_123',
|
||||
@@ -117,6 +120,9 @@ module('Unit | Serializer | credential', function (hooks) {
|
||||
type: 'ssh_private_key',
|
||||
username: 'username',
|
||||
version: 1,
|
||||
passphrase: '',
|
||||
private_key: '',
|
||||
password: '',
|
||||
},
|
||||
type: 'credential',
|
||||
id: 'credspk_123',
|
||||
|
||||
@@ -396,6 +396,20 @@ credential:
|
||||
ssh_private_key: Username & Key Pair
|
||||
help:
|
||||
username_password: This host requires a username and password to connect.
|
||||
ssh_private_key: This host requires a username, public key, and private key to connect.
|
||||
form:
|
||||
username:
|
||||
label: Username
|
||||
help: The username to use when connecting with this credential store.
|
||||
password:
|
||||
label: Password
|
||||
help: The password to use when signing in with this credential store.
|
||||
private_key:
|
||||
label: SSH Private Key
|
||||
help: The private key to use when connecting with this credential store.
|
||||
passphrase:
|
||||
label: Pass phrase
|
||||
help: Optional.
|
||||
scope:
|
||||
title: Scope
|
||||
title_plural: Scopes
|
||||
|
||||
@@ -1,118 +1,9 @@
|
||||
<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'}}
|
||||
disabled={{@model.isSaving}}
|
||||
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.fieldset>
|
||||
<:title>{{t 'form.type.label'}}</:title>
|
||||
<:body>
|
||||
<form.radioGroup
|
||||
@name='types'
|
||||
@selectedValue='username_password'
|
||||
@changed={{@changeType}}
|
||||
as |radioGroup|
|
||||
>
|
||||
<radioGroup.card
|
||||
@label={{t 'resources.credential.types.username_password'}}
|
||||
@helperText={{t 'resources.credential.help.username_password'}}
|
||||
@value='username_password'
|
||||
@layout='wide'
|
||||
readonly={{true}}
|
||||
@disabled={{true}}
|
||||
/>
|
||||
</form.radioGroup>
|
||||
</:body>
|
||||
</form.fieldset>
|
||||
|
||||
<form.input
|
||||
@name='username'
|
||||
@type='username'
|
||||
@value={{@model.username}}
|
||||
@label={{t 'form.username.label'}}
|
||||
@error={{@model.errors.username}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.username}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.username as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
{{#if (or @model.isNew form.isEditable)}}
|
||||
<form.input
|
||||
@name='password'
|
||||
@type='password'
|
||||
@value={{@model.password}}
|
||||
@label={{t 'form.password.label'}}
|
||||
@error={{@model.errors.password}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.password}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.password as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
{{/if}}
|
||||
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@disabled={{if @model.cannotSave @model.cannotSave}}
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
</Rose::Form>
|
||||
{{#if @model.type}}
|
||||
{{component
|
||||
(concat 'form/credential/' @model.type)
|
||||
model=@model
|
||||
submit=@submit
|
||||
cancel=@cancel
|
||||
changeType=@changeType
|
||||
}}
|
||||
{{/if}}
|
||||
@@ -0,0 +1,154 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<form.input
|
||||
@name='name'
|
||||
@value={{@model.name}}
|
||||
@label={{t 'form.name.label'}}
|
||||
@error={{@model.errors.name}}
|
||||
@helperText={{t 'form.name.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
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'
|
||||
@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.fieldset>
|
||||
<:title>{{t 'form.type.label'}}</:title>
|
||||
<:body>
|
||||
{{#if @model.isNew}}
|
||||
<form.radioGroup
|
||||
@name='types'
|
||||
@selectedValue={{@model.type}}
|
||||
@changed={{@changeType}}
|
||||
as |radioGroup|
|
||||
>
|
||||
<radioGroup.card
|
||||
@label={{t 'resources.credential.types.username_password'}}
|
||||
@helperText={{t 'resources.credential.help.username_password'}}
|
||||
@value='username_password'
|
||||
@layout='wide'
|
||||
/>
|
||||
<radioGroup.card
|
||||
@label={{t 'resources.credential.types.ssh_private_key'}}
|
||||
@helperText={{t 'resources.credential.help.ssh_private_key'}}
|
||||
@value='ssh_private_key'
|
||||
@layout='wide'
|
||||
/>
|
||||
</form.radioGroup>
|
||||
{{else}}
|
||||
<form.radioGroup
|
||||
@name='types'
|
||||
@selectedValue={{@model.type}}
|
||||
as |radioGroup|
|
||||
>
|
||||
<radioGroup.card
|
||||
@label={{t (concat 'resources.credential.types.' @model.type)}}
|
||||
@helperText={{t (concat 'resources.credential.help.' @model.type)}}
|
||||
@value={{@model.type}}
|
||||
@layout='wide'
|
||||
@disabled={{true}}
|
||||
readonly={{true}}
|
||||
/>
|
||||
</form.radioGroup>
|
||||
{{/if}}
|
||||
</:body>
|
||||
</form.fieldset>
|
||||
|
||||
<form.input
|
||||
@name='username'
|
||||
@value={{@model.username}}
|
||||
@label={{t 'resources.credential.form.username.label'}}
|
||||
@helperText={{t 'resources.credential.form.username.help'}}
|
||||
@error={{@model.errors.username}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.username}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.username as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
{{#if (or @model.isNew form.isEditable)}}
|
||||
<form.textarea
|
||||
@name='private_key'
|
||||
@value={{@model.private_key}}
|
||||
@label={{t 'resources.credential.form.private_key.label'}}
|
||||
@helperText={{t 'resources.credential.form.private_key.help'}}
|
||||
@error={{@model.errors.private_key}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.private_key}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.private_key as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
<form.textarea
|
||||
@name='passphrase'
|
||||
@value={{@model.passphrase}}
|
||||
@label={{t 'resources.credential.form.passphrase.label'}}
|
||||
@helperText={{t 'resources.credential.form.passphrase.help'}}
|
||||
@error={{@model.errors.passphrase}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.passphrase}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.passphrase as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
{{/if}}
|
||||
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
</Rose::Form>
|
||||
@@ -0,0 +1,136 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<form.input
|
||||
@name='name'
|
||||
@value={{@model.name}}
|
||||
@label={{t 'form.name.label'}}
|
||||
@error={{@model.errors.name}}
|
||||
@helperText={{t 'form.name.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
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'
|
||||
@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.fieldset>
|
||||
<:title>{{t 'form.type.label'}}</:title>
|
||||
<:body>
|
||||
{{#if @model.isNew}}
|
||||
<form.radioGroup
|
||||
@name='types'
|
||||
@selectedValue={{@model.type}}
|
||||
@changed={{@changeType}}
|
||||
as |radioGroup|
|
||||
>
|
||||
<radioGroup.card
|
||||
@label={{t 'resources.credential.types.username_password'}}
|
||||
@helperText={{t 'resources.credential.help.username_password'}}
|
||||
@value='username_password'
|
||||
@layout='wide'
|
||||
/>
|
||||
<radioGroup.card
|
||||
@label={{t 'resources.credential.types.ssh_private_key'}}
|
||||
@helperText={{t 'resources.credential.help.ssh_private_key'}}
|
||||
@value='ssh_private_key'
|
||||
@layout='wide'
|
||||
/>
|
||||
</form.radioGroup>
|
||||
{{else}}
|
||||
<form.radioGroup
|
||||
@name='types'
|
||||
@selectedValue={{@model.type}}
|
||||
as |radioGroup|
|
||||
>
|
||||
<radioGroup.card
|
||||
@label={{t (concat 'resources.credential.types.' @model.type)}}
|
||||
@helperText={{t (concat 'resources.credential.help.' @model.type)}}
|
||||
@value={{@model.type}}
|
||||
@layout='wide'
|
||||
@disabled={{true}}
|
||||
readonly={{true}}
|
||||
/>
|
||||
</form.radioGroup>
|
||||
{{/if}}
|
||||
</:body>
|
||||
</form.fieldset>
|
||||
|
||||
<form.input
|
||||
@name='username'
|
||||
@value={{@model.username}}
|
||||
@label={{t 'resources.credential.form.username.label'}}
|
||||
@helperText={{t 'resources.credential.form.username.help'}}
|
||||
@error={{@model.errors.username}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.username}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.username as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
{{#if (or @model.isNew form.isEditable)}}
|
||||
<form.input
|
||||
@name='password'
|
||||
@value={{@model.password}}
|
||||
@label={{t 'resources.credential.form.password.label'}}
|
||||
@helperText={{t 'resources.credential.form.password.help'}}
|
||||
@error={{@model.errors.password}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.password}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.password as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
{{/if}}
|
||||
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
</Rose::Form>
|
||||
@@ -58,9 +58,6 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialsRoute
|
||||
isNew ? 'notifications.create-success' : 'notifications.save-success'
|
||||
)
|
||||
async save(credential) {
|
||||
//hardcoding the type to its subtype
|
||||
//as the api expects the subtype `username_password`
|
||||
credential.type = 'username_password';
|
||||
await credential.save();
|
||||
if (this.can.can('read model', credential)) {
|
||||
await this.router.transitionTo(
|
||||
|
||||
+10
@@ -19,17 +19,27 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialsNewRou
|
||||
|
||||
/**
|
||||
* Creates a new unsaved credential in current credential store.
|
||||
* Also rollback/destroy any new, unsaved instances from this route before
|
||||
* creating another, but reuse name/description if available.
|
||||
* @return {CredentialModel}
|
||||
*/
|
||||
model({ type = 'username_password' }) {
|
||||
const { id: credential_store_id } = this.modelFor(
|
||||
'scopes.scope.credential-stores.credential-store'
|
||||
);
|
||||
let name, description;
|
||||
if (this.currentModel?.isNew) {
|
||||
({ name, description } = this.currentModel);
|
||||
this.currentModel.rollbackAttributes();
|
||||
}
|
||||
return this.store.createRecord('credential', {
|
||||
type,
|
||||
credential_store_id,
|
||||
name,
|
||||
description,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update type of credential
|
||||
* @param {string} type
|
||||
|
||||
@@ -12,12 +12,15 @@ module(
|
||||
setupMirage(hooks);
|
||||
|
||||
let getCredentialsCount;
|
||||
let getUsernamePasswordCredentialCount;
|
||||
let getUsernameKeyPairCredentialCount;
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
org: null,
|
||||
project: null,
|
||||
},
|
||||
staticCredentialStore: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
@@ -52,19 +55,50 @@ module(
|
||||
getCredentialsCount = () => {
|
||||
return this.server.schema.credentials.all().models.length;
|
||||
};
|
||||
getUsernamePasswordCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({
|
||||
type: 'username_password',
|
||||
}).length;
|
||||
};
|
||||
getUsernameKeyPairCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({ type: 'ssh_private_key' })
|
||||
.length;
|
||||
};
|
||||
authenticateSession({});
|
||||
});
|
||||
|
||||
test('Users can create a new credential', async function (assert) {
|
||||
assert.expect(1);
|
||||
test('Users can create a new username & password credential', async function (assert) {
|
||||
assert.expect(2);
|
||||
const credentialsCount = getCredentialsCount();
|
||||
const usernamePasswordCredentialCount =
|
||||
getUsernamePasswordCredentialCount();
|
||||
await visit(urls.newCredential);
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('[type="submit"]');
|
||||
assert.strictEqual(getCredentialsCount(), credentialsCount + 1);
|
||||
assert.strictEqual(
|
||||
getUsernamePasswordCredentialCount(),
|
||||
usernamePasswordCredentialCount + 1
|
||||
);
|
||||
});
|
||||
|
||||
test('Users can cancel create new credential', async function (assert) {
|
||||
test('Users can create a new username & key pair credential', async function (assert) {
|
||||
assert.expect(2);
|
||||
const credentialsCount = getCredentialsCount();
|
||||
const usernameKeyPairCredentialCount =
|
||||
getUsernameKeyPairCredentialCount();
|
||||
await visit(urls.newCredential);
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('[value="ssh_private_key"]');
|
||||
await click('[type="submit"]');
|
||||
assert.strictEqual(getCredentialsCount(), credentialsCount + 1);
|
||||
assert.strictEqual(
|
||||
getUsernameKeyPairCredentialCount(),
|
||||
usernameKeyPairCredentialCount + 1
|
||||
);
|
||||
});
|
||||
|
||||
test('Users can cancel create new username & password credential', async function (assert) {
|
||||
assert.expect(2);
|
||||
const credentialsCount = getCredentialsCount();
|
||||
await visit(urls.newCredential);
|
||||
@@ -74,6 +108,17 @@ module(
|
||||
assert.strictEqual(getCredentialsCount(), credentialsCount);
|
||||
});
|
||||
|
||||
test('Users can cancel create new username & key pair credential', async function (assert) {
|
||||
assert.expect(2);
|
||||
const credentialsCount = getCredentialsCount();
|
||||
await visit(urls.newCredential);
|
||||
await fillIn('[name="name"]', 'random string');
|
||||
await click('[value="ssh_private_key"]');
|
||||
await click('.rose-form-actions [type="button"]');
|
||||
assert.strictEqual(currentURL(), urls.credentials);
|
||||
assert.strictEqual(getCredentialsCount(), credentialsCount);
|
||||
});
|
||||
|
||||
test('Users cannot navigate to new credential route without proper authorization', async function (assert) {
|
||||
assert.expect(2);
|
||||
instances.staticCredentialStore.authorized_collection_actions.credentials =
|
||||
@@ -87,7 +132,7 @@ module(
|
||||
assert.notOk(find(`[href="${urls.newCredential}"]`));
|
||||
});
|
||||
|
||||
test('saving a new credential with invalid fields displays error messages', async function (assert) {
|
||||
test('saving a new username & password credential with invalid fields displays error messages', async function (assert) {
|
||||
assert.expect(2);
|
||||
this.server.post('/credentials', () => {
|
||||
return new Response(
|
||||
@@ -120,5 +165,40 @@ module(
|
||||
'Field required for creating a username-password credential.'
|
||||
);
|
||||
});
|
||||
|
||||
test('saving a new username & key pair credential with invalid fields displays error messages', async function (assert) {
|
||||
assert.expect(2);
|
||||
this.server.post('/credentials', () => {
|
||||
return new Response(
|
||||
400,
|
||||
{},
|
||||
{
|
||||
status: 400,
|
||||
code: 'invalid_argument',
|
||||
message: 'Error in provided request.',
|
||||
details: {
|
||||
request_fields: [
|
||||
{
|
||||
name: 'attributes.private_key',
|
||||
description:
|
||||
'Field required for creating a username-key-pair credential.',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
await visit(urls.newCredential);
|
||||
await click('[value="ssh_private_key"]');
|
||||
await click('[type="submit"]');
|
||||
assert.ok(
|
||||
find('[role="alert"]').textContent.trim(),
|
||||
'Error in provided request.'
|
||||
);
|
||||
assert.ok(
|
||||
find('.rose-form-error-message').textContent.trim(),
|
||||
'Field required for creating a username-key-pair credential.'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
Reference in new issue
Block a user