diff --git a/ui/app/adapters/pki/action.js b/ui/app/adapters/pki/action.js deleted file mode 100644 index 840b197ecd..0000000000 --- a/ui/app/adapters/pki/action.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { assert } from '@ember/debug'; -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import ApplicationAdapter from '../application'; - -export default class PkiActionAdapter extends ApplicationAdapter { - namespace = 'v1'; - - urlForCreateRecord(modelName, snapshot) { - const { type } = snapshot.record; - const { actionType, useIssuer, issuerRef, mount } = snapshot.adapterOptions; - // if the backend mount is passed, we want that to override the URL's mount path - const backend = mount || snapshot.record.backend; - if (!backend || !actionType) { - throw new Error('URL for create record is missing required attributes'); - } - const baseUrl = `${this.buildURL()}/${encodePath(backend)}`; - switch (actionType) { - case 'import': - return useIssuer ? `${baseUrl}/issuers/import/bundle` : `${baseUrl}/config/ca`; - case 'generate-root': - return useIssuer ? `${baseUrl}/issuers/generate/root/${type}` : `${baseUrl}/root/generate/${type}`; - case 'generate-csr': - return useIssuer - ? `${baseUrl}/issuers/generate/intermediate/${type}` - : `${baseUrl}/intermediate/generate/${type}`; - case 'sign-intermediate': - return `${baseUrl}/issuer/${encodePath(issuerRef)}/sign-intermediate`; - case 'rotate-root': - return `${baseUrl}/root/rotate/${type}`; - default: - assert('actionType must be one of import, generate-root, generate-csr or sign-intermediate'); - } - } - - createRecord(store, type, snapshot) { - const serializer = store.serializerFor(type.modelName); - const url = this.urlForCreateRecord(type.modelName, snapshot); - // Send actionType as serializer requestType so that we serialize data based on the endpoint - const data = serializer.serialize(snapshot, snapshot.adapterOptions.actionType); - return this.ajax(url, 'POST', { data }).then((result) => ({ - // pki/action endpoints don't correspond with a single specific entity, - // so in ember-data we'll map it to the request ID - id: result.request_id, - ...result, - })); - } -} diff --git a/ui/app/adapters/pki/certificate/base.js b/ui/app/adapters/pki/certificate/base.js deleted file mode 100644 index 6f05fb42a3..0000000000 --- a/ui/app/adapters/pki/certificate/base.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import ApplicationAdapter from '../../application'; - -export default class PkiCertificateBaseAdapter extends ApplicationAdapter { - namespace = 'v1'; - - getURL(backend, id) { - const uri = `${this.buildURL()}/${encodePath(backend)}`; - return id ? `${uri}/cert/${id}` : `${uri}/certs`; - } - - fetchByQuery(query) { - const { backend, id } = query; - const data = !id ? { list: true } : {}; - return this.ajax(this.getURL(backend, id), 'GET', { data }).then((resp) => { - resp.data.backend = backend; - if (id) { - resp.data.id = id; - resp.data.serial_number = id; - } - return resp; - }); - } - - query(store, type, query) { - return this.fetchByQuery(query); - } - - queryRecord(store, type, query) { - return this.fetchByQuery(query); - } - - // the only way to update a record is by revoking it which will set the revocationTime property - updateRecord(store, type, snapshot) { - const { backend, serialNumber, certificate } = snapshot.record; - // Revoke certificate requires either serial_number or certificate - const data = serialNumber ? { serial_number: serialNumber } : { certificate }; - return this.ajax(`${this.buildURL()}/${encodePath(backend)}/revoke`, 'POST', { data }).then( - (response) => { - return { - data: { - ...this.serialize(snapshot), - ...response.data, - }, - }; - } - ); - } -} diff --git a/ui/app/adapters/pki/certificate/generate.js b/ui/app/adapters/pki/certificate/generate.js deleted file mode 100644 index 66680257d4..0000000000 --- a/ui/app/adapters/pki/certificate/generate.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import PkiCertificateBaseAdapter from './base'; - -export default class PkiCertificateGenerateAdapter extends PkiCertificateBaseAdapter { - urlForCreateRecord(modelName, snapshot) { - const { role, backend } = snapshot.record; - if (!role || !backend) { - throw new Error('URL for create record is missing required attributes'); - } - return `${this.buildURL()}/${encodePath(backend)}/issue/${encodePath(role)}`; - } -} diff --git a/ui/app/adapters/pki/certificate/sign.js b/ui/app/adapters/pki/certificate/sign.js deleted file mode 100644 index 3443dabdeb..0000000000 --- a/ui/app/adapters/pki/certificate/sign.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import PkiCertificateBaseAdapter from './base'; - -export default class PkiCertificateSignAdapter extends PkiCertificateBaseAdapter { - urlForCreateRecord(modelName, snapshot) { - const { role, backend } = snapshot.record; - if (!role || !backend) { - throw new Error('URL for create record is missing required attributes'); - } - return `${this.buildURL()}/${encodePath(backend)}/sign/${encodePath(role)}`; - } -} diff --git a/ui/app/adapters/pki/config/acme.js b/ui/app/adapters/pki/config/acme.js deleted file mode 100644 index 6ae9fe99d1..0000000000 --- a/ui/app/adapters/pki/config/acme.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import PkiConfigBaseAdapter from './base'; - -export default class PkiConfigAcmeAdapter extends PkiConfigBaseAdapter { - namespace = 'v1'; - - _url(backend) { - return `${this.buildURL()}/${encodePath(backend)}/config/acme`; - } -} diff --git a/ui/app/adapters/pki/config/base.js b/ui/app/adapters/pki/config/base.js deleted file mode 100644 index 8692a61c1a..0000000000 --- a/ui/app/adapters/pki/config/base.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import ApplicationAdapter from '../../application'; - -export default class PkiConfigBaseAdapter extends ApplicationAdapter { - namespace = 'v1'; - - findRecord(store, type, backend) { - return this.ajax(this._url(backend), 'GET').then((resp) => { - return resp.data; - }); - } - - updateRecord(store, type, snapshot) { - const data = snapshot.serialize(); - return this.ajax(this._url(snapshot.record.id), 'POST', { data }); - } -} diff --git a/ui/app/adapters/pki/config/cluster.js b/ui/app/adapters/pki/config/cluster.js deleted file mode 100644 index bcbead7777..0000000000 --- a/ui/app/adapters/pki/config/cluster.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import PkiConfigBaseAdapter from './base'; - -export default class PkiConfigClusterAdapter extends PkiConfigBaseAdapter { - namespace = 'v1'; - - _url(backend) { - return `${this.buildURL()}/${encodePath(backend)}/config/cluster`; - } -} diff --git a/ui/app/adapters/pki/config/crl.js b/ui/app/adapters/pki/config/crl.js deleted file mode 100644 index 218d7d00f2..0000000000 --- a/ui/app/adapters/pki/config/crl.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import PkiConfigBaseAdapter from './base'; - -export default class PkiConfigCrlAdapter extends PkiConfigBaseAdapter { - namespace = 'v1'; - - _url(backend) { - return `${this.buildURL()}/${encodePath(backend)}/config/crl`; - } -} diff --git a/ui/app/adapters/pki/config/urls.js b/ui/app/adapters/pki/config/urls.js deleted file mode 100644 index 73e7129e51..0000000000 --- a/ui/app/adapters/pki/config/urls.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import PkiConfigBaseAdapter from './base'; - -export default class PkiConfigUrlsAdapter extends PkiConfigBaseAdapter { - namespace = 'v1'; - - _url(backend) { - return `${this.buildURL()}/${encodePath(backend)}/config/urls`; - } -} diff --git a/ui/app/adapters/pki/issuer.js b/ui/app/adapters/pki/issuer.js deleted file mode 100644 index 2978b4bb02..0000000000 --- a/ui/app/adapters/pki/issuer.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import ApplicationAdapter from '../application'; -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import { all } from 'rsvp'; -import { verifyCertificates, parseCertificate } from 'vault/utils/parse-pki-cert'; - -export default class PkiIssuerAdapter extends ApplicationAdapter { - namespace = 'v1'; - - _getBackend(snapshot) { - const { record, adapterOptions } = snapshot; - return adapterOptions?.mount || record.backend; - } - - optionsForQuery(id) { - const data = {}; - if (!id) { - data['list'] = true; - } - return { data }; - } - - urlForQuery(backend, id) { - const baseUrl = `${this.buildURL()}/${encodePath(backend)}`; - if (id) { - return `${baseUrl}/issuer/${encodePath(id)}`; - } else { - return `${baseUrl}/issuers`; - } - } - - async getIssuerMetadata(store, type, query, response, id) { - const keyInfo = response.data.key_info[id]; - try { - const issuerRecord = await this.queryRecord(store, type, { id, backend: query.backend }); - const { data } = issuerRecord; - const isRoot = await verifyCertificates(data.certificate, data.certificate); - const parsedCertificate = parseCertificate(data.certificate); - return { - ...keyInfo, - ...data, - isRoot, - parsedCertificate: { common_name: parsedCertificate.common_name }, - }; - } catch (e) { - return { ...keyInfo, issuer_id: id }; - } - } - - updateRecord(store, type, snapshot) { - const { issuerId } = snapshot.record; - const backend = this._getBackend(snapshot); - const data = this.serialize(snapshot); - const url = this.urlForQuery(backend, issuerId); - return this.ajax(url, 'POST', { data }); - } - - query(store, type, query) { - const { backend, isListView } = query; - const url = this.urlForQuery(backend); - - return this.ajax(url, 'GET', this.optionsForQuery()).then(async (res) => { - // To show issuer meta data tags, we have a flag called isListView and only want to - // grab each issuer data only if there are less than 10 issuers to avoid making too many requests - if (isListView && res.data.keys.length <= 10) { - const keyInfoArray = await all( - res.data.keys.map((id) => this.getIssuerMetadata(store, type, query, res, id)) - ); - const keyInfo = {}; - - res.data.keys.forEach((issuerId) => { - keyInfo[issuerId] = keyInfoArray.find((newKey) => newKey.issuer_id === issuerId); - }); - - res.data.key_info = keyInfo; - - return res; - } - - return res; - }); - } - - queryRecord(store, type, query) { - const { backend, id } = query; - - return this.ajax(this.urlForQuery(backend, id), 'GET', this.optionsForQuery(id)); - } - - deleteAllIssuers(backend) { - const deleteAllIssuersAndKeysUrl = `${this.buildURL()}/${encodePath(backend)}/root`; - - return this.ajax(deleteAllIssuersAndKeysUrl, 'DELETE'); - } -} diff --git a/ui/app/adapters/pki/key.js b/ui/app/adapters/pki/key.js deleted file mode 100644 index 121559e06f..0000000000 --- a/ui/app/adapters/pki/key.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import ApplicationAdapter from '../application'; -import { encodePath } from 'vault/utils/path-encoding-helpers'; -export default class PkiKeyAdapter extends ApplicationAdapter { - namespace = 'v1'; - - _baseUrl(backend, id) { - const url = `${this.buildURL()}/${encodePath(backend)}`; - if (id) { - return url + '/key/' + encodePath(id); - } - return url + '/keys'; - } - - createRecord(store, type, snapshot) { - const { record, adapterOptions } = snapshot; - let url = this._baseUrl(record.backend); - if (adapterOptions.import) { - url = `${url}/import`; - } else { - url = `${url}/generate/${record.type}`; - } - return this.ajax(url, 'POST', { data: this.serialize(snapshot) }).then((resp) => { - return resp; - }); - } - - updateRecord(store, type, snapshot) { - const { record } = snapshot; - const { key_name } = this.serialize(snapshot); - const url = this._baseUrl(record.backend, record.id); - return this.ajax(url, 'POST', { data: { key_name } }); - } - - query(store, type, query) { - const { backend } = query; - return this.ajax(this._baseUrl(backend), 'GET', { data: { list: true } }); - } - - queryRecord(store, type, query) { - const { backend, id } = query; - return this.ajax(this._baseUrl(backend, id), 'GET'); - } - - deleteRecord(store, type, snapshot) { - const { id, record } = snapshot; - return this.ajax(this._baseUrl(record.backend, id), 'DELETE'); - } -} diff --git a/ui/app/adapters/pki/role.js b/ui/app/adapters/pki/role.js deleted file mode 100644 index 07b24508bc..0000000000 --- a/ui/app/adapters/pki/role.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import ApplicationAdapter from '../application'; -import { encodePath } from 'vault/utils/path-encoding-helpers'; - -export default class PkiRoleAdapter extends ApplicationAdapter { - namespace = 'v1'; - - _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 }; - } - - createRecord(store, type, snapshot) { - const name = snapshot.attr('name'); - const url = this._urlForRole(snapshot.record.backend, name); - - return this.ajax(url, 'POST', { data: this.serialize(snapshot) }).then(() => { - return { - id: name, - name, - backend: snapshot.record.backend, - }; - }); - } - - updateRecord(store, type, snapshot) { - const { name, backend } = snapshot.record; - const data = this.serialize(snapshot); - const url = this._urlForRole(backend, name); - return this.ajax(url, 'POST', { 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 { ...resp, ...data }; - }); - } - - query(store, type, query) { - return this.fetchByQuery(store, query); - } - - queryRecord(store, type, query) { - return this.fetchByQuery(store, query); - } - - deleteRecord(store, type, snapshot) { - const { id, record } = snapshot; - return this.ajax(this._urlForRole(record.backend, id), 'DELETE'); - } -} diff --git a/ui/app/adapters/pki/sign-intermediate.js b/ui/app/adapters/pki/sign-intermediate.js deleted file mode 100644 index 3269e7ebe9..0000000000 --- a/ui/app/adapters/pki/sign-intermediate.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { encodePath } from 'vault/utils/path-encoding-helpers'; -import ApplicationAdapter from '../application'; - -export default class PkiSignIntermediateAdapter extends ApplicationAdapter { - namespace = 'v1'; - - createRecord(store, type, snapshot) { - const serializer = store.serializerFor(type.modelName); - const { backend, issuerRef } = snapshot.record; - const url = `${this.buildURL()}/${encodePath(backend)}/issuer/${encodePath(issuerRef)}/sign-intermediate`; - const data = serializer.serialize(snapshot, type); - return this.ajax(url, 'POST', { data }).then((result) => ({ - // sign-intermediate can happen multiple times per issuer, - // so the ID needs to be unique from the issuer ID - id: result.request_id, - ...result, - })); - } -} diff --git a/ui/app/adapters/pki/tidy.js b/ui/app/adapters/pki/tidy.js deleted file mode 100644 index 2188523a71..0000000000 --- a/ui/app/adapters/pki/tidy.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ -import ApplicationAdapter from '../application'; -import timestamp from 'core/utils/timestamp'; -import { encodePath } from 'vault/utils/path-encoding-helpers'; - -export default class PkiTidyAdapter extends ApplicationAdapter { - namespace = 'v1'; - - _baseUrl(backend) { - return `${this.buildURL()}/${encodePath(backend)}`; - } - - // single tidy operations (manual) are always a new record - createRecord(store, type, snapshot) { - const { backend } = snapshot.record; - const { tidyType } = snapshot.adapterOptions; - if (tidyType === 'auto') { - throw new Error('Auto tidy type models are never new, please use findRecord'); - } - const id = `${backend}-${timestamp.now().toISOString()}`; - const url = `${this._baseUrl(backend)}/tidy`; - return this.ajax(url, 'POST', { data: this.serialize(snapshot, tidyType) }).then((resp) => { - resp.id = id; - return resp; - }); - } - - // saving auto-tidy config POST requests will always update - updateRecord(store, type, snapshot) { - const backend = snapshot.record.id; - const { tidyType } = snapshot.adapterOptions; - if (tidyType === 'manual') { - throw new Error('Manual tidy type models are always new, please use createRecord'); - } - - const url = `${this._baseUrl(backend)}/config/auto-tidy`; - return this.ajax(url, 'POST', { data: this.serialize(snapshot, tidyType) }); - } - - findRecord(store, type, backend) { - // only auto-tidy will ever be read, no need to pass the type here - return this.ajax(`${this._baseUrl(backend)}/config/auto-tidy`, 'GET').then((resp) => { - return resp.data; - }); - } - - cancelTidy(backend) { - const url = `${this._baseUrl(backend)}`; - return this.ajax(`${url}/tidy-cancel`, 'POST'); - } -} diff --git a/ui/app/app.js b/ui/app/app.js index 1637f87aac..8fb5677d55 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -20,7 +20,6 @@ export default class App extends Application { 'flash-messages', 'namespace', { 'app-router': 'router' }, - 'pagination', 'version', 'custom-messages', 'api', @@ -108,7 +107,6 @@ export default class App extends Application { 'namespace', { 'app-router': 'router' }, 'secret-mount-path', - 'pagination', 'version', ], externalRoutes: { @@ -129,8 +127,6 @@ export default class App extends Application { 'path-help', { 'app-router': 'router' }, 'secret-mount-path', - 'store', - 'pagination', 'version', ], externalRoutes: { @@ -149,7 +145,6 @@ export default class App extends Application { 'store', 'api', 'capabilities', - 'pagination', 'version', ], externalRoutes: { diff --git a/ui/app/components/dashboard/quick-actions-card.hbs b/ui/app/components/dashboard/quick-actions-card.hbs index 7f0b028fe8..8bfb0dcf4a 100644 --- a/ui/app/components/dashboard/quick-actions-card.hbs +++ b/ui/app/components/dashboard/quick-actions-card.hbs @@ -8,8 +8,9 @@ {{#if this.filteredSecretEngines}}

Secrets engines

-

Supported engines include databases, KV - version 2, and PKI.

+

+ Supported engines include databases, KV version 2, and PKI. +

{{#if this.selectedAction}} - {{! use special input to allow searching for KVv2 secrets inside a directory }} - {{#if this.searchSelectParams.isKV}} + {{#if this.fetchOptions.isRunning}} + +

Loading options...

+ {{else if this.searchSelectParams.isKV}} + {{! use special input to allow searching for KVv2 secrets inside a directory }} - {{else if this.searchSelectParams.model}} + {{else}}

{{this.searchSelectParams.title}}

- {{/if}} diff --git a/ui/app/components/dashboard/quick-actions-card.js b/ui/app/components/dashboard/quick-actions-card.js index f26062eca4..7d338d81cd 100644 --- a/ui/app/components/dashboard/quick-actions-card.js +++ b/ui/app/components/dashboard/quick-actions-card.js @@ -8,6 +8,8 @@ import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import { service } from '@ember/service'; import { pathIsDirectory } from 'kv/utils/kv-breadcrumbs'; +import { task } from 'ember-concurrency'; +import { waitFor } from '@ember/test-waiters'; /** * @module DashboardQuickActionsCard * DashboardQuickActionsCard component allows users to see a list of secrets engines filtered by @@ -23,10 +25,13 @@ const QUICK_ACTION_ENGINES = ['pki', 'database']; export default class DashboardQuickActionsCard extends Component { @service router; + @service api; + @service flashMessages; @tracked selectedEngine; @tracked selectedAction; @tracked paramValue; + @tracked searchSelectOptions = []; get actionOptions() { switch (this.selectedEngine.type) { @@ -53,44 +58,34 @@ export default class DashboardQuickActionsCard extends Component { return { title: 'Role to use', buttonText: 'Generate credentials', - model: 'database/role', route: 'vault.cluster.secrets.backend.credentials', - queryObject: { backend: this.selectedEngine.id }, }; case 'Issue certificate': return { title: 'Role to use', placeholder: 'Type to find a role', buttonText: 'Issue leaf certificate', - model: 'pki/role', route: 'vault.cluster.secrets.backend.pki.roles.role.generate', - queryObject: { backend: this.selectedEngine.id }, }; case 'View certificate': return { title: 'Certificate serial number', placeholder: '33:a3:...', buttonText: 'View certificate', - model: 'pki/certificate/base', route: 'vault.cluster.secrets.backend.pki.certificates.certificate.details', - queryObject: { backend: this.selectedEngine.id }, }; case 'View issuer': return { title: 'Issuer', placeholder: 'Type issuer name or ID', buttonText: 'View issuer', - model: 'pki/issuer', route: 'vault.cluster.secrets.backend.pki.issuers.issuer.details', - nameKey: 'issuerName', - queryObject: { backend: this.selectedEngine.id }, - objectKeys: ['id', 'issuerName'], + shouldRenderName: true, }; default: return { placeholder: 'Please select an action above', buttonText: 'Select an action', - model: '', }; } } @@ -109,6 +104,44 @@ export default class DashboardQuickActionsCard extends Component { }); } + fetchOptions = task( + waitFor(async () => { + this.searchSelectOptions = []; + const action = this.selectedAction; + const api = this.api.secrets; + const catchError = (e) => (e.response.status === 404 ? { keys: [] } : Promise.reject(e)); + + try { + // kv-suggestion-input fetches secrets internally -- this handles the remaining action types + if (action && action !== 'Find KV secrets') { + // fetch database roles, pki roles, pki certificates or pki issuers + const methods = { + 'Generate credentials for database': ['databaseListStaticRoles', 'databaseListRoles'], + 'Issue certificate': ['pkiListRoles'], + 'View certificate': ['pkiListCerts'], + 'View issuer': ['pkiListIssuers'], + }[this.selectedAction]; + const responses = await Promise.all( + methods.map((method) => api[method](this.selectedEngine.id, true).catch(catchError)) + ); + responses.forEach((response) => { + const options = + action === 'View issuer' + ? this.api.keyInfoToArray(response).map(({ id, issuer_name }) => ({ + name: issuer_name || id, + id, + })) + : response.keys.map((id) => ({ id })); + this.searchSelectOptions.push(...options); + }); + } + } catch (e) { + const { message } = await this.api.parseError(e); + this.flashMessages.danger(`Error fetching options for selected action: ${message}`); + } + }) + ); + @action handleSearchEngineSelect([selection]) { this.selectedEngine = selection; @@ -121,6 +154,7 @@ export default class DashboardQuickActionsCard extends Component { setSelectedAction(selectedAction) { this.selectedAction = selectedAction; this.paramValue = null; + this.fetchOptions.perform(); } @action diff --git a/ui/app/models/pki/action.js b/ui/app/models/pki/action.js deleted file mode 100644 index 595ec26962..0000000000 --- a/ui/app/models/pki/action.js +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { service } from '@ember/service'; -import { tracked } from '@glimmer/tracking'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import { withModelValidations } from 'vault/decorators/model-validations'; - -const validations = { - type: [{ type: 'presence', message: 'Type is required.' }], - commonName: [{ type: 'presence', message: 'Common name is required.' }], - issuerName: [ - { - validator(model) { - if ( - (model.actionType === 'generate-root' || model.actionType === 'rotate-root') && - model.issuerName === 'default' - ) - return false; - return true; - }, - message: `Issuer name must be unique across all issuers and not be the reserved value 'default'.`, - }, - ], - keyName: [ - { - validator(model) { - if (model.keyName === 'default') return false; - return true; - }, - message: `Key name cannot be the reserved value 'default'`, - }, - ], -}; - -/** - * This model maps to multiple PKI endpoints, specifically the ones that make up the - * configuration/create workflow. These endpoints also share a nontypical behavior in that - * a POST request to the endpoints don't necessarily result in a single entity created -- - * depending on the inputs, some number of issuers, keys, and certificates can be created - * from the API. - */ -@withModelValidations(validations) -@withFormFields() -export default class PkiActionModel extends Model { - @service secretMountPath; - - @tracked actionType; // used to toggle between different form fields when creating configuration - - /* actionType import */ - @attr('string') pemBundle; - - // parsed attrs from parse-pki-cert util if certificate on response - @attr parsedCertificate; - - // readonly attrs returned after importing - @attr importedIssuers; - @attr importedKeys; - @attr mapping; - @attr('string', { readOnly: true, isCertificate: true }) certificate; - - /* actionType generate-root */ - - // readonly attrs returned right after root generation - @attr serialNumber; - @attr('string', { label: 'Issuing CA', readOnly: true, isCertificate: true }) issuingCa; - // end of readonly - - @attr('string', { - possibleValues: ['exported', 'internal', 'existing', 'kms'], - noDefault: true, - }) - type; - - @attr('string') issuerName; - - @attr('string') keyName; - - @attr('string', { - defaultValue: 'default', - label: 'Key reference', - }) - keyRef; // type=existing only - - @attr('string') commonName; // REQUIRED - - @attr('string', { - label: 'Subject Alternative Names (SANs)', - editType: 'stringArray', - }) - altNames; - - @attr('string', { - label: 'IP Subject Alternative Names (IP SANs)', - editType: 'stringArray', - }) - ipSans; - - @attr('string', { - label: 'URI Subject Alternative Names (URI SANs)', - editType: 'stringArray', - }) - uriSans; - - @attr('string', { - label: 'Other SANs', - editType: 'stringArray', - }) - otherSans; - - @attr('string', { - defaultValue: 'pem', - possibleValues: ['pem', 'der', 'pem_bundle'], - }) - format; - - @attr('string', { - defaultValue: 'der', - possibleValues: ['der', 'pkcs8'], - }) - privateKeyFormat; - - @attr('string', { - defaultValue: 'rsa', - possibleValues: ['rsa', 'ed25519', 'ec'], - }) - keyType; - - @attr('string', { - defaultValue: '0', - // options management happens in pki-key-parameters - }) - keyBits; - - @attr('number', { - defaultValue: -1, - }) - maxPathLength; - - @attr('boolean', { - label: 'Exclude common name from SANs', - subText: - 'If checked, the common name will not be included in DNS or Email Subject Alternate Names. This is useful if the CN is a human-readable identifier, not a hostname or email address.', - defaultValue: false, - }) - excludeCnFromSans; - - @attr('string', { - label: 'Permitted DNS domains', - }) - permittedDnsDomains; - - @attr('string', { - label: 'Organizational Units (OU)', - subText: - 'A list of allowed serial numbers to be requested during certificate issuance. Shell-style globbing is supported. If empty, custom-specified serial numbers will be forbidden.', - editType: 'stringArray', - }) - ou; - @attr({ editType: 'stringArray' }) organization; - @attr({ editType: 'stringArray' }) country; - @attr({ editType: 'stringArray' }) locality; - @attr({ editType: 'stringArray' }) province; - @attr({ editType: 'stringArray' }) streetAddress; - @attr({ editType: 'stringArray' }) postalCode; - - @attr('string', { - subText: - "Specifies the requested Subject's named Serial Number value. This has no impact on the Certificate's serial number randomly generated by Vault.", - }) - subjectSerialNumber; - // this is different from the number (16:5e:a0...) randomly generated by Vault - // https://developer.hashicorp.com/vault/api-docs/secret/pki#serial_number - - @attr('boolean', { - subText: 'Whether to add a Basic Constraints extension with CA: true.', - }) - addBasicConstraints; - - @attr({ - label: 'Backdate validity', - detailsLabel: 'Issued certificate backdating', - helperTextDisabled: 'Vault will use the default value, 30s', - helperTextEnabled: - 'Also called the not_before_duration property. Allows certificates to be valid for a certain time period before now. This is useful to correct clock misalignment on various systems when setting up your CA.', - editType: 'ttl', - defaultValue: '30s', - }) - notBeforeDuration; - - @attr('string') managedKeyName; - @attr('string', { - label: 'Managed key UUID', - }) - managedKeyId; - - @attr({ - label: 'Not valid after', - detailsLabel: 'Issued certificates expire after', - subText: - 'The time after which this certificate will no longer be valid. This can be a TTL (a range of time from now) or a specific date.', - editType: 'yield', - }) - customTtl; - @attr('string') ttl; - @attr('date') notAfter; - - @attr('string', { label: 'Issuer ID', readOnly: true, detailLinkTo: 'issuers.issuer.details' }) issuerId; // returned from generate-root action - - // For generating and signing a CSR - @attr('string', { label: 'CSR', isCertificate: true }) csr; - @attr caChain; - @attr('string', { label: 'Key ID', detailLinkTo: 'keys.key.details' }) keyId; - @attr('string', { isCertificate: true }) privateKey; - @attr('string') privateKeyType; - - get backend() { - return this.secretMountPath.currentPath; - } - - // To determine which endpoint the config adapter should use, - // we want to check capabilities on the newer endpoints (those - // prefixed with "issuers") and use the old path as fallback - // if user does not have permissions. - @lazyCapabilities(apiPath`${'backend'}/issuers/import/bundle`, 'backend') importBundlePath; - @lazyCapabilities(apiPath`${'backend'}/issuers/generate/root/${'type'}`, 'backend', 'type') - generateIssuerRootPath; - @lazyCapabilities(apiPath`${'backend'}/issuers/generate/intermediate/${'type'}`, 'backend', 'type') - generateIssuerCsrPath; - @lazyCapabilities(apiPath`${'backend'}/issuers/cross-sign`, 'backend') crossSignPath; - - get canImportBundle() { - return this.importBundlePath.get('canCreate') === true; - } - get canGenerateIssuerRoot() { - return this.generateIssuerRootPath.get('canCreate') === true; - } - get canGenerateIssuerIntermediate() { - return this.generateIssuerCsrPath.get('canCreate') === true; - } - get canCrossSign() { - return this.crossSignPath.get('canCreate') === true; - } -} diff --git a/ui/app/models/pki/certificate/base.js b/ui/app/models/pki/certificate/base.js deleted file mode 100644 index 33b4d61ee5..0000000000 --- a/ui/app/models/pki/certificate/base.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { service } from '@ember/service'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; - -/** - * There are many actions that involve certificates in PKI world. - * The base certificate model contains shared attributes that make up a certificate's content. - * Other models under pki/certificate will extend this model and include additional attributes - * and associated adapter methods for performing various generation and signing actions. - * This model also displays leaf certs and their parsed attributes (which exist as an object in - * the attribute `parsedCertificate`) - */ - -// also displays parsedCertificate values in the template -const certDisplayFields = ['certificate', 'commonName', 'revocationTime', 'serialNumber']; - -@withFormFields(certDisplayFields) -export default class PkiCertificateBaseModel extends Model { - @service secretMountPath; - - get backend() { - return this.secretMountPath.currentPath; - } - - // The attributes parsed from parse-pki-cert util live here - @attr parsedCertificate; - - @attr('string') commonName; - @attr({ - label: 'Not valid after', - detailsLabel: 'Issued certificates expire after', - subText: - 'The time after which this certificate will no longer be valid. This can be a TTL (a range of time from now) or a specific date.', - editType: 'yield', - }) - customTtl; // sets ttl and notAfter via one input - - @attr('boolean', { - label: 'Exclude common name from SANs', - subText: - 'If checked, the common name will not be included in DNS or Email Subject Alternate Names. This is useful if the CN is a human-readable identifier, not a hostname or email address.', - defaultValue: false, - }) - excludeCnFromSans; - - @attr('string', { - label: 'Subject Alternative Names (SANs)', - subText: - 'The requested Subject Alternative Names; if email protection is enabled for the role, this may contain email addresses.', - editType: 'stringArray', - }) - altNames; - - // SANs below are editType: stringArray from openApi - @attr('string', { - label: 'IP Subject Alternative Names (IP SANs)', - subText: 'Only valid if the role allows IP SANs (which is the default).', - }) - ipSans; - - @attr('string', { - label: 'URI Subject Alternative Names (URI SANs)', - subText: 'If any requested URIs do not match role policy, the entire request will be denied.', - }) - uriSans; - - @attr('string', { - subText: 'Requested other SANs with the format ;UTF8: for each entry.', - }) - otherSans; - - // Fixes warning about "no attribute or relationship with the name `role` on `pki/certificate/base`" - @attr('string') role; - - // Attrs that come back from API POST request - @attr({ label: 'CA Chain', isCertificate: true }) caChain; - @attr('string', { isCertificate: true }) certificate; - @attr('number') expiration; - @attr('string', { label: 'Issuing CA', isCertificate: true }) issuingCa; - @attr('string', { isCertificate: true }) privateKey; // only returned for type=exported and /issue - @attr('string') privateKeyType; // only returned for type=exported and /issue - @attr('number', { formatDate: true }) revocationTime; - @attr('string') serialNumber; - - @lazyCapabilities(apiPath`${'backend'}/revoke`, 'backend') revokePath; - get canRevoke() { - return this.revokePath.get('isLoading') || this.revokePath.get('canCreate') !== false; - } -} diff --git a/ui/app/models/pki/certificate/generate.js b/ui/app/models/pki/certificate/generate.js deleted file mode 100644 index 1e7b80e2ba..0000000000 --- a/ui/app/models/pki/certificate/generate.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import PkiCertificateBaseModel from './base'; - -const generateFromRole = [ - { - default: ['commonName', 'userIds', 'customTtl', 'format', 'privateKeyFormat'], - }, - { - 'Subject Alternative Name (SAN) Options': [ - 'excludeCnFromSans', - 'altNames', - 'ipSans', - 'uriSans', - 'otherSans', - ], - }, -]; -// Extra fields returned on the /issue endpoint -const certDisplayFields = [ - 'certificate', - 'commonName', - 'revocationTime', - 'serialNumber', - 'caChain', - 'issuingCa', - 'privateKey', - 'privateKeyType', -]; -@withFormFields(certDisplayFields, generateFromRole) -export default class PkiCertificateGenerateModel extends PkiCertificateBaseModel { - @attr('string') role; // role name to issue certificate against for request URL -} diff --git a/ui/app/models/pki/certificate/sign.js b/ui/app/models/pki/certificate/sign.js deleted file mode 100644 index f546d69e8a..0000000000 --- a/ui/app/models/pki/certificate/sign.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import PkiCertificateBaseModel from './base'; - -const generateFromRole = [ - { - default: ['csr', 'commonName', 'customTtl', 'format', 'removeRootsFromChain'], - }, - { - 'Subject Alternative Name (SAN) Options': [ - 'excludeCnFromSans', - 'altNames', - 'ipSans', - 'uriSans', - 'otherSans', - ], - }, -]; -@withFormFields(null, generateFromRole) -export default class PkiCertificateSignModel extends PkiCertificateBaseModel { - @attr('string') role; // role name to create certificate against for request URL - - @attr('string', { - label: 'CSR', - editType: 'textarea', - }) - csr; - - @attr('boolean', { - subText: 'When checked, the CA chain will not include self-signed CA certificates.', - }) - removeRootsFromChain; -} diff --git a/ui/app/models/pki/config/acme.js b/ui/app/models/pki/config/acme.js deleted file mode 100644 index 5eea3a2393..0000000000 --- a/ui/app/models/pki/config/acme.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; - -@withFormFields() -export default class PkiConfigAcmeModel extends Model { - // This model uses the backend value as the model ID - - // attrs order in the form is determined by order here - @attr('boolean', { - label: 'ACME enabled', - subText: 'When ACME is disabled, all requests to ACME directory URLs will return 404.', - }) - enabled; - - @attr('string', { - subText: - "Specifies the behavior of the default ACME directory. Can be 'forbid', 'sign-verbatim' or a role given by 'role:'. If a role is used, it must be present in 'allowed_roles'.", - }) - defaultDirectoryPolicy; - - @attr('array', { - editType: 'stringArray', - subText: - "The default value '*' allows every role within the mount to be used. If the default_directory_policy specifies a role, it must be allowed under this configuration.", - }) - allowedRoles; - - @attr('boolean', { - label: 'Allow role ExtKeyUsage', - subText: - "When enabled, respect the role's ExtKeyUsage flags. Otherwise, ACME certificates are forced to ServerAuth.", - }) - allowRoleExtKeyUsage; - - @attr('array', { - editType: 'stringArray', - subText: - "Specifies a list of issuers allowed to issue certificates via explicit ACME paths. If an allowed role specifies an issuer outside this list, it will be allowed. The default value '*' allows every issuer within the mount.", - }) - allowedIssuers; - - @attr('string', { - label: 'EAB policy', - possibleValues: ['not-required', 'new-account-required', 'always-required'], - }) - eabPolicy; - - @attr('string', { - label: 'DNS resolver', - subText: - 'An optional overriding DNS resolver to use for challenge verification lookups. When not specified, the default system resolver will be used. This allows domains on peered networks with an accessible DNS resolver to be validated.', - }) - dnsResolver; - - @attr({ - label: 'Max TTL', - editType: 'ttl', - hideToggle: true, - helperTextEnabled: - 'Specify the maximum TTL for ACME certificates. Role TTL values will be limited to this value.', - }) - maxTtl; - - @lazyCapabilities(apiPath`${'id'}/config/acme`, 'id') - acmePath; - - get canSet() { - return this.acmePath.get('canUpdate') !== false; - } -} diff --git a/ui/app/models/pki/config/cluster.js b/ui/app/models/pki/config/cluster.js deleted file mode 100644 index 8588994276..0000000000 --- a/ui/app/models/pki/config/cluster.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; - -@withFormFields() -export default class PkiConfigClusterModel extends Model { - // This model uses the backend value as the model ID - - @attr('string', { - label: "Mount's API path", - subText: - "Specifies the path to this performance replication cluster's API mount path, including any namespaces as path components. This address is used for the ACME directories, which must be served over a TLS-enabled listener.", - }) - path; - @attr('string', { - label: 'AIA path', - subText: - "Specifies the path to this performance replication cluster's AIA distribution point; may refer to an external, non-Vault responder.", - }) - aiaPath; - - // this is for pki-only cluster config, not the universal vault cluster - @lazyCapabilities(apiPath`${'id'}/config/cluster`, 'id') clusterPath; - - get canSet() { - return this.clusterPath.get('canUpdate') !== false; - } -} diff --git a/ui/app/models/pki/config/crl.js b/ui/app/models/pki/config/crl.js deleted file mode 100644 index c42ab1a9d7..0000000000 --- a/ui/app/models/pki/config/crl.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; - -const formFieldGroups = [ - { - 'Certificate Revocation List (CRL)': ['expiry', 'autoRebuildGracePeriod', 'deltaRebuildInterval'], - }, - { - 'Online Certificate Status Protocol (OCSP)': ['ocspExpiry'], - }, - { 'Unified Revocation': ['crossClusterRevocation', 'unifiedCrl', 'unifiedCrlOnExistingPaths'] }, -]; -@withFormFields(null, formFieldGroups) -export default class PkiConfigCrlModel extends Model { - // This model uses the backend value as the model ID - - @attr('boolean') autoRebuild; - @attr('string', { - label: 'Auto-rebuild on', - labelDisabled: 'Auto-rebuild off', - mapToBoolean: 'autoRebuild', - isOppositeValue: false, - editType: 'ttl', - helperTextEnabled: 'Vault will rebuild the CRL in the below grace period before expiration', - helperTextDisabled: 'Vault will not automatically rebuild the CRL', - }) - autoRebuildGracePeriod; - - @attr('boolean') enableDelta; - @attr('string', { - label: 'Delta CRL building on', - labelDisabled: 'Delta CRL building off', - mapToBoolean: 'enableDelta', - isOppositeValue: false, - editType: 'ttl', - helperTextEnabled: 'Vault will rebuild the delta CRL at the interval below:', - helperTextDisabled: 'Vault will not rebuild the delta CRL at an interval', - }) - deltaRebuildInterval; - - @attr('boolean') disable; - @attr('string', { - label: 'Expiry', - labelDisabled: 'No expiry', - mapToBoolean: 'disable', - isOppositeValue: true, - editType: 'ttl', - helperTextDisabled: 'The CRL will not be built.', - helperTextEnabled: 'The CRL will expire after:', - }) - expiry; - - @attr('boolean') ocspDisable; - @attr('string', { - label: 'OCSP responder APIs enabled', - labelDisabled: 'OCSP responder APIs disabled', - mapToBoolean: 'ocspDisable', - isOppositeValue: true, - editType: 'ttl', - helperTextEnabled: "Requests about a certificate's status will be valid for:", - helperTextDisabled: 'Requests cannot be made to check if an individual certificate is valid.', - }) - ocspExpiry; - - // enterprise only params - @attr('boolean', { - label: 'Cross-cluster revocation', - helpText: - 'Enables cross-cluster revocation request queues. When a serial not issued on this local cluster is passed to the /revoke endpoint, it is replicated across clusters and revoked by the issuing cluster if it is online.', - }) - crossClusterRevocation; - - @attr('boolean', { - label: 'Unified CRL', - helpText: - 'Enables unified CRL and OCSP building. This synchronizes all revocations between clusters; a single, unified CRL will be built on the active node of the primary performance replication (PR) cluster.', - }) - unifiedCrl; - - @attr('boolean', { - label: 'Unified CRL on existing paths', - helpText: - 'If enabled, existing CRL and OCSP paths will return the unified CRL instead of a response based on cluster-local data.', - }) - unifiedCrlOnExistingPaths; - - @lazyCapabilities(apiPath`${'id'}/config/crl`, 'id') crlPath; - - get canSet() { - return this.crlPath.get('canUpdate') !== false; - } -} diff --git a/ui/app/models/pki/config/urls.js b/ui/app/models/pki/config/urls.js deleted file mode 100644 index e0cee844c7..0000000000 --- a/ui/app/models/pki/config/urls.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; - -@withFormFields() -export default class PkiConfigUrlsModel extends Model { - // This model uses the backend value as the model ID - - @attr({ - label: 'Issuing certificates', - subText: - 'The URL values for the Issuing Certificate field; these are different URLs for the same resource.', - showHelpText: false, - editType: 'stringArray', - }) - issuingCertificates; - - @attr({ - label: 'CRL distribution points', - subText: 'Specifies the URL values for the CRL Distribution Points field.', - showHelpText: false, - editType: 'stringArray', - }) - crlDistributionPoints; - - @attr({ - label: 'OCSP Servers', - subText: 'Specifies the URL values for the OCSP Servers field.', - showHelpText: false, - editType: 'stringArray', - }) - ocspServers; - - @lazyCapabilities(apiPath`${'id'}/config/urls`, 'id') urlsPath; - - get canSet() { - return this.urlsPath.get('canUpdate') !== false; - } -} diff --git a/ui/app/models/pki/issuer.js b/ui/app/models/pki/issuer.js deleted file mode 100644 index b688e856ee..0000000000 --- a/ui/app/models/pki/issuer.js +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; -import { service } from '@ember/service'; - -const issuerUrls = ['issuingCertificates', 'crlDistributionPoints', 'ocspServers']; -const inputFields = [ - 'issuerName', - 'leafNotAfterBehavior', - 'usage', - 'manualChain', - 'revocationSignatureAlgorithm', - ...issuerUrls, -]; -const displayFields = [ - { - default: ['certificate', 'caChain', 'commonName', 'issuerName', 'issuerId', 'keyId'], - // also displays parsedCertificate values in the template - }, - { 'Issuer URLs': issuerUrls }, -]; -@withFormFields(inputFields, displayFields) -export default class PkiIssuerModel extends Model { - @service secretMountPath; - - get backend() { - return this.secretMountPath.currentPath; - } - get issuerRef() { - return this.issuerName || this.issuerId; - } - - // READ ONLY - @attr isDefault; - @attr('string', { label: 'Issuer ID', detailLinkTo: 'issuers.issuer.details' }) issuerId; - @attr('string', { label: 'Default key ID', detailLinkTo: 'keys.key.details' }) keyId; - @attr({ label: 'CA Chain', isCertificate: true }) caChain; - @attr({ isCertificate: true }) certificate; - @attr('string') serialNumber; - - // parsed from certificate contents in serializer (see parse-pki-cert.js) - @attr parsedCertificate; - @attr('string') commonName; - @attr isRoot; - - @attr subjectSerialNumber; // this is not the UUID serial number field randomly generated by Vault for leaf certificates - @attr({ label: 'Subject Alternative Names (SANs)' }) altNames; - @attr({ label: 'IP SANs' }) ipSans; - @attr({ label: 'URI SANs' }) uriSans; - @attr({ label: 'Other SANs' }) otherSans; - - // UPDATING - @attr('string') issuerName; - - @attr({ - label: 'Leaf notAfter behavior', - subText: - 'What happens when a leaf certificate is issued, but its NotAfter field (and therefore its expiry date) exceeds that of this issuer.', - docLink: '/vault/api-docs/secret/pki#update-issuer', - editType: 'yield', - valueOptions: ['always_enforce_err', 'err', 'truncate', 'permit'], - }) - leafNotAfterBehavior; - - @attr({ - subText: 'Allowed usages for this issuer. It can always be read.', - editType: 'yield', - valueOptions: [ - { label: 'Issuing certificates', value: 'issuing-certificates' }, - { label: 'Signing CRLs', value: 'crl-signing' }, - { label: 'Signing OCSPs', value: 'ocsp-signing' }, - ], - }) - usage; - - @attr('string', { - subText: - "An advanced field useful when automatic chain building isn't desired. The first element must be the present issuer's reference.", - }) - manualChain; - - @attr({ - subText: - 'The signature algorithm to use when building CRLs. The default value (empty string) is for Go to select the signature algorithm automatically, which may not always work.', - noDefault: true, - possibleValues: [ - 'sha256withrsa', - 'ecdsawithsha384', - 'sha256withrsapss', - 'ed25519', - 'sha384withrsapss', - 'sha512withrsapss', - 'pureed25519', - 'sha384withrsa', - 'sha512withrsa', - 'ecdsawithsha256', - 'ecdsawithsha512', - ], - }) - revocationSignatureAlgorithm; - - @attr('string', { - subText: - 'The URL values for the Issuing Certificate field; these are different URLs for the same resource.', - editType: 'stringArray', - }) - issuingCertificates; - - @attr('string', { - label: 'CRL distribution points', - subText: 'Specifies the URL values for the CRL Distribution Points field.', - editType: 'stringArray', - }) - crlDistributionPoints; - - @attr('string', { - label: 'OCSP servers', - subText: 'Specifies the URL values for the OCSP Servers field.', - editType: 'stringArray', - }) - ocspServers; - - // IMPORTING - @attr('string') pemBundle; - // readonly attrs returned after importing - @attr importedIssuers; - @attr importedKeys; - @attr mapping; - - @lazyCapabilities(apiPath`${'backend'}/issuer/${'issuerId'}`, 'backend', 'issuerId') issuerPath; - @lazyCapabilities(apiPath`${'backend'}/root/rotate/exported`, 'backend') rotateExported; - @lazyCapabilities(apiPath`${'backend'}/root/rotate/internal`, 'backend') rotateInternal; - @lazyCapabilities(apiPath`${'backend'}/root/rotate/existing`, 'backend') rotateExisting; - @lazyCapabilities(apiPath`${'backend'}/root`, 'backend') deletePath; - @lazyCapabilities(apiPath`${'backend'}/intermediate/cross-sign`, 'backend') crossSignPath; - @lazyCapabilities(apiPath`${'backend'}/issuer/${'issuerId'}/sign-intermediate`, 'backend', 'issuerId') - signIntermediate; - get canRotateIssuer() { - return ( - this.rotateExported.get('canUpdate') !== false || - this.rotateExisting.get('canUpdate') !== false || - this.rotateInternal.get('canUpdate') !== false - ); - } - get canCrossSign() { - return this.crossSignPath.get('canUpdate') !== false; - } - get canSignIntermediate() { - return this.signIntermediate.get('canUpdate') !== false; - } - get canConfigure() { - return this.issuerPath.get('canUpdate') !== false; - } - get canDeleteAllIssuers() { - return this.deletePath.get('isLoading') || this.deletePath.get('canDelete') !== false; - } -} diff --git a/ui/app/models/pki/key.js b/ui/app/models/pki/key.js deleted file mode 100644 index 1e5911c964..0000000000 --- a/ui/app/models/pki/key.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { service } from '@ember/service'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import { withModelValidations } from 'vault/decorators/model-validations'; - -const validations = { - type: [{ type: 'presence', message: 'Type is required.' }], - keyType: [{ type: 'presence', message: 'Please select a key type.' }], - keyName: [ - { - validator(model) { - if (model.keyName === 'default') return false; - return true; - }, - message: `Key name cannot be the reserved value 'default'`, - }, - ], -}; -const displayFields = ['keyId', 'keyName', 'keyType', 'keyBits']; -const formFieldGroups = [{ default: ['keyName', 'type'] }, { 'Key parameters': ['keyType', 'keyBits'] }]; -@withModelValidations(validations) -@withFormFields(displayFields, formFieldGroups) -export default class PkiKeyModel extends Model { - @service secretMountPath; - - @attr('string', { detailsLabel: 'Key ID' }) keyId; - @attr('string', { - subText: `Optional, human-readable name for this key. The name must be unique across all keys and cannot be 'default'.`, - }) - keyName; - @attr('string', { - noDefault: true, - possibleValues: ['internal', 'exported'], - subText: - 'The type of operation. If exported, the private key will be returned in the response; if internal the private key will not be returned and cannot be retrieved later.', - }) - type; - @attr('string', { - noDefault: true, - possibleValues: ['rsa', 'ec', 'ed25519'], - subText: 'The type of key that will be generated. Must be rsa, ed25519, or ec. ', - }) - keyType; - @attr('string', { - label: 'Key bits', - noDefault: true, - subText: 'Bit length of the key to generate.', - }) - keyBits; // no possibleValues because dependent on selected key type - - @attr('string') pemBundle; - @attr('string') privateKey; - - get backend() { - return this.secretMountPath.currentPath; - } - - /* CAPABILITIES - * Default to show UI elements unless we know they can't access the given path - */ - - @lazyCapabilities(apiPath`${'backend'}/key/${'keyId'}`, 'backend', 'keyId') keyPath; - get canRead() { - return this.keyPath.get('canRead') !== false; - } - get canEdit() { - return this.keyPath.get('canUpdate') !== false; - } - get canDelete() { - return this.keyPath.get('canDelete') !== false; - } - - @lazyCapabilities(apiPath`${'backend'}/keys/generate`, 'backend') generatePath; - get canGenerateKey() { - return this.generatePath.get('canUpdate') !== false; - } - - @lazyCapabilities(apiPath`${'backend'}/keys/import`, 'backend') importPath; - get canImportKey() { - return this.importPath.get('canUpdate') !== false; - } -} diff --git a/ui/app/models/pki/role.js b/ui/app/models/pki/role.js deleted file mode 100644 index 1f1957bce8..0000000000 --- a/ui/app/models/pki/role.js +++ /dev/null @@ -1,384 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { service } from '@ember/service'; -import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; -import { withModelValidations } from 'vault/decorators/model-validations'; -import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes'; - -const validations = { - name: [{ type: 'presence', message: 'Name is required.' }], -}; - -@withExpandedAttributes() -@withModelValidations(validations) -export default class PkiRoleModel extends Model { - @service version; // noStoreMetadata is enterprise-only, so we need this available - - @attr('string', { readOnly: true }) backend; - - get formFieldGroups() { - let defaultArray = [ - 'name', - 'issuerRef', - 'customTtl', - 'notBeforeDuration', - 'maxTtl', - 'generateLease', - 'noStore', - 'noStoreMetadata', - 'basicConstraintsValidForNonCa', - ]; - if (this.version.isCommunity) { - const entFields = ['noStoreMetadata']; - defaultArray = defaultArray.filter((field) => !entFields.includes(field)); - } - return this._expandGroups([ - { - default: defaultArray, - }, - { - 'Domain handling': [ - 'allowedDomains', - 'allowedDomainsTemplate', - 'allowBareDomains', - 'allowSubdomains', - 'allowGlobDomains', - 'allowWildcardCertificates', - 'allowLocalhost', // default: true (returned true by OpenApi) - 'allowAnyName', - 'enforceHostnames', // default: true (returned true by OpenApi) - ], - }, - { - 'Key parameters': ['keyType', 'keyBits', 'signatureBits'], - }, - { - 'Key usage': ['keyUsage', 'extKeyUsage', 'extKeyUsageOids'], - }, - { 'Policy identifiers': ['policyIdentifiers'] }, - { - 'Subject Alternative Name (SAN) Options': [ - 'allowIpSans', - 'allowedUriSans', - 'allowUriSansTemplate', - 'allowedOtherSans', - ], - }, - { - 'Additional subject fields': [ - 'allowedUserIds', - 'allowedSerialNumbers', - 'serialNumberSource', - 'requireCn', - 'useCsrCommonName', - 'useCsrSans', - 'ou', - 'organization', - 'country', - 'locality', - 'province', - 'streetAddress', - 'postalCode', - ], - }, - ]); - } - - /* Overriding OpenApi default options */ - @attr('string', { - label: 'Role name', - fieldValue: 'name', - editDisabled: true, - }) - name; - - @attr('string', { - label: 'Issuer reference', - detailsLabel: 'Issuer', - defaultValue: 'default', - subText: `Specifies the issuer that will be used to create certificates with this role. To find this, run read -field=default pki_int/config/issuers in the console. By default, we will use the mounts default issuer.`, - }) - issuerRef; - - @attr({ - label: 'Not valid after', - detailsLabel: 'Issued certificates expire after', - subText: - 'The time after which this certificate will no longer be valid. This can be a TTL (a range of time from now) or a specific date.', - editType: 'yield', - }) - customTtl; - - @attr({ - label: 'Backdate validity', - detailsLabel: 'Issued certificate backdating', - helperTextDisabled: 'Vault will use the default value, 30s', - helperTextEnabled: - 'Also called the not_before_duration property. Allows certificates to be valid for a certain time period before now. This is useful to correct clock misalignment on various systems when setting up your CA.', - editType: 'ttl', - defaultValue: '30s', - }) - notBeforeDuration; - - @attr({ - label: 'Max TTL', - helperTextDisabled: - 'The maximum Time-To-Live of certificates generated by this role. If not set, the system max lease TTL will be used.', - editType: 'ttl', - defaultShown: 'System default', - }) - maxTtl; - - @attr('boolean', { - label: 'Generate lease with certificate', - subText: - 'Specifies if certificates issued/signed against this role will have Vault leases attached to them.', - docLink: '/vault/api-docs/secret/pki#create-update-role', - }) - generateLease; - - @attr('boolean', { - label: 'Do not store certificates in storage backend', - detailsLabel: 'Store in storage backend', // template reverses value - subText: - 'This can improve performance when issuing large numbers of certificates. However, certificates issued in this way cannot be enumerated or revoked.', - docLink: '/vault/api-docs/secret/pki#create-update-role', - }) - noStore; - - @attr('boolean', { - label: 'Do not store certificate metadata in storage backend', - detailsLabel: 'Store metadata in storage backend', // template reverses value - subText: - 'We don’t recommend storing metadata, since this information creates overhead in storage, and requires clean up.', - }) - noStoreMetadata; - - @attr('boolean', { - label: 'Basic constraints valid for non-CA', - detailsLabel: 'Add basic constraints', - subText: 'Mark Basic Constraints valid when issuing non-CA certificates.', - }) - basicConstraintsValidForNonCa; - /* End of overriding default options */ - - /* Overriding OpenApi Domain handling options */ - @attr({ - label: 'Allowed domains', - subText: 'Specifies the domains this role is allowed to issue certificates for.', - editType: 'stringArray', - }) - allowedDomains; - - @attr('boolean', { - label: 'Allow templates in allowed domains', - }) - allowedDomainsTemplate; - /* End of overriding Domain handling options */ - - /* Overriding OpenApi Key parameters options */ - @attr('string', { - label: 'Key type', - possibleValues: ['rsa', 'ec', 'ed25519', 'any'], - defaultValue: 'rsa', - }) - keyType; - - @attr('string', { - label: 'Key bits', - defaultValue: '2048', - }) - keyBits; // no possibleValues because options are dependent on selected key type - - @attr('string', { - label: 'Signature bits', - subText: `Only applicable for key_type 'RSA'. Ignore for other key types.`, - defaultValue: '0', - possibleValues: ['0', '256', '384', '512'], - }) - signatureBits; - /* End of overriding Key parameters options */ - - /* Overriding API Policy identifier option */ - @attr({ - label: 'Policy identifiers', - subText: 'A list of policy object identifiers (OIDs).', - editType: 'stringArray', - }) - policyIdentifiers; - /* End of overriding Policy identifier options */ - - /* Overriding OpenApi SAN options */ - @attr('boolean', { - label: 'Allow IP SANs', - subText: 'Specifies if clients can request IP Subject Alternative Names.', - defaultValue: true, - }) - allowIpSans; - - @attr({ - label: 'URI Subject Alternative Names (URI SANs)', - subText: 'Defines allowed URI Subject Alternative Names.', - editType: 'stringArray', - docLink: '/vault/docs/concepts/policies', - }) - allowedUriSans; - - @attr('boolean', { - label: 'Allow URI SANs template', - subText: 'If true, the URI SANs above may contain templates, as with ACL Path Templating.', - docLink: '/vault/docs/concepts/policies', - }) - allowUriSansTemplate; - - @attr({ - label: 'Other SANs', - subText: 'Defines allowed custom OID/UTF8-string SANs.', - editType: 'stringArray', - }) - allowedOtherSans; - /* End of overriding SAN options */ - - /* Overriding OpenApi Additional subject field options */ - @attr({ - subText: - 'A list of allowed serial numbers to be requested during certificate issuance. Shell-style globbing is supported. If empty, custom-specified serial numbers will be forbidden.', - editType: 'stringArray', - }) - allowedSerialNumbers; - - @attr({ - editType: 'radio', - possibleValues: [ - { - value: 'json-csr', - subText: - 'The subject serial number will be taken from the "serial_number" parameter and fall back to the serial number in the CSR.', - }, - { - value: 'json', - subText: - 'The subject serial number will be taken from the "serial_number" parameter but will ignore any value in the CSR.', - }, - ], - defaultValue: 'json-csr', - }) - serialNumberSource; - - @attr('boolean', { - label: 'Require common name', - subText: 'If set to false, common name will be optional when generating a certificate.', - defaultValue: true, - }) - requireCn; - - @attr('boolean', { - label: 'Use CSR common name', - subText: - 'When used with the CSR signing endpoint, the common name in the CSR will be used instead of taken from the JSON data.', - defaultValue: true, - }) - useCsrCommonName; - - @attr('boolean', { - label: 'Use CSR SANs', - subText: - 'When used with the CSR signing endpoint, the subject alternate names in the CSR will be used instead of taken from the JSON data.', - defaultValue: true, - }) - useCsrSans; - - @attr({ - label: 'Organization Units (OU)', - subText: - 'A list of allowed serial numbers to be requested during certificate issuance. Shell-style globbing is supported. If empty, custom-specified serial numbers will be forbidden.', - editType: 'stringArray', - }) - ou; - - @attr('array', { - defaultValue() { - return ['DigitalSignature', 'KeyAgreement', 'KeyEncipherment']; - }, - defaultShown: 'None', - }) - keyUsage; - - @attr('array', { - defaultShown: 'None', - }) - extKeyUsage; - - @attr('array', { - defaultShown: 'None', - }) - extKeyUsageOids; - - @attr({ editType: 'stringArray' }) allowedUserIds; - @attr({ editType: 'stringArray' }) organization; - @attr({ editType: 'stringArray' }) country; - @attr({ editType: 'stringArray' }) locality; - @attr({ editType: 'stringArray' }) province; - @attr({ editType: 'stringArray' }) streetAddress; - @attr({ editType: 'stringArray' }) postalCode; - /* End of overriding Additional subject field options */ - - /* CAPABILITIES - * Default to show UI elements unless we know they can't access the given path - */ - @lazyCapabilities(apiPath`${'backend'}/roles/${'id'}`, 'backend', 'id') updatePath; - get canDelete() { - return this.updatePath.get('isLoading') || this.updatePath.get('canCreate') !== false; - } - get canEdit() { - return this.updatePath.get('isLoading') || this.updatePath.get('canUpdate') !== false; - } - get canRead() { - return this.updatePath.get('isLoading') || this.updatePath.get('canRead') !== false; - } - - @lazyCapabilities(apiPath`${'backend'}/issue/${'id'}`, 'backend', 'id') generatePath; - get canGenerateCert() { - return this.generatePath.get('isLoading') || this.generatePath.get('canUpdate') !== false; - } - @lazyCapabilities(apiPath`${'backend'}/sign/${'id'}`, 'backend', 'id') signPath; - get canSign() { - return this.signPath.get('isLoading') || this.signPath.get('canUpdate') !== false; - } - @lazyCapabilities(apiPath`${'backend'}/sign-verbatim/${'id'}`, 'backend', 'id') signVerbatimPath; - get canSignVerbatim() { - return this.signVerbatimPath.get('isLoading') || this.signVerbatimPath.get('canUpdate') !== false; - } - - // Gets header/footer copy for specific toggle groups. - get fieldGroupsInfo() { - return { - 'Domain handling': { - footer: { - text: 'These options can interact intricately with one another. For more information,', - docText: 'learn more here.', - docLink: '/vault/api-docs/secret/pki#allowed_domains', - }, - }, - 'Key parameters': { - header: { - text: `These are the parameters for generating or validating the certificate's key material.`, - }, - }, - 'Subject Alternative Name (SAN) Options': { - header: { - text: `Subject Alternative Names (SANs) are identities (domains, IP addresses, and URIs) Vault attaches to the requested certificates.`, - }, - }, - 'Additional subject fields': { - header: { - text: `Additional identity metadata Vault can attach to the requested certificates.`, - }, - }, - }; - } -} diff --git a/ui/app/models/pki/sign-intermediate.js b/ui/app/models/pki/sign-intermediate.js deleted file mode 100644 index a3c18b087b..0000000000 --- a/ui/app/models/pki/sign-intermediate.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { attr } from '@ember-data/model'; -import { withFormFields } from 'vault/decorators/model-form-fields'; -import { withModelValidations } from 'vault/decorators/model-validations'; -import PkiCertificateBaseModel from './certificate/base'; - -const validations = { - csr: [{ type: 'presence', message: 'CSR is required.' }], -}; -@withModelValidations(validations) -@withFormFields([ - 'csr', - 'useCsrValues', - 'commonName', - 'excludeCnFromSans', - 'customTtl', - 'notBeforeDuration', - 'enforceLeafNotAfterBehavior', - 'format', - 'maxPathLength', -]) -export default class PkiSignIntermediateModel extends PkiCertificateBaseModel { - @attr issuerRef; - - @attr('string', { - label: 'CSR', - editType: 'textarea', - subText: 'The PEM-encoded CSR to be signed.', - }) - csr; - - @attr('boolean', { - label: 'Use CSR values', - subText: - 'Subject information and key usages specified in the CSR will be used over parameters provided here, and extensions in the CSR will be copied into the issued certificate.', - docLink: '/vault/api-docs/secret/pki#use_csr_values', - }) - useCsrValues; - - @attr({ - label: 'Backdate validity', - detailsLabel: 'Issued certificate backdating', - helperTextDisabled: 'Vault will use the default value, 30s', - helperTextEnabled: - 'Also called the not_before_duration property. Allows certificates to be valid for a certain time period before now. This is useful to correct clock misalignment on various systems when setting up your CA.', - editType: 'ttl', - defaultValue: '30s', - }) - notBeforeDuration; - - @attr('boolean', { - subText: "Do not truncate the NotAfter field, use the issuer's configured leaf_not_after_behavior", - }) - enforceLeafNotAfterBehavior; - - @attr({ - subText: 'Specifies the maximum path length to encode in the generated certificate. -1 means no limit', - defaultValue: '-1', - }) - maxPathLength; - - /* Name constraint overrides */ - @attr({ - subText: 'DNS domains for which certificates are allowed to be issued or signed by this CA certificate.', - }) - permittedDnsDomains; - - @attr({ - subText: 'Domains for which this certificate is not allowed to sign or issue child certificates.', - }) - excludedDnsDomains; - - @attr({ - subText: 'Email addresses for which this certificate is not allowed to sign or issue child certificates.', - }) - excludedEmailAddresses; - - @attr({ - subText: - 'IP ranges for which this certificate is not allowed to sign or issue child certificates. Ranges must be specified in the notation of IP address and prefix length, such as "192.0.2.0/24" or "2001:db8::/32", as defined in RFC 4632 and RFC 4291.', - }) - excludedIpRanges; - - @attr({ - subText: 'URI domains for which this certificate is not allowed to sign or issue child certificates.', - }) - excludedUriDomains; - - @attr({ - subText: 'Email addresses for which this certificate is allowed to sign or issue child certificates.', - }) - permittedEmailAddresses; - - @attr({ - subText: - 'IP ranges for which this certificate is allowed to sign or issue child certificates. Ranges must be specified in the notation of IP address and prefix length, such as "192.0.2.0/24" or "2001:db8::/32", as defined in RFC 4632 and RFC 4291.', - }) - permittedIpRanges; - - @attr({ - subText: 'URI domains for which this certificate is allowed to sign or issue child certificates.', - }) - permittedUriDomains; - - /* Signing Options overrides */ - @attr({ - label: 'Use PSS', - subText: - 'If checked, PSS signatures will be used over PKCS#1v1.5 signatures when a RSA-type issuer is used. Ignored for ECDSA/Ed25519 issuers.', - }) - usePss; - - @attr({ - label: 'Subject Key Identifier (SKID)', - subText: - 'Value for the subject key identifier, specified as a string in hex format. If this is empty, Vault will automatically calculate the SKID. ', - }) - skid; - - @attr({ - possibleValues: ['0', '256', '384', '512'], - }) - signatureBits; - - /* Additional subject overrides */ - @attr('string', { - subText: - "Specifies the requested Subject's named Serial Number value. This has no impact on the Certificate's serial number randomly generated by Vault.", - }) - subjectSerialNumber; -} diff --git a/ui/app/models/pki/tidy.js b/ui/app/models/pki/tidy.js deleted file mode 100644 index 10518c10f2..0000000000 --- a/ui/app/models/pki/tidy.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Model, { attr } from '@ember-data/model'; -import { service } from '@ember/service'; -import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes'; - -@withExpandedAttributes() -export default class PkiTidyModel extends Model { - // the backend mount is the model id, only one pki/tidy model will ever persist (the auto-tidy config) - @service version; - - @attr({ - label: 'Tidy ACME enabled', - labelDisabled: 'Tidy ACME disabled', - mapToBoolean: 'tidyAcme', - helperTextDisabled: 'Tidying of ACME accounts, orders and authorizations is disabled.', - helperTextEnabled: - 'The amount of time that must pass after creation that an account with no orders is marked revoked, and the amount of time after being marked revoked or deactivated.', - detailsLabel: 'ACME account safety buffer', - formatTtl: true, - defaultValue: '720h', - }) - acmeAccountSafetyBuffer; - - @attr('boolean', { - label: 'Tidy ACME', - defaultValue: false, - }) - tidyAcme; - - // * auto tidy only fields - @attr('boolean', { - label: 'Automatic tidy enabled', - labelDisabled: 'Automatic tidy disabled', - helperTextDisabled: 'Automatic tidy operations will not run.', - }) - enabled; // renders outside FormField loop as a toggle, auto tidy fields only render if enabled - - @attr({ - editType: 'ttl', - helperTextEnabled: - 'Sets the interval_duration between automatic tidy operations; note that this is from the end of one operation to the start of the next.', - hideToggle: true, - formatTtl: true, - }) - intervalDuration; - - @attr({ - label: 'Minimum startup backoff duration', - editType: 'ttl', - helperTextEnabled: - 'Sets the min_startup_backoff_duration field which forces the minimum delay after Vault startup auto-tidy can run.', - hideToggle: true, - formatTtl: true, - }) - minStartupBackoffDuration; - - @attr({ - label: 'Maximum startup backoff duration', - editType: 'ttl', - helperTextEnabled: - 'Sets the max_startup_backoff_duration field which forces the maximum delay after Vault startup auto-tidy can run.', - hideToggle: true, - formatTtl: true, - }) - maxStartupBackoffDuration; - // * end of auto-tidy only fields - - @attr({ - editType: 'ttl', - helperTextEnabled: - 'Specifies a duration that issuers should be kept for, past their NotAfter validity period. Defaults to 365 days (8760 hours).', - hideToggle: true, - formatTtl: true, - }) - issuerSafetyBuffer; - - @attr('string', { - editType: 'ttl', - helperTextEnabled: - 'Specifies the duration to pause between tidying individual certificates. This releases the revocation lock and allows other operations to continue while tidy is running.', - hideToggle: true, - formatTtl: true, - }) - pauseDuration; - - @attr('string', { - editType: 'ttl', - helperTextEnabled: - 'Specifies a duration after which cross-cluster revocation requests will be removed as expired.', - hideToggle: true, - formatTtl: true, - }) - revocationQueueSafetyBuffer; // enterprise only - - @attr({ - editType: 'ttl', - helperTextEnabled: - 'For a certificate to be expunged, the time must be after the expiration time of the certificate (according to the local clock) plus the safety buffer. Defaults to 72 hours.', - hideToggle: true, - formatTtl: true, - }) - safetyBuffer; - - @attr('boolean', { label: 'Tidy the certificate store' }) - tidyCertStore; - - @attr('boolean') - tidyCertMetadata; - - @attr('boolean', { - label: 'Tidy cross-cluster revoked certificates', - subText: 'Remove expired, cross-cluster revocation entries.', - }) - tidyCrossClusterRevokedCerts; // enterprise only - - @attr('boolean', { - label: 'Tidy CMPv2 nonce store', - }) - tidyCmpv2NonceStore; // enterprise only - - @attr('boolean', { - subText: 'Automatically remove expired issuers after the issuer safety buffer duration has elapsed.', - }) - tidyExpiredIssuers; - - @attr('boolean', { - label: 'Tidy legacy CA bundle', - subText: - 'Backup any legacy CA/issuers bundle (from Vault versions earlier than 1.11) to config/ca_bundle.bak. Migration will only occur after issuer safety buffer has passed.', - }) - tidyMoveLegacyCaBundle; - - @attr('boolean', { - label: 'Tidy cross-cluster revocation requests', - }) - tidyRevocationQueue; // enterprise only - - @attr('boolean', { - label: 'Tidy revoked certificate issuer associations', - }) - tidyRevokedCertIssuerAssociations; - - @attr('boolean', { - label: 'Tidy revoked certificates', - subText: 'Remove all invalid and expired certificates from storage.', - }) - tidyRevokedCerts; - - get allGroups() { - const groups = [{ autoTidy: ['enabled', ...this.autoTidyConfigFields] }, ...this.sharedFields]; - return this._expandGroups(groups); - } - - // fields that are specific to auto-tidy - get autoTidyConfigFields() { - // 'enabled' is not included here because it is responsible for hiding/showing these params and renders separately in the form - return ['intervalDuration', 'minStartupBackoffDuration', 'maxStartupBackoffDuration']; - } - - // shared between auto and manual tidy operations - get sharedFields() { - const groups = [ - { - 'Universal operations': [ - 'tidyCertStore', - 'tidyCertMetadata', - 'tidyRevokedCerts', - 'tidyRevokedCertIssuerAssociations', - 'safetyBuffer', - 'pauseDuration', - ], - }, - { - 'ACME operations': ['tidyAcme', 'acmeAccountSafetyBuffer'], - }, - { - 'Issuer operations': ['tidyExpiredIssuers', 'tidyMoveLegacyCaBundle', 'issuerSafetyBuffer'], - }, - ]; - if (this.version.isEnterprise) { - groups.push({ - 'Cross-cluster operations': [ - 'tidyRevocationQueue', - 'tidyCrossClusterRevokedCerts', - 'tidyCmpv2NonceStore', - 'revocationQueueSafetyBuffer', - ], - }); - } - return groups; - } - - get formFieldGroups() { - return this._expandGroups(this.sharedFields); - } -} diff --git a/ui/app/serializers/pki/action.js b/ui/app/serializers/pki/action.js deleted file mode 100644 index e9cafcf9bc..0000000000 --- a/ui/app/serializers/pki/action.js +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { underscore } from '@ember/string'; -import { keyParamsByType } from 'pki/utils/action-params'; -import ApplicationSerializer from '../application'; -import { parseCertificate } from 'vault/utils/parse-pki-cert'; - -export default class PkiActionSerializer extends ApplicationSerializer { - attrs = { - customTtl: { serialize: false }, - type: { serialize: false }, - }; - - normalizeResponse(store, primaryModelClass, payload, id, requestType) { - if (payload.data.certificate) { - // Parse certificate back from the API and add to payload - const parsedCert = parseCertificate(payload.data.certificate); - const data = { - ...payload.data, - common_name: parsedCert.common_name, - parsed_certificate: parsedCert, - }; - return super.normalizeResponse(store, primaryModelClass, { ...payload, data }, id, requestType); - } - return super.normalizeResponse(...arguments); - } - - serialize(snapshot, requestType) { - const data = super.serialize(snapshot); - // requestType is a custom value specified from the pki/action adapter - const allowedPayloadAttributes = this._allowedParamsByType(requestType, snapshot.record.type); - if (!allowedPayloadAttributes) return data; - // the backend expects the subject's serial number param to be 'serial_number' - // we label it as subject_serial_number to differentiate from the vault generated UUID - data.serial_number = data.subject_serial_number; - - const payload = {}; - allowedPayloadAttributes.forEach((key) => { - if ('undefined' !== typeof data[key]) { - payload[key] = data[key]; - } - }); - return payload; - } - - _allowedParamsByType(actionType, type) { - const keyFields = keyParamsByType(type).map((attrName) => underscore(attrName).toLowerCase()); - const commonProps = [ - 'alt_names', - 'common_name', - 'country', - 'exclude_cn_from_sans', - 'format', - 'ip_sans', - 'locality', - 'organization', - 'other_sans', - 'ou', - 'postal_code', - 'province', - 'serial_number', - 'street_address', - 'type', - 'uri_sans', - ...keyFields, - ]; - switch (actionType) { - case 'import': - return ['pem_bundle']; - case 'generate-root': - return [ - ...commonProps, - 'issuer_name', - 'max_path_length', - 'not_after', - 'not_before_duration', - 'permitted_dns_domains', - 'private_key_format', - 'ttl', - ]; - case 'rotate-root': - return [ - ...commonProps, - 'issuer_name', - 'max_path_length', - 'not_after', - 'not_before_duration', - 'permitted_dns_domains', - 'private_key_format', - 'ttl', - ]; - case 'generate-csr': - return [...commonProps, 'add_basic_constraints']; - case 'sign-intermediate': - return ['common_name', 'issuer_name', 'csr']; - default: - // if type doesn't match, serialize all - return null; - } - } -} diff --git a/ui/app/serializers/pki/certificate.js b/ui/app/serializers/pki/certificate.js deleted file mode 100644 index ae55210000..0000000000 --- a/ui/app/serializers/pki/certificate.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import CertSerializer from './certificate/base'; - -export default class PkiCertificateSerializer extends CertSerializer {} diff --git a/ui/app/serializers/pki/certificate/base.js b/ui/app/serializers/pki/certificate/base.js deleted file mode 100644 index 8450282848..0000000000 --- a/ui/app/serializers/pki/certificate/base.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { parseCertificate } from 'vault/utils/parse-pki-cert'; -import ApplicationSerializer from '../../application'; - -export default class PkiCertificateBaseSerializer extends ApplicationSerializer { - primaryKey = 'serial_number'; - - attrs = { - role: { serialize: false }, - }; - - normalizeResponse(store, primaryModelClass, payload, id, requestType) { - if (payload.data.certificate) { - // Parse certificate back from the API and add to payload - const parsedCert = parseCertificate(payload.data.certificate); - return super.normalizeResponse( - store, - primaryModelClass, - { ...payload, parsed_certificate: parsedCert, common_name: parsedCert.common_name }, - id, - requestType - ); - } - return super.normalizeResponse(...arguments); - } -} diff --git a/ui/app/serializers/pki/certificate/generate.js b/ui/app/serializers/pki/certificate/generate.js deleted file mode 100644 index 9ece878214..0000000000 --- a/ui/app/serializers/pki/certificate/generate.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import PkiCertificateBaseSerializer from './base'; - -export default class PkiCertificateGenerateSerializer extends PkiCertificateBaseSerializer {} diff --git a/ui/app/serializers/pki/certificate/sign.js b/ui/app/serializers/pki/certificate/sign.js deleted file mode 100644 index 9ece878214..0000000000 --- a/ui/app/serializers/pki/certificate/sign.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import PkiCertificateBaseSerializer from './base'; - -export default class PkiCertificateGenerateSerializer extends PkiCertificateBaseSerializer {} diff --git a/ui/app/serializers/pki/issuer.js b/ui/app/serializers/pki/issuer.js deleted file mode 100644 index f95c5e7773..0000000000 --- a/ui/app/serializers/pki/issuer.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { parseCertificate } from 'vault/utils/parse-pki-cert'; -import ApplicationSerializer from '../application'; - -export default class PkiIssuerSerializer extends ApplicationSerializer { - primaryKey = 'issuer_id'; - - attrs = { - caChain: { serialize: false }, - certificate: { serialize: false }, - commonName: { serialize: false }, - isDefault: { serialize: false }, - isRoot: { serialize: false }, - issuerId: { serialize: false }, - keyId: { serialize: false }, - parsedCertificate: { serialize: false }, - serialNumber: { serialize: false }, - }; - - normalizeResponse(store, primaryModelClass, payload, id, requestType) { - if (payload.data.certificate) { - // Parse certificate back from the API and add to payload - const parsedCert = parseCertificate(payload.data.certificate); - const data = { - ...payload.data, - parsed_certificate: parsedCert, - common_name: parsedCert.common_name, - }; - return super.normalizeResponse(store, primaryModelClass, { ...payload, data }, id, requestType); - } - return super.normalizeResponse(...arguments); - } - - // rehydrate each issuers model so all model attributes are accessible from the LIST response - normalizeItems(payload) { - if (payload.data) { - if (payload.data?.keys && Array.isArray(payload.data.keys)) { - return payload.data.keys.map((key) => { - return { - issuer_id: key, - ...payload.data.key_info[key], - }; - }); - } - Object.assign(payload, payload.data); - delete payload.data; - } - return payload; - } -} diff --git a/ui/app/serializers/pki/key.js b/ui/app/serializers/pki/key.js deleted file mode 100644 index 51aecdc026..0000000000 --- a/ui/app/serializers/pki/key.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import ApplicationSerializer from '../application'; - -export default class PkiKeySerializer extends ApplicationSerializer { - primaryKey = 'key_id'; - attrs = { - type: { serialize: false }, - }; - - // rehydrate each keys model so all model attributes are accessible from the LIST response - normalizeItems(payload) { - if (payload.data) { - if (payload.data?.keys && Array.isArray(payload.data.keys)) { - return payload.data.keys.map((key) => ({ key_id: key, ...payload.data.key_info[key] })); - } - Object.assign(payload, payload.data); - delete payload.data; - } - return payload; - } -} diff --git a/ui/app/serializers/pki/role.js b/ui/app/serializers/pki/role.js deleted file mode 100644 index 1bab18daab..0000000000 --- a/ui/app/serializers/pki/role.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import ApplicationSerializer from '../application'; - -export default class PkiRoleSerializer extends ApplicationSerializer { - attrs = { - name: { serialize: false }, - }; - - serialize() { - const json = super.serialize(...arguments); - // attributes with empty arrays are stripped from serialized json - // but an empty list is acceptable for key_usage to specify no default constraints - // intercepting here to ensure an empty array persists (the backend assumes default values) - json.key_usage = json.key_usage || []; - return json; - } -} diff --git a/ui/app/serializers/pki/tidy.js b/ui/app/serializers/pki/tidy.js deleted file mode 100644 index 9c3c90acec..0000000000 --- a/ui/app/serializers/pki/tidy.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import ApplicationSerializer from '../application'; - -export default class PkiTidySerializer extends ApplicationSerializer { - serialize(snapshot, tidyType) { - const data = super.serialize(snapshot); - if (tidyType === 'manual') { - delete data?.enabled; - delete data?.intervalDuration; - delete data?.minStartupBackoffDuration; - delete data?.maxStartupBackoffDuration; - } - return data; - } -} diff --git a/ui/lib/config-ui/addon/components/messages/page/create-and-edit.js b/ui/lib/config-ui/addon/components/messages/page/create-and-edit.js index 623d2c4ff7..56e903d346 100644 --- a/ui/lib/config-ui/addon/components/messages/page/create-and-edit.js +++ b/ui/lib/config-ui/addon/components/messages/page/create-and-edit.js @@ -26,7 +26,6 @@ import timestamp from 'core/utils/timestamp'; export default class MessagesList extends Component { @service('app-router') router; - @service pagination; @service flashMessages; @service customMessages; @service api; diff --git a/ui/lib/config-ui/addon/components/messages/page/details.js b/ui/lib/config-ui/addon/components/messages/page/details.js index 8ea34a94d9..31542c8e0f 100644 --- a/ui/lib/config-ui/addon/components/messages/page/details.js +++ b/ui/lib/config-ui/addon/components/messages/page/details.js @@ -22,7 +22,6 @@ export default class MessageDetails extends Component { @service('app-router') router; @service flashMessages; @service customMessages; - @service pagination; @service api; displayFields = ['active', 'type', 'authenticated', 'title', 'message', 'start_time', 'end_time', 'link']; diff --git a/ui/lib/config-ui/addon/components/messages/page/list.js b/ui/lib/config-ui/addon/components/messages/page/list.js index a1415eb947..8afebc2b26 100644 --- a/ui/lib/config-ui/addon/components/messages/page/list.js +++ b/ui/lib/config-ui/addon/components/messages/page/list.js @@ -27,7 +27,6 @@ export default class MessagesList extends Component { @service customMessages; @service flashMessages; @service namespace; - @service pagination; @service('app-router') router; @service api; diff --git a/ui/lib/config-ui/addon/engine.js b/ui/lib/config-ui/addon/engine.js index 1bd5e4696e..fd4547ed61 100644 --- a/ui/lib/config-ui/addon/engine.js +++ b/ui/lib/config-ui/addon/engine.js @@ -18,7 +18,6 @@ export default class ConfigUiEngine extends Engine { dependencies = { services: [ 'auth', - 'pagination', 'flash-messages', 'namespace', 'app-router', diff --git a/ui/lib/kv/addon/components/page/list.js b/ui/lib/kv/addon/components/page/list.js index 08bdc3f140..7293ebdbf5 100644 --- a/ui/lib/kv/addon/components/page/list.js +++ b/ui/lib/kv/addon/components/page/list.js @@ -27,7 +27,6 @@ import { pathIsDirectory } from 'kv/utils/kv-breadcrumbs'; export default class KvListPageComponent extends Component { @service flashMessages; @service('app-router') router; - @service pagination; @service api; @tracked secretPath; diff --git a/ui/lib/kv/addon/engine.js b/ui/lib/kv/addon/engine.js index 612f53f6e1..e616388608 100644 --- a/ui/lib/kv/addon/engine.js +++ b/ui/lib/kv/addon/engine.js @@ -25,7 +25,6 @@ export default class KvEngine extends Engine { 'namespace', 'app-router', 'secret-mount-path', - 'pagination', 'version', ], externalRoutes: ['secrets', 'syncDestination'], diff --git a/ui/lib/kv/addon/routes/list-directory.js b/ui/lib/kv/addon/routes/list-directory.js index 73e8ab2a9d..7dc8677cc5 100644 --- a/ui/lib/kv/addon/routes/list-directory.js +++ b/ui/lib/kv/addon/routes/list-directory.js @@ -9,7 +9,6 @@ import { pathIsDirectory, breadcrumbsForSecret } from 'kv/utils/kv-breadcrumbs'; import { paginate } from 'core/utils/paginate-list'; export default class KvSecretsListRoute extends Route { - @service pagination; @service('app-router') router; @service secretMountPath; @service api; diff --git a/ui/lib/pki/addon/components/page/pki-configure-create.ts b/ui/lib/pki/addon/components/page/pki-configure-create.ts index 6faedef258..9b10743348 100644 --- a/ui/lib/pki/addon/components/page/pki-configure-create.ts +++ b/ui/lib/pki/addon/components/page/pki-configure-create.ts @@ -8,7 +8,6 @@ import { service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; -import type Store from '@ember-data/store'; import type RouterService from '@ember/routing/router'; import type FlashMessageService from 'vault/services/flash-messages'; import type { Breadcrumb, CapabilitiesMap } from 'vault/vault/app-types'; @@ -28,7 +27,6 @@ interface Args { */ export default class PkiConfigureCreate extends Component { @service declare readonly flashMessages: FlashMessageService; - @service declare readonly store: Store; @service('app-router') declare readonly router: RouterService; @tracked title = 'Configure PKI'; diff --git a/ui/lib/pki/addon/components/page/pki-issuer-import.ts b/ui/lib/pki/addon/components/page/pki-issuer-import.ts index 5a4349ebbc..321cdd3f90 100644 --- a/ui/lib/pki/addon/components/page/pki-issuer-import.ts +++ b/ui/lib/pki/addon/components/page/pki-issuer-import.ts @@ -5,10 +5,11 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import type PkiActionModel from 'vault/vault/models/pki/action'; + +import type { Breadcrumb } from 'vault/vault/app-types'; interface Args { - model: PkiActionModel; + breadcrumbs: Breadcrumb[]; } export default class PagePkiIssuerImportComponent extends Component { diff --git a/ui/lib/pki/addon/components/page/pki-overview.hbs b/ui/lib/pki/addon/components/page/pki-overview.hbs index ed94b1c889..7b1de45e82 100644 --- a/ui/lib/pki/addon/components/page/pki-overview.hbs +++ b/ui/lib/pki/addon/components/page/pki-overview.hbs @@ -51,12 +51,10 @@ class="is-flex-grow-1" @ariaLabel="Role" @selectLimit="1" - @models={{array "pki/role"}} - @backend={{@engine.id}} + @options={{this.searchSelectOptions.roles}} @placeholder="Type to find a role..." @disallowNewItems={{true}} @onChange={{this.handleRolesInput}} - @fallbackComponent="input-search" data-test-issue-certificate-input /> { @service('app-router') declare readonly router: RouterService; - @service declare readonly store: Store; @tracked rolesValue = ''; @tracked certificateValue = ''; @tracked issuerValue = ''; + // format issuers, roles and certificates to pass in to SearchSelect components + get searchSelectOptions() { + const issuers = Array.isArray(this.args.issuers) ? this.args.issuers : []; + const roles = Array.isArray(this.args.roles) ? this.args.roles : []; + const certificates = Array.isArray(this.args.certificates) ? this.args.certificates : []; + + return { + issuers: issuers.map((issuer) => ({ name: issuer, id: issuer })), + roles: roles.map((role) => ({ name: role, id: role })), + certificates: certificates.map((certificate) => ({ name: certificate, id: certificate })), + }; + } + @action transitionToViewCertificates() { this.router.transitionTo( diff --git a/ui/lib/pki/addon/controllers/tidy/index.js b/ui/lib/pki/addon/controllers/tidy/index.js index 4773fb0661..08e3995d8a 100644 --- a/ui/lib/pki/addon/controllers/tidy/index.js +++ b/ui/lib/pki/addon/controllers/tidy/index.js @@ -11,7 +11,6 @@ import { service } from '@ember/service'; const POLL_INTERVAL_MS = 5000; export default class PkiTidyIndexController extends Controller { - @service store; @service secretMountPath; @tracked tidyStatus = null; diff --git a/ui/lib/pki/addon/engine.js b/ui/lib/pki/addon/engine.js index 8e46a2883c..8507bfc605 100644 --- a/ui/lib/pki/addon/engine.js +++ b/ui/lib/pki/addon/engine.js @@ -26,8 +26,6 @@ export default class PkiEngine extends Engine { 'path-help', 'app-router', 'secret-mount-path', - 'store', - 'pagination', 'version', ], externalRoutes: ['secrets', 'secretsListRootConfiguration', 'externalMountIssuer'], diff --git a/ui/lib/pki/addon/routes/application.js b/ui/lib/pki/addon/routes/application.js deleted file mode 100644 index e715a8490e..0000000000 --- a/ui/lib/pki/addon/routes/application.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Route from '@ember/routing/route'; -import { service } from '@ember/service'; -import { hash } from 'rsvp'; - -export default class PkiRoute extends Route { - @service pathHelp; - @service secretMountPath; - - beforeModel() { - // We call pathHelp for all the models in this engine that use OpenAPI before any model hooks - // so that the model attributes hydrate correctly. These only need to be called once to add - // the openAPI attributes to the model prototype - const mountPath = this.secretMountPath.currentPath; - return hash({ - acme: this.pathHelp.hydrateModel('pki/config/acme', mountPath), - certGenerate: this.pathHelp.hydrateModel('pki/certificate/generate', mountPath), - certSign: this.pathHelp.hydrateModel('pki/certificate/sign', mountPath), - cluster: this.pathHelp.hydrateModel('pki/config/cluster', mountPath), - key: this.pathHelp.hydrateModel('pki/key', mountPath), - role: this.pathHelp.hydrateModel('pki/role', mountPath), - signCsr: this.pathHelp.hydrateModel('pki/sign-intermediate', mountPath), - tidy: this.pathHelp.hydrateModel('pki/tidy', mountPath), - urls: this.pathHelp.hydrateModel('pki/config/urls', mountPath), - }); - } -} diff --git a/ui/lib/pki/addon/routes/certificates/index.js b/ui/lib/pki/addon/routes/certificates/index.js index e4f3cf4963..48f86d018a 100644 --- a/ui/lib/pki/addon/routes/certificates/index.js +++ b/ui/lib/pki/addon/routes/certificates/index.js @@ -12,7 +12,6 @@ import { paginate } from 'core/utils/paginate-list'; @withConfig() export default class PkiCertificatesIndexRoute extends Route { - @service pagination; @service secretMountPath; @service api; diff --git a/ui/lib/pki/addon/routes/configuration/index.js b/ui/lib/pki/addon/routes/configuration/index.js index 117f3221ac..f82582cb5f 100644 --- a/ui/lib/pki/addon/routes/configuration/index.js +++ b/ui/lib/pki/addon/routes/configuration/index.js @@ -4,29 +4,17 @@ */ import Route from '@ember/routing/route'; -import { service } from '@ember/service'; import { withConfig } from 'pki/decorators/check-issuers'; -import { hash } from 'rsvp'; import { PKI_DEFAULT_EMPTY_STATE_MSG } from 'pki/routes/overview'; @withConfig() export default class ConfigurationIndexRoute extends Route { - @service store; - - async fetchMountConfig(backend) { - const mountConfig = await this.store.query('secret-engine', { path: backend }); - if (mountConfig) { - return mountConfig[0]; - } - } - - model() { - const configRouteModel = this.modelFor('configuration'); - return hash({ + async model() { + return { hasConfig: this.pkiMountHasConfig, - mountConfig: this.fetchMountConfig(configRouteModel.engine.id), - ...configRouteModel, - }); + mountConfig: this.modelFor('application'), + ...this.modelFor('configuration'), + }; } setupController(controller, resolvedModel) { diff --git a/ui/lib/pki/addon/routes/issuers/import.js b/ui/lib/pki/addon/routes/issuers/import.js index 89a18deb46..2f8f26e6d6 100644 --- a/ui/lib/pki/addon/routes/issuers/import.js +++ b/ui/lib/pki/addon/routes/issuers/import.js @@ -5,11 +5,8 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { withConfirmLeave } from 'core/decorators/confirm-leave'; -@withConfirmLeave() export default class PkiIssuersImportRoute extends Route { - @service store; @service secretMountPath; setupController(controller, resolvedModel) { diff --git a/ui/lib/pki/addon/routes/issuers/issuer/cross-sign.js b/ui/lib/pki/addon/routes/issuers/issuer/cross-sign.js index af5f84fa83..60cf5e545d 100644 --- a/ui/lib/pki/addon/routes/issuers/issuer/cross-sign.js +++ b/ui/lib/pki/addon/routes/issuers/issuer/cross-sign.js @@ -5,11 +5,8 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { withConfirmLeave } from 'core/decorators/confirm-leave'; -@withConfirmLeave() export default class PkiIssuerCrossSignRoute extends Route { - @service store; @service secretMountPath; model() { diff --git a/ui/lib/pki/addon/routes/issuers/issuer/rotate-root.js b/ui/lib/pki/addon/routes/issuers/issuer/rotate-root.js index 08e91dee7f..24103c6a45 100644 --- a/ui/lib/pki/addon/routes/issuers/issuer/rotate-root.js +++ b/ui/lib/pki/addon/routes/issuers/issuer/rotate-root.js @@ -10,7 +10,6 @@ import { parseCertificate } from 'vault/utils/parse-pki-cert'; export default class PkiIssuerRotateRootRoute extends Route { @service secretMountPath; - @service store; model() { const oldRoot = this.modelFor('issuers.issuer'); diff --git a/ui/lib/pki/addon/routes/issuers/issuer/sign.js b/ui/lib/pki/addon/routes/issuers/issuer/sign.js index 1cb9340c18..f397729401 100644 --- a/ui/lib/pki/addon/routes/issuers/issuer/sign.js +++ b/ui/lib/pki/addon/routes/issuers/issuer/sign.js @@ -8,7 +8,6 @@ import { service } from '@ember/service'; import PkiIssuersSignIntermediateForm from 'vault/forms/secrets/pki/issuers/sign-intermediate'; export default class PkiIssuerSignRoute extends Route { - @service store; @service secretMountPath; model() { diff --git a/ui/lib/pki/addon/routes/keys/create.js b/ui/lib/pki/addon/routes/keys/create.js index 4749dfc6bd..fe364343c7 100644 --- a/ui/lib/pki/addon/routes/keys/create.js +++ b/ui/lib/pki/addon/routes/keys/create.js @@ -9,7 +9,6 @@ import PkiKeyForm from 'vault/forms/secrets/pki/key'; export default class PkiKeysCreateRoute extends Route { @service secretMountPath; - @service store; model() { return new PkiKeyForm({}, { isNew: true }); diff --git a/ui/lib/pki/addon/routes/keys/import.js b/ui/lib/pki/addon/routes/keys/import.js index df6fe92f6b..8d02301073 100644 --- a/ui/lib/pki/addon/routes/keys/import.js +++ b/ui/lib/pki/addon/routes/keys/import.js @@ -8,7 +8,6 @@ import { service } from '@ember/service'; export default class PkiKeysImportRoute extends Route { @service secretMountPath; - @service store; setupController(controller, resolvedModel) { super.setupController(controller, resolvedModel); diff --git a/ui/lib/pki/addon/routes/overview.js b/ui/lib/pki/addon/routes/overview.js index 9c58fcc0d2..2b9e8f1e4d 100644 --- a/ui/lib/pki/addon/routes/overview.js +++ b/ui/lib/pki/addon/routes/overview.js @@ -7,6 +7,11 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; import { withConfig } from 'pki/decorators/check-issuers'; import { hash } from 'rsvp'; +import { + PkiListCertsListEnum, + PkiListRolesListEnum, + PkiListIssuersListEnum, +} from '@hashicorp/vault-client-typescript'; export const PKI_DEFAULT_EMPTY_STATE_MSG = "This PKI mount hasn't yet been configured with a certificate issuer."; @@ -21,29 +26,41 @@ export const getCliMessage = (msg) => { export default class PkiOverviewRoute extends Route { @service secretMountPath; @service auth; - @service store; + @service api; async fetchAllCertificates() { try { - return await this.store.query('pki/certificate/base', { backend: this.secretMountPath.currentPath }); + const { keys } = await this.api.secrets.pkiListCerts( + this.secretMountPath.currentPath, + PkiListCertsListEnum.TRUE + ); + return keys; } catch (e) { - return e.httpStatus; + return e.response.status; } } async fetchAllRoles() { try { - return await this.store.query('pki/role', { backend: this.secretMountPath.currentPath }); + const { keys } = await this.api.secrets.pkiListRoles( + this.secretMountPath.currentPath, + PkiListRolesListEnum.TRUE + ); + return keys; } catch (e) { - return e.httpStatus; + return e.response.status; } } async fetchAllIssuers() { try { - return await this.store.query('pki/issuer', { backend: this.secretMountPath.currentPath }); + const { keys } = await this.api.secrets.pkiListIssuers( + this.secretMountPath.currentPath, + PkiListIssuersListEnum.TRUE + ); + return keys; } catch (e) { - return e.httpStatus; + return e.response.status; } } diff --git a/ui/lib/pki/addon/routes/roles/role/sign.js b/ui/lib/pki/addon/routes/roles/role/sign.js index 91a494db1e..439d1bc3a0 100644 --- a/ui/lib/pki/addon/routes/roles/role/sign.js +++ b/ui/lib/pki/addon/routes/roles/role/sign.js @@ -8,7 +8,6 @@ import { service } from '@ember/service'; import PkiCertificateForm from 'vault/forms/secrets/pki/certificate'; export default class PkiRoleSignRoute extends Route { - @service store; @service secretMountPath; model() { diff --git a/ui/lib/pki/addon/routes/tidy/auto/configure.js b/ui/lib/pki/addon/routes/tidy/auto/configure.js index 7ec539e892..e0c0798b1f 100644 --- a/ui/lib/pki/addon/routes/tidy/auto/configure.js +++ b/ui/lib/pki/addon/routes/tidy/auto/configure.js @@ -8,7 +8,6 @@ import { service } from '@ember/service'; import PkiTidyForm from 'vault/forms/secrets/pki/tidy'; export default class PkiTidyAutoConfigureRoute extends Route { - @service store; @service secretMountPath; model() { diff --git a/ui/lib/pki/addon/routes/tidy/auto/index.js b/ui/lib/pki/addon/routes/tidy/auto/index.js index ccbc944702..533ddaefb3 100644 --- a/ui/lib/pki/addon/routes/tidy/auto/index.js +++ b/ui/lib/pki/addon/routes/tidy/auto/index.js @@ -8,7 +8,6 @@ import { service } from '@ember/service'; export default class TidyAutoIndexRoute extends Route { @service secretMountPath; - @service store; // inherits model from tidy/auto diff --git a/ui/lib/pki/addon/routes/tidy/manual.js b/ui/lib/pki/addon/routes/tidy/manual.js index 174bcc413f..af41f74155 100644 --- a/ui/lib/pki/addon/routes/tidy/manual.js +++ b/ui/lib/pki/addon/routes/tidy/manual.js @@ -8,7 +8,6 @@ import { service } from '@ember/service'; import PkiTidyForm from 'vault/forms/secrets/pki/tidy'; export default class PkiTidyManualRoute extends Route { - @service store; @service secretMountPath; model() { diff --git a/ui/lib/pki/addon/templates/overview.hbs b/ui/lib/pki/addon/templates/overview.hbs index 1e213e313a..b676cc5c89 100644 --- a/ui/lib/pki/addon/templates/overview.hbs +++ b/ui/lib/pki/addon/templates/overview.hbs @@ -16,7 +16,12 @@ {{#if this.model.hasConfig}} - + {{else}} { @service('app-router') declare readonly router: RouterService; - @service declare readonly pagination: PaginationService; @service declare readonly flashMessages: FlashMessageService; @service declare readonly api: ApiService; @service declare readonly capabilities: CapabilitiesService; diff --git a/ui/lib/sync/addon/components/secrets/page/destinations.ts b/ui/lib/sync/addon/components/secrets/page/destinations.ts index 29131ff872..5a2e2def2a 100644 --- a/ui/lib/sync/addon/components/secrets/page/destinations.ts +++ b/ui/lib/sync/addon/components/secrets/page/destinations.ts @@ -13,7 +13,6 @@ import { next } from '@ember/runloop'; import apiMethodResolver from 'sync/utils/api-method-resolver'; import type RouterService from '@ember/routing/router-service'; -import type PaginationService from 'vault/services/pagination'; import type FlashMessageService from 'vault/services/flash-messages'; import type { CapabilitiesMap, EngineOwner } from 'vault/app-types'; import type { DestinationName, DestinationType, ListDestination } from 'vault/sync'; @@ -30,7 +29,6 @@ interface Args { export default class SyncSecretsDestinationsPageComponent extends Component { @service('app-router') declare readonly router: RouterService; - @service declare readonly pagination: PaginationService; @service declare readonly flashMessages: FlashMessageService; @service declare readonly api: ApiService; diff --git a/ui/lib/sync/addon/components/secrets/page/destinations/destination/sync.ts b/ui/lib/sync/addon/components/secrets/page/destinations/destination/sync.ts index 18f224a5a3..33ee077eaf 100644 --- a/ui/lib/sync/addon/components/secrets/page/destinations/destination/sync.ts +++ b/ui/lib/sync/addon/components/secrets/page/destinations/destination/sync.ts @@ -13,7 +13,6 @@ import { keyIsFolder } from 'core/utils/key-utils'; import type { Destination } from 'vault/sync'; import type RouterService from '@ember/routing/router-service'; import type ApiService from 'vault/services/api'; -import type PaginationService from 'vault/services/pagination'; import type FlashMessageService from 'vault/services/flash-messages'; import type { SearchSelectOption } from 'vault/app-types'; @@ -25,7 +24,6 @@ export default class DestinationSyncPageComponent extends Component { @service('app-router') declare readonly router: RouterService; @service declare readonly api: ApiService; @service declare readonly flashMessages: FlashMessageService; - @service declare readonly pagination: PaginationService; constructor(owner: unknown, args: Args) { super(owner, args); diff --git a/ui/lib/sync/addon/engine.js b/ui/lib/sync/addon/engine.js index cc58bcabf7..ee40c18be2 100644 --- a/ui/lib/sync/addon/engine.js +++ b/ui/lib/sync/addon/engine.js @@ -14,16 +14,7 @@ export default class SyncEngine extends Engine { modulePrefix = modulePrefix; Resolver = Resolver; dependencies = { - services: [ - 'flash-messages', - 'flags', - 'app-router', - 'store', - 'api', - 'capabilities', - 'pagination', - 'version', - ], + services: ['flash-messages', 'flags', 'app-router', 'store', 'api', 'capabilities', 'version'], externalRoutes: ['kvSecretOverview', 'clientCountOverview'], }; } diff --git a/ui/lib/sync/addon/routes/secrets/destinations/index.ts b/ui/lib/sync/addon/routes/secrets/destinations/index.ts index ec302bf037..dd89bdba22 100644 --- a/ui/lib/sync/addon/routes/secrets/destinations/index.ts +++ b/ui/lib/sync/addon/routes/secrets/destinations/index.ts @@ -9,7 +9,6 @@ import { SystemListSyncDestinationsListEnum } from '@hashicorp/vault-client-type import { listDestinationsTransform } from 'sync/utils/api-transforms'; import { paginate } from 'core/utils/paginate-list'; -import type PaginationService from 'vault/services/pagination'; import type RouterService from '@ember/routing/router-service'; import type { ModelFrom } from 'vault/vault/route'; import type SyncDestinationModel from 'vault/vault/models/sync/destination'; @@ -38,7 +37,6 @@ interface SyncSecretsDestinationsController extends Controller { } export default class SyncSecretsDestinationsIndexRoute extends Route { - @service declare readonly pagination: PaginationService; @service declare readonly api: ApiService; @service('app-router') declare readonly router: RouterService; @service declare readonly capabilities: CapabilitiesService; diff --git a/ui/tests/acceptance/pki/pki-engine-workflow-test.js b/ui/tests/acceptance/pki/pki-engine-workflow-test.js index 6e1554df74..bccc7a1a05 100644 --- a/ui/tests/acceptance/pki/pki-engine-workflow-test.js +++ b/ui/tests/acceptance/pki/pki-engine-workflow-test.js @@ -15,7 +15,7 @@ import { runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands'; import { create } from 'ember-cli-page-object'; import flashMessage from 'vault/tests/pages/components/flash-message'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; -import { CERTIFICATES, clearRecords } from 'vault/tests/helpers/pki/pki-helpers'; +import { CERTIFICATES } from 'vault/tests/helpers/pki/pki-helpers'; import { PKI_CONFIGURE_CREATE, PKI_CONFIG_EDIT, @@ -36,13 +36,11 @@ module('Acceptance | pki workflow', function (hooks) { setupApplicationTest(hooks); hooks.beforeEach(async function () { - this.store = this.owner.lookup('service:store'); await login(); // Setup PKI engine const mountPath = `pki-workflow-${uuidv4()}`; await enablePage.enable('pki', mountPath); this.mountPath = mountPath; - clearRecords(this.store); }); hooks.afterEach(async function () { @@ -56,7 +54,6 @@ module('Acceptance | pki workflow', function (hooks) { await login(); const pki_admin_policy = adminPolicy(this.mountPath, 'roles'); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd(`pki-admin-${this.mountPath}`, pki_admin_policy)); - clearRecords(this.store); }); test('empty state messages are correct when PKI not configured', async function (assert) { @@ -119,7 +116,6 @@ module('Acceptance | pki workflow', function (hooks) { tokenWithPolicyCmd(`pki-editor-${this.mountPath}`, pki_editor_policy) ); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd(`pki-admin-${this.mountPath}`, pki_admin_policy)); - clearRecords(this.store); }); test('shows correct items if user has all permissions', async function (assert) { @@ -269,7 +265,6 @@ module('Acceptance | pki workflow', function (hooks) { this.pkiKeyReader = await runCmd(tokenWithPolicyCmd(`pki-reader-${this.mountPath}`, pki_reader_policy)); this.pkiKeyEditor = await runCmd(tokenWithPolicyCmd(`pki-editor-${this.mountPath}`, pki_editor_policy)); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd(`pki-admin-${this.mountPath}`, pki_admin_policy)); - clearRecords(this.store); }); test('shows correct items if user has all permissions', async function (assert) { @@ -391,7 +386,6 @@ module('Acceptance | pki workflow', function (hooks) { await runCmd([ `write ${this.mountPath}/root/generate/internal common_name="Hashicorp Test" name="Hashicorp Test"`, ]); - clearRecords(this.store); }); test('lists the correct issuer metadata info', async function (assert) { await login(this.pkiAdminToken); diff --git a/ui/tests/acceptance/pki/pki-overview-test.js b/ui/tests/acceptance/pki/pki-overview-test.js index 0e912b3f9f..be103b3749 100644 --- a/ui/tests/acceptance/pki/pki-overview-test.js +++ b/ui/tests/acceptance/pki/pki-overview-test.js @@ -11,7 +11,6 @@ import { login } from 'vault/tests/helpers/auth/auth-helpers'; import enablePage from 'vault/tests/pages/settings/mount-secret-backend'; import { click, currentURL, currentRouteName, visit } from '@ember/test-helpers'; import { runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands'; -import { clearRecords } from 'vault/tests/helpers/pki/pki-helpers'; import { PKI_OVERVIEW } from 'vault/tests/helpers/pki/pki-selectors'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; const { overviewCard } = GENERAL; @@ -20,7 +19,6 @@ module('Acceptance | pki overview', function (hooks) { setupApplicationTest(hooks); hooks.beforeEach(async function () { - this.store = this.owner.lookup('service:store'); await login(); // Setup PKI engine const mountPath = `pki-${uuidv4()}`; @@ -46,8 +44,6 @@ module('Acceptance | pki overview', function (hooks) { this.pkiRolesList = await runCmd(tokenWithPolicyCmd('pki-roles-list', pki_roles_list_policy)); this.pkiIssuersList = await runCmd(tokenWithPolicyCmd('pki-issuers-list', pki_issuers_list_policy)); this.pkiAdminToken = await runCmd(tokenWithPolicyCmd('pki-admin', pki_admin_policy)); - - clearRecords(this.store); }); hooks.afterEach(async function () { diff --git a/ui/tests/helpers/pki/pki-helpers.ts b/ui/tests/helpers/pki/pki-helpers.ts index e3f6ea78d3..07b36f3bab 100644 --- a/ui/tests/helpers/pki/pki-helpers.ts +++ b/ui/tests/helpers/pki/pki-helpers.ts @@ -3,28 +3,8 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import type Store from '@ember-data/store'; - export const PKI_BASE_URL = `/vault/cluster/secrets/backend/pki/roles`; -// Clears pki-related data and capabilities so that admin -// capabilities from setup don't rollover -export function clearRecords(store: Store) { - store.unloadAll('pki/action'); - store.unloadAll('pki/issuer'); - store.unloadAll('pki/key'); - store.unloadAll('pki/role'); - store.unloadAll('pki/sign-intermediate'); - store.unloadAll('pki/tidy'); - store.unloadAll('pki/config/urls'); - store.unloadAll('pki/config/crl'); - store.unloadAll('pki/config/cluster'); - store.unloadAll('pki/config/acme'); - store.unloadAll('pki/certificate/generate'); - store.unloadAll('pki/certificate/sign'); - store.unloadAll('capabilities'); -} - export const configCapabilities = { canImportBundle: true, canSetAcme: true, diff --git a/ui/tests/integration/components/dashboard/quick-actions-card-test.js b/ui/tests/integration/components/dashboard/quick-actions-card-test.js index c6b6c39e07..44e04cbeae 100644 --- a/ui/tests/integration/components/dashboard/quick-actions-card-test.js +++ b/ui/tests/integration/components/dashboard/quick-actions-card-test.js @@ -12,36 +12,54 @@ import { fillIn } from '@ember/test-helpers'; import { selectChoose } from 'ember-power-select/test-support'; import sinon from 'sinon'; import { DASHBOARD } from 'vault/tests/helpers/components/dashboard/dashboard-selectors'; +import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { setRunOptions } from 'ember-a11y-testing/test-support'; import { setupMirage } from 'ember-cli-mirage/test-support'; +import SecretsEngineResource from 'vault/resources/secrets/engine'; +import { getErrorResponse } from 'vault/tests/helpers/api/error-response'; module('Integration | Component | dashboard/quick-actions-card', function (hooks) { setupRenderingTest(hooks); setupMirage(hooks); hooks.beforeEach(function () { - const store = this.owner.lookup('service:store'); + this.api = this.owner.lookup('service:api').secrets; const router = this.owner.lookup('service:router'); this.transitionStub = sinon.stub(router, 'transitionTo'); - const models = [ - { accessor: 'kubernetes_f3400dee', path: 'kubernetes-test/', type: 'kubernetes' }, - { accessor: 'database_f3400dee', path: 'database-test/', type: 'database' }, - { accessor: 'pki_i1234dd', path: 'apki-test/', type: 'pki' }, - { accessor: 'secrets_j2350ii', path: 'secrets-test/', type: 'kv' }, - { accessor: 'nomad_123hh', path: 'nomad/', type: 'nomad' }, - { accessor: 'pki_f3400dee', path: 'pki-0-test/', type: 'pki' }, - { accessor: 'pki_i1234dd', path: 'pki-1-test/', description: 'pki-1-path-description', type: 'pki' }, - { accessor: 'secrets_j2350ii', path: 'kv-v2-test/', options: { version: 2 }, type: 'kv' }, - { accessor: 'secrets_j2350ii', path: 'kv-v1-test/', options: { version: 1 }, type: 'kv' }, + this.secretsEngines = [ + new SecretsEngineResource({ + accessor: 'kubernetes_f3400dee', + path: 'kubernetes-test/', + type: 'kubernetes', + }), + new SecretsEngineResource({ accessor: 'database_f3400dee', path: 'database-test/', type: 'database' }), + new SecretsEngineResource({ accessor: 'pki_i1234dd', path: 'apki-test/', type: 'pki' }), + new SecretsEngineResource({ accessor: 'secrets_j2350ii', path: 'secrets-test/', type: 'kv' }), + new SecretsEngineResource({ accessor: 'nomad_123hh', path: 'nomad/', type: 'nomad' }), + new SecretsEngineResource({ accessor: 'pki_f3400dee', path: 'pki-0-test/', type: 'pki' }), + new SecretsEngineResource({ + accessor: 'pki_i1234dd', + path: 'pki-1-test/', + description: 'pki-1-path-description', + type: 'pki', + }), + new SecretsEngineResource({ + accessor: 'secrets_j2350ii', + path: 'kv-v2-test/', + options: { version: 2 }, + type: 'kv', + }), + new SecretsEngineResource({ + accessor: 'secrets_j2350ii', + path: 'kv-v1-test/', + options: { version: 1 }, + type: 'kv', + }), ]; - models.forEach((modelData) => { - store.pushPayload('secret-engine', { modelName: 'secret-engine', data: modelData }); - }); - this.secretsEngines = store.peekAll('secret-engine', {}); - this.renderComponent = () => { - return render(hbs``); - }; + + this.renderComponent = () => + render(hbs``); setRunOptions({ rules: { @@ -74,15 +92,19 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks test('it selects a pki role and issues a leaf certificate', async function (assert) { const backend = 'pki-0-test'; - this.server.get(`/${backend}/roles`, () => ({ data: { keys: ['some-role'] } })); - await this.renderComponent(); + this.apiStub = sinon.stub(this.api, 'pkiListRoles').resolves({ keys: ['some-role'] }); + await this.renderComponent(); await selectChoose(DASHBOARD.searchSelect('secrets-engines'), backend); await fillIn(DASHBOARD.selectEl, 'Issue certificate'); + + assert.true(this.apiStub.calledWith(backend, true), 'Request made to fetch options'); assert.dom(DASHBOARD.emptyState('quick-actions')).doesNotExist(); assert.dom(DASHBOARD.subtitle('param')).hasText('Role to use'); + await selectChoose(DASHBOARD.searchSelect('params'), 'some-role'); assert.dom(DASHBOARD.actionButton('Issue leaf certificate')).exists({ count: 1 }); + await click(DASHBOARD.actionButton('Issue leaf certificate')); const [route, backendParam, roleParam] = this.transitionStub.lastCall.args; assert.strictEqual( @@ -96,16 +118,20 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks test('it views a pki certificate', async function (assert) { const backend = 'pki-0-test'; - this.server.get(`/${backend}/certs`, () => ({ data: { keys: ['51:1c:39:42:ba'] } })); - await this.renderComponent(); + this.apiStub = sinon.stub(this.api, 'pkiListCerts').resolves({ keys: ['51:1c:39:42:ba'] }); + await this.renderComponent(); await selectChoose(DASHBOARD.searchSelect('secrets-engines'), backend); await fillIn(DASHBOARD.selectEl, 'View certificate'); + + assert.true(this.apiStub.calledWith(backend, true), 'Request made to fetch options'); assert.dom(DASHBOARD.emptyState('quick-actions')).doesNotExist(); assert.dom(DASHBOARD.subtitle('param')).hasText('Certificate serial number'); assert.dom(DASHBOARD.actionButton('View certificate')).exists({ count: 1 }); + await selectChoose(DASHBOARD.searchSelect('params'), '.ember-power-select-option', 0); await click(DASHBOARD.actionButton('View certificate')); + const [route, backendParam, certParam] = this.transitionStub.lastCall.args; assert.strictEqual( route, @@ -118,18 +144,22 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks test('it views a pki issuer', async function (assert) { const backend = 'pki-0-test'; - this.server.get(`/${backend}/issuers`, () => { - return { data: { key_info: { '101709a1': { issuer_name: 'test' } }, keys: ['101709a1'] } }; - }); - await this.renderComponent(); + this.apiStub = sinon + .stub(this.api, 'pkiListIssuers') + .resolves({ key_info: { '101709a1': { issuer_name: 'test' } }, keys: ['101709a1'] }); + await this.renderComponent(); await selectChoose(DASHBOARD.searchSelect('secrets-engines'), backend); await fillIn(DASHBOARD.selectEl, 'View issuer'); + + assert.true(this.apiStub.calledWith(backend, true), 'Request made to fetch options'); assert.dom(DASHBOARD.emptyState('quick-actions')).doesNotExist(); assert.dom(DASHBOARD.subtitle('param')).hasText('Issuer'); assert.dom(DASHBOARD.actionButton('View issuer')).exists({ count: 1 }); + await selectChoose(DASHBOARD.searchSelect('params'), '.ember-power-select-option', 0); await click(DASHBOARD.actionButton('View issuer')); + const [route, backendParam, issuerParam] = this.transitionStub.lastCall.args; assert.strictEqual( route, @@ -142,16 +172,27 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks test('it selects a role and generates credentials for a database', async function (assert) { const backend = 'database-test'; + this.staticStub = sinon.stub(this.api, 'databaseListStaticRoles').resolves({ keys: ['static-role'] }); + this.dynamicStub = sinon.stub(this.api, 'databaseListRoles').resolves({ keys: ['dynamic-role'] }); this.server.get(`/${backend}/roles`, () => ({ data: { keys: ['my-role'] } })); await this.renderComponent(); await selectChoose(DASHBOARD.searchSelect('secrets-engines'), backend); await fillIn(DASHBOARD.selectEl, 'Generate credentials for database'); + + assert.true(this.staticStub.calledWith(backend, true), 'Request made to fetch static roles'); + assert.true(this.dynamicStub.calledWith(backend, true), 'Request made to fetch dynamic roles'); assert.dom(DASHBOARD.emptyState('quick-actions')).doesNotExist(); assert.dom(DASHBOARD.subtitle('param')).hasText('Role to use'); assert.dom(DASHBOARD.actionButton('Generate credentials')).exists({ count: 1 }); - await selectChoose(DASHBOARD.searchSelect('params'), '.ember-power-select-option', 0); + + await clickTrigger(DASHBOARD.searchSelect('params')); + assert.dom(GENERAL.searchSelect.option(0)).hasText('static-role', 'Static roles render in dropdown'); + assert.dom(GENERAL.searchSelect.option(1)).hasText('dynamic-role', 'Dynamic roles render in dropdown'); + + await selectChoose(DASHBOARD.searchSelect('params'), '.ember-power-select-option', 1); await click(DASHBOARD.actionButton('Generate credentials')); + const [route, backendParam, issuerParam] = this.transitionStub.lastCall.args; assert.strictEqual( route, @@ -159,7 +200,7 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks 'transition is called with expected route' ); assert.strictEqual(backendParam, backend, 'transition has expected backend param'); - assert.strictEqual(issuerParam, 'my-role', 'transition has expected role param'); + assert.strictEqual(issuerParam, 'dynamic-role', 'transition has expected role param'); }); test('it should show correct actions for kv', async function (assert) { @@ -176,4 +217,46 @@ module('Integration | Component | dashboard/quick-actions-card', function (hooks assert.dom(DASHBOARD.kvSearchSelect).exists('Shows option to search fo KVv2 secret'); assert.dom(DASHBOARD.actionButton('Read secrets')).exists({ count: 1 }); }); + + test('it should handle 404 errors when fetching options', async function (assert) { + let backend = 'database-test'; + this.dbStaticStub = sinon.stub(this.api, 'databaseListStaticRoles').rejects(getErrorResponse()); + this.dbDynamicStub = sinon.stub(this.api, 'databaseListRoles').resolves({ keys: ['dynamic-role'] }); + this.pkiStub = sinon.stub(this.api, 'pkiListIssuers').rejects(getErrorResponse()); + + await this.renderComponent(); + await selectChoose(DASHBOARD.searchSelect('secrets-engines'), backend); + await fillIn(DASHBOARD.selectEl, 'Generate credentials for database'); + await clickTrigger(DASHBOARD.searchSelect('params')); + assert + .dom(GENERAL.searchSelect.options) + .exists({ count: 1 }, 'Roles render in list when fetching one type fails'); + assert + .dom(GENERAL.searchSelect.option(0)) + .hasText('dynamic-role', 'Dynamic roles render when static fetch fails'); + + backend = 'pki-0-test'; + await click(GENERAL.searchSelect.removeSelected); + await selectChoose(DASHBOARD.searchSelect('secrets-engines'), backend); + await fillIn(DASHBOARD.selectEl, 'View issuer'); + await clickTrigger(DASHBOARD.searchSelect('params')); + assert + .dom(GENERAL.searchSelect.option(0)) + .hasText('Type to search', 'Placeholder renders when no options are returned'); + }); + + test('it should display fetch errors other than 404 in flash message', async function (assert) { + const backend = 'pki-0-test'; + const error = { errors: ['not a 404'] }; + this.apiStub = sinon.stub(this.api, 'pkiListIssuers').rejects(getErrorResponse(error, 500)); + this.flashStub = sinon.stub(this.owner.lookup('service:flash-messages'), 'danger'); + + await this.renderComponent(); + await selectChoose(DASHBOARD.searchSelect('secrets-engines'), backend); + await fillIn(DASHBOARD.selectEl, 'View issuer'); + assert.true( + this.flashStub.calledWith('Error fetching options for selected action: not a 404'), + 'Flash message displays on fetch error' + ); + }); }); diff --git a/ui/tests/integration/components/pki-paginated-list-test.js b/ui/tests/integration/components/pki-paginated-list-test.js index d23e606099..de7f85c9bb 100644 --- a/ui/tests/integration/components/pki-paginated-list-test.js +++ b/ui/tests/integration/components/pki-paginated-list-test.js @@ -15,149 +15,115 @@ module('Integration | Component | pki-paginated-list', function (hooks) { setupEngine(hooks, 'pki'); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.secretMountPath.currentPath = 'pki-test'; - this.store.pushPayload('pki/key', { - modelName: 'pki/key', - data: { + this.secretMountPath = this.owner.lookup('service:secret-mount-path').update('pki-test'); + this.list = [ + { key_id: '724862ff-6438-bad0-b598-77a6c7f4e934', key_type: 'ec', key_name: 'test-key', }, - }); - this.store.pushPayload('pki/key', { - modelName: 'pki/key', - data: { + { key_id: '9fdddf12-9ce3-0268-6b34-dc1553b00175', key_type: 'rsa', key_name: 'another-key', }, - }); - // mimic what happens in lazyPaginatedQuery - const keyModels = this.store.peekAll('pki/key'); - keyModels.meta = STANDARD_META; - this.list = keyModels; - const emptyList = this.store.peekAll('pki/foo'); - emptyList.meta = { + ]; + // mimic what happens in paginate util + this.list.meta = STANDARD_META; + this.emptyList = []; + this.emptyList.meta = { meta: { total: 0, currentPage: 1, pageSize: 100, }, }; - this.emptyList = emptyList; + this.hasConfig = true; + + this.renderComponent = () => + render( + hbs` + + <:actions> +
Action
+ + <:description> + Description goes here + + <:list as |items|> + {{#each items as |item|}} +
{{item.key_name}}
+ {{/each}} + + <:empty> + No items found + + <:configure> + Not configured + +
+ `, + { owner: this.engine } + ); }); test('it renders correctly with a list', async function (assert) { - this.set('hasConfig', null); - await render( - hbs` - - <:list as |items|> - {{#each items as |item|}} -
{{item.keyName}}
- {{/each}} - - <:empty> - No items found - - <:configure> - Not configured - -
- `, - { owner: this.engine } - ); + this.hasConfig = null; + await this.renderComponent(); - assert.dom(this.element).doesNotContainText('Not configured', 'defaults to has config if not boolean'); - assert.dom(this.element).doesNotContainText('No items found', 'does not render empty state'); + assert.dom('[data-test-no-config]').doesNotExist('defaults to has config if not boolean'); + assert.dom('[data-test-empty]').doesNotExist('No items found', 'does not render empty state'); assert.dom('[data-test-item]').exists({ count: 2 }, 'lists the items'); assert.dom('[data-test-item="724862ff-6438-bad0-b598-77a6c7f4e934"]').hasText('test-key'); assert.dom('[data-test-item="9fdddf12-9ce3-0268-6b34-dc1553b00175"]').hasText('another-key'); assert.dom('[data-test-pagination]').exists('shows pagination'); - await this.set('hasConfig', false); - assert.dom(this.element).doesNotContainText('No items found', 'does not render empty state'); - assert.dom(this.element).containsText('Not configured', 'shows configuration prompt'); + + this.hasConfig = false; + await this.renderComponent(); + assert.dom('[data-test-empty]').doesNotExist('No items found', 'does not render empty state'); + assert.dom('[data-test-no-config]').hasText('Not configured', 'shows configuration prompt'); assert.dom('[data-test-item]').doesNotExist('Does not show list items when not configured'); assert.dom('[data-test-pagination]').doesNotExist('hides pagination'); }); test('it renders correctly with an empty list', async function (assert) { - this.set('hasConfig', true); - await render( - hbs` - - <:list> - List item - - <:empty> - No items found - - <:configure> - Not configured - - - `, - { owner: this.engine } - ); - - assert.dom(this.element).doesNotContainText('list item', 'does not render list items if empty'); - assert.dom(this.element).hasText('No items found', 'shows empty block'); - assert.dom(this.element).doesNotContainText('Not configured', 'does not show configuration prompt'); + this.list = this.emptyList; + await this.renderComponent(); + assert.dom('[data-test-item]').doesNotExist('does not render list items if empty'); + assert.dom('[data-test-empty]').hasText('No items found', 'shows empty block'); + assert.dom('[data-test-no-config]').doesNotExist('does not show configuration prompt'); assert.dom('[data-test-pagination]').doesNotExist('hides pagination'); - await this.set('hasConfig', false); - assert.dom(this.element).doesNotContainText('list item', 'does not render list items if empty'); - assert.dom(this.element).doesNotContainText('No items found', 'does not show empty state'); - assert.dom(this.element).hasText('Not configured', 'shows configuration prompt'); + + this.hasConfig = false; + await this.renderComponent(); + assert.dom('[data-test-item]').doesNotExist('does not render list items if empty'); + assert.dom('[data-test-empty]').doesNotExist('does not show empty state'); + assert.dom('[data-test-no-config]').hasText('Not configured', 'shows configuration prompt'); assert.dom('[data-test-pagination]').doesNotExist('hides pagination'); }); test('it renders actions, description, pagination', async function (assert) { - this.set('hasConfig', true); - this.set('model', this.list); - await render( - hbs` - - <:actions> -
Action
- - <:description> - Description goes here - - <:list> - List items - - <:empty> - No items found - - <:configure> - Not configured - -
- `, - { owner: this.engine } - ); + await this.renderComponent(); assert .dom('[data-test-button]') .includesText('Action', 'Renders actions in toolbar when list and config'); assert - .dom(this.element) - .includesText('Description goes here', 'renders description when list and config'); + .dom('[data-test-description]') + .hasText('Description goes here', 'renders description when list and config'); assert.dom('[data-test-pagination]').exists('shows pagination when list and config'); - this.set('model', this.emptyList); + this.list = this.emptyList; + await this.renderComponent(); assert .dom('[data-test-button]') .hasText('Action', 'Renders actions in toolbar when empty list and config'); - assert - .dom(this.element) - .doesNotIncludeText('Description goes here', 'hides description when empty list and config'); + assert.dom('[data-test-description]').doesNotExist('hides description when empty list and config'); assert.dom('[data-test-pagination]').doesNotExist('hides pagination when empty list and config'); - this.set('hasConfig', false); + this.hasConfig = false; + await this.renderComponent(); assert.dom('[data-test-button]').hasText('Action', 'Renders actions in toolbar when no config'); - assert.dom(this.element).doesNotIncludeText('Description goes here', 'hides description when no config'); + assert.dom('[data-test-description]').doesNotExist('hides description when no config'); assert.dom('[data-test-pagination]').doesNotExist('hides pagination when no config'); }); }); diff --git a/ui/tests/integration/components/pki/page/pki-issuer-generate-intermediate-test.js b/ui/tests/integration/components/pki/page/pki-issuer-generate-intermediate-test.js index 9b3adfecbe..da247c458c 100644 --- a/ui/tests/integration/components/pki/page/pki-issuer-generate-intermediate-test.js +++ b/ui/tests/integration/components/pki/page/pki-issuer-generate-intermediate-test.js @@ -5,15 +5,12 @@ import { module, test } from 'qunit'; import { click, fillIn, render } from '@ember/test-helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; import { hbs } from 'ember-cli-htmlbars'; import { setupEngine } from 'ember-engines/test-support'; -import { Response } from 'miragejs'; -import { v4 as uuidv4 } from 'uuid'; - import { setupRenderingTest } from 'vault/tests/helpers'; -import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; +import sinon from 'sinon'; +import { getErrorResponse } from 'vault/tests/helpers/api/error-response'; /** * this test is for the page component only. A separate test is written for the form rendered @@ -21,58 +18,58 @@ import { GENERAL } from 'vault/tests/helpers/general-selectors'; module('Integration | Component | page/pki-issuer-generate-intermediate', function (hooks) { setupRenderingTest(hooks); setupEngine(hooks, 'pki'); - setupMirage(hooks); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); this.breadcrumbs = [{ label: 'something' }]; - this.model = this.store.createRecord('pki/action', { - actionType: 'generate-csr', - }); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.secretMountPath.currentPath = 'pki-component'; - this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub()); + this.backend = 'pki-component'; + this.secretMountPath = this.owner.lookup('service:secret-mount-path').update(this.backend); + + this.generateStub = sinon + .stub(this.owner.lookup('service:api').secrets, 'pkiIssuersGenerateIntermediate') + .resolves(); + this.capabilitiesStub = sinon + .stub(this.owner.lookup('service:capabilities'), 'for') + .resolves({ canCreate: true }); + + this.renderComponent = () => + render(hbs``, { + owner: this.engine, + }); }); test('it renders correct title before and after submit', async function (assert) { - assert.expect(3); - this.server.post(`/pki-component/issuers/generate/intermediate/internal`, () => { - assert.true(true, 'Issuers endpoint called'); - return { - request_id: uuidv4(), - data: { - csr: '------BEGIN CERTIFICATE------', - key_id: 'some-key-id', - }, - }; - }); + assert.expect(4); - await render( - hbs``, - { - owner: this.engine, - } - ); + await this.renderComponent(); assert.dom(GENERAL.title).hasText('Generate intermediate CSR'); - await fillIn(GENERAL.inputByAttr('type'), 'internal'); + + const { backend } = this; + const type = 'internal'; + await fillIn(GENERAL.inputByAttr('type'), type); await fillIn(GENERAL.inputByAttr('common_name'), 'foobar'); await click('[data-test-submit]'); + + const payload = { + common_name: 'foobar', + format: 'pem', + key_type: 'rsa', + not_before_duration: 30, + private_key_format: 'der', + }; + assert.true( + this.capabilitiesStub.calledWith('pkiIssuersGenerateIntermediate', { backend, type }), + 'Capabilities checked for api path' + ); + assert.true(this.generateStub.calledWith(type, backend, payload), 'API called with correct params'); assert.dom(GENERAL.title).hasText('View Generated CSR'); }); test('it does not update title if API response is an error', async function (assert) { assert.expect(2); - this.server.post( - '/pki-component/issuers/generate/intermediate/internal', - () => new Response(403, {}, { errors: ['API returns this error'] }) - ); - await render( - hbs``, - { - owner: this.engine, - } - ); + this.generateStub.rejects(getErrorResponse({ errors: ['API returns this error'] }, 403)); + + await this.renderComponent(); assert.dom(GENERAL.title).hasText('Generate intermediate CSR'); // Fill in await fillIn(GENERAL.inputByAttr('type'), 'internal'); diff --git a/ui/tests/integration/components/pki/page/pki-issuer-import-test.js b/ui/tests/integration/components/pki/page/pki-issuer-import-test.js index 4cf4d7367f..77fa5ff5f0 100644 --- a/ui/tests/integration/components/pki/page/pki-issuer-import-test.js +++ b/ui/tests/integration/components/pki/page/pki-issuer-import-test.js @@ -5,15 +5,13 @@ import { module, test } from 'qunit'; import { click, fillIn, render } from '@ember/test-helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; import { hbs } from 'ember-cli-htmlbars'; import { setupEngine } from 'ember-engines/test-support'; -import { Response } from 'miragejs'; -import { v4 as uuidv4 } from 'uuid'; import { setupRenderingTest } from 'vault/tests/helpers'; -import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { PKI_CONFIGURE_CREATE } from 'vault/tests/helpers/pki/pki-selectors'; +import sinon from 'sinon'; +import { getErrorResponse } from 'vault/tests/helpers/api/error-response'; /** * this test is for the page component only. A separate test is written for the form rendered @@ -21,46 +19,43 @@ import { PKI_CONFIGURE_CREATE } from 'vault/tests/helpers/pki/pki-selectors'; module('Integration | Component | page/pki-issuer-import', function (hooks) { setupRenderingTest(hooks); setupEngine(hooks, 'pki'); - setupMirage(hooks); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); this.breadcrumbs = [{ label: 'something' }]; - this.model = this.store.createRecord('pki/action', { - actionType: 'generate-csr', - }); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.secretMountPath.currentPath = 'pki-component'; - this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub()); + this.backend = 'pki-component'; + this.secretMountPath = this.owner.lookup('service:secret-mount-path').update(this.backend); + + this.importStub = sinon + .stub(this.owner.lookup('service:api').secrets, 'pkiIssuersImportBundle') + .resolves({}); + + this.renderComponent = () => + render(hbs``, { + owner: this.engine, + }); }); test('it renders correct title before and after submit', async function (assert) { assert.expect(3); - this.server.post(`/pki-component/issuers/import/bundle`, () => { - assert.true(true, 'Import endpoint called'); - return { - request_id: uuidv4(), - data: {}, - }; - }); - await render(hbs``, { - owner: this.engine, - }); + await this.renderComponent(); assert.dom(GENERAL.title).hasText('Import a CA'); + + const pem_bundle = 'dummy-pem-bundle'; await click(GENERAL.textToggle); - await fillIn(GENERAL.maskedInput, 'dummy-pem-bundle'); + await fillIn(GENERAL.maskedInput, pem_bundle); await click(PKI_CONFIGURE_CREATE.importSubmit); + + assert.true(this.importStub.calledWith(this.backend, { pem_bundle }), 'API called with correct params'); assert.dom(GENERAL.title).hasText('View imported items'); }); test('it does not update title if API response is an error', async function (assert) { assert.expect(2); - this.server.post(`/pki-component/issuers/import/bundle`, () => new Response(404, {}, { errors: [] })); - await render(hbs``, { - owner: this.engine, - }); + this.importStub.rejects(getErrorResponse()); + + await this.renderComponent(); assert.dom(GENERAL.title).hasText('Import a CA'); // Fill in await click(GENERAL.textToggle); diff --git a/ui/tests/integration/components/pki/page/pki-overview-test.js b/ui/tests/integration/components/pki/page/pki-overview-test.js index b8114bfce3..cc63ea979b 100644 --- a/ui/tests/integration/components/pki/page/pki-overview-test.js +++ b/ui/tests/integration/components/pki/page/pki-overview-test.js @@ -8,7 +8,6 @@ import { setupRenderingTest } from 'ember-qunit'; import { render } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import { setupEngine } from 'ember-engines/test-support'; -import { setupMirage } from 'ember-cli-mirage/test-support'; import { PKI_OVERVIEW } from 'vault/tests/helpers/pki/pki-selectors'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; @@ -16,31 +15,25 @@ const { overviewCard } = GENERAL; module('Integration | Component | Page::PkiOverview', function (hooks) { setupRenderingTest(hooks); setupEngine(hooks, 'pki'); - setupMirage(hooks); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.secretMountPath.currentPath = 'pki-test'; + this.backend = 'pki-test'; + this.secretMountPath = this.owner.lookup('service:secret-mount-path').update(this.backend); - this.store.createRecord('pki/issuer', { issuerId: 'abcd-efgh' }); - this.store.createRecord('pki/issuer', { issuerId: 'ijkl-mnop' }); - this.store.createRecord('pki/role', { name: 'role-0' }); - this.store.createRecord('pki/role', { name: 'role-1' }); - this.store.createRecord('pki/role', { name: 'role-2' }); - this.store.createRecord('pki/certificate/base', { serialNumber: '22:2222:22222:2222' }); - this.store.createRecord('pki/certificate/base', { serialNumber: '33:3333:33333:3333' }); - - this.issuers = this.store.peekAll('pki/issuer'); - this.roles = this.store.peekAll('pki/role'); + this.issuers = ['abcd-efgh', 'ijkl-mnop']; + this.roles = ['role-0', 'role-1', 'role-2']; + this.certificates = ['22:2222:22222:2222', '33:3333:33333:3333']; this.engineId = 'pki'; + + this.renderComponent = () => + render( + hbs``, + { owner: this.engine } + ); }); test('shows the correct information on issuer card', async function (assert) { - await render( - hbs`,`, - { owner: this.engine } - ); + await this.renderComponent(); assert .dom(overviewCard.container('Issuers')) .hasText( @@ -49,20 +42,14 @@ module('Integration | Component | Page::PkiOverview', function (hooks) { }); test('shows the correct information on roles card', async function (assert) { - await render( - hbs`,`, - { owner: this.engine } - ); + await this.renderComponent(); assert .dom(overviewCard.container('Roles')) .hasText( 'Roles View roles The total number of roles in this PKI mount that have been created to generate certificates. 3' ); this.roles = 404; - await render( - hbs`,`, - { owner: this.engine } - ); + await this.renderComponent(); assert .dom(overviewCard.container('Roles')) .hasText( @@ -71,20 +58,14 @@ module('Integration | Component | Page::PkiOverview', function (hooks) { }); test('shows the input search fields for View Certificates card', async function (assert) { - await render( - hbs`,`, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(overviewCard.title('Issue certificate')).hasText('Issue certificate'); assert.dom(PKI_OVERVIEW.issueCertificateInput).exists(); assert.dom(PKI_OVERVIEW.issueCertificateButton).hasText('Issue'); }); test('shows the input search fields for Issue Certificates card', async function (assert) { - await render( - hbs`,`, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(overviewCard.title('View certificate')).hasText('View certificate'); assert.dom(PKI_OVERVIEW.viewCertificateInput).exists(); assert.dom(PKI_OVERVIEW.viewCertificateButton).hasText('View'); diff --git a/ui/tests/unit/adapters/pki/action-test.js b/ui/tests/unit/adapters/pki/action-test.js deleted file mode 100644 index eb7f06136d..0000000000 --- a/ui/tests/unit/adapters/pki/action-test.js +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; -import { CERTIFICATES } from 'vault/tests/helpers/pki/pki-helpers'; - -const { rootPem } = CERTIFICATES; - -module('Unit | Adapter | pki/action', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.backend = 'pki-test'; - this.secretMountPath.currentPath = this.backend; - this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub()); - this.emptyResponse = { - // Action adapter uses request_id as ember data id for response - request_id: '123', - data: {}, - }; - }); - - test('it exists', function (assert) { - const adapter = this.owner.lookup('adapter:pki/action'); - assert.ok(adapter); - }); - - module('actionType import', function (hooks) { - hooks.beforeEach(function () { - this.payload = { - pem_bundle: rootPem, - }; - }); - - test('it calls the correct endpoint when useIssuer = false', async function (assert) { - assert.expect(1); - - this.server.post(`${this.backend}/config/ca`, () => { - assert.ok(true, 'request made to correct endpoint on create'); - return this.emptyResponse; - }); - - await this.store - .createRecord('pki/action', this.payload) - .save({ adapterOptions: { actionType: 'import', useIssuer: false } }); - }); - - test('it calls the correct endpoint when useIssuer = true', async function (assert) { - assert.expect(1); - this.server.post(`${this.backend}/issuers/import/bundle`, () => { - assert.ok(true, 'request made to correct endpoint on create'); - return this.emptyResponse; - }); - - await this.store - .createRecord('pki/action', this.payload) - .save({ adapterOptions: { actionType: 'import', useIssuer: true } }); - }); - }); - - module('actionType generate-root', function () { - test('it calls the correct endpoint when useIssuer = false', async function (assert) { - assert.expect(4); - const adapterOptions = { adapterOptions: { actionType: 'generate-root', useIssuer: false } }; - this.server.post(`${this.backend}/root/generate/internal`, () => { - assert.ok(true, 'request made correctly when type = internal'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/root/generate/exported`, () => { - assert.ok(true, 'request made correctly when type = exported'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/root/generate/existing`, () => { - assert.ok(true, 'request made correctly when type = exising'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/root/generate/kms`, () => { - assert.ok(true, 'request made correctly when type = kms'); - return this.emptyResponse; - }); - - await this.store - .createRecord('pki/action', { - type: 'internal', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'exported', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'existing', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'kms', - }) - .save(adapterOptions); - }); - - test('it calls the correct endpoint when useIssuer = true', async function (assert) { - assert.expect(4); - const adapterOptions = { adapterOptions: { actionType: 'generate-root', useIssuer: true } }; - this.server.post(`${this.backend}/issuers/generate/root/internal`, () => { - assert.ok(true, 'request made correctly when type = internal'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/issuers/generate/root/exported`, () => { - assert.ok(true, 'request made correctly when type = exported'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/issuers/generate/root/existing`, () => { - assert.ok(true, 'request made correctly when type = exising'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/issuers/generate/root/kms`, () => { - assert.ok(true, 'request made correctly when type = kms'); - return this.emptyResponse; - }); - - await this.store - .createRecord('pki/action', { - type: 'internal', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'exported', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'existing', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'kms', - }) - .save(adapterOptions); - }); - }); - - module('actionType generate-csr', function () { - test('it calls the correct endpoint when useIssuer = false', async function (assert) { - assert.expect(4); - const adapterOptions = { adapterOptions: { actionType: 'generate-csr', useIssuer: false } }; - this.server.post(`${this.backend}/intermediate/generate/internal`, () => { - assert.ok(true, 'request made correctly when type = internal'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/intermediate/generate/exported`, () => { - assert.ok(true, 'request made correctly when type = exported'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/intermediate/generate/existing`, () => { - assert.ok(true, 'request made correctly when type = exising'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/intermediate/generate/kms`, () => { - assert.ok(true, 'request made correctly when type = kms'); - return this.emptyResponse; - }); - - await this.store - .createRecord('pki/action', { - type: 'internal', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'exported', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'existing', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'kms', - }) - .save(adapterOptions); - }); - - test('it calls the correct endpoint when useIssuer = true', async function (assert) { - assert.expect(4); - const adapterOptions = { adapterOptions: { actionType: 'generate-csr', useIssuer: true } }; - this.server.post(`${this.backend}/issuers/generate/intermediate/internal`, () => { - assert.ok(true, 'request made correctly when type = internal'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/issuers/generate/intermediate/exported`, () => { - assert.ok(true, 'request made correctly when type = exported'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/issuers/generate/intermediate/existing`, () => { - assert.ok(true, 'request made correctly when type = exising'); - return this.emptyResponse; - }); - this.server.post(`${this.backend}/issuers/generate/intermediate/kms`, () => { - assert.ok(true, 'request made correctly when type = kms'); - return this.emptyResponse; - }); - - await this.store - .createRecord('pki/action', { - type: 'internal', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'exported', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'existing', - }) - .save(adapterOptions); - await this.store - .createRecord('pki/action', { - type: 'kms', - }) - .save(adapterOptions); - }); - }); - - module('actionType sign-intermediate', function () { - test('it overrides backend when adapter options specify a mount', async function (assert) { - assert.expect(1); - const mount = 'foo'; - const issuerRef = 'ref'; - const adapterOptions = { - adapterOptions: { actionType: 'sign-intermediate', mount, issuerRef }, - }; - - this.server.post(`${mount}/issuer/${issuerRef}/sign-intermediate`, () => { - assert.ok(true, 'request made to correct mount'); - return this.emptyResponse; - }); - - await this.store - .createRecord('pki/action', { - csr: '---BEGIN REQUEST---', - }) - .save(adapterOptions); - }); - }); -}); diff --git a/ui/tests/unit/adapters/pki/certificate/base-test.js b/ui/tests/unit/adapters/pki/certificate/base-test.js deleted file mode 100644 index 191ad09138..0000000000 --- a/ui/tests/unit/adapters/pki/certificate/base-test.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; - -module('Unit | Adapter | pki/certificate/base', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.owner.lookup('service:secretMountPath').update('pki-test'); - }); - - test('it should make request to correct endpoint on queryRecord', async function (assert) { - assert.expect(1); - - this.server.get('/pki-test/cert/1234', () => { - assert.ok(true, 'Request made to correct endpoint on queryRecord'); - return { data: {} }; - }); - - await this.store.queryRecord('pki/certificate/base', { backend: 'pki-test', id: '1234' }); - }); - - test('it should make request to correct endpoint on query', async function (assert) { - assert.expect(1); - - this.server.get('/pki-test/certs', (schema, req) => { - assert.strictEqual(req.queryParams.list, 'true', 'Request made to correct endpoint on query'); - return { data: { keys: [] } }; - }); - - await this.store.query('pki/certificate/base', { backend: 'pki-test' }); - }); - - test('it should make request to correct endpoint on update', async function (assert) { - assert.expect(1); - - this.store.pushPayload('pki/certificate/base', { - modelName: 'pki/certificate/base', - data: { - serial_number: '1234', - }, - }); - - this.server.post('pki-test/revoke', (schema, req) => { - assert.deepEqual( - JSON.parse(req.requestBody), - { serial_number: '1234' }, - 'Request made to correct endpoint on update' - ); - return { data: {} }; - }); - - await this.store.peekRecord('pki/certificate/base', '1234').save(); - }); -}); diff --git a/ui/tests/unit/adapters/pki/certificate/generate-test.js b/ui/tests/unit/adapters/pki/certificate/generate-test.js deleted file mode 100644 index fed65d1c71..0000000000 --- a/ui/tests/unit/adapters/pki/certificate/generate-test.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; - -module('Unit | Adapter | pki/certificate/generate', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.backend = 'pki-test'; - this.secretMountPath.currentPath = this.backend; - this.data = { - serial_number: 'my-serial-number', - certificate: 'some-cert', - }; - }); - - test('it should make request to correct endpoint on create', async function (assert) { - assert.expect(1); - const generateData = { - role: 'my-role', - common_name: 'example.com', - }; - this.server.post(`${this.backend}/issue/${generateData.role}`, () => { - assert.ok(true, 'request made to correct endpoint on create'); - return { - data: { - serial_number: 'this-serial-number', - }, - }; - }); - - const model = await this.store.createRecord('pki/certificate/generate', generateData); - await model.save(); - }); -}); diff --git a/ui/tests/unit/adapters/pki/certificate/sign-test.js b/ui/tests/unit/adapters/pki/certificate/sign-test.js deleted file mode 100644 index 15efd48dbc..0000000000 --- a/ui/tests/unit/adapters/pki/certificate/sign-test.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import { CERTIFICATES } from 'vault/tests/helpers/pki/pki-helpers'; - -const { csr2 } = CERTIFICATES; -module('Unit | Adapter | pki/certificate/sign', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.backend = 'pki-test'; - this.secretMountPath.currentPath = this.backend; - this.data = { - serial_number: 'my-serial-number', - certificate: 'some-cert', - }; - }); - - test('it should make request to correct endpoint on create', async function (assert) { - assert.expect(1); - const generateData = { - role: 'my-role', - csr: csr2, - }; - this.server.post(`${this.backend}/sign/${generateData.role}`, () => { - assert.ok(true, 'request made to correct endpoint on create'); - return { - data: this.data, - }; - }); - - await this.store.createRecord('pki/certificate/sign', generateData).save(); - }); -}); diff --git a/ui/tests/unit/adapters/pki/config-test.js b/ui/tests/unit/adapters/pki/config-test.js deleted file mode 100644 index 580d5ab6e1..0000000000 --- a/ui/tests/unit/adapters/pki/config-test.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; - -module('Unit | Adapter | pki/config', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(async function () { - this.store = this.owner.lookup('service:store'); - this.backend = 'pki-engine'; - }); - - const testHelper = (test) => { - test('it should make request to correct endpoint on update', async function (assert) { - assert.expect(1); - - this.server.post(`/${this.backend}/config/${this.endpoint}`, () => { - assert.ok(true, `request made to POST config/${this.endpoint} endpoint on update`); - }); - - this.store.pushPayload(`pki/config/${this.endpoint}`, { - modelName: `pki/config/${this.endpoint}`, - id: this.backend, - }); - - const model = this.store.peekRecord(`pki/config/${this.endpoint}`, this.backend); - await model.save(); - }); - - test('it should make request to correct endpoint on find', async function (assert) { - assert.expect(1); - - this.server.get(`/${this.backend}/config/${this.endpoint}`, () => { - assert.ok(true, `request is made to GET /config/${this.endpoint} endpoint on find`); - return { data: { id: this.backend } }; - }); - - this.store.findRecord(`pki/config/${this.endpoint}`, this.backend); - }); - }; - - module('cluster', function (hooks) { - hooks.beforeEach(async function () { - this.endpoint = 'cluster'; - }); - testHelper(test); - }); - - module('urls', function (hooks) { - hooks.beforeEach(async function () { - this.endpoint = 'urls'; - }); - testHelper(test); - }); - - module('crl', function (hooks) { - hooks.beforeEach(async function () { - this.endpoint = 'crl'; - }); - testHelper(test); - }); -}); diff --git a/ui/tests/unit/adapters/pki/key-test.js b/ui/tests/unit/adapters/pki/key-test.js deleted file mode 100644 index 5960ca9cb7..0000000000 --- a/ui/tests/unit/adapters/pki/key-test.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import { setupMirage } from 'ember-cli-mirage/test-support'; - -module('Unit | Adapter | pki/key', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.backend = 'pki-test'; - this.secretMountPath.currentPath = this.backend; - this.data = { - key_id: '724862ff-6438-bad0-b598-77a6c7f4e934', - key_type: 'ec', - key_name: 'test-key', - key_bits: '256', - }; - }); - - hooks.afterEach(function () { - this.server.shutdown(); - }); - - test('it should make request to correct endpoint on query', async function (assert) { - assert.expect(1); - const { key_id, ...otherAttrs } = this.data; // excludes key_id from key_info data - const key_info = { [key_id]: { ...otherAttrs } }; - this.server.get(`${this.backend}/keys`, (schema, req) => { - assert.strictEqual(req.queryParams.list, 'true', 'request is made to correct endpoint on query'); - return { data: { keys: [key_id], key_info } }; - }); - - this.store.query('pki/key', { backend: this.backend }); - }); - - test('it should make request to correct endpoint on queryRecord', async function (assert) { - assert.expect(1); - - this.server.get(`${this.backend}/key/${this.data.key_id}`, () => { - assert.ok(true, 'request is made to correct endpoint on query record'); - return { data: this.data }; - }); - - this.store.queryRecord('pki/key', { backend: this.backend, id: this.data.key_id }); - }); - - test('it should make request to correct endpoint on delete', async function (assert) { - assert.expect(1); - this.store.pushPayload('pki/key', { modelName: 'pki/key', ...this.data }); - this.server.get(`${this.backend}/key/${this.data.key_id}`, () => ({ data: this.data })); - this.server.delete(`${this.backend}/key/${this.data.key_id}`, () => { - assert.ok(true, 'request made to correct endpoint on delete'); - }); - - const model = await this.store.queryRecord('pki/key', { backend: this.backend, id: this.data.key_id }); - await model.destroyRecord(); - }); -}); diff --git a/ui/tests/unit/adapters/pki/role-test.js b/ui/tests/unit/adapters/pki/role-test.js deleted file mode 100644 index 70fdcddad0..0000000000 --- a/ui/tests/unit/adapters/pki/role-test.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import { setupMirage } from 'ember-cli-mirage/test-support'; - -module('Unit | Adapter | pki/role', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.store.unloadAll('pki/role'); - }); - - test('it should make request to correct endpoint when updating a record', async function (assert) { - assert.expect(1); - this.server.post('/pki-not-hardcoded/roles/pki-test', () => { - assert.ok(true, 'POST request made to correct endpoint when updating a record'); - }); - - this.store.pushPayload('pki/role', { - modelName: 'pki/role', - backend: 'pki-not-hardcoded', - id: 'pki-test', - name: 'pki-test', - }); - const record = this.store.peekRecord('pki/role', 'pki-test'); - await record.save(); - }); -}); diff --git a/ui/tests/unit/adapters/pki/sign-intermediate-test.js b/ui/tests/unit/adapters/pki/sign-intermediate-test.js deleted file mode 100644 index b32e249136..0000000000 --- a/ui/tests/unit/adapters/pki/sign-intermediate-test.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; - -module('Unit | Adapter | pki/sign-intermediate', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.backend = 'pki-test'; - this.secretMountPath.currentPath = this.backend; - this.payload = { - issuerRef: 'my-issuer-id', - }; - }); - - test('it exists', function (assert) { - const adapter = this.owner.lookup('adapter:pki/sign-intermediate'); - assert.ok(adapter); - }); - - test('it calls the correct endpoint on save', async function (assert) { - assert.expect(2); - - this.server.post(`${this.backend}/issuer/my-issuer-id/sign-intermediate`, () => { - assert.ok(true, 'correct endpoint called'); - return { - request_id: 'unique-request-id', - data: { - foo: 'bar', - }, - }; - }); - - const result = await this.store.createRecord('pki/sign-intermediate', this.payload).save(); - assert.strictEqual(result.id, 'unique-request-id', 'Resulting model has ID matching request ID'); - }); -}); diff --git a/ui/tests/unit/adapters/pki/tidy-test.js b/ui/tests/unit/adapters/pki/tidy-test.js deleted file mode 100644 index 3f59398631..0000000000 --- a/ui/tests/unit/adapters/pki/tidy-test.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { setupMirage } from 'ember-cli-mirage/test-support'; -import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; - -module('Unit | Adapter | pki/tidy', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.backend = 'pki-test'; - this.secretMountPath.currentPath = this.backend; - this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub()); - }); - - test('it exists', function (assert) { - const adapter = this.owner.lookup('adapter:pki/tidy'); - assert.ok(adapter); - }); - - test('it calls the correct endpoint when tidyType = manual', async function (assert) { - assert.expect(1); - - this.server.post(`${this.backend}/tidy`, () => { - assert.ok(true, 'request made to correct endpoint on create'); - return {}; - }); - this.payload = { - tidy_cert_store: true, - tidy_revocation_queue: false, - safetyBuffer: '120h', - backend: this.backend, - }; - await this.store.createRecord('pki/tidy', this.payload).save({ adapterOptions: { tidyType: 'manual' } }); - }); - - test('it should make a request to correct endpoint for findRecord', async function (assert) { - assert.expect(1); - this.server.get(`${this.backend}/config/auto-tidy`, () => { - assert.ok(true, 'request made to correct endpoint on create'); - return { - request_id: '2a4a1f36-20df-e71c-02d6-be15a09656f9', - lease_id: '', - renewable: false, - lease_duration: 0, - data: { - acme_account_safety_buffer: 2592000, - enabled: false, - interval_duration: 43200, - issuer_safety_buffer: 31536000, - maintain_stored_certificate_counts: false, - pause_duration: '0s', - publish_stored_certificate_count_metrics: false, - revocation_queue_safety_buffer: 172800, - safety_buffer: 259200, - tidy_acme: false, - tidy_cert_store: false, - tidy_cross_cluster_revoked_certs: false, - tidy_expired_issuers: false, - tidy_move_legacy_ca_bundle: false, - tidy_revocation_queue: false, - tidy_revoked_cert_issuer_associations: false, - tidy_revoked_certs: false, - }, - wrap_info: null, - warnings: null, - auth: null, - }; - }); - - this.store.findRecord('pki/tidy', this.backend); - }); -}); diff --git a/ui/tests/unit/serializers/pki/action-test.js b/ui/tests/unit/serializers/pki/action-test.js deleted file mode 100644 index 8913ffde26..0000000000 --- a/ui/tests/unit/serializers/pki/action-test.js +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2025 - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupTest } from 'vault/tests/helpers'; -import { CERTIFICATES } from 'vault/tests/helpers/pki/pki-helpers'; - -const { rootPem } = CERTIFICATES; - -module('Unit | Serializer | pki/action', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - const store = this.owner.lookup('service:store'); - const serializer = store.serializerFor('pki/action'); - - assert.ok(serializer); - }); - - module('actionType import', function (hooks) { - hooks.beforeEach(function () { - this.actionType = 'import'; - this.pemBundle = rootPem; - }); - - test('it serializes only valid params', function (assert) { - const store = this.owner.lookup('service:store'); - const record = store.createRecord('pki/action', { - pemBundle: this.pemBundle, - issuerRef: 'do-not-send', - keyType: 'do-not-send', - }); - const expectedResult = { - pem_bundle: this.pemBundle, - }; - - const serializedRecord = record.serialize(this.actionType); - assert.deepEqual( - serializedRecord, - expectedResult, - 'Serializes only parameters valid for import action' - ); - }); - }); - - module('actionType generate-root', function (hooks) { - hooks.beforeEach(function () { - this.actionType = 'generate-root'; - this.allKeyFields = { - keyName: 'key name', - keyType: 'rsa', - keyBits: '0', - keyRef: 'key ref', - managedKeyName: 'managed name', - managedKeyId: 'managed id', - }; - this.withDefaults = { - exclude_cn_from_sans: false, - format: 'pem', - max_path_length: -1, - not_before_duration: '30s', - private_key_format: 'der', - }; - }); - - test('it serializes only params with values', function (assert) { - const store = this.owner.lookup('service:store'); - const record = store.createRecord('pki/action', { - excludeCnFromSans: false, - format: 'pem', - maxPathLength: -1, - notBeforeDuration: '30s', - privateKeyFormat: 'der', - type: 'external', // only used for endpoint in adapter - customTtl: '40m', // UI-only value - issuerName: 'my issuer', - commonName: undefined, - foo: 'bar', - }); - const expectedResult = { - ...this.withDefaults, - key_bits: '0', - key_ref: 'default', - key_type: 'rsa', - issuer_name: 'my issuer', - }; - - // without passing `actionType` it will not compare against an allowlist - const serializedRecord = record.serialize(); - assert.deepEqual(serializedRecord, expectedResult); - }); - - test('it serializes only valid params for type = external', function (assert) { - const store = this.owner.lookup('service:store'); - const record = store.createRecord('pki/action', { - ...this.allKeyFields, - type: 'external', - customTtl: '40m', - issuerName: 'my issuer', - commonName: 'my common name', - }); - const expectedResult = { - ...this.withDefaults, - issuer_name: 'my issuer', - common_name: 'my common name', - key_name: 'key name', - key_type: 'rsa', - key_bits: '0', - }; - - const serializedRecord = record.serialize(this.actionType); - assert.deepEqual(serializedRecord, expectedResult); - }); - - test('it serializes only valid params for type = internal', function (assert) { - const store = this.owner.lookup('service:store'); - const record = store.createRecord('pki/action', { - ...this.allKeyFields, - type: 'internal', - customTtl: '40m', - issuerName: 'my issuer', - commonName: 'my common name', - }); - const expectedResult = { - ...this.withDefaults, - issuer_name: 'my issuer', - common_name: 'my common name', - key_name: 'key name', - key_type: 'rsa', - key_bits: '0', - }; - - const serializedRecord = record.serialize(this.actionType); - assert.deepEqual(serializedRecord, expectedResult); - }); - - test('it serializes only valid params for type = existing', function (assert) { - const store = this.owner.lookup('service:store'); - const record = store.createRecord('pki/action', { - ...this.allKeyFields, - type: 'existing', - customTtl: '40m', - issuerName: 'my issuer', - commonName: 'my common name', - }); - const expectedResult = { - ...this.withDefaults, - issuer_name: 'my issuer', - common_name: 'my common name', - key_ref: 'key ref', - }; - - const serializedRecord = record.serialize(this.actionType); - assert.deepEqual(serializedRecord, expectedResult); - }); - - test('it serializes only valid params for type = kms', function (assert) { - const store = this.owner.lookup('service:store'); - const record = store.createRecord('pki/action', { - ...this.allKeyFields, - type: 'kms', - customTtl: '40m', - issuerName: 'my issuer', - commonName: 'my common name', - }); - const expectedResult = { - ...this.withDefaults, - issuer_name: 'my issuer', - common_name: 'my common name', - key_name: 'key name', - managed_key_name: 'managed name', - managed_key_id: 'managed id', - }; - - const serializedRecord = record.serialize(this.actionType); - assert.deepEqual(serializedRecord, expectedResult); - }); - }); -}); diff --git a/ui/tests/unit/services/path-help-test.js b/ui/tests/unit/services/path-help-test.js index 4303093b8a..86cf21ceee 100644 --- a/ui/tests/unit/services/path-help-test.js +++ b/ui/tests/unit/services/path-help-test.js @@ -103,16 +103,4 @@ module('Unit | Service | path-help', function (hooks) { }); }); }); - - module('hydrateModel', function () { - test('it should hydrate an existing model', async function (assert) { - this.server.get(`/pki2/roles/example`, () => openapiStub); - - const modelType = 'pki/role'; - await this.pathHelp.hydrateModel(modelType, 'pki2'); - const model = this.store.createRecord(modelType); - model.set('username', 'foobar'); - assert.strictEqual(model.username, 'foobar', 'sets value of key that only exists in openAPI response'); - }); - }); });