Merge pull request #636 from hashicorp/feature-credentials-modeling

feat: 🎸 initial credential-store modeling and mocking
This commit is contained in:
Susmitha Girumala authored and GitHub committed 2021-07-08 08:18:40 -04:00
commit ef80dd87c1
18 files changed
+389

No files matched your search

@@ -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 GeneratedAccountModel 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,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,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;
}
@@ -0,0 +1,3 @@
import ApplicationSerializer from './application';
export default class CredentialStoreSerializer extends ApplicationSerializer {}
@@ -0,0 +1,24 @@
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];
return value;
}
}
@@ -0,0 +1 @@
export { default } from 'api/models/credential-store';
@@ -0,0 +1 @@
export { default } from 'api/models/fragment-credential-store-attributes';
@@ -0,0 +1 @@
export { default } from 'api/serializers/credential-store';
@@ -0,0 +1 @@
export { default } from 'api/serializers/fragment-credential-store-attributes';
@@ -0,0 +1,25 @@
import factory from '../generated/factories/credential-store';
import { random, internet, datatype } from 'faker';
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',
};
}
},
});
@@ -0,0 +1,12 @@
import { Factory } from 'ember-cli-mirage';
import { random, date } from 'faker';
/**
* GeneratedAuthMethodModelFactory
*/
export default Factory.extend({
name: () => random.words(),
description: () => random.words(),
created_time: () => date.recent(),
updated_time: () => date.recent(),
});
@@ -0,0 +1,5 @@
import { Model, belongsTo } from 'ember-cli-mirage';
export default Model.extend({
scope: belongsTo(),
});
+1
View File
@@ -49,5 +49,6 @@ export default function (server) {
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 });
});
}
@@ -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;
},
});
@@ -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,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);
});
});
@@ -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,31 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module(
'Unit | Serializer | 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 serializer = store.serializerFor(
'fragment-credential-store-attributes'
);
assert.ok(serializer);
});
test('it serializes records', function (assert) {
let store = this.owner.lookup('service:store');
let record = store.createRecord(
'fragment-credential-store-attributes',
{}
);
let serializedRecord = record.serialize();
assert.ok(serializedRecord);
});
}
);