Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca56c37fb0 | ||
|
|
5e2d9e9e2b | ||
|
|
d0ca5cc041 | ||
|
|
310539e2de | ||
|
|
9da86f8411 | ||
|
|
76e721bd91 | ||
|
|
a19aa2ff0c | ||
|
|
cdecf570e2 | ||
|
|
c04c8f4f0a | ||
|
|
8fb33b8ff5 | ||
|
|
e89f31a93b | ||
|
|
518bf19318 | ||
|
|
d35823fae6 | ||
|
|
0231dabbe6 | ||
|
|
263d2b8d95 | ||
|
|
98537f708c | ||
|
|
5596d55035 | ||
|
|
a8f1780731 | ||
|
|
8e6988c179 | ||
|
|
ea293d947f | ||
|
|
d23afb1c71 | ||
|
|
b8ec749d03 | ||
|
|
0c5c9387b5 | ||
|
|
bbbd5cea65 | ||
|
|
66888a4c95 | ||
|
|
ca35246a7b | ||
|
|
16fe712f4e | ||
|
|
296b5fbecd | ||
|
|
4240bbee56 | ||
|
|
c3a9ecb0ec | ||
|
|
e40ff1f63c | ||
|
|
87479b8e3c | ||
|
|
264f81a6f3 | ||
|
|
772c8b38c0 | ||
|
|
03aa4f32f1 | ||
|
|
0b145fac47 | ||
|
|
9bd7193833 | ||
|
|
f3a4017e5f | ||
|
|
43eca819bf | ||
|
|
9aa3f8f5ad | ||
|
|
8cceb4090f | ||
|
|
dc7ebec668 | ||
|
|
cffa419dd6 | ||
|
|
83a938fada | ||
|
|
61c80518de | ||
|
|
d1119efd11 | ||
|
|
a9cb6f35f9 | ||
|
|
6f85419675 | ||
|
|
0cb90353f0 | ||
|
|
cd9f05962c | ||
|
|
9d342cf517 | ||
|
|
88fc063ab9 | ||
|
|
d3e707d740 | ||
|
|
fc4af516a2 | ||
|
|
edb8836a2c | ||
|
|
b29f877e35 | ||
|
|
e299740951 | ||
|
|
03fbc19c2c | ||
|
|
e42647e6a2 | ||
|
|
a03a3811d4 | ||
|
|
d4ef4bc58c | ||
|
|
3d7166d059 | ||
|
|
025ae1bfe2 | ||
|
|
0bc4cd6e21 | ||
|
|
4a6555317a | ||
|
|
be7e3bb56e | ||
|
|
ed0c774714 | ||
|
|
2bdb008f65 | ||
|
|
8109d7789c | ||
|
|
3eb9f07fe0 | ||
|
|
a07cd9e1a1 | ||
|
|
bbbb1620f6 | ||
|
|
1d71e3bec8 | ||
|
|
63d302d716 | ||
|
|
341baa76fc | ||
|
|
cc6f0aa1b4 | ||
|
|
1fb98f8ed3 | ||
|
|
08543e9868 | ||
|
|
0fbcf8e16e | ||
|
|
7ff569779c | ||
|
|
8f2deadc49 | ||
|
|
89b7d0345a | ||
|
|
ad3b9ed684 | ||
|
|
a7a84d64b9 | ||
|
|
7fd89bb946 | ||
|
|
dbfc21ace3 | ||
|
|
7bd8d87ad2 | ||
|
|
f5b80f93d2 | ||
|
|
1e5d52410e | ||
|
|
0dedbfb411 | ||
|
|
992df6b34b | ||
|
|
af7e539f3d | ||
|
|
83f9ab5c61 | ||
|
|
a9c06638c7 | ||
|
|
0637d805d3 | ||
|
|
f089200dc0 | ||
|
|
2726538c41 | ||
|
|
b6291bd3c7 | ||
|
|
0e89ecabdc | ||
|
|
debf6fbe06 | ||
|
|
517ba08f55 | ||
|
|
7f13365336 | ||
|
|
8ebdd995e3 | ||
|
|
e73cd828a5 | ||
|
|
d2a42f7468 | ||
|
|
b1614dc65d | ||
|
|
9931b8d3ca | ||
|
|
b7e5477b3d | ||
|
|
027371567b | ||
|
|
95612186d8 | ||
|
|
f7dd5ebe55 | ||
|
|
a6be658aa5 | ||
|
|
123476a3a8 | ||
|
|
d44528fc2c | ||
|
|
b2c85b9805 | ||
|
|
03ce6be3ee | ||
|
|
a35c4e0ffd | ||
|
|
710bcccfd5 | ||
|
|
8b9d945efb | ||
|
|
aa2114062b | ||
|
|
90055ea142 | ||
|
|
e1aa0c6bcb | ||
|
|
af337626f5 | ||
|
|
dad2d456d0 | ||
|
|
9dac6fc446 | ||
|
|
00348e4c55 | ||
|
|
b0dd9bf803 | ||
|
|
2e23bbea3e | ||
|
|
7e7bad050f | ||
|
|
afadb1d83a | ||
|
|
52c9fb5570 | ||
|
|
2dd3e13cd3 | ||
|
|
74fa0876fb | ||
|
|
c531553c07 | ||
|
|
0ad6d027e6 | ||
|
|
2eb7084643 | ||
|
|
06d162ae89 | ||
|
|
c43ced0380 | ||
|
|
28a4a7304e | ||
|
|
e74fd0202e | ||
|
|
0823c1cb2d | ||
|
|
d034631e4d | ||
|
|
c5af15f4c1 | ||
|
|
fce79d46d7 | ||
|
|
c45357bf25 | ||
|
|
d96d7425f8 | ||
|
|
2000c10006 | ||
|
|
a348e4830a | ||
|
|
7c496b5b90 | ||
|
|
5d6da1e52d | ||
|
|
2eb1d01622 | ||
|
|
f012d10dcb | ||
|
|
a96689e2cc | ||
|
|
c5f72c53d8 | ||
|
|
699d5063d9 | ||
|
|
08deda59aa | ||
|
|
f8cce58d49 | ||
|
|
f69477e556 | ||
|
|
7f5a792af4 | ||
|
|
dd96423ead | ||
|
|
a1117529e5 | ||
|
|
44510ea8d4 | ||
|
|
9c93ff6c13 | ||
|
|
59818fa24d | ||
|
|
98e14a642f | ||
|
|
45fc58fe75 | ||
|
|
6dad1cd276 | ||
|
|
2212c7dd9e | ||
|
|
98bc86497a | ||
|
|
f447fb11ef | ||
|
|
e091730ffb | ||
|
|
3e479fc02a | ||
|
|
ce2be6b4b3 | ||
|
|
816b50eb1b | ||
|
|
9067558082 | ||
|
|
d6129a7dfc | ||
|
|
3b4bb24eb7 | ||
|
|
2217482329 | ||
|
|
32bdf2acdc | ||
|
|
03187e2e2e | ||
|
|
cf72af4004 | ||
|
|
e4a0150ee7 | ||
|
|
1a46a6b183 | ||
|
|
cf377ad3dc | ||
|
|
c82a8be1ed | ||
|
|
b9c5ed0de3 | ||
|
|
885ce5ebf5 | ||
|
|
9f7d528582 | ||
|
|
8544d61e9c | ||
|
|
a1b3e5382b | ||
|
|
1fb41665e5 | ||
|
|
bd50c4f78c | ||
|
|
0f73cd5faf | ||
|
|
96de8f262f | ||
|
|
4a871134a5 | ||
|
|
9bcb402fa2 | ||
|
|
bb1580ab6e | ||
|
|
75f45a134d | ||
|
|
b272f85e68 | ||
|
|
fdb46df9f2 | ||
|
|
f39ac156cb | ||
|
|
986cde5941 | ||
|
|
c94f54c90f | ||
|
|
4c648ad4be | ||
|
|
cd10c30ca8 | ||
|
|
d964411f70 | ||
|
|
9d68eb495e | ||
|
|
ef80dd87c1 | ||
|
|
a59e012f52 | ||
|
|
580a2f4159 | ||
|
|
b0d999cf3c | ||
|
|
97b82818cf | ||
|
|
4f0f8fce26 | ||
|
|
2b7563ee7b | ||
|
|
d8f43531e1 | ||
|
|
0404fe308f | ||
|
|
d3bdab9014 | ||
|
|
4b41c8dc7a | ||
|
|
12913e70ce | ||
|
|
7630f454fd | ||
|
|
9a2e5d23b9 | ||
|
|
a0f371bfc5 | ||
|
|
9100fe9a34 | ||
|
|
87b76a91c4 | ||
|
|
aa4a487e97 | ||
|
|
94cfca561c | ||
|
|
58f46e9334 | ||
|
|
5519cf8557 | ||
|
|
371b89b5ad | ||
|
|
07d2667d14 | ||
|
|
39d19cab06 | ||
|
|
d7659bd9de | ||
|
|
e855697601 | ||
|
|
e5c4ac650e | ||
|
|
688b6652d1 |
No files matched your search
@@ -22,6 +22,7 @@ testem.log
|
||||
yarn-error.log
|
||||
storybook-static
|
||||
**/.storybook/*.html
|
||||
.watchman-cookie*
|
||||
|
||||
# ember-try
|
||||
.node_modules.ember-try
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import BaseModel from '../../models/base';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
/**
|
||||
* A credential library is a resource that can generate and retrieve
|
||||
* credentials.
|
||||
*/
|
||||
export default class GeneratedCredentialLibraryModel extends BaseModel {
|
||||
// =attributes
|
||||
|
||||
@attr('string', {
|
||||
description: 'The type of the resource, to help differentiate schemas',
|
||||
})
|
||||
type;
|
||||
|
||||
@attr('string', {
|
||||
description: 'The owning credential store ID.',
|
||||
})
|
||||
credential_store_id;
|
||||
|
||||
@attr('string', {
|
||||
description: 'Optional name for identification purposes',
|
||||
})
|
||||
name;
|
||||
|
||||
@attr('string', {
|
||||
description: 'Optional user-set description for identification purposes',
|
||||
})
|
||||
description;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was created\nOutput only.',
|
||||
readOnly: true,
|
||||
})
|
||||
created_time;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was last updated\nOutput only.',
|
||||
readOnly: true,
|
||||
})
|
||||
updated_time;
|
||||
|
||||
@attr('number', {
|
||||
description: 'Current version number of this resource.',
|
||||
})
|
||||
version;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import BaseModel from '../../models/base';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
/**
|
||||
* A credential store is a resource that can retrieve, store, and potentially
|
||||
* generate credentials.
|
||||
*/
|
||||
export default class GeneratedCredentialStoreModel extends BaseModel {
|
||||
// =attributes
|
||||
|
||||
@attr('string', {
|
||||
description: 'The type of the resource, to help differentiate schemas',
|
||||
})
|
||||
type;
|
||||
|
||||
@attr('string', {
|
||||
description: 'Optional name for identification purposes',
|
||||
})
|
||||
name;
|
||||
|
||||
@attr('string', {
|
||||
description: 'Optional user-set description for identification purposes',
|
||||
})
|
||||
description;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was created\nOutput only.',
|
||||
readOnly: true,
|
||||
})
|
||||
created_time;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was last updated\nOutput only.',
|
||||
readOnly: true,
|
||||
})
|
||||
updated_time;
|
||||
|
||||
@attr('number', {
|
||||
description: 'Current version number of this resource.',
|
||||
})
|
||||
version;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import GeneratedCredentialLibraryModel from '../generated/models/credential-library';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { fragment } from 'ember-data-model-fragments/attributes';
|
||||
|
||||
/**
|
||||
* Enum options for credential library.
|
||||
*/
|
||||
export const options = {
|
||||
http_method: ['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 }) attributes_http_request_body;
|
||||
@attr('string', { readOnly: true }) attributes_path;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Attributes of this resource, if any, represented as a JSON fragment.
|
||||
* @type {FragmentCredentialLibraryAttributesModel}
|
||||
*/
|
||||
@fragment('fragment-credential-library-attributes', { defaultValue: {} })
|
||||
attributes;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import GeneratedCredentialStoreModel from '../generated/models/credential-store';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { fragment } from 'ember-data-model-fragments/attributes';
|
||||
import { equal } from '@ember/object/computed';
|
||||
|
||||
export default class CredentialStoreModel extends GeneratedCredentialStoreModel {
|
||||
// =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_address;
|
||||
@attr('string', { readOnly: true }) attributes_namespace;
|
||||
@attr('string', { readOnly: true }) attributes_ca_cert;
|
||||
@attr('string', { readOnly: true }) attributes_tls_server_name;
|
||||
@attr('string', { readOnly: true }) attributes_tls_skip_verify;
|
||||
@attr('string', { readOnly: true }) attributes_token;
|
||||
@attr('string', { readOnly: true }) attributes_client_certificate;
|
||||
@attr('string', { readOnly: true }) attributes_client_certificate_key;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Attributes of this resource, if any, represented as a JSON fragment.
|
||||
* @type {FragmentAuthMethodAttributesModel}
|
||||
*/
|
||||
@fragment('fragment-credential-store-attributes', { defaultValue: {} })
|
||||
attributes;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
@equal('type', 'vault') isVault;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
import { options } from './credential-library';
|
||||
|
||||
export default class FragmentCredentialLibraryAttributesModel extends Fragment {
|
||||
//= attributes (vault)
|
||||
|
||||
@attr('string', {
|
||||
description:
|
||||
'The HTTP method the library uses when requesting credentials from Vault.',
|
||||
defaultValue: () => options.http_method[0],
|
||||
})
|
||||
http_method;
|
||||
|
||||
@attr('string', {
|
||||
description:
|
||||
'The body of the HTTP request the library sends to Vault when requesting credentials. Only valid if `http_method` is set to `POST`.',
|
||||
})
|
||||
http_request_body;
|
||||
|
||||
@attr('string', {
|
||||
description: 'The path in Vault to request credentials from.',
|
||||
})
|
||||
path;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
export default class FragmentCredentialStoreAttributesModel extends Fragment {
|
||||
// =attributes (vault)
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description:
|
||||
"The address to Vault server. This should be a complete URL such as 'https://127.0.0.1:8200'",
|
||||
})
|
||||
address;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description: 'The namespace within Vault to use.',
|
||||
})
|
||||
namespace;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description:
|
||||
"A PEM-encoded CA certificate to verify the Vault server's TLS certificate.",
|
||||
})
|
||||
ca_cert;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description:
|
||||
'Name to use as the SNI host when connecting to Vault via TLS.',
|
||||
})
|
||||
tls_server_name;
|
||||
|
||||
@attr('boolean', {
|
||||
for: 'vault',
|
||||
description: 'Whether or not to skip TLS verification.',
|
||||
})
|
||||
tls_skip_verify;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description: 'A token used for accessing Vault.',
|
||||
})
|
||||
token;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description: 'The Vault token hmac.',
|
||||
readOnly: true,
|
||||
})
|
||||
token_hmac;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description:
|
||||
'A PEM-encoded client certificate to use for TLS authentication to the Vault server.',
|
||||
})
|
||||
client_certificate;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description:
|
||||
"A PEM-encoded private key matching the client certificate from 'client_certificate'.",
|
||||
})
|
||||
client_certificate_key;
|
||||
|
||||
@attr('string', {
|
||||
for: 'vault',
|
||||
description: 'The Vault client certificate key hmac.',
|
||||
readOnly: true,
|
||||
})
|
||||
client_certificate_key_hmac;
|
||||
}
|
||||
@@ -20,6 +20,16 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
})
|
||||
host_sets;
|
||||
|
||||
/**
|
||||
* Credential library ids are read only and can be
|
||||
* persisted via a dedicated call to `addCredentialLibraries()`.
|
||||
*/
|
||||
@fragmentArray('fragment-string', {
|
||||
readOnly: true,
|
||||
emptyArrayIfMissing: true,
|
||||
})
|
||||
application_credential_library_ids;
|
||||
|
||||
/**
|
||||
* An array of resolved host set and host catalog instances. Model instances
|
||||
* must already be loaded into the store (this method will not load unloaded
|
||||
@@ -36,6 +46,24 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
.filter((hostSetRef) => hostSetRef.model !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of resolved credential library instances. Model instances
|
||||
* must already be loaded into the store (this method will not load unloaded
|
||||
* instances). Unresolvable instances are excluded from the array.
|
||||
* @type {[CredentialLibraryModel]}
|
||||
*/
|
||||
@computed('application_credential_library_ids.[]', 'store')
|
||||
get credentialLibraries() {
|
||||
return this.application_credential_library_ids
|
||||
.map((credential_library_fragment) =>
|
||||
this.store.peekRecord(
|
||||
'credential-library',
|
||||
credential_library_fragment.value
|
||||
)
|
||||
)
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sessions associated with this target (but only already loaded sessions).
|
||||
* @type {SessionModel[]}
|
||||
@@ -122,4 +150,66 @@ export default class TargetModel extends GeneratedTargetModel {
|
||||
removeHostSet(hostSetID, options) {
|
||||
return this.removeHostSets([hostSetID], options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds credential libraries via the `add-credential-libraries` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {[string]} credentialLibraryIDs
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
addCredentialLibraries(
|
||||
credentialLibraryIDs,
|
||||
options = { adapterOptions: {} }
|
||||
) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'add-credential-libraries',
|
||||
credentialLibraryIDs,
|
||||
};
|
||||
// There is no "deep merge" in ES.
|
||||
return this.save({
|
||||
...options,
|
||||
adapterOptions: {
|
||||
...defaultAdapterOptions,
|
||||
...options.adapterOptions,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete credential libraries via the `remove-credential-libraries` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {[string]} credentialLibraryIDs
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
removeCredentialLibraries(
|
||||
credentialLibraryIDs,
|
||||
options = { adapterOptions: {} }
|
||||
) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'remove-credential-libraries',
|
||||
credentialLibraryIDs,
|
||||
};
|
||||
// There is no "deep merge" in ES.
|
||||
return this.save({
|
||||
...options,
|
||||
adapterOptions: {
|
||||
...defaultAdapterOptions,
|
||||
...options.adapterOptions,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single credential library set via the `remove-credential-libraries` method.
|
||||
* @param {number} credentialLibraryID
|
||||
* @param {object} options
|
||||
* @return {Promise}
|
||||
*/
|
||||
removeCredentialLibrary(credentialLibraryID, options) {
|
||||
return this.removeCredentialLibraries([credentialLibraryID], options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default class CredentialLibrarySerializer extends ApplicationSerializer {}
|
||||
@@ -0,0 +1,3 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default class CredentialStoreSerializer extends ApplicationSerializer {}
|
||||
@@ -0,0 +1,27 @@
|
||||
import JSONSerializer from '@ember-data/serializer/json';
|
||||
|
||||
export default class FragmentCredentialLibraryAttributesSerializer extends JSONSerializer {
|
||||
/**
|
||||
* If an attribute is annotated as readOnly in the model, don't serialize it.
|
||||
* Otherwise delegate to default attribute serializer.
|
||||
* Only serialize http_request_body when `http_method` is set to `POST`.
|
||||
* @override
|
||||
* @method serializeAttribute
|
||||
* @param {Snapshot} snapshot
|
||||
* @param {Object} json
|
||||
* @param {String} key
|
||||
* @param {Object} attribute
|
||||
*/
|
||||
serializeAttribute(snapshot, json, key, attribute) {
|
||||
const { type, options } = attribute;
|
||||
let value = super.serializeAttribute(...arguments);
|
||||
// Convert empty strings to null.
|
||||
if (type === 'string' && json[key] === '') json[key] = null;
|
||||
// Do not serialize read-only attributes.
|
||||
if (options.readOnly) delete json[key];
|
||||
// Do not serialize http_request_body unless http_method is POST
|
||||
if (key === 'http_request_body' && !json['http_method']?.match(/post/i))
|
||||
delete json[key];
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import JSONSerializer from '@ember-data/serializer/json';
|
||||
|
||||
export default class FragmentCredentialStoreAttributesSerializer extends JSONSerializer {
|
||||
/**
|
||||
* If an attribute is annotated as readOnly in the model, don't serialize it.
|
||||
* Otherwise delegate to default attribute serializer.
|
||||
*
|
||||
* @override
|
||||
* @method serializeAttribute
|
||||
* @param {Snapshot} snapshot
|
||||
* @param {Object} json
|
||||
* @param {String} key
|
||||
* @param {Object} attribute
|
||||
*/
|
||||
serializeAttribute(snapshot, json, key, attribute) {
|
||||
const { type, options } = attribute;
|
||||
let value = super.serializeAttribute(...arguments);
|
||||
// Convert empty strings to null.
|
||||
if (type === 'string' && json[key] === '') json[key] = null;
|
||||
// Do not serialize read-only attributes.
|
||||
if (options.readOnly) delete json[key];
|
||||
// Token cannot be unset. If it's falsy, it must be removed.
|
||||
if (key === 'token' && !json[key]) delete json[key];
|
||||
// Client certificate key cannot be unset when certificate is set.
|
||||
// If it's falsy, it must be removed.
|
||||
if (
|
||||
key === 'client_certificate_key' &&
|
||||
!json[key] &&
|
||||
json['client_certificate']
|
||||
)
|
||||
delete json[key];
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,11 @@ export default class TargetSerializer extends ApplicationSerializer {
|
||||
/**
|
||||
* If `adapterOptions.hostSets` is set to an array of host set models,
|
||||
* the resulting target serialization should include **only host sets**
|
||||
* and the version. Normally, host sets are not serialized.
|
||||
* and the version.
|
||||
* If `adapterOptions.credentialLibraries` is set to an array of
|
||||
* credential library models, the resulting target serialization should
|
||||
* include **only credential libraries** and the version.
|
||||
* Normally, neither host sets or credential libraries are not serialized.
|
||||
* @override
|
||||
* @param {Snapshot} snapshot
|
||||
* @return {object}
|
||||
@@ -16,6 +20,12 @@ export default class TargetSerializer extends ApplicationSerializer {
|
||||
const hostSetIDs = snapshot?.adapterOptions?.hostSetIDs;
|
||||
if (hostSetIDs)
|
||||
serialized = this.serializeWithHostSets(snapshot, hostSetIDs);
|
||||
const credentialLibraryIDs = snapshot?.adapterOptions?.credentialLibraryIDs;
|
||||
if (credentialLibraryIDs)
|
||||
serialized = this.serializeWithCredentialLibraries(
|
||||
snapshot,
|
||||
credentialLibraryIDs
|
||||
);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
@@ -32,4 +42,18 @@ export default class TargetSerializer extends ApplicationSerializer {
|
||||
host_set_ids: hostSetIDs,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a payload containing only the credential_library_ids array using IDs
|
||||
* passed into the function (rather than existing credential libraries on the model).
|
||||
* @param {Snapshot} snapshot
|
||||
* @param {[string]} credentialLibraryIDs
|
||||
* @return {object}
|
||||
*/
|
||||
serializeWithCredentialLibraries(snapshot, credentialLibraryIDs) {
|
||||
return {
|
||||
version: snapshot.attr('version'),
|
||||
application_credential_library_ids: credentialLibraryIDs,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/credential-library';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/credential-store';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/fragment-credential-library-attributes';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/fragment-credential-store-attributes';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/credential-library';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/credential-store';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/fragment-credential-library-attributes';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/fragment-credential-store-attributes';
|
||||
@@ -366,10 +366,10 @@ export default function () {
|
||||
const target = targets.find(id);
|
||||
const updatedAttrs = {
|
||||
version: attrs.version,
|
||||
hostSetIds: target.hostSetIds,
|
||||
};
|
||||
// If adding host sets, push them into the array
|
||||
if (method === 'add-host-sets') {
|
||||
updatedAttrs.hostSetIds = target.hostSetIds;
|
||||
attrs.hostSetIds.forEach((id) => {
|
||||
if (!updatedAttrs.hostSetIds.includes(id)) {
|
||||
updatedAttrs.hostSetIds.push(id);
|
||||
@@ -378,10 +378,28 @@ export default function () {
|
||||
}
|
||||
// If deleting host sets, filter them out of the array
|
||||
if (method === 'remove-host-sets') {
|
||||
updatedAttrs.hostSetIds = target.hostSetIds;
|
||||
updatedAttrs.hostSetIds = updatedAttrs.hostSetIds.filter((id) => {
|
||||
return !attrs.hostSetIds.includes(id);
|
||||
});
|
||||
}
|
||||
// If adding credential libraries, push them into the array
|
||||
if (method === 'add-credential-libraries') {
|
||||
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
|
||||
attrs.applicationCredentialLibraryIds.forEach((id) => {
|
||||
if (!updatedAttrs.credentialLibraryIds.includes(id)) {
|
||||
updatedAttrs.credentialLibraryIds.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
// If deleting credential libraries, filter them out of the array
|
||||
if (method === 'remove-credential-libraries') {
|
||||
updatedAttrs.credentialLibraryIds = target.credentialLibraryIds;
|
||||
updatedAttrs.credentialLibraryIds =
|
||||
updatedAttrs.credentialLibraryIds.filter((id) => {
|
||||
return !attrs.applicationCredentialLibraryIds.includes(id);
|
||||
});
|
||||
}
|
||||
return target.update(updatedAttrs);
|
||||
}
|
||||
);
|
||||
@@ -456,6 +474,41 @@ export default function () {
|
||||
return new Response(200);
|
||||
});
|
||||
|
||||
// credential-stores
|
||||
|
||||
this.get(
|
||||
'/credential-stores',
|
||||
({ credentialStores }, { queryParams: { scope_id: scopeId } }) =>
|
||||
credentialStores.where({ scopeId })
|
||||
);
|
||||
|
||||
this.get('/credential-stores/:id');
|
||||
this.post('/credential-stores');
|
||||
this.patch('/credential-stores/:id');
|
||||
this.del('/credential-stores/:id');
|
||||
|
||||
// credential-libraries
|
||||
|
||||
this.get(
|
||||
'/credential-libraries',
|
||||
(
|
||||
{ credentialLibraries },
|
||||
{ queryParams: { credential_store_id: credentialStoreId } }
|
||||
) => credentialLibraries.where({ credentialStoreId })
|
||||
);
|
||||
this.get('/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');
|
||||
|
||||
/* Uncomment the following line and the Response import above
|
||||
* Then change the response code to simulate error responses.
|
||||
* this.get('/scopes', () => new Response(505));
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import factory from '../generated/factories/credential-library';
|
||||
import { random, system } from 'faker';
|
||||
|
||||
const types = ['vault'];
|
||||
|
||||
export default factory.extend({
|
||||
id: (i) => `credential-library-id-${i}`,
|
||||
type: (i) => types[i % types.length],
|
||||
|
||||
attributes() {
|
||||
switch (this.type) {
|
||||
case 'vault':
|
||||
return {
|
||||
http_method: 'GET',
|
||||
http_request_body: random.word(),
|
||||
path: system.directoryPath(),
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import factory from '../generated/factories/credential-store';
|
||||
import { random, internet, datatype } from 'faker';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
|
||||
const types = ['vault'];
|
||||
|
||||
export default factory.extend({
|
||||
id: (i) => `credential-store-id-${i}`,
|
||||
type: (i) => types[i % types.length],
|
||||
|
||||
attributes() {
|
||||
switch (this.type) {
|
||||
case 'vault':
|
||||
return {
|
||||
address: internet.ip(),
|
||||
namespace: random.word(),
|
||||
ca_cert: 'ca-cert-123',
|
||||
tls_server_name: internet.domainName(),
|
||||
tls_skip_verify: datatype.boolean(),
|
||||
token_hmac: 'token-hmac-abcde0987654321',
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key_hmac: 'client-cert-key-hmac-abcde0987654321',
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
withAssociations: trait({
|
||||
afterCreate(credentialStore, server) {
|
||||
const { scope } = credentialStore;
|
||||
server.createList('credential-library', 2, {
|
||||
scope,
|
||||
credentialStore,
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -7,7 +7,7 @@ const hostSetChance = 0.3;
|
||||
|
||||
export default factory.extend({
|
||||
/**
|
||||
* -1 means "unlimited" and we want to generate these on occassion.
|
||||
* -1 means "unlimited" and we want to generate these on occasion.
|
||||
*/
|
||||
session_connection_limit: () => random.arrayElement([-1, datatype.number()]),
|
||||
|
||||
@@ -25,11 +25,11 @@ export default factory.extend({
|
||||
},
|
||||
|
||||
/**
|
||||
* Randomly selects existing host sets to assign to target.
|
||||
* Randomly selects existing host sets and credential libraries to assign to target.
|
||||
*/
|
||||
withRandomHostSets: trait({
|
||||
withAssociations: trait({
|
||||
afterCreate(target, server) {
|
||||
let randomlySelectedHostSets;
|
||||
let randomlySelectedHostSets, randomlySelectedCredentialLibraries;
|
||||
randomlySelectedHostSets = server.schema.hostSets
|
||||
// BLERG: fun fact, for no reason at all, the element passed
|
||||
// into a where function is not a full model instance, as you might
|
||||
@@ -38,7 +38,17 @@ export default factory.extend({
|
||||
// the result set of `where` _is a collection of model instances_.
|
||||
.where((hostSet) => hostSet.scopeId === target.scope.id)
|
||||
.models.filter(() => randomBoolean(hostSetChance));
|
||||
target.update({ hostSets: randomlySelectedHostSets });
|
||||
|
||||
randomlySelectedCredentialLibraries = server.schema.credentialLibraries
|
||||
.where(
|
||||
(credentialLibrary) => credentialLibrary.scopeId === target.scope.id
|
||||
)
|
||||
.models.filter(() => randomBoolean());
|
||||
|
||||
target.update({
|
||||
hostSets: randomlySelectedHostSets,
|
||||
credentialLibraries: randomlySelectedCredentialLibraries,
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Factory } from 'ember-cli-mirage';
|
||||
import { random, date } from 'faker';
|
||||
|
||||
/**
|
||||
* GeneratedCredentialLibraryModelFactory
|
||||
*/
|
||||
export default Factory.extend({
|
||||
name: () => random.words(),
|
||||
description: () => random.words(),
|
||||
created_time: () => date.recent(),
|
||||
updated_time: () => date.recent(),
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Factory } from 'ember-cli-mirage';
|
||||
import { random, date } from 'faker';
|
||||
|
||||
/**
|
||||
* GeneratedCredentialStoreModelFactory
|
||||
*/
|
||||
export default Factory.extend({
|
||||
name: () => random.words(),
|
||||
description: () => random.words(),
|
||||
created_time: () => date.recent(),
|
||||
updated_time: () => date.recent(),
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Model, belongsTo } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
scope: belongsTo(),
|
||||
credentialStore: belongsTo(),
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Model, belongsTo } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
scope: belongsTo(),
|
||||
});
|
||||
@@ -3,4 +3,5 @@ import { Model, belongsTo, hasMany } from 'ember-cli-mirage';
|
||||
export default Model.extend({
|
||||
scope: belongsTo(),
|
||||
hostSets: hasMany(),
|
||||
credentialLibraries: hasMany(),
|
||||
});
|
||||
@@ -47,7 +47,8 @@ export default function (server) {
|
||||
// Other resources
|
||||
server.schema.scopes.where({ type: 'project' }).models.forEach((scope) => {
|
||||
server.createList('host-catalog', 2, { scope }, 'withChildren');
|
||||
server.createList('target', 2, { scope }, 'withRandomHostSets');
|
||||
server.createList('session', 4, { scope }, 'withAssociations');
|
||||
server.createList('credential-store', 3, { scope }, 'withAssociations');
|
||||
server.createList('target', 2, { scope }, 'withAssociations');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
modelName: 'credential-library',
|
||||
|
||||
_hashForModel(model) {
|
||||
const json = ApplicationSerializer.prototype._hashForModel.apply(
|
||||
this,
|
||||
arguments
|
||||
);
|
||||
json.credential_store_id = model.credentialStoreId;
|
||||
return json;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
modelName: 'credential-store',
|
||||
|
||||
_hashForModel(/* model */) {
|
||||
const json = ApplicationSerializer.prototype._hashForModel.apply(
|
||||
this,
|
||||
arguments
|
||||
);
|
||||
// in a real API, the following fields are set-only and returned in
|
||||
// associated HMAC fields
|
||||
delete json.attributes?.token;
|
||||
delete json.attributes?.client_certificate_key;
|
||||
return json;
|
||||
},
|
||||
});
|
||||
@@ -23,6 +23,9 @@ export default ApplicationSerializer.extend({
|
||||
return { id: host_set_id, host_catalog_id };
|
||||
});
|
||||
}
|
||||
if (model.credentialLibraryIds?.length) {
|
||||
json.application_credential_library_ids = model.credentialLibraryIds;
|
||||
}
|
||||
return json;
|
||||
},
|
||||
});
|
||||
@@ -29,20 +29,20 @@
|
||||
"ember-cli-mirage": "^1.1.8",
|
||||
"ember-data": "^3.20.0",
|
||||
"ember-data-model-fragments": "5.0.0-beta.2",
|
||||
"ember-fetch": "^8.0.1"
|
||||
"ember-fetch": "^8.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/test-helpers": "^2.1.4",
|
||||
"@ember/test-helpers": "^2.2.8",
|
||||
"@glimmer/component": "^1.0.3",
|
||||
"@glimmer/tracking": "^1.0.3",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"doctoc": "^2.0.0",
|
||||
"ember-auto-import": "^1.10.1",
|
||||
"ember-cli": "~3.24.0",
|
||||
"ember-cli": "~3.27.0",
|
||||
"ember-cli-dependency-checker": "^3.2.0",
|
||||
"ember-cli-inject-live-reload": "^2.0.2",
|
||||
"ember-cli-inject-live-reload": "^2.1.0",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-terser": "^4.0.1",
|
||||
"ember-cli-update": "^0.54.6",
|
||||
@@ -59,8 +59,8 @@
|
||||
"ember-source-channel-url": "^3.0.0",
|
||||
"ember-template-lint": "^2.15.0",
|
||||
"ember-try": "^1.4.0",
|
||||
"eslint": "^7.17.0",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-ember": "^10.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Model | credential library', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('credential-library', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Model | credential store', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('credential-store', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module(
|
||||
'Unit | Model | fragment credential library attributes',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord(
|
||||
'fragment-credential-library-attributes',
|
||||
{}
|
||||
);
|
||||
assert.ok(model);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,13 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Model | fragment credential store attributes', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('fragment-credential-store-attributes', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -223,4 +223,162 @@ module('Unit | Model | target', function (hooks) {
|
||||
'Target has two associated sessions loaded in the store'
|
||||
);
|
||||
});
|
||||
|
||||
test('it has a `credentialLibraries` array of resolved model instances (if those instances are already in the store)', function (assert) {
|
||||
assert.expect(4);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
},
|
||||
},
|
||||
});
|
||||
const target = store.peekRecord('target', '123abc');
|
||||
assert.equal(
|
||||
target.application_credential_library_ids.length,
|
||||
2,
|
||||
'Target has two entires in application_credential_library_ids'
|
||||
);
|
||||
assert.equal(
|
||||
target.credentialLibraries.length,
|
||||
0,
|
||||
'Target has no resolved credentialLibraries because they are not loaded yet'
|
||||
);
|
||||
store.push({
|
||||
data: {
|
||||
id: '1',
|
||||
type: 'credential-library',
|
||||
attributes: {},
|
||||
},
|
||||
});
|
||||
store.push({
|
||||
data: {
|
||||
id: '2',
|
||||
type: 'credential-library',
|
||||
attributes: {},
|
||||
},
|
||||
});
|
||||
// Since `credentialLibraries` is computed on `application_credential_library_ids`,
|
||||
// not the store itself, it's necessary to do this assignment to kick-off the
|
||||
// computed update.
|
||||
/* eslint-disable no-self-assign */
|
||||
target.application_credential_library_ids =
|
||||
target.application_credential_library_ids;
|
||||
/* eslint-enable no-self-assign */
|
||||
assert.equal(
|
||||
target.application_credential_library_ids.length,
|
||||
2,
|
||||
'Target has two entires in application_credential_library_ids'
|
||||
);
|
||||
assert.equal(
|
||||
target.credentialLibraries.length,
|
||||
2,
|
||||
'Target has two resolved credentialLibraries'
|
||||
);
|
||||
});
|
||||
|
||||
test('it has an `addCredentialLibraries` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:add-credential-libraries',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
application_credential_library_ids: ['123_abc', 'foobar'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
}
|
||||
);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
type: 'scope',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.addCredentialLibraries(['123_abc', 'foobar']);
|
||||
});
|
||||
|
||||
test('it has a `removeCredentialLibraries` method that targets a specific POST API endpoint and serialization', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-credential-libraries',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
application_credential_library_ids: ['1', '2'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
}
|
||||
);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
type: 'scope',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeCredentialLibraries(['1', '2']);
|
||||
});
|
||||
|
||||
test('it has a `removeCredentialLibrary` method that deletes a single credential library using `removeCredentialLibraries` method', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post(
|
||||
'/v1/targets/123abc:remove-credential-libraries',
|
||||
(schema, request) => {
|
||||
const body = JSON.parse(request.requestBody);
|
||||
assert.deepEqual(body, {
|
||||
application_credential_library_ids: ['2'],
|
||||
version: 1,
|
||||
});
|
||||
return { id: '123abc' };
|
||||
}
|
||||
);
|
||||
const store = this.owner.lookup('service:store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '123abc',
|
||||
type: 'target',
|
||||
attributes: {
|
||||
name: 'Target',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
scope: {
|
||||
scope_id: 'o_1',
|
||||
type: 'scope',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const model = store.peekRecord('target', '123abc');
|
||||
await model.removeCredentialLibrary('2');
|
||||
});
|
||||
});
|
||||
@@ -203,6 +203,7 @@ module('Unit | Serializer | application', function (hooks) {
|
||||
name: 'Target 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
host_sets: [],
|
||||
application_credential_library_ids: [],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Serializer | credential library', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes correctly on create', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('credential-library');
|
||||
const record = store.createRecord('credential-library', {
|
||||
type: 'vault',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
path: '/vault/path',
|
||||
http_method: 'GET',
|
||||
},
|
||||
version: 1,
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
type: 'vault',
|
||||
credential_store_id: null,
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
version: 1,
|
||||
attributes: {
|
||||
path: '/vault/path',
|
||||
http_method: 'GET',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes correctly on update', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('credential-library');
|
||||
store.push({
|
||||
data: {
|
||||
id: '1',
|
||||
type: 'credential-library',
|
||||
attributes: {
|
||||
type: 'vault',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
path: '/vault/path',
|
||||
http_method: 'GET',
|
||||
},
|
||||
version: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
const record = store.peekRecord('credential-library', '1');
|
||||
const snapshot = record._createSnapshot();
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
type: 'vault',
|
||||
credential_store_id: null,
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
path: '/vault/path',
|
||||
http_method: 'GET',
|
||||
},
|
||||
version: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Serializer | credential store', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes vault-type correctly on create', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('credential-store');
|
||||
const record = store.createRecord('credential-store', {
|
||||
type: 'vault',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
address: 'http://localhost:5000',
|
||||
ca_cert: 'ca-cert-123',
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: 'client-cert-key-456',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
token: 'foo',
|
||||
},
|
||||
version: 1,
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
type: 'vault',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
version: 1,
|
||||
attributes: {
|
||||
address: 'http://localhost:5000',
|
||||
ca_cert: 'ca-cert-123',
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: 'client-cert-key-456',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
token: 'foo',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes vault-type correctly on update', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('credential-store');
|
||||
store.push({
|
||||
data: {
|
||||
id: '1',
|
||||
type: 'credential-store',
|
||||
attributes: {
|
||||
type: 'vault',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
address: 'http://localhost:5000',
|
||||
ca_cert: 'ca-cert-123',
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: 'client-cert-key-456',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
token: 'foo',
|
||||
},
|
||||
version: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
const record = store.peekRecord('credential-store', '1');
|
||||
const snapshot = record._createSnapshot();
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
type: 'vault',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
attributes: {
|
||||
address: 'http://localhost:5000',
|
||||
ca_cert: 'ca-cert-123',
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: 'client-cert-key-456',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
token: 'foo',
|
||||
},
|
||||
version: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module(
|
||||
'Unit | Serializer | fragment credential library attributes',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes empty strings to null', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord(
|
||||
'fragment-credential-library-attributes',
|
||||
{
|
||||
http_method: '',
|
||||
path: null,
|
||||
}
|
||||
);
|
||||
let serializedRecord = record.serialize();
|
||||
assert.deepEqual(serializedRecord, {
|
||||
http_method: null,
|
||||
path: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('it does not serialize http_request_body unless http_method is set to POST', function (assert) {
|
||||
assert.expect(2);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord(
|
||||
'fragment-credential-library-attributes',
|
||||
{
|
||||
http_method: 'GET',
|
||||
http_request_body: 'body',
|
||||
}
|
||||
);
|
||||
let serializedRecord = record.serialize();
|
||||
assert.deepEqual(
|
||||
serializedRecord,
|
||||
{
|
||||
http_method: 'GET',
|
||||
path: null,
|
||||
},
|
||||
'http_request_body attribute is not expected'
|
||||
);
|
||||
|
||||
record.http_method = 'POST';
|
||||
serializedRecord = record.serialize();
|
||||
assert.deepEqual(
|
||||
serializedRecord,
|
||||
{
|
||||
http_method: 'POST',
|
||||
http_request_body: 'body',
|
||||
path: null,
|
||||
},
|
||||
'http_request_body attribute is expected'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,117 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module(
|
||||
'Unit | Serializer | fragment credential store attributes',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes empty strings to null', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord(
|
||||
'fragment-credential-store-attributes',
|
||||
{
|
||||
address: '',
|
||||
ca_cert: '',
|
||||
client_certificate: '',
|
||||
client_certificate_key: '',
|
||||
namespace: '',
|
||||
tls_server_name: '',
|
||||
tls_skip_verify: false,
|
||||
}
|
||||
);
|
||||
let serializedRecord = record.serialize();
|
||||
assert.deepEqual(serializedRecord, {
|
||||
address: null,
|
||||
ca_cert: null,
|
||||
client_certificate: null,
|
||||
client_certificate_key: null,
|
||||
namespace: null,
|
||||
tls_server_name: null,
|
||||
tls_skip_verify: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('it does not serialize token if present but falsy', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord(
|
||||
'fragment-credential-store-attributes',
|
||||
{
|
||||
token: null,
|
||||
address: 'http://localhost:5000',
|
||||
ca_cert: 'ca-cert-123',
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: 'client-cert-key-456',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
}
|
||||
);
|
||||
let serializedRecord = record.serialize();
|
||||
assert.deepEqual(
|
||||
serializedRecord,
|
||||
{
|
||||
address: 'http://localhost:5000',
|
||||
ca_cert: 'ca-cert-123',
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: 'client-cert-key-456',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
},
|
||||
'token attribute is not expected'
|
||||
);
|
||||
});
|
||||
|
||||
test('it does not serialize client certificate key if present but falsy when client certificate is present', function (assert) {
|
||||
assert.expect(2);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord(
|
||||
'fragment-credential-store-attributes',
|
||||
{
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: '',
|
||||
address: 'http://localhost:5000',
|
||||
token: 'foo',
|
||||
ca_cert: 'ca-cert-123',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
}
|
||||
);
|
||||
let serializedRecord = record.serialize();
|
||||
assert.deepEqual(
|
||||
serializedRecord,
|
||||
{
|
||||
client_certificate: 'client-cert-123',
|
||||
address: 'http://localhost:5000',
|
||||
token: 'foo',
|
||||
ca_cert: 'ca-cert-123',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
},
|
||||
'client certificate key attribute is not expected'
|
||||
);
|
||||
|
||||
record.client_certificate_key = 'client-cert-key-456';
|
||||
serializedRecord = record.serialize();
|
||||
assert.deepEqual(
|
||||
serializedRecord,
|
||||
{
|
||||
client_certificate: 'client-cert-123',
|
||||
client_certificate_key: 'client-cert-key-456',
|
||||
address: 'http://localhost:5000',
|
||||
token: 'foo',
|
||||
ca_cert: 'ca-cert-123',
|
||||
namespace: 'foobar',
|
||||
tls_server_name: 'yes',
|
||||
tls_skip_verify: false,
|
||||
},
|
||||
'client certificate key attribute is expected'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -4,7 +4,7 @@ import { setupTest } from 'ember-qunit';
|
||||
module('Unit | Serializer | target', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes targets normally, without host sets', function (assert) {
|
||||
test('it serializes targets normally, without host sets or credential libraries', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
@@ -15,6 +15,7 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
scope: {
|
||||
scope_id: 'org_1',
|
||||
type: 'org',
|
||||
@@ -63,6 +64,27 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
});
|
||||
});
|
||||
|
||||
test('it serializes only credential libraries and version when an `adapterOptions.credentialLibraryIDs` array is passed', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
const record = store.createRecord('target', {
|
||||
name: 'User',
|
||||
description: 'Description',
|
||||
application_credential_library_ids: [{ value: '1' }, { value: '2' }],
|
||||
version: 1,
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {
|
||||
credentialLibraryIDs: ['4', '5'],
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
application_credential_library_ids: ['4', '5'],
|
||||
version: 1,
|
||||
});
|
||||
});
|
||||
|
||||
test('it normalizes records with array fields', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
@@ -75,6 +97,7 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
application_credential_library_ids: ['1'],
|
||||
};
|
||||
const normalized = serializer.normalizeSingleResponse(
|
||||
store,
|
||||
@@ -92,13 +115,14 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
{ host_set_id: '1', host_catalog_id: '2' },
|
||||
{ host_set_id: '3', host_catalog_id: '4' },
|
||||
],
|
||||
application_credential_library_ids: [{ value: '1' }],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('it normalizes missing host_sets to empty array', function (assert) {
|
||||
test('it normalizes missing host_sets and credential libraries to empty array', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('target');
|
||||
@@ -122,6 +146,7 @@ module('Unit | Serializer | target', function (hooks) {
|
||||
name: 'Target 1',
|
||||
scope: { scope_id: 'o_123' },
|
||||
host_sets: [],
|
||||
application_credential_library_ids: [],
|
||||
},
|
||||
relationships: {},
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/test-helpers": "^2.1.4",
|
||||
"@ember/test-helpers": "^2.2.8",
|
||||
"@glimmer/component": "^1.0.3",
|
||||
"@glimmer/tracking": "^1.0.3",
|
||||
"babel-eslint": "^10.1.0",
|
||||
@@ -35,9 +35,9 @@
|
||||
"core": "*",
|
||||
"doctoc": "^2.0.0",
|
||||
"ember-auto-import": "^1.10.1",
|
||||
"ember-cli": "~3.24.0",
|
||||
"ember-cli": "~3.27.0",
|
||||
"ember-cli-dependency-checker": "^3.2.0",
|
||||
"ember-cli-inject-live-reload": "^2.0.2",
|
||||
"ember-cli-inject-live-reload": "^2.1.0",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-terser": "^4.0.1",
|
||||
"ember-cli-update": "^0.54.6",
|
||||
@@ -53,8 +53,8 @@
|
||||
"ember-source-channel-url": "^3.0.0",
|
||||
"ember-template-lint": "^2.15.0",
|
||||
"ember-try": "^1.4.0",
|
||||
"eslint": "^7.17.0",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-ember": "^10.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
|
||||
@@ -43,16 +43,16 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/test-helpers": "^2.1.4",
|
||||
"@ember/test-helpers": "^2.2.8",
|
||||
"@glimmer/component": "^1.0.3",
|
||||
"@glimmer/tracking": "^1.0.3",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"doctoc": "^2.0.0",
|
||||
"ember-auto-import": "^1.10.1",
|
||||
"ember-cli": "~3.24.0",
|
||||
"ember-cli": "~3.27.0",
|
||||
"ember-cli-dependency-checker": "^3.2.0",
|
||||
"ember-cli-inject-live-reload": "^2.0.2",
|
||||
"ember-cli-inject-live-reload": "^2.1.0",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-terser": "^4.0.1",
|
||||
"ember-disable-prototype-extensions": "^1.1.3",
|
||||
@@ -68,8 +68,8 @@
|
||||
"ember-source-channel-url": "^3.0.0",
|
||||
"ember-template-lint": "^2.15.0",
|
||||
"ember-try": "^1.4.0",
|
||||
"eslint": "^7.17.0",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-ember": "^10.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.4577 1.67511L5.56323 7.36908L10.5292 13.3821L13.2972 11.0961L17.1724 15.5238L13.7226 18.5433L15.2044 20.2364L18.6543 17.2169L20.1097 18.8797L16.662 21.8973L18.1438 23.5904L23.2846 19.091L15.0328 9.66276L17.4237 7.68814L12.4577 1.67511Z" fill="black"/>
|
||||
<mask id="path-2-inside-1" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.766 4.35302L5.62911 4.255L5.53249 11.2901L9.12206 11.3394L9.20978 17.2227L4.62573 17.2911L4.6593 19.5409L9.24333 19.4725L9.27627 21.682L4.69506 21.7503L4.7286 24L11.5596 23.8982L11.3728 11.3703L12.6694 11.3882L12.766 4.35302Z"/>
|
||||
</mask>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.766 4.35302L5.62911 4.255L5.53249 11.2901L9.12206 11.3394L9.20978 17.2227L4.62573 17.2911L4.6593 19.5409L9.24333 19.4725L9.27627 21.682L4.69506 21.7503L4.7286 24L11.5596 23.8982L11.3728 11.3703L12.6694 11.3882L12.766 4.35302Z" fill="black"/>
|
||||
<path d="M5.62911 4.255L5.64971 2.75515L4.14985 2.73455L4.12925 4.23441L5.62911 4.255ZM12.766 4.35302L14.2659 4.37362L14.2865 2.87376L12.7866 2.85316L12.766 4.35302ZM5.53249 11.2901L4.03264 11.2695L4.01204 12.7694L5.5119 12.79L5.53249 11.2901ZM9.12206 11.3394L10.6219 11.3171L10.6002 9.85959L9.14265 9.83958L9.12206 11.3394ZM9.20978 17.2227L9.23216 18.7226L10.732 18.7002L10.7096 17.2004L9.20978 17.2227ZM4.62573 17.2911L4.60335 15.7913L3.10352 15.8137L3.1259 17.3135L4.62573 17.2911ZM4.6593 19.5409L3.15947 19.5633L3.18185 21.0631L4.68168 21.0407L4.6593 19.5409ZM9.24333 19.4725L10.7432 19.4501L10.7208 17.9503L9.22095 17.9727L9.24333 19.4725ZM9.27627 21.682L9.29863 23.1818L10.7985 23.1594L10.7761 21.6596L9.27627 21.682ZM4.69506 21.7503L4.6727 20.2504L3.17286 20.2728L3.19522 21.7726L4.69506 21.7503ZM4.7286 24L3.22877 24.0224L3.25113 25.5222L4.75096 25.4998L4.7286 24ZM11.5596 23.8982L11.5819 25.398L13.0818 25.3756L13.0594 23.8758L11.5596 23.8982ZM11.3728 11.3703L11.3934 9.87048L9.84992 9.84929L9.87293 11.3927L11.3728 11.3703ZM12.6694 11.3882L12.6488 12.888L14.1487 12.9086L14.1693 11.4087L12.6694 11.3882ZM5.60851 5.75486L12.7454 5.85288L12.7866 2.85316L5.64971 2.75515L5.60851 5.75486ZM7.03235 11.3107L7.12897 4.2756L4.12925 4.23441L4.03264 11.2695L7.03235 11.3107ZM9.14265 9.83958L5.55309 9.79028L5.5119 12.79L9.10146 12.8393L9.14265 9.83958ZM7.62222 11.3618L7.70995 17.2451L10.7096 17.2004L10.6219 11.3171L7.62222 11.3618ZM9.1874 15.7229L4.60335 15.7913L4.64811 18.791L9.23216 18.7226L9.1874 15.7229ZM3.1259 17.3135L3.15947 19.5633L6.15913 19.5185L6.12557 17.2688L3.1259 17.3135ZM4.68168 21.0407L9.2657 20.9723L9.22095 17.9727L4.63692 18.0411L4.68168 21.0407ZM7.74349 19.4949L7.77644 21.7043L10.7761 21.6596L10.7432 19.4501L7.74349 19.4949ZM9.25391 20.1821L4.6727 20.2504L4.71742 23.2501L9.29863 23.1818L9.25391 20.1821ZM3.19522 21.7726L3.22877 24.0224L6.22843 23.9777L6.19489 21.7279L3.19522 21.7726ZM4.75096 25.4998L11.5819 25.398L11.5372 22.3983L4.70624 22.5002L4.75096 25.4998ZM13.0594 23.8758L12.8726 11.348L9.87293 11.3927L10.0597 23.9205L13.0594 23.8758ZM12.69 9.88829L11.3934 9.87048L11.3522 12.8702L12.6488 12.888L12.69 9.88829ZM11.2662 4.33242L11.1696 11.3676L14.1693 11.4087L14.2659 4.37362L11.2662 4.33242Z" fill="black" mask="url(#path-2-inside-1)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.2796 1.26121C9.20838 0.46864 7.88301 0 6.44818 0C2.88695 0 0 2.88695 0 6.44818C0 9.00798 1.4916 11.2194 3.65306 12.2607L3.68943 9.61275C2.80735 8.84314 2.25 7.71075 2.25 6.44818C2.25 4.12959 4.12959 2.25 6.44818 2.25C7.18811 2.25 7.88333 2.44143 8.48703 2.77746L10.2796 1.26121Z" fill="black"/>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.18544 1.11673L4.58911 4.91271L7.89977 8.92139L9.74509 7.3974L12.3286 10.3492L10.0287 12.3622L11.0166 13.4909L13.3165 11.4779L14.2867 12.5865L11.9883 14.5982L12.9762 15.7269L16.4034 12.7273L10.9021 6.44183L12.4961 5.12541L9.18544 1.11673Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.39097 2.9997L4.63303 2.93436L4.56862 7.62444L6.96166 7.65731L7.02014 11.5795L3.96411 11.6251L3.98649 13.1249L7.04251 13.0793L7.06447 14.5523L4.01033 14.5979L4.03269 16.0977L8.58667 16.0298L8.46213 7.67792L9.32656 7.68979L9.39097 2.9997Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.73342 0.840799C7.01928 0.312425 6.13571 0 5.17915 0C2.805 0 0.880371 1.92463 0.880371 4.29878C0.880371 6.00532 1.87477 7.47961 3.31575 8.1738L3.33999 6.4085C2.75194 5.89543 2.38037 5.1405 2.38037 4.29878C2.38037 2.75306 3.63343 1.5 5.17915 1.5C5.67244 1.5 6.13592 1.62762 6.53838 1.85163L7.73342 0.840799Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -33,3 +33,4 @@ edit-form: Edit Form
|
||||
change-origin: Change Origin URL
|
||||
change-state: Change State
|
||||
close: Close
|
||||
toggle-secret: Toggle secret visibility
|
||||
@@ -253,18 +253,96 @@ target:
|
||||
actions:
|
||||
create: New Target
|
||||
delete: Delete Target
|
||||
add-host-sets: Add Host Sets
|
||||
add-host-sets: Add Host Sources
|
||||
add-credential-libraries: Add Credential Sources
|
||||
types:
|
||||
tcp: TCP
|
||||
host-set:
|
||||
title: Host Source
|
||||
title_plural: Host Sources
|
||||
messages:
|
||||
welcome:
|
||||
title: Welcome to Host Sets
|
||||
description: No hosts sets in this target.
|
||||
title: Welcome to Host Sources
|
||||
description: No hosts sources in this target.
|
||||
none:
|
||||
title: No Host Sets Available
|
||||
description: No hosts sets available to add to target.
|
||||
title: No Host Sources Available
|
||||
description: No hosts sources available to add to target.
|
||||
add:
|
||||
title: Add Host Sets
|
||||
description: Select host sets to assign to this target.
|
||||
secret: ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
|
||||
title: Add Host Sources
|
||||
description: Select host sources to assign to this target.
|
||||
credential-library:
|
||||
title: Credential Source
|
||||
title_plural: Credential Sources
|
||||
messages:
|
||||
welcome:
|
||||
title: Welcome to Credential Sources
|
||||
description: No credential sources in this target.
|
||||
none:
|
||||
title: No Credential Sources Available
|
||||
description: No credential sources available to add to this target.
|
||||
add:
|
||||
title: Add Credential Sources
|
||||
description: Select credential sources to assign to this target.
|
||||
credential-store:
|
||||
title: Credential Store
|
||||
title_plural: Credential Stores
|
||||
description: A credential store is a collection of credentials and credential libraries.
|
||||
messages:
|
||||
welcome:
|
||||
title: Welcome to Credential Stores
|
||||
actions:
|
||||
delete: Delete Credential Store
|
||||
types:
|
||||
vault: Vault
|
||||
form:
|
||||
address:
|
||||
label: Address
|
||||
help: The address of the Vault server. This should be a complete URL such as https://127.0.0.1:8200.
|
||||
namespace:
|
||||
label: Namespace
|
||||
help: A Vault namespace. Requires Vault Enterprise.
|
||||
ca_cert:
|
||||
label: CA Certificate
|
||||
help: A PEM-encoded CA certificate to verify the Vault server's TLS certificate.
|
||||
tls_server_name:
|
||||
label: TLS Server Name
|
||||
help: Name to use as the SNI host when connecting to Vault via TLS.
|
||||
tls_skip_verify:
|
||||
label: TLS Skip Verify
|
||||
help: Disable verification of TLS certificates. Using this option is highly discouraged as it decreases the security of data transmissions to and from the Vault server.
|
||||
token:
|
||||
label: Token
|
||||
help: A token used for accessing Vault.
|
||||
token_hmac:
|
||||
label: Token HMAC
|
||||
client_certificate:
|
||||
label: Client Certificate
|
||||
help: A PEM-encoded client certificate to use for TLS authentication to the Vault server.
|
||||
client_certificate_key:
|
||||
label: Client Certificate Key
|
||||
help: A PEM-encoded private key matching the client certificate from Client Certificate.
|
||||
client_certificate_key_hmac:
|
||||
label: Client Certificate Key HMAC
|
||||
credential-library:
|
||||
title: Credential Library
|
||||
title_plural: Credential Libraries
|
||||
description: A credential library is a resource that provides credentials.
|
||||
actions:
|
||||
create: New Credential Library
|
||||
delete: Delete Credential Library
|
||||
messages:
|
||||
none:
|
||||
title: No Credential Libraries Available
|
||||
description: No Credential Libraries available in this credential store.
|
||||
form:
|
||||
vault_path:
|
||||
label: Vault Path
|
||||
help: The path the library uses when requesting credentials from Vault.
|
||||
http_method:
|
||||
label: HTTP Method
|
||||
help: The HTTP method the library uses when requesting credentials from Vault.
|
||||
http_request_body:
|
||||
label: HTTP Method POST Request Body
|
||||
help: The body of the HTTP request the library sends to Vault when requesting credentials. Only allowed when HTTP method is set to POST.
|
||||
types:
|
||||
vault: Vault
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="rose-form-checkbox{{if @error " error"}}">
|
||||
<div class="rose-form-checkbox{{if @error " error"}} {{if @inline " inline"}}">
|
||||
|
||||
<Input
|
||||
...attributes
|
||||
|
||||
@@ -68,3 +68,61 @@ Renders a form input checkbox.
|
||||
}
|
||||
}}</Story>
|
||||
</Preview>
|
||||
|
||||
|
||||
```htmlbars
|
||||
<Rose::Form::Checkbox
|
||||
@name="boolean-value"
|
||||
@label="Yes?"
|
||||
@checked={{false}}
|
||||
@error={{false}}
|
||||
@disabled={{false}}
|
||||
@inline={{false}}
|
||||
/>
|
||||
```
|
||||
|
||||
<Preview>
|
||||
<Story name="Checkbox with inline style" height="10rem">{{
|
||||
template: hbs`
|
||||
<Rose::Form::Checkbox
|
||||
@name='checkbox-1'
|
||||
@label='Checkbox1 🛎'
|
||||
@description={{description}}
|
||||
@helperText={{helperText}}
|
||||
@checked={{checked}}
|
||||
@error={{error}}
|
||||
@disabled={{disabled}}
|
||||
@inline={{inline}}
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@name='checkbox-2'
|
||||
@label='Checkbox2 🛎'
|
||||
@description={{description}}
|
||||
@helperText={{helperText}}
|
||||
@checked={{checked}}
|
||||
@error={{error}}
|
||||
@disabled={{disabled}}
|
||||
@inline={{inline}}
|
||||
/>
|
||||
<Rose::Form::Checkbox
|
||||
@name='checkbox-3'
|
||||
@label='Checkbox3 🛎'
|
||||
@description={{description}}
|
||||
@helperText={{helperText}}
|
||||
@checked={{checked}}
|
||||
@error={{error}}
|
||||
@disabled={{disabled}}
|
||||
@inline={{inline}}
|
||||
/>
|
||||
`,
|
||||
context: {
|
||||
description: text('description', 'Short description in label'),
|
||||
helperText: text('helperText', 'This is an optional help text.'),
|
||||
checked: boolean('checked', false),
|
||||
error: boolean('error', false),
|
||||
disabled: boolean('disabled', false),
|
||||
inline: boolean('inline', false)
|
||||
}
|
||||
}}</Story>
|
||||
|
||||
</Preview>
|
||||
@@ -20,6 +20,7 @@
|
||||
input=(component 'rose/form/input' disabled=this.disabled)
|
||||
select=(component 'rose/form/select' disabled=this.disabled)
|
||||
textarea=(component 'rose/form/textarea' disabled=this.disabled)
|
||||
fieldset=(component 'rose/form/fieldset')
|
||||
actions=(component
|
||||
(if this.showEditToggleButton
|
||||
'rose/form/actions/edit-toggle'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{{yield (hash
|
||||
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue disabled=@disabled changed=@changed)
|
||||
)}}
|
||||
radio=(component 'rose/form/radio/radio' name=@name selectedValue=@selectedValue disabled=@disabled changed=@changed inline=@inline)
|
||||
)}}
|
||||
@@ -15,6 +15,7 @@ Renders a form radiofield group.
|
||||
@name={{name}}
|
||||
@selectedValue="disagree"
|
||||
@changed={{fn}}
|
||||
@inline={{false}}
|
||||
as |radioGroup|
|
||||
>
|
||||
<radioGroup.radio
|
||||
@@ -39,6 +40,7 @@ Renders a form radiofield group.
|
||||
@name={{name}}
|
||||
@selectedValue={{selectedValue}}
|
||||
@changed={{changed}}
|
||||
@inline={{inline}}
|
||||
as |radioGroup|
|
||||
>
|
||||
<radioGroup.radio
|
||||
@@ -59,6 +61,7 @@ Renders a form radiofield group.
|
||||
context: {
|
||||
name: text('name', 'mustagree'),
|
||||
disabled: boolean('disabled', false),
|
||||
inline: boolean('inline', true),
|
||||
selectedValue: text('selectedValue', 'disagree'),
|
||||
firstFieldLabel: text('first label', 'Agree'),
|
||||
firstFieldValue: text('first value', 'agree'),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="rose-form-radio{{if @error " error"}}">
|
||||
<div class="rose-form-radio{{if @error " error"}} {{if @inline "inline"}}">
|
||||
|
||||
<RadioButton
|
||||
...attributes
|
||||
|
||||
@@ -54,3 +54,71 @@ Renders a form radiofield.
|
||||
}
|
||||
}}</Story>
|
||||
</Preview>
|
||||
|
||||
|
||||
```htmlbars
|
||||
<Rose::Form::Radio::Radio
|
||||
@label="Yes"
|
||||
@error={{false}}
|
||||
@value="yes-agree"
|
||||
@selectedValue="yes-agree"
|
||||
@disabled={{false}}
|
||||
@changed={{fn}}
|
||||
@inline={{true}}
|
||||
/>
|
||||
```
|
||||
|
||||
<Preview>
|
||||
<Story name="Radio with inline style" height="10rem">{{
|
||||
template: hbs`
|
||||
<Rose::Form::Radio::Radio
|
||||
@label='Radiofield1'
|
||||
@error={{error}}
|
||||
@value={{value}}
|
||||
@selectedValue={{selectedValue}}
|
||||
@disabled={{disabled}}
|
||||
@icon={{icon}}
|
||||
@inline={{inline}}
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@label='Radiofield2'
|
||||
@error={{error}}
|
||||
@value={{value}}
|
||||
@selectedValue={{selectedValue}}
|
||||
@disabled={{disabled}}
|
||||
@icon={{icon}}
|
||||
@inline={{inline}}
|
||||
/>
|
||||
<Rose::Form::Radio::Radio
|
||||
@label='Radiofield3'
|
||||
@error={{error}}
|
||||
@value={{value}}
|
||||
@selectedValue={{selectedValue}}
|
||||
@disabled={{disabled}}
|
||||
@icon={{icon}}
|
||||
@inline={{inline}}
|
||||
/>
|
||||
`,
|
||||
context: {
|
||||
error: boolean('error', false),
|
||||
value: text('value', 'value and selectedValue must match to select radiofield'),
|
||||
selectedValue: text(
|
||||
'selectedValue',
|
||||
'value and selectedValue must match to select radiofield'
|
||||
),
|
||||
disabled: boolean('disabled', false),
|
||||
icon: select(
|
||||
'icon',
|
||||
{
|
||||
'none': null,
|
||||
'user-organization': 'user-organization',
|
||||
'copy-action': 'copy-action',
|
||||
'unfold-more': 'unfold-more',
|
||||
'unfold-less': 'unfold-less',
|
||||
},
|
||||
null
|
||||
),
|
||||
inline: boolean('inline', false)
|
||||
}
|
||||
}}</Story>
|
||||
</Preview>
|
||||
@@ -2,6 +2,10 @@
|
||||
@use '../../utilities/type';
|
||||
|
||||
.rose-form-checkbox {
|
||||
&.inline {
|
||||
display: inline-block;
|
||||
margin-right: sizing.rems(s);
|
||||
}
|
||||
@include type.type(m);
|
||||
position: relative;
|
||||
margin-bottom: sizing.rems(m);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
margin-bottom: sizing.rems(m);
|
||||
|
||||
.rose-form-fieldset-title {
|
||||
@include type.type(l, bold);
|
||||
@include type.type(s, semibold);
|
||||
}
|
||||
|
||||
.rose-form-fieldset-description {
|
||||
|
||||
@@ -21,8 +21,13 @@ $radio-svg: (
|
||||
);
|
||||
|
||||
$data-svg-prefix: 'data:image/svg+xml;utf-8,';
|
||||
|
||||
.rose-form-radio {
|
||||
&.inline {
|
||||
display: inline-block;
|
||||
.rose-form-radio-label {
|
||||
margin-right: sizing.rems(s);
|
||||
}
|
||||
}
|
||||
@include type.type(m);
|
||||
position: relative;
|
||||
margin-bottom: sizing.rems(xs);
|
||||
|
||||
+10
-10
@@ -40,22 +40,22 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/test-helpers": "^2.1.4",
|
||||
"@ember/test-helpers": "^2.2.8",
|
||||
"@glimmer/component": "^1.0.3",
|
||||
"@glimmer/tracking": "^1.0.3",
|
||||
"@hashicorp/structure-icons": "^1.9.0-0",
|
||||
"@storybook/addon-actions": "^6.0.26",
|
||||
"@storybook/addon-docs": "^6.0.26",
|
||||
"@storybook/addon-knobs": "^6.0.26",
|
||||
"@storybook/ember": "^6.0.26",
|
||||
"@storybook/addon-actions": "^6.3.1",
|
||||
"@storybook/addon-docs": "^6.3.1",
|
||||
"@storybook/addon-knobs": "^6.3.0",
|
||||
"@storybook/ember": "^6.3.1",
|
||||
"@storybook/ember-cli-storybook": "^0.4.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"doctoc": "^2.0.0",
|
||||
"ember-auto-import": "^1.10.1",
|
||||
"ember-cli": "~3.24.0",
|
||||
"ember-cli": "~3.27.0",
|
||||
"ember-cli-dependency-checker": "^3.2.0",
|
||||
"ember-cli-inject-live-reload": "^2.0.2",
|
||||
"ember-cli-inject-live-reload": "^2.1.0",
|
||||
"ember-cli-sass": "^10.0.1",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-terser": "^4.0.1",
|
||||
@@ -73,8 +73,8 @@
|
||||
"ember-source-channel-url": "^3.0.0",
|
||||
"ember-template-lint": "^2.15.0",
|
||||
"ember-try": "^1.4.0",
|
||||
"eslint": "^7.17.0",
|
||||
"eslint-config-prettier": "^7.1.0",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-ember": "^10.1.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
@@ -83,7 +83,7 @@
|
||||
"prettier": "^2.2.1",
|
||||
"qunit": "^2.13.0",
|
||||
"qunit-dom": "^1.6.0",
|
||||
"sass": "^1.26.5",
|
||||
"sass": "^1.36.0",
|
||||
"sass-lint": "^1.13.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
+6
-2
@@ -52,7 +52,7 @@
|
||||
"request": "~2.68.0",
|
||||
"tunnel-agent": "~0.6.0",
|
||||
"cryptiles": "~4.1.2",
|
||||
"prismjs": "~1.23.0",
|
||||
"prismjs": "^1.24.0",
|
||||
"npm-user-validate": "^1.0.1",
|
||||
"caniuse-lite": "^1.0.30001157",
|
||||
"immer": "^8.0.1",
|
||||
@@ -71,7 +71,11 @@
|
||||
"ws": "^7.4.6",
|
||||
"css-what": "^5.0.1",
|
||||
"trim-newlines": "^3.0.1",
|
||||
"clean-css": "^4.1.11"
|
||||
"clean-css": "^4.1.11",
|
||||
"mem": "^4.0.0",
|
||||
"minimist": "^1.2.3",
|
||||
"normalize-url": "^4.5.1",
|
||||
"glob-parent": "^5.1.2"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<form.input
|
||||
@value={{@model.type}}
|
||||
@label={{t 'form.type.label'}}
|
||||
readonly={{true}}
|
||||
@disabled={{true}}
|
||||
/>
|
||||
|
||||
<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.input
|
||||
@name='vault_path'
|
||||
@type='text'
|
||||
@value={{@model.attributes.path}}
|
||||
@label={{t 'resources.credential-library.form.vault_path.label'}}
|
||||
@error={{@model.errors.attributes_path}}
|
||||
@helperText={{t 'resources.credential-library.form.vault_path.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_path}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_path as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.fieldset>
|
||||
<:title>{{t 'resources.credential-library.form.http_method.label'}}</:title>
|
||||
<:description>{{t
|
||||
'resources.credential-library.form.http_method.help'
|
||||
}}</:description>
|
||||
<:body>
|
||||
<form.radioGroup
|
||||
@name='http_method'
|
||||
@selectedValue={{@model.attributes.http_method}}
|
||||
@changed={{fn (mut @model.attributes.http_method)}}
|
||||
@inline={{true}}
|
||||
disabled={{@model.isSaving}}
|
||||
as |radioGroup|
|
||||
>
|
||||
{{#each this.httpMethodOptions as |httpMethod|}}
|
||||
<radioGroup.radio @label={{httpMethod}} @value={{httpMethod}} />
|
||||
{{/each}}
|
||||
</form.radioGroup>
|
||||
</:body>
|
||||
</form.fieldset>
|
||||
|
||||
{{#if this.isHttpRequestBodyAllowed}}
|
||||
<form.textarea
|
||||
@name='http_request_body'
|
||||
@type='text'
|
||||
@value={{@model.attributes.http_request_body}}
|
||||
@label={{t 'resources.credential-library.form.http_request_body.label'}}
|
||||
@error={{@model.errors.attributes_http_request_body}}
|
||||
@helperText={{t
|
||||
'resources.credential-library.form.http_request_body.help'
|
||||
}}
|
||||
@disabled={{this.cannotSaveHttpRequestBody}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_http_request_body}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_http_request_body 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}
|
||||
*/
|
||||
httpMethodOptions = options.http_method;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@error={{@model.errors.base}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
<form.input
|
||||
@value={{@model.type}}
|
||||
@label={{t 'form.type.label'}}
|
||||
readonly={{true}}
|
||||
@disabled={{true}}
|
||||
/>
|
||||
|
||||
<form.input
|
||||
@name='name'
|
||||
@type='name'
|
||||
@value={{@model.name}}
|
||||
@label={{t 'form.name.label'}}
|
||||
@helperText={{t 'form.name.help'}}
|
||||
@error={{@model.errors.name}}
|
||||
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='address'
|
||||
@type='text'
|
||||
@value={{@model.attributes.address}}
|
||||
@label={{t 'resources.credential-store.form.address.label'}}
|
||||
@error={{@model.errors.attributes_address}}
|
||||
@helperText={{t 'resources.credential-store.form.address.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_address}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_address as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.input
|
||||
@name='token'
|
||||
@type='text'
|
||||
@value={{@model.attributes.token}}
|
||||
@label={{t 'resources.credential-store.form.token.label'}}
|
||||
@error={{@model.errors.attributes_token}}
|
||||
@helperText={{t 'resources.credential-store.form.token.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_token}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_token as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
{{#unless @model.isNew}}
|
||||
<form.input
|
||||
@value={{@model.attributes.token_hmac}}
|
||||
@label={{t 'resources.credential-store.form.token_hmac.label'}}
|
||||
readonly={{true}}
|
||||
@disabled={{true}}
|
||||
/>
|
||||
{{/unless}}
|
||||
|
||||
<form.input
|
||||
@name='namespace'
|
||||
@type='text'
|
||||
@value={{@model.attributes.namespace}}
|
||||
@label={{t 'resources.credential-store.form.namespace.label'}}
|
||||
@error={{@model.errors.attributes_namespace}}
|
||||
@helperText={{t 'resources.credential-store.form.namespace.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_namespace}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_namespace as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.input
|
||||
@name='tls_server_name'
|
||||
@type='text'
|
||||
@value={{@model.attributes.tls_server_name}}
|
||||
@label={{t 'resources.credential-store.form.tls_server_name.label'}}
|
||||
@error={{@model.errors.attributes_tls_server_name}}
|
||||
@helperText={{t 'resources.credential-store.form.tls_server_name.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_tls_server_name}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_tls_server_name as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.checkbox
|
||||
@label={{t 'resources.credential-store.form.tls_skip_verify.label'}}
|
||||
@helperText={{t 'resources.credential-store.form.tls_skip_verify.help'}}
|
||||
@checked={{@model.attributes.tls_skip_verify}}
|
||||
onChange={{fn
|
||||
(mut @model.attributes.tls_skip_verify)
|
||||
(if @model.attributes.tls_skip_verify false true)
|
||||
}}
|
||||
@error={{@model.errors.attributes_tls_skip_verify}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_tls_skip_verify}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_tls_skip_verify as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.checkbox>
|
||||
|
||||
<form.textarea
|
||||
@name='client_certificate'
|
||||
@type='text'
|
||||
@value={{@model.attributes.client_certificate}}
|
||||
@label={{t 'resources.credential-store.form.client_certificate.label'}}
|
||||
@error={{@model.errors.attributes_client_certificate}}
|
||||
@helperText={{t 'resources.credential-store.form.client_certificate.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_client_certificate}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_client_certificate as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
|
||||
<form.textarea
|
||||
@name='client_certificate_key'
|
||||
@type='text'
|
||||
@value={{@model.attributes.client_certificate_key}}
|
||||
@label={{t 'resources.credential-store.form.client_certificate_key.label'}}
|
||||
@error={{@model.errors.attributes_client_certificate_key}}
|
||||
@helperText={{t
|
||||
'resources.credential-store.form.client_certificate_key.help'
|
||||
}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_client_certificate_key}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_client_certificate_key as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
|
||||
{{#unless @model.isNew}}
|
||||
<form.input
|
||||
@value={{@model.attributes.client_certificate_key_hmac}}
|
||||
@label={{t
|
||||
'resources.credential-store.form.client_certificate_key_hmac.label'
|
||||
}}
|
||||
readonly={{true}}
|
||||
@disabled={{true}}
|
||||
/>
|
||||
{{/unless}}
|
||||
|
||||
<form.textarea
|
||||
@name='ca_cert'
|
||||
@type='text'
|
||||
@value={{@model.attributes.ca_cert}}
|
||||
@label={{t 'resources.credential-store.form.ca_cert.label'}}
|
||||
@error={{@model.errors.attributes_ca_cert}}
|
||||
@helperText={{t 'resources.credential-store.form.ca_cert.help'}}
|
||||
disabled={{@model.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|
|
||||
>
|
||||
{{#if @model.errors.attributes_ca_cert}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.errors.attributes_ca_cert as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
|
||||
<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,63 @@
|
||||
{{#if this.hasAvailableCredentialLibraries}}
|
||||
<Rose::Form
|
||||
class='full-width'
|
||||
@onSubmit={{fn this.submit @submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@error={{@model.errors.base}}
|
||||
as |form|
|
||||
>
|
||||
<form.actions
|
||||
@submitText={{t 'resources.target.actions.add-credential-libraries'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
|
||||
<Rose::Table as |table|>
|
||||
<table.header as |header|>
|
||||
<header.row as |row|>
|
||||
<row.headerCell>{{t 'form.id.label'}}</row.headerCell>
|
||||
<row.headerCell>{{t 'form.name.label'}}</row.headerCell>
|
||||
<row.headerCell>{{t 'form.type.label'}}</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
{{#each this.filteredCredentialLibraries as |credentialLibrary|}}
|
||||
<body.row as |row|>
|
||||
<row.headerCell>
|
||||
<form.checkbox
|
||||
@label={{credentialLibrary.id}}
|
||||
@description={{credentialLibrary.description}}
|
||||
onChange={{fn this.toggleCredentialLibrary credentialLibrary}}
|
||||
/>
|
||||
</row.headerCell>
|
||||
<row.cell>{{credentialLibrary.name}}</row.cell>
|
||||
<row.cell>
|
||||
<Rose::Badge>{{t
|
||||
(concat
|
||||
'resources.credential-library.types.' credentialLibrary.type
|
||||
)
|
||||
}}</Rose::Badge>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
</Rose::Form>
|
||||
{{/if}}
|
||||
|
||||
{{#unless this.hasAvailableCredentialLibraries}}
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message
|
||||
@title={{t 'resources.target.credential-library.messages.none.title'}}
|
||||
as |message|
|
||||
>
|
||||
<message.description>
|
||||
{{t 'resources.target.credential-library.messages.none.description'}}
|
||||
</message.description>
|
||||
<message.link @route='scopes.scope.targets.target.credential-libraries'>
|
||||
<Rose::Icon @name='chevron-left' />
|
||||
{{t 'actions.back'}}
|
||||
</message.link>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
{{/unless}}
|
||||
@@ -0,0 +1,65 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { computed, action } from '@ember/object';
|
||||
import { A } from '@ember/array';
|
||||
|
||||
export default class FormTargetAddCredentialLibrariesIndexComponent extends Component {
|
||||
// =properties
|
||||
|
||||
/**
|
||||
* Array of selected credential library IDs.
|
||||
* @type {EmberArray}
|
||||
*/
|
||||
selectedCredentialLibraryIDs = A();
|
||||
|
||||
/**
|
||||
* Checks for unassigned credential libraries.
|
||||
* @param {[CredentialLibraryModel]} filteredCredentialLibraries
|
||||
* @type {boolean}
|
||||
*/
|
||||
@computed('filteredCredentialLibraries.length')
|
||||
get hasAvailableCredentialLibraries() {
|
||||
return this.filteredCredentialLibraries.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out credential libraries not already added to the target.
|
||||
* @type {[CredentialLibraryModel]}
|
||||
*/
|
||||
@computed(
|
||||
'args.{credentialLibraries.[],model.application_credential_library_ids.[]}'
|
||||
)
|
||||
get filteredCredentialLibraries() {
|
||||
// Get IDs for credential libraries already added to the current target
|
||||
const currentCredentialLibraryIDs =
|
||||
this.args.model.application_credential_library_ids.map(
|
||||
(credentialLibrary) => credentialLibrary.value
|
||||
);
|
||||
const notAddedCredentialLibraries = this.args.credentialLibraries.filter(
|
||||
({ id }) => !currentCredentialLibraryIDs.includes(id)
|
||||
);
|
||||
return notAddedCredentialLibraries;
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Add/Remove credential library to current selection
|
||||
* @param CredentialLibraryModel
|
||||
*/
|
||||
@action
|
||||
toggleCredentialLibrary(credentialLibrary) {
|
||||
if (!this.selectedCredentialLibraryIDs.includes(credentialLibrary.id)) {
|
||||
this.selectedCredentialLibraryIDs.addObject(credentialLibrary.id);
|
||||
} else {
|
||||
this.selectedCredentialLibraryIDs.removeObject(credentialLibrary.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit selected credential library ids
|
||||
*/
|
||||
@action
|
||||
submit(fn) {
|
||||
fn(this.selectedCredentialLibraryIDs);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
|
||||
<form.actions
|
||||
@submitText={{t 'actions.add-host-sets'}}
|
||||
@submitText={{t 'resources.target.actions.add-host-sets'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeCredentialStoresController extends Controller {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// = attributes
|
||||
/**
|
||||
* Translated breadcrumb
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('resources.credential-store.title_plural');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Controller from '@ember/controller';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreController extends Controller {
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* A credential store breadcrumb
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.model.displayName;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesController extends Controller {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Translated credential libraries breadcrumb
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('resources.credential-library.title_plural');
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesNewController extends Controller {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Translated new credential libraries breadcrumb
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('titles.new');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeTargetsTargetCredentialLibrariesController extends Controller {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Translated credential library breadcrumb
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('resources.credential-library.title_plural');
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,8 @@ Router.map(function () {
|
||||
this.route('target', { path: ':target_id' }, function () {
|
||||
this.route('host-sets', function () {});
|
||||
this.route('add-host-sets');
|
||||
this.route('credential-libraries');
|
||||
this.route('add-credential-libraries');
|
||||
});
|
||||
this.route('new');
|
||||
});
|
||||
@@ -86,6 +88,23 @@ Router.map(function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
this.route('credential-stores', function () {
|
||||
this.route(
|
||||
'credential-store',
|
||||
{ path: ':credential_store_id' },
|
||||
function () {
|
||||
this.route('credential-libraries', function () {
|
||||
this.route('new');
|
||||
this.route(
|
||||
'credential-library',
|
||||
{ path: ':credential_library_id' },
|
||||
function () {}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
this.route('new');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { action } from '@ember/object';
|
||||
import loading from 'ember-loading/decorator';
|
||||
import { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeCredentialStoresRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Load all credential stores under current scope
|
||||
* @returns {Promise[CredentialStoreModel]}
|
||||
*/
|
||||
async model() {
|
||||
const { id: scope_id } = this.modelFor('scopes.scope');
|
||||
return this.store.query('credential-store', { scope_id });
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Handle save
|
||||
* @param {CredentialStoreModel} credentialStore
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message)
|
||||
@notifySuccess('notifications.save-success')
|
||||
async save(credentialStore) {
|
||||
await credentialStore.save();
|
||||
await this.transitionTo(
|
||||
'scopes.scope.credential-stores.credential-store',
|
||||
credentialStore
|
||||
);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback changes on credential stores.
|
||||
* @param {CredentialStoreModel} credentialStore
|
||||
*/
|
||||
@action
|
||||
cancel(credentialStore) {
|
||||
const { isNew } = credentialStore;
|
||||
credentialStore.rollbackAttributes();
|
||||
if (isNew) this.transitionTo('scopes.scope.credential-stores');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the credential store and redirects to index
|
||||
* @param {CredentialStoreModel} credentialStore
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@confirm('questions.delete-confirm')
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.delete-success')
|
||||
async delete(credentialStore) {
|
||||
await credentialStore.destroyRecord();
|
||||
await this.replaceWith('scopes.scope.credential-stores');
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreRoute extends Route {
|
||||
/**
|
||||
* Load a specific credential store in current scope
|
||||
* @return {Promise[CredentialStoreModel]}
|
||||
*/
|
||||
async model({ credential_store_id }) {
|
||||
return this.store.findRecord('credential-store', credential_store_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render credential store header, navigation and actions page sections.
|
||||
* @override
|
||||
*/
|
||||
renderTemplate() {
|
||||
super.renderTemplate(...arguments);
|
||||
|
||||
this.render('scopes/scope/credential-stores/credential-store/-header', {
|
||||
into: 'scopes/scope/credential-stores/credential-store',
|
||||
outlet: 'header',
|
||||
});
|
||||
|
||||
this.render('scopes/scope/credential-stores/credential-store/-navigation', {
|
||||
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',
|
||||
});
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { action } from '@ember/object';
|
||||
import loading from 'ember-loading/decorator';
|
||||
import { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Loads all credential libraries under the current credential store.
|
||||
* @return {Promise{[CredentialLibraryModel]}}
|
||||
*/
|
||||
model() {
|
||||
const { id: credential_store_id } = this.modelFor(
|
||||
'scopes.scope.credential-stores.credential-store'
|
||||
);
|
||||
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.credential-library',
|
||||
credentialLibrary
|
||||
);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle delete of a credential library
|
||||
* @param {CredentialLibraryModel} credentialLibrary
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@confirm('questions.delete-confirm')
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.delete-success')
|
||||
async delete(credentialLibrary) {
|
||||
await credentialLibrary.destroyRecord();
|
||||
await this.replaceWith(
|
||||
'scopes.scope.credential-stores.credential-store.credential-libraries'
|
||||
);
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesCredentialLibraryRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Load a credential-library using current credential store and its parent scope.
|
||||
* @param {object} params
|
||||
* @param {string} params.credential_library_id
|
||||
* @return {CredentialLibraryModel}
|
||||
*/
|
||||
async model({ credential_library_id }) {
|
||||
return this.store.findRecord('credential-library', credential_library_id, {
|
||||
reload: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the 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/credential-library/-header',
|
||||
{
|
||||
into: 'scopes/scope/credential-stores/credential-store',
|
||||
outlet: 'header',
|
||||
}
|
||||
);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/credential-stores/credential-store/credential-libraries/credential-library/-navigation',
|
||||
{
|
||||
into: 'scopes/scope/credential-stores/credential-store',
|
||||
outlet: 'navigation',
|
||||
}
|
||||
);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/credential-stores/credential-store/credential-libraries/credential-library/-actions',
|
||||
{
|
||||
into: 'scopes/scope/credential-stores/credential-store',
|
||||
outlet: 'actions',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesCredentialLibraryIndexRoute extends Route {}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesIndexRoute extends Route {}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
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, type } = this.modelFor(
|
||||
'scopes.scope.credential-stores.credential-store'
|
||||
);
|
||||
return this.store.createRecord('credential-library', {
|
||||
type,
|
||||
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',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeCredentialStoresCredentialStoreIndexRoute extends Route {}
|
||||
@@ -0,0 +1,3 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeCredentialStoresIndexRoute extends Route {}
|
||||
@@ -0,0 +1,15 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeCredentialStoresNewRoute extends Route {
|
||||
// =methods
|
||||
/**
|
||||
* Creates a new unsaved credential-store
|
||||
*/
|
||||
model() {
|
||||
const scopeModel = this.modelFor('scopes.scope');
|
||||
return this.store.createRecord('credential-store', {
|
||||
type: 'vault',
|
||||
scopeModel,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { all } from 'rsvp';
|
||||
import { action } from '@ember/object';
|
||||
import loading from 'ember-loading/decorator';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeTargetsTargetAddCredentialLibrariesRoute extends Route {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
@service notify;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Empty out any previously loaded credential libraries.
|
||||
*/
|
||||
beforeModel() {
|
||||
this.store.unloadAll('credential-library');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current target and all credential libraries.
|
||||
* @return {{target: TargetModel, credentialLibraries: [CredentialLibraryModel]}}
|
||||
*/
|
||||
async model() {
|
||||
const target = this.modelFor('scopes.scope.targets.target');
|
||||
const { id: scope_id } = this.modelFor('scopes.scope');
|
||||
const credentialStores = await this.store.query('credential-store', {
|
||||
scope_id,
|
||||
});
|
||||
await all(
|
||||
credentialStores.map(({ id: credential_store_id }) =>
|
||||
this.store.query('credential-library', { credential_store_id })
|
||||
)
|
||||
);
|
||||
const credentialLibraries = this.store.peekAll('credential-library');
|
||||
return {
|
||||
target,
|
||||
credentialLibraries,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders credential libraries specific header template.
|
||||
* @override
|
||||
*/
|
||||
renderTemplate() {
|
||||
super.renderTemplate(...arguments);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/targets/target/add-credential-libraries/-header',
|
||||
{
|
||||
into: 'scopes/scope/targets/target',
|
||||
outlet: 'header',
|
||||
}
|
||||
);
|
||||
|
||||
this.render('-empty', {
|
||||
into: 'scopes/scope/targets/target',
|
||||
outlet: 'navigation',
|
||||
});
|
||||
|
||||
this.render('-empty', {
|
||||
into: 'scopes/scope/targets/target',
|
||||
outlet: 'actions',
|
||||
});
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Add credential libraries to current target
|
||||
* @param {TargetModel} target
|
||||
* @param {[string]} credentialLibraryIDs
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.add-success')
|
||||
async save(target, credentialLibraryIDs) {
|
||||
await target.addCredentialLibraries(credentialLibraryIDs);
|
||||
this.replaceWith('scopes.scope.targets.target.credential-libraries');
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to target credential libraries as if nothing ever happened.
|
||||
*/
|
||||
@action
|
||||
cancel() {
|
||||
this.replaceWith('scopes.scope.targets.target.credential-libraries');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { all } from 'rsvp';
|
||||
import { action } from '@ember/object';
|
||||
import loading from 'ember-loading/decorator';
|
||||
import { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeTargetsTargetCredentialLibrariesRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Loads all credential libraries under the current target.
|
||||
* @return {Promise{[CredentialLibraryModel]}}
|
||||
*/
|
||||
beforeModel() {
|
||||
const { application_credential_library_ids: credential_libraries } =
|
||||
this.modelFor('scopes.scope.targets.target');
|
||||
return all(
|
||||
credential_libraries.map((credential_library) =>
|
||||
this.store.findRecord('credential-library', credential_library.value, {
|
||||
reload: true,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previously loaded target instance.
|
||||
* @return {TargetModel}
|
||||
*/
|
||||
model() {
|
||||
return this.modelFor('scopes.scope.targets.target');
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Remove a credential library from the current target.
|
||||
* @param {TargetModel} target
|
||||
* @param {CredentialLibraryModel} credentialLibrary
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@confirm('questions.remove-confirm')
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.remove-success')
|
||||
async removeCredentialLibrary(target, credentialLibrary) {
|
||||
await target.removeCredentialLibrary(credentialLibrary.id);
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,13 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.rose-table-header-cell {
|
||||
p {
|
||||
color: var(--ui-gray-subtler-1);
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.rose-form-checkbox {
|
||||
margin-bottom: 0;
|
||||
|
||||
|
||||
@@ -83,6 +83,12 @@
|
||||
<Rose::Icon @name='app-icons/host-catalogs' @size='medium' />
|
||||
{{t 'resources.host-catalog.title_plural'}}
|
||||
</nav.link>
|
||||
{{#if (feature-flag 'credential-store')}}
|
||||
<nav.link @route='scopes.scope.credential-stores'>
|
||||
<Rose::Icon @name='app-icons/credentials' @size='medium' />
|
||||
{{t 'resources.credential-store.title_plural'}}
|
||||
</nav.link>
|
||||
{{/if}}
|
||||
</Rose::Nav::Sidebar>
|
||||
{{/if}}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{{page-title (t 'resources.credential-store.title_plural')}}
|
||||
|
||||
{{outlet}}
|
||||
@@ -0,0 +1,24 @@
|
||||
{{page-title @model.displayName}}
|
||||
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
{{outlet 'header'}}
|
||||
</page.header>
|
||||
|
||||
<page.navigation>
|
||||
{{outlet 'navigation'}}
|
||||
</page.navigation>
|
||||
|
||||
<page.actions>
|
||||
{{outlet 'actions'}}
|
||||
</page.actions>
|
||||
|
||||
<page.body>
|
||||
{{outlet}}
|
||||
</page.body>
|
||||
</Rose::Layout::Page>
|
||||
@@ -0,0 +1,20 @@
|
||||
<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>
|
||||
<dropdown.separator />
|
||||
<dropdown.button
|
||||
@style='danger'
|
||||
@disabled={{@model.canSave}}
|
||||
{{on 'click' (route-action 'delete' @model)}}
|
||||
>
|
||||
{{t 'resources.credential-store.actions.delete'}}
|
||||
</dropdown.button>
|
||||
</Rose::Dropdown>
|
||||
@@ -0,0 +1,9 @@
|
||||
<h2>{{t 'resources.credential-store.title'}}</h2>
|
||||
<p>{{t 'resources.credential-store.description'}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
<code>{{@model.id}}</code>
|
||||
</Copyable>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<Rose::Nav::Tabs as |nav|>
|
||||
<nav.link @route='scopes.scope.credential-stores.credential-store.index'>
|
||||
{{t 'titles.details'}}
|
||||
</nav.link>
|
||||
<nav.link
|
||||
@route='scopes.scope.credential-stores.credential-store.credential-libraries'
|
||||
>
|
||||
{{t 'resources.credential-library.title_plural'}}
|
||||
</nav.link>
|
||||
</Rose::Nav::Tabs>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{{page-title (t 'resources.credential-library.title_plural')}}
|
||||
|
||||
{{outlet}}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{{page-title @model.displayName}}
|
||||
|
||||
{{outlet}}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<Rose::Dropdown
|
||||
@text={{t 'actions.manage'}}
|
||||
@dropdownRight={{true}}
|
||||
as |dropdown|
|
||||
>
|
||||
<dropdown.button
|
||||
@style='danger'
|
||||
@disabled={{@model.canSave}}
|
||||
{{on 'click' (route-action 'delete' @model)}}
|
||||
>
|
||||
{{t 'resources.credential-library.actions.delete'}}
|
||||
</dropdown.button>
|
||||
</Rose::Dropdown>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<h2>
|
||||
{{t 'resources.credential-library.title'}}
|
||||
<DocLink @doc='credential-library' @iconSize='large' />
|
||||
</h2>
|
||||
<p>{{t 'resources.credential-library.description'}}</p>
|
||||
<Copyable
|
||||
@text={{@model.id}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
<code>{{@model.id}}</code>
|
||||
</Copyable>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<Rose::Nav::Tabs as |nav|>
|
||||
<nav.link
|
||||
@route='scopes.scope.credential-stores.credential-store.credential-libraries.credential-library.index'
|
||||
>
|
||||
{{t 'titles.details'}}
|
||||
</nav.link>
|
||||
</Rose::Nav::Tabs>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<Form::CredentialLibrary
|
||||
@model={{@model}}
|
||||
@submit={{route-action 'save' @model}}
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
/>
|
||||
Loaded 100 of 153 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user