UI: VAULT-14972 VAULT-13808 VAULT-12777 Remove pki beta, old pki, remove unused cert attributes (#20062)
This commit is contained in:
parent
1b5d527521
commit
e6b890f7ed
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Adapter from './pki';
|
||||
|
||||
export default Adapter.extend({
|
||||
url(_, snapshot) {
|
||||
const backend = snapshot.attr('backend');
|
||||
return `/v1/${backend}/root/sign-intermediate`;
|
||||
},
|
||||
});
|
|
@ -1,73 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { parsePkiCert } from 'vault/utils/parse-pki-cert';
|
||||
import ApplicationAdapter from './application';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
namespace: 'v1',
|
||||
|
||||
url(snapshot, action) {
|
||||
const { backend, caType, type } = snapshot.attributes();
|
||||
if (action === 'sign-intermediate') {
|
||||
return `/v1/${backend}/root/sign-intermediate`;
|
||||
}
|
||||
if (action === 'set-signed-intermediate') {
|
||||
return `/v1/${backend}/intermediate/set-signed`;
|
||||
}
|
||||
if (action === 'upload') {
|
||||
return `/v1/${backend}/config/ca`;
|
||||
}
|
||||
return `/v1/${backend}/${caType}/generate/${type}`;
|
||||
},
|
||||
|
||||
createRecordOrUpdate(store, type, snapshot, requestType) {
|
||||
const serializer = store.serializerFor('application');
|
||||
const isUpload = snapshot.attr('uploadPemBundle');
|
||||
const isSetSignedIntermediate = snapshot.adapterOptions.method === 'setSignedIntermediate';
|
||||
let action = snapshot.adapterOptions.method === 'signIntermediate' ? 'sign-intermediate' : null;
|
||||
let data;
|
||||
if (isUpload) {
|
||||
action = 'upload';
|
||||
data = { pem_bundle: snapshot.attr('pemBundle') };
|
||||
} else if (isSetSignedIntermediate) {
|
||||
action = 'set-signed-intermediate';
|
||||
data = { certificate: snapshot.attr('certificate') };
|
||||
} else {
|
||||
data = serializer.serialize(snapshot, requestType);
|
||||
|
||||
// The type parameter is serialized but is part of the URL. This means
|
||||
// we'll get an unknown parameter warning back from the server if we
|
||||
// send it. Remove it instead.
|
||||
delete data.type;
|
||||
}
|
||||
|
||||
return this.ajax(this.url(snapshot, action), 'POST', { data }).then((response) => {
|
||||
// uploading CA, setting signed intermediate cert, and attempting to generate
|
||||
// a new CA if one exists, all return a 204
|
||||
if (!response) {
|
||||
response = {};
|
||||
}
|
||||
response.id = snapshot.id;
|
||||
response.modelName = type.modelName;
|
||||
// only parse if certificate is attached to response
|
||||
if (response.data && response.data.certificate) {
|
||||
const caCertMetadata = parsePkiCert(response.data);
|
||||
const transformedResponse = { ...response, ...caCertMetadata };
|
||||
store.pushPayload(type.modelName, transformedResponse);
|
||||
} else {
|
||||
store.pushPayload(type.modelName, response);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createRecord() {
|
||||
return this.createRecordOrUpdate(...arguments);
|
||||
},
|
||||
|
||||
updateRecord() {
|
||||
return this.createRecordOrUpdate(...arguments);
|
||||
},
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Adapter from './pki';
|
||||
|
||||
export default Adapter.extend({
|
||||
url(role, snapshot) {
|
||||
if (snapshot.attr('signVerbatim') === true) {
|
||||
return `/v1/${role.backend}/sign-verbatim/${role.name}`;
|
||||
}
|
||||
return `/v1/${role.backend}/sign/${role.name}`;
|
||||
},
|
||||
|
||||
pathForType() {
|
||||
return 'sign';
|
||||
},
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { assert } from '@ember/debug';
|
||||
import ApplicationAdapter from './application';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
namespace: 'v1',
|
||||
|
||||
url(/*role*/) {
|
||||
assert('Override the `url` method to extend the PKI adapter', false);
|
||||
},
|
||||
|
||||
createRecord(store, type, snapshot, requestType) {
|
||||
const serializer = store.serializerFor(type.modelName);
|
||||
const data = serializer.serialize(snapshot, requestType);
|
||||
const role = snapshot.attr('role');
|
||||
|
||||
return this.ajax(this.url(role, snapshot), 'POST', { data }).then((response) => {
|
||||
response.id = snapshot.id;
|
||||
response.modelName = type.modelName;
|
||||
store.pushPayload(type.modelName, response);
|
||||
});
|
||||
},
|
||||
});
|
|
@ -1,133 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import AdapterError from '@ember-data/adapter/error';
|
||||
import { hash, resolve } from 'rsvp';
|
||||
import { capitalize } from '@ember/string';
|
||||
import { set } from '@ember/object';
|
||||
import ApplicationAdapter from '../application';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
namespace: 'v1',
|
||||
|
||||
urlFor(backend, section) {
|
||||
const urls = {
|
||||
tidy: `/v1/${backend}/tidy`,
|
||||
urls: `/v1/${backend}/config/urls`,
|
||||
crl: `/v1/${backend}/config/crl`,
|
||||
};
|
||||
return urls[section];
|
||||
},
|
||||
|
||||
createOrUpdate(store, type, snapshot) {
|
||||
const url = this.urlFor(snapshot.record.get('backend'), snapshot.adapterOptions.method);
|
||||
const serializer = store.serializerFor(type.modelName);
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
const data = snapshot.adapterOptions.fields.reduce((data, field) => {
|
||||
const attr = snapshot.attr(field);
|
||||
if (attr) {
|
||||
serializer.serializeAttribute(snapshot, data, field, attr);
|
||||
} else {
|
||||
data[serializer.keyForAttribute(field)] = attr;
|
||||
}
|
||||
return data;
|
||||
}, {});
|
||||
return this.ajax(url, 'POST', { data }).then((resp) => {
|
||||
const response = resp || {};
|
||||
response.id = `${snapshot.record.get('backend')}-${snapshot.adapterOptions.method}`;
|
||||
return response;
|
||||
});
|
||||
},
|
||||
|
||||
createRecord() {
|
||||
return this.createOrUpdate(...arguments);
|
||||
},
|
||||
|
||||
updateRecord() {
|
||||
return this.createOrUpdate(...arguments, 'update');
|
||||
},
|
||||
|
||||
fetchSection(backendPath, section) {
|
||||
const sections = ['cert', 'urls', 'crl', 'tidy'];
|
||||
if (!section || !sections.includes(section)) {
|
||||
const error = new AdapterError();
|
||||
set(error, 'httpStatus', 404);
|
||||
throw error;
|
||||
}
|
||||
return this[`fetch${capitalize(section)}`](backendPath);
|
||||
},
|
||||
|
||||
id(backendPath) {
|
||||
return backendPath + '-config-ca';
|
||||
},
|
||||
|
||||
fetchCert(backendPath) {
|
||||
// these are all un-authed so using `fetch` directly works
|
||||
const derURL = `/v1/${backendPath}/ca`;
|
||||
const pemURL = `${derURL}/pem`;
|
||||
const chainURL = `${derURL}_chain`;
|
||||
|
||||
return hash({
|
||||
backend: backendPath,
|
||||
id: this.id(backendPath),
|
||||
der: this.rawRequest(derURL, 'GET', { unauthenticated: true }).then((response) => response.blob()),
|
||||
pem: this.rawRequest(pemURL, 'GET', { unauthenticated: true }).then((response) => response.text()),
|
||||
ca_chain: this.rawRequest(chainURL, 'GET', { unauthenticated: true }).then((response) =>
|
||||
response.text()
|
||||
),
|
||||
});
|
||||
},
|
||||
|
||||
fetchUrls(backendPath) {
|
||||
const url = `/v1/${backendPath}/config/urls`;
|
||||
const id = this.id(backendPath);
|
||||
return this.ajax(url, 'GET')
|
||||
.then((resp) => {
|
||||
resp.id = id;
|
||||
resp.backend = backendPath;
|
||||
return resp;
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.httpStatus === 404) {
|
||||
return resolve({ id });
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
fetchCrl(backendPath) {
|
||||
const url = `/v1/${backendPath}/config/crl`;
|
||||
const id = this.id(backendPath);
|
||||
return this.ajax(url, 'GET')
|
||||
.then((resp) => {
|
||||
resp.id = id;
|
||||
resp.backend = backendPath;
|
||||
return resp;
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.httpStatus === 404) {
|
||||
return { id };
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
fetchTidy(backendPath) {
|
||||
const id = this.id(backendPath);
|
||||
return resolve({ id, backend: backendPath });
|
||||
},
|
||||
|
||||
queryRecord(store, type, query) {
|
||||
const { backend, section } = query;
|
||||
return this.fetchSection(backend, section).then((resp) => {
|
||||
resp.backend = backend;
|
||||
return resp;
|
||||
});
|
||||
},
|
||||
});
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { assign } from '@ember/polyfills';
|
||||
import ApplicationAdapter from '../application';
|
||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||
|
||||
export default ApplicationAdapter.extend({
|
||||
namespace: 'v1',
|
||||
|
||||
createOrUpdate(store, type, snapshot, requestType) {
|
||||
const { name, backend } = snapshot.record;
|
||||
const serializer = store.serializerFor(type.modelName);
|
||||
const data = serializer.serialize(snapshot, requestType);
|
||||
const url = this.urlForRole(backend, name);
|
||||
|
||||
return this.ajax(url, 'POST', { data });
|
||||
},
|
||||
|
||||
createRecord() {
|
||||
return this.createOrUpdate(...arguments);
|
||||
},
|
||||
|
||||
updateRecord() {
|
||||
return this.createOrUpdate(...arguments, 'update');
|
||||
},
|
||||
|
||||
deleteRecord(store, type, snapshot) {
|
||||
const { id } = snapshot;
|
||||
return this.ajax(this.urlForRole(snapshot.record.get('backend'), id), 'DELETE');
|
||||
},
|
||||
|
||||
pathForType() {
|
||||
return 'roles';
|
||||
},
|
||||
|
||||
urlForRole(backend, id) {
|
||||
let url = `${this.buildURL()}/${encodePath(backend)}/roles`;
|
||||
if (id) {
|
||||
url = url + '/' + encodePath(id);
|
||||
}
|
||||
return url;
|
||||
},
|
||||
|
||||
optionsForQuery(id) {
|
||||
const data = {};
|
||||
if (!id) {
|
||||
data['list'] = true;
|
||||
}
|
||||
return { data };
|
||||
},
|
||||
|
||||
fetchByQuery(store, query) {
|
||||
const { id, backend } = query;
|
||||
return this.ajax(this.urlForRole(backend, id), 'GET', this.optionsForQuery(id)).then((resp) => {
|
||||
const data = {
|
||||
id,
|
||||
name: id,
|
||||
backend,
|
||||
};
|
||||
|
||||
return assign({}, resp, data);
|
||||
});
|
||||
},
|
||||
|
||||
query(store, type, query) {
|
||||
return this.fetchByQuery(store, query);
|
||||
},
|
||||
|
||||
queryRecord(store, type, query) {
|
||||
return this.fetchByQuery(store, query);
|
||||
},
|
||||
});
|
|
@ -68,7 +68,6 @@ export default class App extends Application {
|
|||
externalRoutes: {
|
||||
secrets: 'vault.cluster.secrets.backends',
|
||||
externalMountIssuer: 'vault.cluster.secrets.backend.pki.issuers.issuer.details',
|
||||
secretsListRoot: 'vault.cluster.secrets.backend.list-root',
|
||||
secretsListRootConfiguration: 'vault.cluster.secrets.backend.configuration',
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import { not } from '@ember/object/computed';
|
||||
import Component from '@ember/component';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: 'config-pki-ca',
|
||||
store: service('store'),
|
||||
flashMessages: service(),
|
||||
errors: null,
|
||||
|
||||
/*
|
||||
* @param boolean
|
||||
* @private
|
||||
* bool that gets flipped if you have a CA cert and click the Replace Cert button
|
||||
*/
|
||||
replaceCA: false,
|
||||
|
||||
/*
|
||||
* @param boolean
|
||||
* @private
|
||||
* bool that gets flipped if you push the click the "Set signed intermediate" button
|
||||
*/
|
||||
setSignedIntermediate: false,
|
||||
|
||||
/*
|
||||
* @param boolean
|
||||
* @private
|
||||
* bool that gets flipped if you push the click the "Set signed intermediate" button
|
||||
*/
|
||||
signIntermediate: false,
|
||||
|
||||
/*
|
||||
* @param boolean
|
||||
* @private
|
||||
*
|
||||
* true when there's no CA cert currently configured
|
||||
*/
|
||||
needsConfig: not('config.pem'),
|
||||
|
||||
/*
|
||||
* @param DS.Model
|
||||
* @private
|
||||
*
|
||||
* a `pki-ca-certificate` model used to back the form when uploading or creating a CA cert
|
||||
* created and set on `init`, and unloaded on willDestroy
|
||||
*
|
||||
*/
|
||||
model: null,
|
||||
|
||||
/*
|
||||
* @param DS.Model
|
||||
* @public
|
||||
*
|
||||
* a `pki-config` model - passed in in the component usage
|
||||
*
|
||||
*/
|
||||
config: null,
|
||||
|
||||
/*
|
||||
* @param Function
|
||||
* @public
|
||||
*
|
||||
* function that gets called to refresh the config model
|
||||
*
|
||||
*/
|
||||
onRefresh() {},
|
||||
|
||||
loading: false,
|
||||
|
||||
willDestroy() {
|
||||
const ca = this.model;
|
||||
if (ca && !ca.isDestroyed && !ca.isDestroying) {
|
||||
ca.unloadRecord();
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
|
||||
createOrReplaceModel(modelType) {
|
||||
const ca = this.model;
|
||||
const config = this.config;
|
||||
const store = this.store;
|
||||
const backend = config.get('backend');
|
||||
if (ca) {
|
||||
ca.unloadRecord();
|
||||
}
|
||||
const caCert = store.createRecord(modelType || 'pki-ca-certificate', {
|
||||
id: `${backend}-ca-cert`,
|
||||
backend,
|
||||
});
|
||||
this.set('model', caCert);
|
||||
},
|
||||
|
||||
/*
|
||||
* @private
|
||||
* @returns array
|
||||
*
|
||||
* When a CA is configured, we let them download
|
||||
* the CA in der, pem, and the CA Chain in pem (if one exists)
|
||||
*
|
||||
* This array provides the text and download hrefs for those links.
|
||||
*
|
||||
*/
|
||||
downloadHrefs: computed('config', 'config.{backend,pem,caChain,der}', function () {
|
||||
const config = this.config;
|
||||
const { backend, pem, caChain, der } = config;
|
||||
|
||||
if (!pem) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const pemFile = new Blob([pem], { type: 'text/plain' });
|
||||
const links = [
|
||||
{
|
||||
display: 'Download CA Certificate in PEM format',
|
||||
name: `${backend}_ca.pem`,
|
||||
url: URL.createObjectURL(pemFile),
|
||||
},
|
||||
{
|
||||
display: 'Download CA Certificate in DER format',
|
||||
name: `${backend}_ca.der`,
|
||||
url: URL.createObjectURL(der),
|
||||
},
|
||||
];
|
||||
if (caChain) {
|
||||
const caChainFile = new Blob([caChain], { type: 'text/plain' });
|
||||
links.push({
|
||||
display: 'Download CA Certificate Chain',
|
||||
name: `${backend}_ca_chain.pem`,
|
||||
url: URL.createObjectURL(caChainFile),
|
||||
});
|
||||
}
|
||||
return links;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
saveCA(method) {
|
||||
this.set('loading', true);
|
||||
const model = this.model;
|
||||
const isUpload = this.model.uploadPemBundle;
|
||||
model
|
||||
.save({ adapterOptions: { method } })
|
||||
.then(() => {
|
||||
if (method === 'setSignedIntermediate' || isUpload) {
|
||||
this.send('refresh');
|
||||
this.flashMessages.success('The certificate for this backend has been updated.');
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
this.set('errors', e.errors);
|
||||
})
|
||||
.finally(() => {
|
||||
this.set('loading', false);
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
this.setProperties({
|
||||
setSignedIntermediate: false,
|
||||
signIntermediate: false,
|
||||
replaceCA: false,
|
||||
});
|
||||
this.onRefresh();
|
||||
},
|
||||
toggleReplaceCA() {
|
||||
if (!this.replaceCA) {
|
||||
this.createOrReplaceModel();
|
||||
}
|
||||
this.toggleProperty('replaceCA');
|
||||
},
|
||||
toggleVal(name, val) {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
const model = name === 'signIntermediate' ? 'pki-ca-certificate-sign' : null;
|
||||
if (!this.get(name)) {
|
||||
this.createOrReplaceModel(model);
|
||||
}
|
||||
if (val !== undefined) {
|
||||
this.set(name, val);
|
||||
} else {
|
||||
this.toggleProperty(name);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: 'config-pki',
|
||||
flashMessages: service(),
|
||||
errors: null,
|
||||
/*
|
||||
*
|
||||
* @param String
|
||||
* @public
|
||||
* String corresponding to the route parameter for the current section
|
||||
*
|
||||
*/
|
||||
|
||||
section: null,
|
||||
|
||||
/*
|
||||
* @param DS.Model
|
||||
* @public
|
||||
*
|
||||
* a `pki-config` model - passed in in the component usage
|
||||
*
|
||||
*/
|
||||
config: null,
|
||||
|
||||
/*
|
||||
* @param Function
|
||||
* @public
|
||||
*
|
||||
* function that gets called to refresh the config model
|
||||
*
|
||||
*/
|
||||
onRefresh() {},
|
||||
|
||||
loading: false,
|
||||
|
||||
actions: {
|
||||
handleCrlTtl({ enabled, goSafeTimeString }) {
|
||||
this.config.disable = !enabled; // when TTL enabled, config disable=false
|
||||
this.config.expiry = goSafeTimeString;
|
||||
},
|
||||
save(section) {
|
||||
this.set('loading', true);
|
||||
const config = this.config;
|
||||
config
|
||||
.save({
|
||||
adapterOptions: {
|
||||
method: section,
|
||||
fields: get(config, `${section}Attrs`).map((attr) => attr.name),
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.flashMessages.success(`The ${section} config for this backend has been updated.`);
|
||||
// attrs aren't persistent for Tidy
|
||||
if (section === 'tidy') {
|
||||
config.rollbackAttributes();
|
||||
}
|
||||
this.send('refresh');
|
||||
})
|
||||
.catch((e) => {
|
||||
this.set('errors', e.errors);
|
||||
})
|
||||
.finally(() => {
|
||||
this.set('loading', false);
|
||||
});
|
||||
},
|
||||
refresh() {
|
||||
this.onRefresh();
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,29 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
/**
|
||||
* @module PkiCertPopup
|
||||
* PkiCertPopup component is the hotdog menu button that allows you to see details or revoke a certificate.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <PkiCertPopup @item={{@item}}/>
|
||||
* ```
|
||||
* @param {class} item - the PKI cert in question.
|
||||
*/
|
||||
|
||||
export default class PkiCertPopup extends Component {
|
||||
get item() {
|
||||
return this.args.item || null;
|
||||
}
|
||||
|
||||
@action
|
||||
delete(item) {
|
||||
item.save({ adapterOptions: { method: 'revoke' } });
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import RoleEdit from '../role-edit';
|
||||
|
||||
export default RoleEdit.extend({
|
||||
actions: {
|
||||
delete() {
|
||||
this.model.save({ adapterOptions: { method: 'revoke' } });
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import RoleEdit from '../role-edit';
|
||||
|
||||
export default RoleEdit.extend({
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.set('backendType', 'pki');
|
||||
},
|
||||
});
|
|
@ -87,7 +87,7 @@ const MOUNTABLE_SECRET_ENGINES = [
|
|||
{
|
||||
displayName: 'PKI Certificates',
|
||||
type: 'pki',
|
||||
// engineRoute: 'pki.overview', // TODO VAULT-13822
|
||||
engineRoute: 'pki.overview',
|
||||
category: 'generic',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { attr } from '@ember-data/model';
|
||||
import { copy } from 'ember-copy';
|
||||
import { computed } from '@ember/object';
|
||||
import Certificate from './pki-certificate-sign';
|
||||
|
||||
export default Certificate.extend({
|
||||
backend: attr('string', {
|
||||
readOnly: true,
|
||||
}),
|
||||
useCsrValues: attr('boolean', {
|
||||
defaultValue: false,
|
||||
label: 'Use CSR values',
|
||||
}),
|
||||
maxPathLength: attr('number', {
|
||||
defaultValue: -1,
|
||||
}),
|
||||
permittedDnsDomains: attr('string', {
|
||||
label: 'Permitted DNS domains',
|
||||
}),
|
||||
ou: attr({
|
||||
label: 'OU (OrganizationalUnit)',
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
organization: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
country: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
locality: attr({
|
||||
editType: 'stringArray',
|
||||
label: 'Locality/City',
|
||||
}),
|
||||
province: attr({
|
||||
editType: 'stringArray',
|
||||
label: 'Province/State',
|
||||
}),
|
||||
streetAddress: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
postalCode: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
|
||||
fieldGroups: computed('useCsrValues', function () {
|
||||
const options = [
|
||||
{
|
||||
Options: [
|
||||
'altNames',
|
||||
'ipSans',
|
||||
'ttl',
|
||||
'excludeCnFromSans',
|
||||
'maxPathLength',
|
||||
'permittedDnsDomains',
|
||||
'ou',
|
||||
'organization',
|
||||
'otherSans',
|
||||
],
|
||||
},
|
||||
{
|
||||
'Address Options': ['country', 'locality', 'province', 'streetAddress', 'postalCode'],
|
||||
},
|
||||
];
|
||||
let groups = [
|
||||
{
|
||||
default: ['csr', 'commonName', 'format', 'useCsrValues'],
|
||||
},
|
||||
];
|
||||
if (this.useCsrValues === false) {
|
||||
groups = groups.concat(options);
|
||||
}
|
||||
|
||||
return this.fieldsToAttrs(copy(groups, true));
|
||||
}),
|
||||
});
|
|
@ -1,157 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import Certificate from './pki/cert';
|
||||
|
||||
export default Certificate.extend({
|
||||
DISPLAY_FIELDS: computed(function () {
|
||||
return [
|
||||
'csr',
|
||||
'certificate',
|
||||
'commonName',
|
||||
'issueDate',
|
||||
'expiryDate',
|
||||
'issuingCa',
|
||||
'caChain',
|
||||
'privateKey',
|
||||
'privateKeyType',
|
||||
'serialNumber',
|
||||
];
|
||||
}),
|
||||
addBasicConstraints: attr('boolean', {
|
||||
label: 'Add a Basic Constraints extension with CA: true',
|
||||
helpText:
|
||||
'Only needed as a workaround in some compatibility scenarios with Active Directory Certificate Services',
|
||||
}),
|
||||
backend: attr('string', {
|
||||
readOnly: true,
|
||||
}),
|
||||
canParse: attr('boolean'),
|
||||
caType: attr('string', {
|
||||
possibleValues: ['root', 'intermediate'],
|
||||
defaultValue: 'root',
|
||||
label: 'CA Type',
|
||||
readOnly: true,
|
||||
}),
|
||||
commonName: attr('string'),
|
||||
csr: attr('string', {
|
||||
editType: 'textarea',
|
||||
label: 'CSR',
|
||||
masked: true,
|
||||
}),
|
||||
expiryDate: attr('string', {
|
||||
label: 'Expiration date',
|
||||
}),
|
||||
issueDate: attr('string'),
|
||||
keyBits: attr('number', {
|
||||
defaultValue: 2048,
|
||||
}),
|
||||
keyType: attr('string', {
|
||||
possibleValues: ['rsa', 'ec', 'ed25519'],
|
||||
defaultValue: 'rsa',
|
||||
}),
|
||||
maxPathLength: attr('number', {
|
||||
defaultValue: -1,
|
||||
}),
|
||||
organization: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
ou: attr({
|
||||
label: 'OU (OrganizationalUnit)',
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
pemBundle: attr('string', {
|
||||
label: 'PEM bundle',
|
||||
editType: 'file',
|
||||
}),
|
||||
permittedDnsDomains: attr('string', {
|
||||
label: 'Permitted DNS domains',
|
||||
}),
|
||||
privateKeyFormat: attr('string', {
|
||||
possibleValues: ['', 'der', 'pem', 'pkcs8'],
|
||||
defaultValue: '',
|
||||
}),
|
||||
type: attr('string', {
|
||||
possibleValues: ['internal', 'exported'],
|
||||
defaultValue: 'internal',
|
||||
}),
|
||||
uploadPemBundle: attr('boolean', {
|
||||
label: 'Upload PEM bundle',
|
||||
readOnly: true,
|
||||
}),
|
||||
|
||||
// address attrs
|
||||
country: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
locality: attr({
|
||||
editType: 'stringArray',
|
||||
label: 'Locality/City',
|
||||
}),
|
||||
streetAddress: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
postalCode: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
province: attr({
|
||||
editType: 'stringArray',
|
||||
label: 'Province/State',
|
||||
}),
|
||||
|
||||
fieldDefinition: computed('caType', 'uploadPemBundle', function () {
|
||||
const type = this.caType;
|
||||
const isUpload = this.uploadPemBundle;
|
||||
const groups = [{ default: ['caType', 'uploadPemBundle'] }];
|
||||
if (isUpload) {
|
||||
groups[0].default.push('pemBundle');
|
||||
} else {
|
||||
groups[0].default.push('type', 'commonName');
|
||||
if (type === 'root') {
|
||||
groups.push({
|
||||
Options: [
|
||||
'altNames',
|
||||
'ipSans',
|
||||
'ttl',
|
||||
'format',
|
||||
'privateKeyFormat',
|
||||
'keyType',
|
||||
'keyBits',
|
||||
'maxPathLength',
|
||||
'permittedDnsDomains',
|
||||
'excludeCnFromSans',
|
||||
'ou',
|
||||
'organization',
|
||||
'otherSans',
|
||||
],
|
||||
});
|
||||
}
|
||||
if (type === 'intermediate') {
|
||||
groups.push({
|
||||
Options: [
|
||||
'altNames',
|
||||
'ipSans',
|
||||
'format',
|
||||
'privateKeyFormat',
|
||||
'keyType',
|
||||
'keyBits',
|
||||
'excludeCnFromSans',
|
||||
'addBasicConstraints',
|
||||
'ou',
|
||||
'organization',
|
||||
'otherSans',
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
groups.push({
|
||||
'Address Options': ['country', 'locality', 'province', 'streetAddress', 'postalCode'],
|
||||
});
|
||||
|
||||
return groups;
|
||||
}),
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { attr } from '@ember-data/model';
|
||||
import { copy } from 'ember-copy';
|
||||
import { computed } from '@ember/object';
|
||||
import Certificate from './pki/cert';
|
||||
import { combineFieldGroups } from 'vault/utils/openapi-to-attrs';
|
||||
|
||||
export default Certificate.extend({
|
||||
signVerbatim: attr('boolean', {
|
||||
readOnly: true,
|
||||
defaultValue: false,
|
||||
}),
|
||||
useOpenAPI: true,
|
||||
csr: attr('string', {
|
||||
label: 'Certificate Signing Request (CSR)',
|
||||
editType: 'textarea',
|
||||
}),
|
||||
|
||||
fieldGroups: computed('newFields', 'signVerbatim', function () {
|
||||
const options = { Options: ['altNames', 'ipSans', 'ttl', 'excludeCnFromSans', 'otherSans'] };
|
||||
let groups = [
|
||||
{
|
||||
default: ['csr', 'commonName', 'format', 'signVerbatim'],
|
||||
},
|
||||
];
|
||||
if (this.newFields) {
|
||||
groups = combineFieldGroups(groups, this.newFields, []);
|
||||
}
|
||||
if (this.signVerbatim === false) {
|
||||
groups.push(options);
|
||||
}
|
||||
|
||||
return this.fieldsToAttrs(copy(groups, true));
|
||||
}),
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
# PKI filenames
|
||||
|
||||
> delete this file when PKI redesign is complete
|
||||
|
||||
Now that all things PKI live in a `/pki` folder we are removing `pki` from the filename in any newly created files for the pki redesign (ex. 'role.js' instead of 'pki-role.js'). Aside from `cert.js` all of the old pki files are prepended with `pki-`
|
||||
|
||||
Old files:
|
||||
|
||||
├── models/
|
||||
│ ├── pki/
|
||||
│ │ ├── cert.js
|
||||
│ │ ├── pki-config.js
|
||||
│ │ ├── pki-role.js
|
||||
│ ├── pki-ca-certificate-sign.js
|
||||
│ ├── pki-ca-certificate.js
|
||||
│ ├── pki-certificate-sign.js
|
||||
├── serializers/
|
||||
│ ├── pki/
|
||||
│ │ ├── cert.js
|
||||
│ │ ├── pki-config.js
|
||||
│ │ ├── pki-role.js
|
||||
├── adapters/
|
||||
│ ├── pki/
|
||||
│ │ ├── cert.js
|
||||
│ │ ├── pki-config.js
|
||||
│ │ ├── pki-role.js
|
||||
│ ├── pki-ca-certificate-sign.js
|
||||
│ ├── pki-ca-certificate.js
|
||||
│ ├── pki-certificate-sign.js
|
||||
│ ├── pki.js
|
|
@ -1,139 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { alias } from '@ember/object/computed';
|
||||
import { computed } from '@ember/object';
|
||||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import fieldToAttrs, { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
|
||||
export default Model.extend({
|
||||
idPrefix: 'cert/',
|
||||
//the id prefixed with `cert/` so we can use it as the *secret param for the secret show route
|
||||
idForNav: attr('string', {
|
||||
readOnly: true,
|
||||
}),
|
||||
DISPLAY_FIELDS: computed(function () {
|
||||
return [
|
||||
'certificate',
|
||||
'commonName',
|
||||
'issuingCa',
|
||||
'caChain',
|
||||
'privateKey',
|
||||
'privateKeyType',
|
||||
'revocationTime',
|
||||
'issueDate',
|
||||
'expiryDate',
|
||||
'serialNumber',
|
||||
];
|
||||
}),
|
||||
|
||||
altNames: attr('string', {
|
||||
label: 'DNS/Email Subject Alternative Names (SANs)',
|
||||
}),
|
||||
backend: attr('string', {
|
||||
readOnly: true,
|
||||
}),
|
||||
caChain: attr('string', {
|
||||
label: 'CA chain',
|
||||
masked: true,
|
||||
}),
|
||||
canParse: attr('boolean'),
|
||||
certificate: attr('string', {
|
||||
masked: true,
|
||||
}),
|
||||
commonName: attr('string'),
|
||||
excludeCnFromSans: attr('boolean', {
|
||||
label: 'Exclude Common Name from Subject Alternative Names (SANs)',
|
||||
defaultValue: false,
|
||||
}),
|
||||
expiryDate: attr('string', {
|
||||
label: 'Expiration date',
|
||||
}),
|
||||
format: attr('string', {
|
||||
defaultValue: 'pem',
|
||||
possibleValues: ['pem', 'der', 'pem_bundle'],
|
||||
}),
|
||||
ipSans: attr('string', {
|
||||
label: 'IP Subject Alternative Names (SANs)',
|
||||
}),
|
||||
issueDate: attr('string'),
|
||||
issuingCa: attr('string', {
|
||||
label: 'Issuing CA',
|
||||
masked: true,
|
||||
}),
|
||||
otherSans: attr({
|
||||
editType: 'stringArray',
|
||||
label: 'Other SANs',
|
||||
helpText:
|
||||
'The format is the same as OpenSSL: <oid>;<type>:<value> where the only current valid type is UTF8',
|
||||
}),
|
||||
privateKey: attr('string', {
|
||||
masked: true,
|
||||
}),
|
||||
privateKeyType: attr('string'),
|
||||
revocationTime: attr('number'),
|
||||
role: attr('object', {
|
||||
readOnly: true,
|
||||
}),
|
||||
serialNumber: attr('string'),
|
||||
ttl: attr({
|
||||
label: 'TTL',
|
||||
editType: 'ttl',
|
||||
}),
|
||||
|
||||
fieldsToAttrs(fieldGroups) {
|
||||
return fieldToAttrs(this, fieldGroups);
|
||||
},
|
||||
|
||||
fieldDefinition: computed(function () {
|
||||
const groups = [
|
||||
{ default: ['commonName', 'format'] },
|
||||
{ Options: ['altNames', 'ipSans', 'ttl', 'excludeCnFromSans', 'otherSans'] },
|
||||
];
|
||||
return groups;
|
||||
}),
|
||||
|
||||
fieldGroups: computed('fieldDefinition', function () {
|
||||
return this.fieldsToAttrs(this.fieldDefinition);
|
||||
}),
|
||||
|
||||
attrs: computed('DISPLAY_FIELDS', 'certificate', 'csr', function () {
|
||||
const keys = this.certificate || this.csr ? this.DISPLAY_FIELDS.slice(0) : [];
|
||||
return expandAttributeMeta(this, keys);
|
||||
}),
|
||||
|
||||
toCreds: computed(
|
||||
'certificate',
|
||||
'issuingCa',
|
||||
'caChain',
|
||||
'privateKey',
|
||||
'privateKeyType',
|
||||
'revocationTime',
|
||||
'serialNumber',
|
||||
function () {
|
||||
const props = {
|
||||
certificate: this.certificate,
|
||||
issuingCa: this.issuingCa,
|
||||
caChain: this.caChain,
|
||||
privateKey: this.privateKey,
|
||||
privateKeyType: this.privateKeyType,
|
||||
revocationTime: this.revocationTime,
|
||||
serialNumber: this.serialNumber,
|
||||
};
|
||||
const propsWithVals = Object.keys(props).reduce((ret, prop) => {
|
||||
if (props[prop]) {
|
||||
ret[prop] = props[prop];
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}, {});
|
||||
return JSON.stringify(propsWithVals, null, 2);
|
||||
}
|
||||
),
|
||||
|
||||
revokePath: lazyCapabilities(apiPath`${'backend'}/revoke`, 'backend'),
|
||||
canRevoke: alias('revokePath.canUpdate'),
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
|
||||
export default Model.extend({
|
||||
backend: attr('string'),
|
||||
der: attr(),
|
||||
pem: attr('string'),
|
||||
caChain: attr('string'),
|
||||
attrList(keys) {
|
||||
return expandAttributeMeta(this, keys);
|
||||
},
|
||||
|
||||
//urls
|
||||
urlsAttrs: computed(function () {
|
||||
const keys = ['issuingCertificates', 'crlDistributionPoints', 'ocspServers'];
|
||||
return this.attrList(keys);
|
||||
}),
|
||||
issuingCertificates: attr({
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
crlDistributionPoints: attr({
|
||||
label: 'CRL Distribution Points',
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
ocspServers: attr({
|
||||
label: 'OCSP Servers',
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
|
||||
//tidy
|
||||
tidyAttrs: computed(function () {
|
||||
const keys = ['tidyCertStore', 'tidyRevocationList', 'safetyBuffer'];
|
||||
return this.attrList(keys);
|
||||
}),
|
||||
tidyCertStore: attr('boolean', {
|
||||
defaultValue: false,
|
||||
label: 'Tidy the Certificate Store',
|
||||
}),
|
||||
tidyRevocationList: attr('boolean', {
|
||||
defaultValue: false,
|
||||
label: 'Tidy the Revocation List (CRL)',
|
||||
}),
|
||||
safetyBuffer: attr({
|
||||
defaultValue: '72h',
|
||||
editType: 'ttl',
|
||||
}),
|
||||
|
||||
crlAttrs: computed(function () {
|
||||
const keys = ['expiry', 'disable'];
|
||||
return this.attrList(keys);
|
||||
}),
|
||||
//crl
|
||||
expiry: attr('string', { defaultValue: '72h' }),
|
||||
disable: attr('boolean', { defaultValue: false }),
|
||||
});
|
|
@ -1,92 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { alias } from '@ember/object/computed';
|
||||
import { computed } from '@ember/object';
|
||||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import fieldToAttrs from 'vault/utils/field-to-attrs';
|
||||
import { combineFieldGroups } from 'vault/utils/openapi-to-attrs';
|
||||
|
||||
export default Model.extend({
|
||||
backend: attr('string', {
|
||||
readOnly: true,
|
||||
}),
|
||||
name: attr('string', {
|
||||
label: 'Role name',
|
||||
fieldValue: 'name',
|
||||
readOnly: true,
|
||||
}),
|
||||
useOpenAPI: true,
|
||||
getHelpUrl: function (backend) {
|
||||
return `/v1/${backend}/roles/example?help=1`;
|
||||
},
|
||||
updatePath: lazyCapabilities(apiPath`${'backend'}/roles/${'id'}`, 'backend', 'id'),
|
||||
canDelete: alias('updatePath.canDelete'),
|
||||
canEdit: alias('updatePath.canUpdate'),
|
||||
canRead: alias('updatePath.canRead'),
|
||||
|
||||
generatePath: lazyCapabilities(apiPath`${'backend'}/issue/${'id'}`, 'backend', 'id'),
|
||||
canGenerate: alias('generatePath.canUpdate'),
|
||||
|
||||
signPath: lazyCapabilities(apiPath`${'backend'}/sign/${'id'}`, 'backend', 'id'),
|
||||
canSign: alias('signPath.canUpdate'),
|
||||
|
||||
signVerbatimPath: lazyCapabilities(apiPath`${'backend'}/sign-verbatim/${'id'}`, 'backend', 'id'),
|
||||
canSignVerbatim: alias('signVerbatimPath.canUpdate'),
|
||||
|
||||
fieldGroups: computed('newFields', function () {
|
||||
let groups = [
|
||||
{ default: ['name', 'keyType'] },
|
||||
{
|
||||
Options: [
|
||||
'keyBits',
|
||||
'ttl',
|
||||
'maxTtl',
|
||||
'allowAnyName',
|
||||
'enforceHostnames',
|
||||
'allowIpSans',
|
||||
'requireCn',
|
||||
'useCsrCommonName',
|
||||
'useCsrSans',
|
||||
'ou',
|
||||
'organization',
|
||||
'keyUsage',
|
||||
'allowedOtherSans',
|
||||
'notBeforeDuration',
|
||||
],
|
||||
},
|
||||
{
|
||||
'Address Options': ['country', 'locality', 'province', 'streetAddress', 'postalCode'],
|
||||
},
|
||||
{
|
||||
'Domain Handling': [
|
||||
'allowLocalhost',
|
||||
'allowBareDomains',
|
||||
'allowSubdomains',
|
||||
'allowGlobDomains',
|
||||
'allowedDomains',
|
||||
],
|
||||
},
|
||||
{
|
||||
'Extended Key Usage': [
|
||||
'serverFlag',
|
||||
'clientFlag',
|
||||
'codeSigningFlag',
|
||||
'emailProtectionFlag',
|
||||
'extKeyUsageOids',
|
||||
],
|
||||
},
|
||||
{
|
||||
Advanced: ['generateLease', 'noStore', 'basicConstraintsValidForNonCa', 'policyIdentifiers'],
|
||||
},
|
||||
];
|
||||
const excludedFields = ['extKeyUsage'];
|
||||
if (this.newFields) {
|
||||
groups = combineFieldGroups(groups, this.newFields, excludedFields);
|
||||
}
|
||||
return fieldToAttrs(this, groups);
|
||||
}),
|
||||
});
|
|
@ -7,7 +7,7 @@ import AdapterError from '@ember-data/adapter/error';
|
|||
import { set } from '@ember/object';
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
const CONFIGURABLE_BACKEND_TYPES = ['aws', 'ssh', 'pki'];
|
||||
const CONFIGURABLE_BACKEND_TYPES = ['aws', 'ssh'];
|
||||
|
||||
export default Route.extend({
|
||||
store: service(),
|
||||
|
@ -32,15 +32,9 @@ export default Route.extend({
|
|||
});
|
||||
},
|
||||
|
||||
afterModel(model, transition) {
|
||||
afterModel(model) {
|
||||
const type = model.get('type');
|
||||
if (type === 'pki') {
|
||||
if (transition.targetName === this.routeName) {
|
||||
return this.transitionTo(`${this.routeName}.section`, 'cert');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'aws') {
|
||||
return this.store
|
||||
.queryRecord('secret-engine', {
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default Route.extend({
|
||||
beforeModel(transition) {
|
||||
const type = this.modelFor('vault.cluster.settings.configure-secret-backend').get('type');
|
||||
if (type === 'pki' && transition.targetName === this.routeName) {
|
||||
return this.transitionTo('vault.cluster.settings.configure-secret-backend.section', 'cert');
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,59 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import AdapterError from '@ember-data/adapter/error';
|
||||
import { set } from '@ember/object';
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
// ARG TODO glimmerize
|
||||
const SECTIONS_FOR_TYPE = {
|
||||
pki: ['cert', 'urls', 'crl', 'tidy'],
|
||||
};
|
||||
|
||||
export default Route.extend({
|
||||
store: service(),
|
||||
|
||||
fetchModel() {
|
||||
const { section_name: sectionName } = this.paramsFor(this.routeName);
|
||||
const backendModel = this.modelFor('vault.cluster.settings.configure-secret-backend');
|
||||
const type = backendModel.get('type');
|
||||
let modelType;
|
||||
if (type === 'pki') {
|
||||
// pki models are in models/pki
|
||||
modelType = `${type}/${type}-config`;
|
||||
} else {
|
||||
modelType = `${type}-config`;
|
||||
}
|
||||
return this.store
|
||||
.queryRecord(modelType, {
|
||||
backend: backendModel.id,
|
||||
section: sectionName,
|
||||
})
|
||||
.then((model) => {
|
||||
model.set('backendType', type);
|
||||
model.set('section', sectionName);
|
||||
return model;
|
||||
});
|
||||
},
|
||||
|
||||
model(params) {
|
||||
const { section_name: sectionName } = params;
|
||||
const backendModel = this.modelFor('vault.cluster.settings.configure-secret-backend');
|
||||
const sections = SECTIONS_FOR_TYPE[backendModel.get('type')];
|
||||
const hasSection = sections.includes(sectionName);
|
||||
if (!backendModel || !hasSection) {
|
||||
const error = new AdapterError();
|
||||
set(error, 'httpStatus', 404);
|
||||
throw error;
|
||||
}
|
||||
return this.fetchModel();
|
||||
},
|
||||
|
||||
setupController(controller) {
|
||||
this._super(...arguments);
|
||||
controller.set('onRefresh', () => this.fetchModel());
|
||||
},
|
||||
});
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import RESTSerializer from '@ember-data/serializer/rest';
|
||||
import { isNone, isBlank } from '@ember/utils';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { decamelize } from '@ember/string';
|
||||
import { parsePkiCert } from 'vault/utils/parse-pki-cert';
|
||||
|
||||
export default RESTSerializer.extend({
|
||||
keyForAttribute: function (attr) {
|
||||
return decamelize(attr);
|
||||
},
|
||||
|
||||
pushPayload(store, payload) {
|
||||
const transformedPayload = this.normalizeResponse(
|
||||
store,
|
||||
store.modelFor(payload.modelName),
|
||||
payload,
|
||||
payload.id,
|
||||
'findRecord'
|
||||
);
|
||||
return store.push(transformedPayload);
|
||||
},
|
||||
|
||||
normalizeItems(payload) {
|
||||
if (payload.data && payload.data.keys && Array.isArray(payload.data.keys)) {
|
||||
const ret = payload.data.keys.map((key) => {
|
||||
const model = {
|
||||
id_for_nav: `cert/${key}`,
|
||||
id: key,
|
||||
};
|
||||
if (payload.backend) {
|
||||
model.backend = payload.backend;
|
||||
}
|
||||
return model;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
assign(payload, payload.data);
|
||||
delete payload.data;
|
||||
return payload;
|
||||
},
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
const responseJSON = this.normalizeItems(payload);
|
||||
const { modelName } = primaryModelClass;
|
||||
let transformedPayload, certMetadata;
|
||||
// hits cert/list endpoint first which returns an array of keys, only want to parse if response contains certificates
|
||||
if (!Array.isArray(responseJSON)) {
|
||||
certMetadata = parsePkiCert(responseJSON);
|
||||
transformedPayload = { [modelName]: { ...certMetadata, ...responseJSON } };
|
||||
} else {
|
||||
transformedPayload = { [modelName]: responseJSON };
|
||||
}
|
||||
return this._super(store, primaryModelClass, transformedPayload, id, requestType);
|
||||
},
|
||||
|
||||
serializeAttribute(snapshot, json, key, attributes) {
|
||||
const val = snapshot.attr(key);
|
||||
const valHasNotChanged = isNone(snapshot.changedAttributes()[key]);
|
||||
const valIsBlank = isBlank(val);
|
||||
if (attributes.options.readOnly) {
|
||||
return;
|
||||
}
|
||||
if (attributes.type === 'object' && val && Object.keys(val).length > 0 && valHasNotChanged) {
|
||||
return;
|
||||
}
|
||||
if (valIsBlank && valHasNotChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._super(snapshot, json, key, attributes);
|
||||
},
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import RESTSerializer from '@ember-data/serializer/rest';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { decamelize } from '@ember/string';
|
||||
|
||||
export default RESTSerializer.extend({
|
||||
keyForAttribute: function (attr) {
|
||||
return decamelize(attr);
|
||||
},
|
||||
|
||||
normalizeAll(payload) {
|
||||
if (payload.data) {
|
||||
const data = assign({}, payload, payload.data);
|
||||
return [data];
|
||||
}
|
||||
return [payload];
|
||||
},
|
||||
|
||||
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
|
||||
const records = this.normalizeAll(payload);
|
||||
const { modelName } = primaryModelClass;
|
||||
let transformedPayload = { [modelName]: records };
|
||||
// just return the single object because ember is picky
|
||||
if (requestType === 'queryRecord') {
|
||||
transformedPayload = { [modelName]: records[0] };
|
||||
}
|
||||
|
||||
return this._super(store, primaryModelClass, transformedPayload, id, requestType);
|
||||
},
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import RoleSerializer from '../role';
|
||||
|
||||
export default RoleSerializer.extend();
|
|
@ -24,11 +24,6 @@
|
|||
width: 12px;
|
||||
}
|
||||
|
||||
&.is-success {
|
||||
background-color: $green-050;
|
||||
color: $green-700;
|
||||
}
|
||||
|
||||
&.has-extra-padding {
|
||||
padding: $size-11 $spacing-xxs;
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<NamespaceReminder @mode="save" @noun="PKI change" />
|
||||
|
||||
{{#if (eq this.section "tidy")}}
|
||||
<p class="has-text-grey-dark" data-test-text="true">
|
||||
You can tidy up the backend storage and/or CRL by removing certificates that have expired and are past a certain buffer
|
||||
period beyond their expiration time.
|
||||
</p>
|
||||
{{else if (eq this.section "crl")}}
|
||||
<h2 class="title is-5" data-test-title="true">
|
||||
Certificate Revocation List (CRL) config
|
||||
</h2>
|
||||
<p class="has-text-grey-dark" data-test-text="true">
|
||||
Set the duration for which the generated CRL should be marked valid.
|
||||
</p>
|
||||
{{/if}}
|
||||
|
||||
<MessageError @model={{this.config}} @errors={{this.errors}} />
|
||||
<form {{action "save" this.section on="submit"}} class="box is-shadowless is-marginless is-fullwidth has-slim-padding">
|
||||
{{#if (eq this.section "crl")}}
|
||||
<TtlPicker
|
||||
data-test-input="expiry"
|
||||
@onChange={{action "handleCrlTtl"}}
|
||||
@label={{if (get this.config "disable") "CRL building disabled" "CRL building enabled"}}
|
||||
@helperTextDisabled="The CRL will not be built."
|
||||
@helperTextEnabled="The CRL will expire after"
|
||||
@initialValue={{get this.config "expiry"}}
|
||||
@initialEnabled={{not (get this.config "disable")}}
|
||||
/>
|
||||
{{else}}
|
||||
{{#each (get this.config (concat this.section "Attrs")) as |attr|}}
|
||||
<FormField @attr={{attr}} @model={{this.config}} @data-test-field={{attr.name}} />
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
<div class="field has-top-margin-m is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button
|
||||
data-test-submit
|
||||
type="submit"
|
||||
class="button is-primary {{if this.loading 'is-loading'}}"
|
||||
disabled={{this.loading}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
|
@ -1,19 +0,0 @@
|
|||
<div class="tabs-container box is-bottomless is-fullwidth is-paddingless">
|
||||
<nav class="tabs">
|
||||
<ul>
|
||||
{{#each (array "cert" "urls" "crl" "tidy") as |section|}}
|
||||
<LinkTo @route="vault.cluster.settings.configure-secret-backend.section" @model={{section}}>
|
||||
{{#if (eq section "cert")}}
|
||||
CA certificate
|
||||
{{else if (eq section "urls")}}
|
||||
URLs
|
||||
{{else if (eq section "crl")}}
|
||||
CRL
|
||||
{{else if (eq section "tidy")}}
|
||||
Tidy
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
|
@ -1,25 +0,0 @@
|
|||
<PopupMenu @name="role-aws-nav" @contentClass="is-wide">
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@item.idForNav}} data-test-pki-cert-link="show">
|
||||
Details
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{#if @item.canRevoke}}
|
||||
<li class="action">
|
||||
<ConfirmAction
|
||||
@buttonClasses="link is-destroy"
|
||||
@onConfirmAction={{fn this.delete @item}}
|
||||
@confirmTitle="Revoke this cert?"
|
||||
@confirmMessage="Any services using this cert may be affected."
|
||||
@confirmButtonText="Revoke"
|
||||
data-test-cert-revoke-delete={{@item.id}}
|
||||
>
|
||||
Revoke
|
||||
</ConfirmAction>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
</PopupMenu>
|
|
@ -1,94 +0,0 @@
|
|||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<KeyValueHeader
|
||||
@baseKey={{hash display=this.model.id id=this.model.idForNav}}
|
||||
@path="vault.cluster.secrets.backend.list"
|
||||
@mode={{this.mode}}
|
||||
@root={{this.root}}
|
||||
@showCurrent={{true}}
|
||||
/>
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3" data-test-secret-header="true">
|
||||
PKI Certificate
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
{{#if (eq this.model.canParse false)}}
|
||||
<AlertBanner
|
||||
@type="info"
|
||||
@message="There was an error parsing the certificate's metadata. As a result, Vault cannot display the common name or the issue and expiration dates. This will not interfere with the certificate's functionality."
|
||||
data-test-warning
|
||||
/>
|
||||
{{/if}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
<MessageError @model={{this.model}} />
|
||||
{{#each this.model.attrs as |attr|}}
|
||||
{{#if (eq attr.type "object")}}
|
||||
<InfoTableRow
|
||||
data-test-table-row
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{stringify (get this.model attr.name)}}
|
||||
/>
|
||||
{{else}}
|
||||
{{#if (and attr.options.masked (get this.model attr.name))}}
|
||||
<InfoTableRow
|
||||
data-test-table-row
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
>
|
||||
<MaskedInput @value={{get this.model attr.name}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{else if (and (get this.model attr.name) (or (eq attr.name "issueDate") (eq attr.name "expiryDate")))}}
|
||||
<InfoTableRow
|
||||
data-test-table-row
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{date-format (get this.model attr.name) "MMM dd, yyyy hh:mm:ss a" isFormatted=true}}
|
||||
/>
|
||||
{{else if (eq attr.name "revocationTime")}}
|
||||
<InfoTableRow
|
||||
data-test-table-row
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{date-format (get this.model attr.name) "MMM dd, yyyy hh:mm:ss a"}}
|
||||
/>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
data-test-table-row
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped is-grouped-split box is-fullwidth is-bottomless">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<CopyButton
|
||||
@clipboardText={{this.model.toCreds}}
|
||||
@class="button is-primary"
|
||||
@buttonType="button"
|
||||
@success={{action (set-flash-message "Credentials copied!")}}
|
||||
>
|
||||
Copy credentials
|
||||
</CopyButton>
|
||||
</div>
|
||||
<div class="control">
|
||||
<LinkTo @route="vault.cluster.secrets.backend.list-root" @query={{hash tab="cert"}} class="button">
|
||||
Back
|
||||
</LinkTo>
|
||||
</div>
|
||||
</div>
|
||||
{{#if (and (not this.model.revocationTime) this.model.canRevoke)}}
|
||||
<ConfirmAction
|
||||
@buttonClasses="button"
|
||||
@onConfirmAction={{action "delete"}}
|
||||
@confirmTitle="Revoke this cert?"
|
||||
@confirmMessage="Any services using this cert may be affected."
|
||||
@confirmButtonText="Revoke"
|
||||
>
|
||||
Revoke
|
||||
</ConfirmAction>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -1,113 +0,0 @@
|
|||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<KeyValueHeader
|
||||
@baseKey={{this.model}}
|
||||
@path="vault.cluster.secrets.backend.list"
|
||||
@mode={{this.mode}}
|
||||
@root={{this.root}}
|
||||
@showCurrent={{true}}
|
||||
/>
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3" data-test-secret-header="true">
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create a PKI Role
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Edit PKI Role
|
||||
{{else}}
|
||||
PKI Role
|
||||
<code>{{this.model.id}}</code>
|
||||
{{/if}}
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
{{#if (eq this.mode "show")}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
{{#if this.model.canDelete}}
|
||||
<ConfirmAction @buttonClasses="toolbar-link" @onConfirmAction={{action "delete"}} data-test-role-delete="true">
|
||||
Delete role
|
||||
</ConfirmAction>
|
||||
<div class="toolbar-separator"></div>
|
||||
{{/if}}
|
||||
{{#if this.model.canGenerate}}
|
||||
<ToolbarSecretLink
|
||||
@secret={{this.model.id}}
|
||||
@mode="credentials"
|
||||
@queryParams={{hash action="issue"}}
|
||||
data-test-credentials-link={{true}}
|
||||
>
|
||||
Generate Certificate
|
||||
</ToolbarSecretLink>
|
||||
{{/if}}
|
||||
{{#if this.model.canSign}}
|
||||
<ToolbarSecretLink
|
||||
@secret={{this.model.id}}
|
||||
@mode="credentials"
|
||||
@queryParams={{hash action="sign"}}
|
||||
data-test-sign-link={{true}}
|
||||
>
|
||||
Sign Certificate
|
||||
</ToolbarSecretLink>
|
||||
{{/if}}
|
||||
{{#if this.model.canEdit}}
|
||||
<ToolbarSecretLink @secret={{this.model.id}} @mode="edit" data-test-edit-link={{true}} @replace={{true}}>
|
||||
Edit role
|
||||
</ToolbarSecretLink>
|
||||
{{/if}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
{{#if (or (eq this.mode "edit") (eq this.mode "create"))}}
|
||||
<form onsubmit={{action "createOrUpdate" "create"}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<MessageError @model={{this.model}} />
|
||||
<NamespaceReminder @mode={{this.mode}} @noun="PKI role" />
|
||||
<FormFieldGroupsLoop @model={{this.model}} @mode={{this.mode}} />
|
||||
</div>
|
||||
<div class="field is-grouped-split box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" disabled={{this.buttonDisabled}} class="button is-primary" data-test-role-create={{true}}>
|
||||
{{#if (eq this.mode "create")}}
|
||||
Create role
|
||||
{{else if (eq this.mode "edit")}}
|
||||
Save
|
||||
{{/if}}
|
||||
</button>
|
||||
<SecretLink @mode={{if (eq this.mode "create") "list" "show"}} class="button" @secret={{this.model.id}}>
|
||||
Cancel
|
||||
</SecretLink>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
{{#each this.model.fieldGroups as |fieldGroup|}}
|
||||
{{#each-in fieldGroup as |group fields|}}
|
||||
{{#if (or (eq group "default") (eq group "Options"))}}
|
||||
{{#each fields as |attr|}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{true}}
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
/>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<h2 class="title is-5">
|
||||
{{group}}
|
||||
</h2>
|
||||
{{#each fields as |attr|}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{true}}
|
||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||
@value={{get this.model attr.name}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each-in}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
|
@ -1,24 +0,0 @@
|
|||
<LinkedBlock
|
||||
@params={{array (concat "vault.cluster.secrets.backend." "show" (unless @item.id "-root")) @item.idForNav}}
|
||||
class="list-item-row"
|
||||
data-test-secret-link={{@item.id}}
|
||||
@encode={{true}}
|
||||
>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-10">
|
||||
<LinkTo
|
||||
@route={{concat "vault.cluster.secrets.backend." "show" (unless @item.id "-root")}}
|
||||
@model={{@item.idForNav}}
|
||||
class="has-text-black has-text-weight-semibold"
|
||||
>
|
||||
<Icon @name="file" class="has-text-grey-light is-pulled-left" />
|
||||
<div class="role-item-details">
|
||||
<span class="is-underline">{{if (eq @item.id " ") "(self)" (or @item.keyWithoutParent @item.id)}}</span>
|
||||
</div>
|
||||
</LinkTo>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
<Pki::PkiCertPopup @item={{@item}} />
|
||||
</div>
|
||||
</div>
|
||||
</LinkedBlock>
|
|
@ -1,101 +0,0 @@
|
|||
<LinkedBlock
|
||||
@params={{array (concat "vault.cluster.secrets.backend." "credentials" (unless @item.id "-root")) @item.backend @item.id}}
|
||||
@queryParams={{hash action="issue"}}
|
||||
class="list-item-row"
|
||||
data-test-secret-link={{@item.id}}
|
||||
@encode={{true}}
|
||||
>
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-10">
|
||||
<LinkTo
|
||||
@route={{concat "vault.cluster.secrets.backend." "credentials" (unless @item.id "-root")}}
|
||||
@model={{@item.id}}
|
||||
@query={{hash action="issue"}}
|
||||
class="has-text-black has-text-weight-semibold"
|
||||
>
|
||||
<Icon @name="user" class="has-text-grey-light is-pulled-left" />
|
||||
<div class="role-item-details">
|
||||
<span class="is-underline">{{if (eq @item.id " ") "(self)" (or @item.keyWithoutParent @item.id)}}</span>
|
||||
</div>
|
||||
</LinkTo>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
<PopupMenu @name="role-aws-nav">
|
||||
<Confirm as |c|>
|
||||
<nav class="menu">
|
||||
{{#if (or @item.generatePath.isPending @item.signPath.isPending)}}
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
<button disabled type="button" class="link button is-loading is-transparent">
|
||||
loading
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
{{else if (or @item.canGenerate @item.canSign)}}
|
||||
<ul class="menu-list">
|
||||
{{#if @item.canGenerate}}
|
||||
<li class="action">
|
||||
<LinkTo
|
||||
@route="vault.cluster.secrets.backend.credentials"
|
||||
@model={{@item.id}}
|
||||
@query={{hash action="issue"}}
|
||||
data-test-role-pki-link="generate-certificate"
|
||||
>
|
||||
Generate certificate
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if @item.canSign}}
|
||||
<li class="action">
|
||||
<LinkTo
|
||||
@route="vault.cluster.secrets.backend.credentials"
|
||||
@model={{@item.id}}
|
||||
@query={{hash action="sign"}}
|
||||
data-test-role-pki-link="sign-certificate"
|
||||
>
|
||||
Sign certificate
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
<ul class="menu-list">
|
||||
{{#if @item.updatePath.isPending}}
|
||||
<li class="action">
|
||||
<button disabled type="button" class="link button is-loading is-transparent">
|
||||
loading
|
||||
</button>
|
||||
</li>
|
||||
<li class="action">
|
||||
<button disabled type="button" class="link button is-loading is-transparent">
|
||||
loading
|
||||
</button>
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if @item.canRead}}
|
||||
<li class="action">
|
||||
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@item.id}} data-test-role-pki-link="show">
|
||||
Details
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if @item.canEdit}}
|
||||
<li class="action">
|
||||
<LinkTo @route="vault.cluster.secrets.backend.edit" @model={{@item.id}} data-test-role-pki-link="edit">
|
||||
Edit
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if @item.canDelete}}
|
||||
<li class="action">
|
||||
<c.Message @id={{@item.id}} @onConfirm={{@delete}} data-test-pki-role-delete={{@item.id}} />
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
</Confirm>
|
||||
</PopupMenu>
|
||||
</div>
|
||||
</div>
|
||||
</LinkedBlock>
|
|
@ -35,8 +35,6 @@
|
|||
@saveAWSRoot={{action "save" "saveAWSRoot"}}
|
||||
@saveAWSLease={{action "save" "saveAWSLease"}}
|
||||
/>
|
||||
{{else if (eq this.model.type "pki")}}
|
||||
<Pki::ConfigurePkiSecret />
|
||||
{{else if (eq this.model.type "ssh")}}
|
||||
<ConfigureSshSecret
|
||||
@model={{this.model}}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{{#if (eq this.model.backendType "pki")}}
|
||||
{{#if (eq this.model.section "cert")}}
|
||||
<Pki::ConfigPkiCa @config={{this.model}} @onRefresh={{this.onRefresh}} />
|
||||
{{else}}
|
||||
<Pki::ConfigPki @config={{this.model}} @onRefresh={{this.onRefresh}} @section={{this.model.section}} />
|
||||
{{/if}}
|
||||
{{/if}}
|
|
@ -64,8 +64,6 @@ export function parseCertificate(certificateContent) {
|
|||
return {
|
||||
...parsedCertificateValues,
|
||||
can_parse: true,
|
||||
expiry_date: expiryDate, // remove along with old PKI work
|
||||
issue_date: issueDate, // remove along with old PKI work
|
||||
not_valid_after: getUnixTime(expiryDate),
|
||||
not_valid_before: getUnixTime(issueDate),
|
||||
ttl,
|
||||
|
|
|
@ -25,93 +25,6 @@
|
|||
<h1 class="title is-3">
|
||||
<Icon @name={{@model.icon}} @size="24" class="has-text-grey-light" />
|
||||
{{@model.id}}
|
||||
|
||||
{{#if this.isPki}}
|
||||
{{#if @isEngine}}
|
||||
<span>
|
||||
<LinkToExternal
|
||||
@route="secretsListRoot"
|
||||
class="tag is-borderless is-underline has-text-weight-semibold has-extra-padding"
|
||||
data-test-new-pki-beta-button
|
||||
>
|
||||
<Icon @name="arrow-left" />
|
||||
Return to old PKI
|
||||
</LinkToExternal>
|
||||
</span>
|
||||
{{else}}
|
||||
{{#if this.shouldHidePkiBetaModal}}
|
||||
<span>
|
||||
<LinkTo
|
||||
@route="vault.cluster.secrets.backend.pki.overview"
|
||||
class="tag is-success is-borderless is-underline has-text-weight-semibold has-extra-padding"
|
||||
>
|
||||
<Icon @name="key" />
|
||||
New PKI UI available
|
||||
</LinkTo>
|
||||
</span>
|
||||
{{else}}
|
||||
<button
|
||||
type="button"
|
||||
class="tag is-success is-v-centered text-button is-underline has-text-weight-semibold has-extra-padding"
|
||||
{{on "click" (fn (mut this.modalOpen) true)}}
|
||||
data-test-old-pki-beta-button
|
||||
>
|
||||
<Icon @name="key" />
|
||||
New PKI UI available
|
||||
</button>
|
||||
<Modal
|
||||
@title="New PKI Beta"
|
||||
@onClose={{fn (mut this.modalOpen) false}}
|
||||
@isActive={{this.modalOpen}}
|
||||
@showCloseButton={{true}}
|
||||
>
|
||||
<section class="modal-card-body">
|
||||
The new PKI beta includes, among other things:
|
||||
<ul class="bullet">
|
||||
<li>Multiple issuers can now be generated and managed in the UI.</li>
|
||||
<li>Cross-signing: cross-sign multiple intermediates in a single step.</li>
|
||||
<li>UI upgrades: smoother configuration, an overview page, and more certificate information.</li>
|
||||
</ul>
|
||||
<br />
|
||||
You will always be able to return to this version.
|
||||
<br />
|
||||
<br />
|
||||
You can also copy the following URL and bookmark it to always go to the beta version:
|
||||
<br />
|
||||
<code class="has-text-danger has-background-white-bis">
|
||||
{{this.windowOrigin}}/ui/vault/secrets/{{@model.id}}/pki/overview
|
||||
</code>
|
||||
<br />
|
||||
<br />
|
||||
<Input
|
||||
id="hide-beta-modal"
|
||||
@type="checkbox"
|
||||
@checked={{this.hidePkiBetaModal}}
|
||||
{{on "change" this.toggleHidePkiBetaModal}}
|
||||
/>
|
||||
|
||||
<label class="has-text-weight-semibold" for="hide-beta-modal">
|
||||
Don't show me this again
|
||||
</label>
|
||||
</section>
|
||||
<footer class="modal-card-foot modal-card-foot-outlined">
|
||||
<button type="button" class="button is-primary" {{on "click" this.transitionToNewPki}}>
|
||||
Go to beta
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="button is-secondary"
|
||||
{{on "click" (fn (mut this.modalOpen) false)}}
|
||||
data-test-cancel-pki-beta-modal
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</footer>
|
||||
</Modal>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if this.isKV}}
|
||||
<span class="tag" data-test-kv-version-badge>
|
||||
Version
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import localStorage from 'vault/lib/local-storage';
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
/**
|
||||
|
@ -32,36 +28,7 @@ import Component from '@glimmer/component';
|
|||
*/
|
||||
|
||||
export default class SecretListHeader extends Component {
|
||||
@service router;
|
||||
@tracked hidePkiBetaModal = false;
|
||||
|
||||
get isKV() {
|
||||
return ['kv', 'generic'].includes(this.args.model.engineType);
|
||||
}
|
||||
|
||||
get isPki() {
|
||||
return this.args.model.engineType === 'pki';
|
||||
}
|
||||
|
||||
get shouldHidePkiBetaModal() {
|
||||
return localStorage.getItem('hidePkiBetaModal');
|
||||
}
|
||||
|
||||
get windowOrigin() {
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
@action
|
||||
transitionToNewPki() {
|
||||
this.router.transitionTo('vault.cluster.secrets.backend.pki.overview', this.args.model.id);
|
||||
}
|
||||
|
||||
@action
|
||||
toggleHidePkiBetaModal() {
|
||||
this.hidePkiBetaModal = !this.hidePkiBetaModal;
|
||||
|
||||
this.hidePkiBetaModal
|
||||
? localStorage.setItem('hidePkiBetaModal', true)
|
||||
: localStorage.removeItem('hidePkiBetaModal');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,33 +54,6 @@ const SECRET_BACKENDS = {
|
|||
editComponent: 'role-aws-edit',
|
||||
listItemPartial: 'secret-list/aws-role-item',
|
||||
},
|
||||
pki: {
|
||||
displayName: 'PKI',
|
||||
navigateTree: false,
|
||||
listItemPartial: 'secret-list/pki-role-item',
|
||||
tabs: [
|
||||
{
|
||||
name: 'roles',
|
||||
label: 'Roles',
|
||||
searchPlaceholder: 'Filter roles',
|
||||
item: 'role',
|
||||
create: 'Create role',
|
||||
editComponent: 'pki/role-pki-edit',
|
||||
},
|
||||
{
|
||||
name: 'cert',
|
||||
modelPrefix: 'cert/',
|
||||
label: 'Certificates',
|
||||
searchPlaceholder: 'Filter certificates',
|
||||
item: 'certificate',
|
||||
message: 'Issue a certificate from a role.',
|
||||
create: 'Create role',
|
||||
tab: 'cert',
|
||||
listItemPartial: 'secret-list/pki-cert-item',
|
||||
editComponent: 'pki/pki-cert-show',
|
||||
},
|
||||
],
|
||||
},
|
||||
ssh: {
|
||||
displayName: 'SSH',
|
||||
searchPlaceholder: 'Filter roles',
|
||||
|
|
|
@ -27,7 +27,7 @@ export default class PkiEngine extends Engine {
|
|||
'store',
|
||||
'version',
|
||||
],
|
||||
externalRoutes: ['secrets', 'secretsListRoot', 'secretsListRootConfiguration', 'externalMountIssuer'],
|
||||
externalRoutes: ['secrets', 'secretsListRootConfiguration', 'externalMountIssuer'],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { currentRouteName, currentURL, settled } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import editPage from 'vault/tests/pages/secrets/backend/pki/edit-role';
|
||||
import listPage from 'vault/tests/pages/secrets/backend/list';
|
||||
import generatePage from 'vault/tests/pages/secrets/backend/pki/generate-cert';
|
||||
import configPage from 'vault/tests/pages/settings/configure-secret-backends/pki/section-cert';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import { SELECTORS } from 'vault/tests/helpers/pki';
|
||||
import { csr } from 'vault/tests/helpers/pki/values';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
module('Acceptance | secrets/pki/list?tab=cert', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
return authPage.login();
|
||||
});
|
||||
// important for this comment to stay here otherwise the formatting mangles the CSR
|
||||
// prettier-ignore
|
||||
const CSR = csr;
|
||||
|
||||
// mount, generate CA, nav to create role page
|
||||
const setup = async (assert, action = 'issue') => {
|
||||
const path = `pki-${uuidv4()}`;
|
||||
const roleName = 'role';
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await configPage.visit({ backend: path }).form.generateCA();
|
||||
await settled();
|
||||
await editPage.visitRoot({ backend: path });
|
||||
await settled();
|
||||
await editPage.createRole('role', 'example.com');
|
||||
await settled();
|
||||
await generatePage.visit({ backend: path, id: roleName, action });
|
||||
await settled();
|
||||
return path;
|
||||
};
|
||||
|
||||
test('it issues a cert', async function (assert) {
|
||||
assert.expect(10);
|
||||
const mount = await setup(assert);
|
||||
await settled();
|
||||
await generatePage.issueCert('foo');
|
||||
await settled();
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${mount}/credentials/role?action=issue`);
|
||||
assert.dom(SELECTORS.certificate).exists('displays masked certificate');
|
||||
assert.dom(SELECTORS.commonName).exists('displays common name');
|
||||
assert.dom(SELECTORS.issueDate).exists('displays issue date');
|
||||
assert.dom(SELECTORS.expiryDate).exists('displays expiration date');
|
||||
assert.dom(SELECTORS.issuingCa).exists('displays masked issuing CA');
|
||||
assert.dom(SELECTORS.privateKey).exists('displays masked private key');
|
||||
assert.dom(SELECTORS.serialNumber).exists('displays serial number');
|
||||
assert.dom(SELECTORS.caChain).exists('displays the CA chain');
|
||||
|
||||
await generatePage.back();
|
||||
await settled();
|
||||
assert.notOk(generatePage.commonNameValue, 'the form is cleared');
|
||||
});
|
||||
|
||||
test('it signs a csr', async function (assert) {
|
||||
assert.expect(4);
|
||||
await setup(assert, 'sign');
|
||||
await settled();
|
||||
await generatePage.sign('common', CSR);
|
||||
await settled();
|
||||
assert.ok(SELECTORS.certificate, 'displays masked certificate');
|
||||
assert.ok(SELECTORS.commonName, 'displays common name');
|
||||
assert.ok(SELECTORS.issuingCa, 'displays masked issuing CA');
|
||||
assert.ok(SELECTORS.serialNumber, 'displays serial number');
|
||||
});
|
||||
|
||||
test('it views a cert', async function (assert) {
|
||||
assert.expect(12);
|
||||
const path = await setup(assert);
|
||||
await generatePage.issueCert('foo');
|
||||
await settled();
|
||||
await listPage.visitRoot({ backend: path, tab: 'cert' });
|
||||
await settled();
|
||||
assert.strictEqual(currentURL(), `/vault/secrets/${path}/list?tab=cert`);
|
||||
assert.strictEqual(listPage.secrets.length, 2, 'lists certs');
|
||||
await listPage.secrets.objectAt(0).click();
|
||||
await settled();
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.secrets.backend.show',
|
||||
'navigates to the show page'
|
||||
);
|
||||
assert.dom(SELECTORS.certificate).exists('displays masked certificate');
|
||||
assert.dom(SELECTORS.commonName).exists('displays common name');
|
||||
assert.dom(SELECTORS.issueDate).exists('displays issue date');
|
||||
assert.dom(SELECTORS.expiryDate).exists('displays expiration date');
|
||||
assert.dom(SELECTORS.serialNumber).exists('displays serial number');
|
||||
assert.dom(SELECTORS.revocationTime).doesNotExist('does not display revocation time of 0');
|
||||
assert.dom(SELECTORS.issuingCa).doesNotExist('does not display empty issuing CA');
|
||||
assert.dom(SELECTORS.caChain).doesNotExist('does not display empty CA chain');
|
||||
assert.dom(SELECTORS.privateKey).doesNotExist('does not display empty private key');
|
||||
});
|
||||
});
|
|
@ -1,67 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { click, currentRouteName, settled } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import page from 'vault/tests/pages/secrets/backend/list';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
|
||||
module('Acceptance | secrets/pki/list', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.uid = uuidv4();
|
||||
return authPage.login();
|
||||
});
|
||||
|
||||
const mountAndNav = async (uid) => {
|
||||
const path = `pki-${uid}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await page.visitRoot({ backend: path });
|
||||
};
|
||||
|
||||
test('it renders an empty list', async function (assert) {
|
||||
assert.expect(5);
|
||||
await mountAndNav(this.uid);
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.secrets.backend.list-root',
|
||||
'redirects from the index'
|
||||
);
|
||||
assert.ok(page.createIsPresent, 'create button is present');
|
||||
await click('[data-test-configuration-tab]');
|
||||
assert.ok(page.configureIsPresent, 'configure button is present');
|
||||
assert.strictEqual(page.tabs.length, 2, 'shows 2 tabs');
|
||||
assert.ok(page.backendIsEmpty);
|
||||
});
|
||||
|
||||
test('it navigates to the create page', async function (assert) {
|
||||
assert.expect(1);
|
||||
await mountAndNav(this.uid);
|
||||
await page.create();
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.secrets.backend.create-root',
|
||||
'links to the create page'
|
||||
);
|
||||
});
|
||||
|
||||
test('it navigates to the configure page', async function (assert) {
|
||||
assert.expect(1);
|
||||
await mountAndNav(this.uid);
|
||||
await click('[data-test-configuration-tab]');
|
||||
await page.configure();
|
||||
await settled();
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.settings.configure-secret-backend.section',
|
||||
'links to the configure page'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { currentRouteName, settled, visit, waitUntil } from '@ember/test-helpers';
|
||||
import { module, test, skip } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import editPage from 'vault/tests/pages/secrets/backend/pki/edit-role';
|
||||
import showPage from 'vault/tests/pages/secrets/backend/pki/show';
|
||||
import listPage from 'vault/tests/pages/secrets/backend/list';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
|
||||
module('Acceptance | secrets/pki/create', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.uid = uuidv4();
|
||||
return authPage.login();
|
||||
});
|
||||
|
||||
skip('it creates a role and redirects', async function (assert) {
|
||||
const path = `pki-create-${this.uid}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await editPage.visitRoot({ backend: path });
|
||||
await settled();
|
||||
await editPage.createRole('role', 'example.com');
|
||||
await settled();
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.secrets.backend.show',
|
||||
'redirects to the show page'
|
||||
);
|
||||
assert.dom('[data-test-edit-link="true"]').exists('shows the edit button');
|
||||
assert.dom('[data-test-credentials-link="true"]').exists('shows the generate button');
|
||||
assert.dom('[data-test-sign-link="true"]').exists('shows the sign button');
|
||||
|
||||
await showPage.visit({ backend: path, id: 'role' });
|
||||
await settled();
|
||||
await showPage.generateCert();
|
||||
await settled();
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.secrets.backend.credentials',
|
||||
'navs to the credentials page'
|
||||
);
|
||||
|
||||
await showPage.visit({ backend: path, id: 'role' });
|
||||
await settled();
|
||||
await visit(`/vault/secrets/${path}/credentials/role?action=sign`);
|
||||
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.secrets.backend.credentials',
|
||||
'navs to the credentials page'
|
||||
);
|
||||
|
||||
await listPage.visitRoot({ backend: path });
|
||||
await settled();
|
||||
assert.strictEqual(listPage.secrets.length, 1, 'shows role in the list');
|
||||
const secret = listPage.secrets.objectAt(0);
|
||||
await secret.menuToggle();
|
||||
await settled();
|
||||
assert.ok(listPage.menuItems.length > 0, 'shows links in the menu');
|
||||
});
|
||||
|
||||
test('it deletes a role', async function (assert) {
|
||||
const path = `pki-delete-${this.uid}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await editPage.visitRoot({ backend: path });
|
||||
await settled();
|
||||
await editPage.createRole('role', 'example.com');
|
||||
await settled();
|
||||
await showPage.visit({ backend: path, id: 'role' });
|
||||
await settled();
|
||||
await showPage.deleteRole();
|
||||
await settled();
|
||||
assert.ok(
|
||||
await waitUntil(() => currentRouteName() === 'vault.cluster.secrets.backend.list-root'),
|
||||
'redirects to list page'
|
||||
);
|
||||
assert.ok(listPage.backendIsEmpty, 'no roles listed');
|
||||
});
|
||||
});
|
|
@ -8,7 +8,7 @@ import { module, test } from 'qunit';
|
|||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import page from 'vault/tests/pages/settings/configure-secret-backends/pki/index';
|
||||
import { visit } from '@ember/test-helpers';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
import { create } from 'ember-cli-page-object';
|
||||
|
@ -31,7 +31,7 @@ module('Acceptance | settings/configure/secrets/ssh', function (hooks) {
|
|||
const path = `ssh-configure-${this.uid}`;
|
||||
await enablePage.enable('ssh', path);
|
||||
await settled();
|
||||
await page.visit({ backend: path });
|
||||
visit(`/vault/settings/secrets/configure/${path}`);
|
||||
await settled();
|
||||
assert.dom(SELECTORS.generateSigningKey).isChecked('generate_signing_key defaults to true');
|
||||
await click(SELECTORS.generateSigningKey);
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { currentRouteName, settled } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import page from 'vault/tests/pages/settings/configure-secret-backends/pki/index';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
|
||||
module('Acceptance | settings/configure/secrets/pki', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.uid = uuidv4();
|
||||
return authPage.login();
|
||||
});
|
||||
|
||||
test('it redirects to the cert section', async function (assert) {
|
||||
const path = `pki-cert-${this.uid}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await page.visit({ backend: path });
|
||||
await settled();
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
'vault.cluster.settings.configure-secret-backend.section',
|
||||
'redirects from the index'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,149 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { currentRouteName, settled, click } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import page from 'vault/tests/pages/settings/configure-secret-backends/pki/section-cert';
|
||||
import { SELECTORS } from 'vault/tests/helpers/pki';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
module('Acceptance | settings/configure/secrets/pki/cert', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
return authPage.login();
|
||||
});
|
||||
|
||||
//prettier-ignore
|
||||
const PEM_BUNDLE = `-----BEGIN CERTIFICATE-----
|
||||
MIIDGjCCAgKgAwIBAgIUFvnhb2nQ8+KNS3SzjlfYDMHGIRgwDQYJKoZIhvcNAQEL
|
||||
BQAwDTELMAkGA1UEAxMCZmEwHhcNMTgwMTEwMTg1NDI5WhcNMTgwMjExMTg1NDU5
|
||||
WjANMQswCQYDVQQDEwJmYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AN2VtBn6EMlA4aYre/xoKHxlgNDxJnfSQWfs6yF/K201qPnt4QF9AXChatbmcKVn
|
||||
OaURq+XEJrGVgF/u2lSos3NRZdhWVe8o3/sOetsGxcrd0gXAieOSmkqJjp27bYdl
|
||||
uY3WsxhyiPvdfS6xz39OehsK/YCB6qCzwB4eEfSKqbkvfDL9sLlAiOlaoHC9pczf
|
||||
6/FANKp35UDwInSwmq5vxGbnWk9zMkh5Jq6hjOWHZnVc2J8J49PYvkIM8uiHDgOE
|
||||
w71T2xM5plz6crmZnxPCOcTKIdF7NTEP2lUfiqc9lONV9X1Pi4UclLPHJf5bwTmn
|
||||
JaWgbKeY+IlF61/mgxzhC7cCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
|
||||
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFLDtc6+HZN2lv60JSDAZq3+IHoq7MB8GA1Ud
|
||||
IwQYMBaAFLDtc6+HZN2lv60JSDAZq3+IHoq7MA0GA1UdEQQGMASCAmZhMA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQDVt6OddTV1MB0UvF5v4zL1bEB9bgXvWx35v/FdS+VGn/QP
|
||||
cC2c4ZNukndyHhysUEPdqVg4+up1aXm4eKXzNmGMY/ottN2pEhVEWQyoIIA1tH0e
|
||||
8Kv/bysYpHZKZuoGg5+mdlHS2p2Dh2bmYFyBLJ8vaeP83NpTs2cNHcmEvWh/D4UN
|
||||
UmYDODRN4qh9xYruKJ8i89iMGQfbdcq78dCC4JwBIx3bysC8oF4lqbTYoYNVTnAi
|
||||
LVqvLdHycEOMlqV0ecq8uMLhPVBalCmIlKdWNQFpXB0TQCsn95rCCdi7ZTsYk5zv
|
||||
Q4raFvQrZth3Cz/X5yPTtQL78oBYrmHzoQKDFJ2z
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEA3ZW0GfoQyUDhpit7/GgofGWA0PEmd9JBZ+zrIX8rbTWo+e3h
|
||||
AX0BcKFq1uZwpWc5pRGr5cQmsZWAX+7aVKizc1Fl2FZV7yjf+w562wbFyt3SBcCJ
|
||||
45KaSomOnbtth2W5jdazGHKI+919LrHPf056Gwr9gIHqoLPAHh4R9IqpuS98Mv2w
|
||||
uUCI6VqgcL2lzN/r8UA0qnflQPAidLCarm/EZudaT3MySHkmrqGM5YdmdVzYnwnj
|
||||
09i+Qgzy6IcOA4TDvVPbEzmmXPpyuZmfE8I5xMoh0Xs1MQ/aVR+Kpz2U41X1fU+L
|
||||
hRyUs8cl/lvBOaclpaBsp5j4iUXrX+aDHOELtwIDAQABAoIBACLdk2Ei/9Eq7FaB
|
||||
MRkeKoCoWASIbU0dQD1iAf1bTTH554Sr8WOSj89xFqaJy9+6xk864Jleq9f1diWi
|
||||
J6h6gwH6JNRNgWgIPnX6aUpdXnH1RT6ydP/h6XUg/9fBzhIn53Jx/ewy2WsIBtJ6
|
||||
F/QoHP50VD8MMibnIaubf6fCycHhc97u4BKM2QdnAugn1sWjSiTIoYmFw/3Ej8mB
|
||||
bItLWZTg9oMASgCtDwPEstlKn7yPqirOJj+G/a+6sIcP2fynd0fISsfLZ0ovN+yW
|
||||
d3SV3orC0RNj83GVwYykqwCc/3pP0mRfX9fl8DKbXusITqUiGL8LGb+H6YDDpbNU
|
||||
5Fj7VwECgYEA5P6aIcGfCZayEJtHKlTCA2/KBkGTOP/0iNKWhntBQT/GK+bjmr+D
|
||||
GO1zR8ZFEIRdlUA5MjC9wU2AQikgFQzzmtz604Wt34fDN2NFrxq8sWN7Hjr65Fjf
|
||||
ivJ6faT5r5gcNEq3EM/GLF9oJH8M+B5ccFe9iXH8AbmZHOO0FZtYxIcCgYEA97dm
|
||||
Kj1qyuKlINXKt4KXdYMuIT+Z3G1B92wNN9TY/eJZgCJ7zlNcinUF/OFbiGgsk4t+
|
||||
P0yVMs8BENQML0TH4Gebf4HfnDFno4J1M9HDt6HSMhsLKyvFYjFvb8hF4SPrY1pF
|
||||
wW3lM3zMMzAVi8044vRrTvxfxL8QJX+1Hesye1ECgYAT5/H8Fzm8+qWV/fmMu3t2
|
||||
EwSr0I18uftG3Y+KNzKv+lw+ur50WEuMIjAQQDMGwYrlC4UtUMFeCV+p4KtSSSLw
|
||||
Bl+jfY5kzQdyTCXll9xpSy2LrjLbIMKl8Hgnbezqj7176jbJtlYSy2RhL84vz2vX
|
||||
tDjcttTiTYD62uxvqGZqBwKBgFQ3tPM9aDZL8coFBWN4cZfRHnjNT7kCKEA/KwtF
|
||||
QPSn5LfMgXz3GGo2OO/tihoJGMac0TIiDkN03y7ieLYFU1L2xoYGGIjYvxx2+PPC
|
||||
KCEhUf4Y9aYavoOQvQsq8p8FgDyJ71dAzoC/uAjbGygpgGKgqG71HHYeYxXsoh3m
|
||||
3YXRAoGAE7MBnVJWiIN5s63gGz9f9V6k1dPLfcPE1I0KMM/SDOIV0oLMsYQecTTB
|
||||
ZzkXwRCdcJARkaKulTfjby7+oGpQydP8iZr+CNKFxwf838UbhhsXHnN6rc62qzYD
|
||||
BXUV2Uwtxf+QCphnlht9muX2fsLIzDJea0JipWj1uf2H8OZsjE8=
|
||||
-----END RSA PRIVATE KEY-----`;
|
||||
|
||||
const mountAndNav = async (assert, prefix) => {
|
||||
const path = `${prefix}pki-${uuidv4()}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await page.visit({ backend: path });
|
||||
await settled();
|
||||
return path;
|
||||
};
|
||||
|
||||
test('cert config: generate', async function (assert) {
|
||||
assert.expect(10);
|
||||
await mountAndNav(assert);
|
||||
await settled();
|
||||
assert.strictEqual(currentRouteName(), 'vault.cluster.settings.configure-secret-backend.section');
|
||||
|
||||
await page.form.generateCA();
|
||||
await settled();
|
||||
|
||||
assert.dom(SELECTORS.certificate).exists('certificate is present and masked');
|
||||
assert.dom(SELECTORS.commonName).exists('displays common name');
|
||||
assert.dom(SELECTORS.issueDate).exists('displays issue date');
|
||||
assert.dom(SELECTORS.expiryDate).exists('displays expiration date');
|
||||
assert.dom(SELECTORS.issuingCa).exists('displays masked issuing CA');
|
||||
assert.dom(SELECTORS.serialNumber).exists('displays serial number');
|
||||
assert.dom(SELECTORS.csr).doesNotExist('does not display empty CSR');
|
||||
assert.dom(SELECTORS.caChain).doesNotExist('does not display empty CA chain');
|
||||
assert.dom(SELECTORS.privateKey).doesNotExist('does not display empty private key');
|
||||
await page.form.back();
|
||||
await page.form.generateCA();
|
||||
await settled();
|
||||
});
|
||||
|
||||
test('cert config: upload', async function (assert) {
|
||||
assert.expect(2);
|
||||
await mountAndNav(assert);
|
||||
await settled();
|
||||
assert.strictEqual(page.form.downloadLinks.length, 0, 'there are no download links');
|
||||
|
||||
await page.form.uploadCA(PEM_BUNDLE);
|
||||
await settled();
|
||||
assert.ok(
|
||||
page.flash.latestMessage.startsWith('The certificate for this backend has been updated'),
|
||||
'flash message displays properly'
|
||||
);
|
||||
});
|
||||
|
||||
test('cert config: sign intermediate and set signed intermediate', async function (assert) {
|
||||
assert.expect(3);
|
||||
const rootPath = await mountAndNav(assert, 'root-');
|
||||
await page.form.generateCA();
|
||||
await settled();
|
||||
const intermediatePath = await mountAndNav(assert, 'intermediate-');
|
||||
await page.form.generateCA('Intermediate CA', 'intermediate');
|
||||
await settled();
|
||||
// cache csr
|
||||
await click('.masked-input-toggle');
|
||||
const csrVal = document.querySelector('.masked-value').innerText;
|
||||
await page.form.back();
|
||||
await settled();
|
||||
await page.visit({ backend: rootPath });
|
||||
await settled();
|
||||
await page.form.signIntermediate('Intermediate CA');
|
||||
await settled();
|
||||
await page.form.csrField(csrVal).submit();
|
||||
await settled();
|
||||
assert.dom(SELECTORS.caChain).exists('full CA chain is shown');
|
||||
assert.dom(SELECTORS.privateKey).doesNotExist('does not display empty private key');
|
||||
await click('.masked-input-toggle');
|
||||
const intermediateCert = document.querySelector('[data-test-masked-input]').innerText;
|
||||
await page.form.back();
|
||||
await settled();
|
||||
await page.visit({ backend: intermediatePath });
|
||||
await settled();
|
||||
await page.form.setSignedIntermediateBtn().signedIntermediate(intermediateCert);
|
||||
await settled();
|
||||
await page.form.submit();
|
||||
await settled();
|
||||
assert.dom('[data-test-go-replace-ca]').exists();
|
||||
});
|
||||
});
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { currentRouteName, settled } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import page from 'vault/tests/pages/settings/configure-secret-backends/pki/section';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
|
||||
module('Acceptance | settings/configure/secrets/pki/crl', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.uid = uuidv4();
|
||||
return authPage.login();
|
||||
});
|
||||
|
||||
test('it saves crl config', async function (assert) {
|
||||
const path = `pki-crl-${this.uid}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await page.visit({ backend: path, section: 'crl' });
|
||||
await settled();
|
||||
assert.strictEqual(currentRouteName(), 'vault.cluster.settings.configure-secret-backend.section');
|
||||
await page.form.fillInUnit('h');
|
||||
await page.form.fillInValue(3);
|
||||
await page.form.submit();
|
||||
await settled();
|
||||
assert.strictEqual(page.lastMessage, 'The crl config for this backend has been updated.');
|
||||
});
|
||||
});
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { currentRouteName, settled } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import page from 'vault/tests/pages/settings/configure-secret-backends/pki/section';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
|
||||
module('Acceptance | settings/configure/secrets/pki/tidy', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
return authPage.login();
|
||||
});
|
||||
|
||||
test('it saves tidy config', async function (assert) {
|
||||
const path = `pki-tidy-${uuidv4()}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await page.visit({ backend: path, section: 'tidy' });
|
||||
await settled();
|
||||
assert.strictEqual(currentRouteName(), 'vault.cluster.settings.configure-secret-backend.section');
|
||||
await page.form.fields.objectAt(0).clickLabel();
|
||||
|
||||
await page.form.submit();
|
||||
await settled();
|
||||
assert.strictEqual(page.lastMessage, 'The tidy config for this backend has been updated.');
|
||||
});
|
||||
});
|
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { currentRouteName, settled, find, waitUntil } from '@ember/test-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import page from 'vault/tests/pages/settings/configure-secret-backends/pki/section';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import enablePage from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
|
||||
module('Acceptance | settings/configure/secrets/pki/urls', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.uid = uuidv4();
|
||||
return authPage.login();
|
||||
});
|
||||
|
||||
test('it saves urls config', async function (assert) {
|
||||
const path = `pki-config-urls-${this.uid}`;
|
||||
await enablePage.enable('pki', path);
|
||||
await settled();
|
||||
await page.visit({ backend: path, section: 'urls' });
|
||||
await settled();
|
||||
assert.strictEqual(currentRouteName(), 'vault.cluster.settings.configure-secret-backend.section');
|
||||
|
||||
await page.form.fields.objectAt(0).textarea('foo').change();
|
||||
await page.form.submit();
|
||||
await waitUntil(() => find('[data-test-error]'));
|
||||
assert.ok(page.form.hasError, 'shows error on invalid input');
|
||||
|
||||
await page.form.fields.objectAt(0).textarea('foo.example.com').change();
|
||||
await page.form.submit();
|
||||
await settled();
|
||||
assert.strictEqual(page.lastMessage, 'The urls config for this backend has been updated.');
|
||||
});
|
||||
});
|
|
@ -1,85 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { resolve } from 'rsvp';
|
||||
import EmberObject from '@ember/object';
|
||||
import Service from '@ember/service';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { create } from 'ember-cli-page-object';
|
||||
import configPki from 'vault/tests/pages/components/pki/config-pki-ca';
|
||||
import apiStub from 'vault/tests/helpers/noop-all-api-requests';
|
||||
|
||||
const component = create(configPki);
|
||||
|
||||
const storeStub = Service.extend({
|
||||
createRecord(type, args) {
|
||||
return EmberObject.create(args, {
|
||||
save() {
|
||||
return resolve(this);
|
||||
},
|
||||
destroyRecord() {},
|
||||
send() {},
|
||||
unloadRecord() {},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
module('Integration | Component | config pki ca', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.server = apiStub();
|
||||
this.owner.lookup('service:flash-messages').registerTypes(['success']);
|
||||
this.owner.register('service:store', storeStub);
|
||||
this.storeService = this.owner.lookup('service:store');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
this.server.shutdown();
|
||||
});
|
||||
|
||||
const config = function (pem) {
|
||||
return EmberObject.create({
|
||||
pem: pem,
|
||||
backend: 'pki',
|
||||
caChain: 'caChain',
|
||||
der: new Blob(['der'], { type: 'text/plain' }),
|
||||
});
|
||||
};
|
||||
|
||||
const setupAndRender = async function (context, onRefresh) {
|
||||
const refreshFn = onRefresh || function () {};
|
||||
context.set('config', config());
|
||||
context.set('onRefresh', refreshFn);
|
||||
await context.render(hbs`<Pki::ConfigPkiCa @onRefresh={{this.onRefresh}} @config={{this.config}} />`);
|
||||
};
|
||||
|
||||
test('it renders, no pem', async function (assert) {
|
||||
await setupAndRender(this);
|
||||
|
||||
assert.notOk(component.hasTitle, 'no title in the default state');
|
||||
assert.strictEqual(component.replaceCAText, 'Configure CA');
|
||||
assert.strictEqual(component.downloadLinks.length, 0, 'there are no download links');
|
||||
|
||||
await component.replaceCA();
|
||||
assert.strictEqual(component.title, 'Configure CA Certificate');
|
||||
await component.back();
|
||||
|
||||
await component.setSignedIntermediateBtn();
|
||||
assert.strictEqual(component.title, 'Set signed intermediate');
|
||||
});
|
||||
|
||||
test('it renders, with pem', async function (assert) {
|
||||
const c = config('pem');
|
||||
this.set('config', c);
|
||||
await render(hbs`<Pki::ConfigPkiCa @config={{this.config}} />`);
|
||||
assert.notOk(component.hasTitle, 'no title in the default state');
|
||||
assert.strictEqual(component.replaceCAText, 'Add CA');
|
||||
assert.strictEqual(component.downloadLinks.length, 3, 'shows download links');
|
||||
});
|
||||
});
|
|
@ -1,154 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { resolve } from 'rsvp';
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { click, render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { create } from 'ember-cli-page-object';
|
||||
import configPki from 'vault/tests/pages/components/pki/config-pki';
|
||||
|
||||
const component = create(configPki);
|
||||
|
||||
module('Integration | Component | config pki', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
this.owner.lookup('service:flash-messages').registerTypes(['success']);
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.config = await this.store.createRecord('pki/pki-config');
|
||||
this.mockConfigSave = function (saveFn) {
|
||||
const { tidyAttrs, crlAttrs, urlsAttrs } = this.config;
|
||||
return {
|
||||
save: saveFn,
|
||||
rollbackAttributes: () => {},
|
||||
tidyAttrs,
|
||||
crlAttrs,
|
||||
urlsAttrs,
|
||||
set: () => {},
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
const setupAndRender = async function (context, config, section = 'tidy') {
|
||||
context.set('config', config);
|
||||
context.set('section', section);
|
||||
await context.render(hbs`<Pki::ConfigPki @section={{this.section}} @config={{this.config}} />`);
|
||||
};
|
||||
|
||||
test('it renders tidy section', async function (assert) {
|
||||
await setupAndRender(this, this.config);
|
||||
assert.ok(component.text.startsWith('You can tidy up the backend'));
|
||||
assert.notOk(component.hasTitle, 'No title for tidy section');
|
||||
assert.strictEqual(component.fields.length, 3, 'renders all three tidy fields');
|
||||
assert.ok(component.fields.objectAt(0).labelText, 'Tidy the Certificate Store');
|
||||
assert.ok(component.fields.objectAt(1).labelText, 'Tidy the Revocation List (CRL)');
|
||||
assert.ok(component.fields.objectAt(1).labelText, 'Safety buffer');
|
||||
});
|
||||
|
||||
test('it renders crl section', async function (assert) {
|
||||
await setupAndRender(this, this.config, 'crl');
|
||||
assert.false(this.config.disable, 'CRL config defaults disable=false');
|
||||
assert.ok(component.hasTitle, 'renders form title');
|
||||
assert.strictEqual(component.title, 'Certificate Revocation List (CRL) config');
|
||||
assert.ok(
|
||||
component.text.startsWith('Set the duration for which the generated CRL'),
|
||||
'renders form subtext'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-ttl-form-label="CRL building enabled"]')
|
||||
.hasText('CRL building enabled', 'renders enabled field title');
|
||||
assert
|
||||
.dom('[data-test-ttl-form-subtext]')
|
||||
.hasText('The CRL will expire after', 'renders enabled field subtext');
|
||||
assert.dom('[data-test-input="expiry"] input').isChecked('defaults to enabling CRL build');
|
||||
assert.dom('[data-test-ttl-value="CRL building enabled"]').hasValue('3', 'default value is 3 (72h)');
|
||||
assert.dom('[data-test-select="ttl-unit"]').hasValue('d', 'default unit value is days');
|
||||
await click('[data-test-input="expiry"] input');
|
||||
assert
|
||||
.dom('[data-test-ttl-form-subtext]')
|
||||
.hasText('The CRL will not be built.', 'renders disabled text when toggled off');
|
||||
|
||||
// assert 'disable' attr on pki-config model updates with toggle
|
||||
assert.true(this.config.disable, 'when toggled off, sets CRL config to disable=true');
|
||||
await click('[data-test-input="expiry"] input');
|
||||
assert
|
||||
.dom('[data-test-ttl-form-subtext]')
|
||||
.hasText('The CRL will expire after', 'toggles back to enabled text');
|
||||
assert.false(this.config.disable, 'CRL config toggles back to disable=false');
|
||||
});
|
||||
|
||||
test('it renders urls section', async function (assert) {
|
||||
await setupAndRender(this, this.config, 'urls');
|
||||
assert.notOk(component.hasTitle, 'No title for urls section');
|
||||
assert.strictEqual(component.fields.length, 3);
|
||||
assert.ok(component.fields.objectAt(0).labelText, 'Issuing certificates');
|
||||
assert.ok(component.fields.objectAt(1).labelText, 'CRL Distribution Points');
|
||||
assert.ok(component.fields.objectAt(2).labelText, 'OCSP Servers');
|
||||
});
|
||||
|
||||
test('it calls save with the correct arguments for tidy', async function (assert) {
|
||||
assert.expect(3);
|
||||
const section = 'tidy';
|
||||
this.set('onRefresh', () => {
|
||||
assert.ok(true, 'refresh called');
|
||||
});
|
||||
this.set(
|
||||
'config',
|
||||
this.mockConfigSave((options) => {
|
||||
assert.strictEqual(options.adapterOptions.method, section, 'method passed to save');
|
||||
assert.deepEqual(
|
||||
options.adapterOptions.fields,
|
||||
['tidyCertStore', 'tidyRevocationList', 'safetyBuffer'],
|
||||
'tidy fields passed to save'
|
||||
);
|
||||
return resolve();
|
||||
})
|
||||
);
|
||||
this.set('section', section);
|
||||
await render(
|
||||
hbs`<Pki::ConfigPki @section={{this.section}} @config={{this.config}} @onRefresh={{this.onRefresh}} />`
|
||||
);
|
||||
|
||||
component.submit();
|
||||
});
|
||||
|
||||
test('it calls save with the correct arguments for crl', async function (assert) {
|
||||
assert.expect(3);
|
||||
const section = 'crl';
|
||||
this.set('onRefresh', () => {
|
||||
assert.ok(true, 'refresh called');
|
||||
});
|
||||
this.set(
|
||||
'config',
|
||||
this.mockConfigSave((options) => {
|
||||
assert.strictEqual(options.adapterOptions.method, section, 'method passed to save');
|
||||
assert.deepEqual(options.adapterOptions.fields, ['expiry', 'disable'], 'CRL fields passed to save');
|
||||
return resolve();
|
||||
})
|
||||
);
|
||||
this.set('section', section);
|
||||
await render(
|
||||
hbs`<Pki::ConfigPki @section={{this.section}} @config={{this.config}} @onRefresh={{this.onRefresh}} />`
|
||||
);
|
||||
component.submit();
|
||||
});
|
||||
|
||||
test('it correctly sets toggle when initial CRL config is disable=true', async function (assert) {
|
||||
assert.expect(3);
|
||||
// change default config attrs
|
||||
const configDisabled = this.config;
|
||||
configDisabled.expiry = '1m';
|
||||
configDisabled.disable = true;
|
||||
await setupAndRender(this, configDisabled, 'crl');
|
||||
assert.dom('[data-test-input="expiry"] input').isNotChecked('toggle disabled when CRL config disabled');
|
||||
await click('[data-test-input="expiry"] input');
|
||||
assert
|
||||
.dom('[data-test-ttl-value="CRL building enabled"]')
|
||||
.hasValue('1', 'when toggled on shows last set expired value');
|
||||
assert.dom('[data-test-select="ttl-unit"]').hasValue('m', 'when toggled back on shows last set unit');
|
||||
});
|
||||
});
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, click } from '@ember/test-helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { supportedSecretBackends } from 'vault/helpers/supported-secret-backends';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
|
@ -28,7 +28,6 @@ module('Integration | Component | secret-list-header', function (hooks) {
|
|||
<SecretListHeader
|
||||
@model={{this.model}}
|
||||
/>
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
const selector = '[data-test-kv-version-badge]';
|
||||
|
||||
|
@ -44,91 +43,4 @@ module('Integration | Component | secret-list-header', function (hooks) {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('it should render new pki beta button and remain the same for other engines', async function (assert) {
|
||||
const backends = supportedSecretBackends();
|
||||
const numExpects = backends.length + 1;
|
||||
assert.expect(numExpects);
|
||||
|
||||
this.server.post('/sys/capabilities-self', () => {});
|
||||
|
||||
for (const type of backends) {
|
||||
const data = this.server.create('secret-engine', 2, { type });
|
||||
this.model = mirageToModels(data);
|
||||
await render(hbs`
|
||||
<SecretListHeader
|
||||
@model={{this.model}}
|
||||
/>
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
const oldPkiBetaButtonSelector = '[data-test-old-pki-beta-button]';
|
||||
const oldPkiBetaModalSelector = '[data-test-modal-background="New PKI Beta"]';
|
||||
|
||||
if (type === 'pki') {
|
||||
assert.dom(oldPkiBetaButtonSelector).hasText('New PKI UI available');
|
||||
await click(oldPkiBetaButtonSelector);
|
||||
assert.dom(oldPkiBetaModalSelector).exists();
|
||||
} else {
|
||||
assert
|
||||
.dom(oldPkiBetaButtonSelector)
|
||||
.doesNotExist(`Version badge does not render for ${type} engine type`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('it should render return to old pki from new pki', async function (assert) {
|
||||
const backends = supportedSecretBackends();
|
||||
assert.expect(backends.length);
|
||||
|
||||
this.server.post('/sys/capabilities-self', () => {});
|
||||
|
||||
for (const type of backends) {
|
||||
const data = this.server.create('secret-engine', 2, { type });
|
||||
this.model = mirageToModels(data);
|
||||
await render(hbs`
|
||||
<SecretListHeader
|
||||
@model={{this.model}}
|
||||
@isEngine={{true}}
|
||||
/>
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
const newPkiButtonSelector = '[data-test-new-pki-beta-button]';
|
||||
|
||||
if (type === 'pki') {
|
||||
assert.dom(newPkiButtonSelector).hasText('Return to old PKI');
|
||||
} else {
|
||||
assert.dom(newPkiButtonSelector).doesNotExist(`No return to old pki exists`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('it should show the pki modal when New PKI UI available button is clicked', async function (assert) {
|
||||
const backends = supportedSecretBackends();
|
||||
const numExpects = backends.length + 1;
|
||||
assert.expect(numExpects);
|
||||
|
||||
this.server.post('/sys/capabilities-self', () => {});
|
||||
|
||||
for (const type of backends) {
|
||||
const data = this.server.create('secret-engine', 2, { type });
|
||||
this.model = mirageToModels(data);
|
||||
await render(hbs`
|
||||
<SecretListHeader
|
||||
@model={{this.model}}
|
||||
/>
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
const oldPkiButtonSelector = '[data-test-old-pki-beta-button]';
|
||||
const cancelPkiBetaModal = '[data-test-cancel-pki-beta-modal]';
|
||||
|
||||
if (type === 'pki') {
|
||||
await click(oldPkiButtonSelector);
|
||||
assert.dom('.modal.is-active').exists('Pki beta modal is open');
|
||||
await click(cancelPkiBetaModal);
|
||||
assert.dom('.modal').exists('Pki beta modal is closed');
|
||||
} else {
|
||||
assert.dom(oldPkiButtonSelector).doesNotExist(`No return to old pki exists`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -48,10 +48,8 @@ module('Integration | Util | parse pki certificate', function (hooks) {
|
|||
country: 'France',
|
||||
other_sans: '1.3.1.4.1.5.9.2.6;UTF8:some-utf-string',
|
||||
exclude_cn_from_sans: true,
|
||||
expiry_date: {},
|
||||
ip_sans: '192.158.1.38, 1234:0fd2:5621:0001:0089:0000:0000:4500',
|
||||
key_usage: 'CertSign, CRLSign',
|
||||
issue_date: {},
|
||||
locality: 'Paris',
|
||||
max_path_length: 17,
|
||||
not_valid_after: 1678210083,
|
||||
|
@ -239,8 +237,6 @@ module('Integration | Util | parse pki certificate', function (hooks) {
|
|||
common_name: null,
|
||||
country: null,
|
||||
exclude_cn_from_sans: false,
|
||||
expiry_date: {},
|
||||
issue_date: {},
|
||||
key_usage: null,
|
||||
locality: null,
|
||||
max_path_length: 10,
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { clickable, collection, fillable, text, selectable, isPresent } from 'ember-cli-page-object';
|
||||
|
||||
import fields from '../form-field';
|
||||
|
||||
export default {
|
||||
...fields,
|
||||
scope: '.config-pki-ca',
|
||||
text: text('[data-test-text]'),
|
||||
title: text('[data-test-title]'),
|
||||
|
||||
hasTitle: isPresent('[data-test-title]'),
|
||||
hasError: isPresent('[data-test-error]'),
|
||||
hasSignIntermediateForm: isPresent('[data-test-sign-intermediate-form]'),
|
||||
|
||||
replaceCA: clickable('[data-test-go-replace-ca]'),
|
||||
replaceCAText: text('[data-test-go-replace-ca]'),
|
||||
setSignedIntermediateBtn: clickable('[data-test-go-set-signed-intermediate]'),
|
||||
signIntermediateBtn: clickable('[data-test-go-sign-intermediate]'),
|
||||
caType: selectable('[data-test-input="caType"]'),
|
||||
submit: clickable('[data-test-submit]'),
|
||||
back: clickable('[data-test-back-button]'),
|
||||
|
||||
signedIntermediate: fillable('[data-test-signed-intermediate]'),
|
||||
downloadLinks: collection('[data-test-ca-download-link]'),
|
||||
rows: collection('[data-test-table-row]'),
|
||||
rowValues: collection('[data-test-row-value]'),
|
||||
csr: text('[data-test-row-value="CSR"]', { normalize: false }),
|
||||
csrField: fillable('[data-test-input="csr"]'),
|
||||
certificate: text('[data-test-row-value="Certificate"]', { normalize: false }),
|
||||
uploadCert: clickable('[data-test-input="uploadPemBundle"]'),
|
||||
enterCertAsText: clickable('[data-test-text-toggle]'),
|
||||
pemBundle: fillable('[data-test-text-file-textarea]'),
|
||||
commonName: fillable('[data-test-input="commonName"]'),
|
||||
|
||||
async generateCA(commonName = 'PKI CA', type = 'root') {
|
||||
if (type === 'intermediate') {
|
||||
return await this.replaceCA().commonName(commonName).caType('intermediate').submit();
|
||||
}
|
||||
return await this.replaceCA().commonName(commonName).submit();
|
||||
},
|
||||
|
||||
async uploadCA(pem) {
|
||||
return await this.replaceCA().uploadCert().enterCertAsText().pemBundle(pem).submit();
|
||||
},
|
||||
|
||||
async signIntermediate(commonName) {
|
||||
return await this.signIntermediateBtn().commonName(commonName);
|
||||
},
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { clickable, fillable, text, isPresent } from 'ember-cli-page-object';
|
||||
import fields from '../form-field';
|
||||
|
||||
export default {
|
||||
...fields,
|
||||
scope: '.config-pki',
|
||||
text: text('[data-test-text]'),
|
||||
title: text('[data-test-title]'),
|
||||
hasTitle: isPresent('[data-test-title]'),
|
||||
hasError: isPresent('[data-test-error]'),
|
||||
submit: clickable('[data-test-submit]'),
|
||||
enableTtl: clickable('[data-test-toggle-input]'),
|
||||
fillInValue: fillable('[data-test-ttl-value]'),
|
||||
fillInUnit: fillable('[data-test-select="ttl-unit"]'),
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { create, visitable } from 'ember-cli-page-object';
|
||||
|
||||
export default create({
|
||||
visit: visitable('/vault/settings/secrets/configure/:backend/'),
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { create, visitable } from 'ember-cli-page-object';
|
||||
|
||||
import ConfigPKICA from 'vault/tests/pages/components/pki/config-pki-ca';
|
||||
import flashMessages from 'vault/tests/pages/components/flash-message';
|
||||
|
||||
export default create({
|
||||
visit: visitable('/vault/settings/secrets/configure/:backend/cert'),
|
||||
form: ConfigPKICA,
|
||||
flash: flashMessages,
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*/
|
||||
|
||||
import { create, visitable, collection } from 'ember-cli-page-object';
|
||||
|
||||
import { getter } from 'ember-cli-page-object/macros';
|
||||
import ConfigPKI from 'vault/tests/pages/components/pki/config-pki';
|
||||
|
||||
export default create({
|
||||
visit: visitable('/vault/settings/secrets/configure/:backend/:section'),
|
||||
form: ConfigPKI,
|
||||
lastMessage: getter(function () {
|
||||
const count = this.flashMessages.length;
|
||||
return this.flashMessages.objectAt(count - 1).text;
|
||||
}),
|
||||
flashMessages: collection('[data-test-flash-message-body]'),
|
||||
});
|
Loading…
Reference in New Issue