From 88a69457e1ee2c7d5f0828989a5e2a38d6efffc9 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Tue, 20 Sep 2022 08:25:57 -0700 Subject: [PATCH] PKI Issuer List view (#17210) * initial setup for issuers toolbar and some slight changes to roles model after discussion with design. * wip * wip ... :/ * finalizes serializer and linkedblock iteration of is_default * clean up * fix * forgot this bit * pr comments amendments: * small PR comment changes --- ui/app/adapters/pki/pki-issuer-engine.js | 28 +++++++ ui/app/models/pki/pki-issuer-engine.js | 51 ++++++++++++ ui/app/models/pki/pki-role-engine.js | 15 ++-- ui/app/serializers/pki/pki-issuer-engine.js | 15 ++++ ui/lib/pki/addon/controllers/issuers/index.js | 10 +++ ui/lib/pki/addon/controllers/roles/index.js | 2 +- ui/lib/pki/addon/routes.js | 4 +- .../configuration/create/generate-csr.js | 3 + .../configuration/create/generate-root.js | 3 + .../routes/configuration/create/import-ca.js | 3 + ui/lib/pki/addon/routes/issuers/index.js | 23 ++++++ .../configuration/create/generate-csr.hbs | 1 + .../configuration/create/generate-root.hbs | 1 + .../configuration/create/import-ca.hbs | 1 + ui/lib/pki/addon/templates/issuers.hbs | 3 +- ui/lib/pki/addon/templates/issuers/index.hbs | 79 +++++++++++++++++++ ui/lib/pki/addon/templates/roles/index.hbs | 16 +--- 17 files changed, 233 insertions(+), 25 deletions(-) create mode 100644 ui/app/adapters/pki/pki-issuer-engine.js create mode 100644 ui/app/models/pki/pki-issuer-engine.js create mode 100644 ui/app/serializers/pki/pki-issuer-engine.js create mode 100644 ui/lib/pki/addon/controllers/issuers/index.js create mode 100644 ui/lib/pki/addon/routes/configuration/create/generate-csr.js create mode 100644 ui/lib/pki/addon/routes/configuration/create/generate-root.js create mode 100644 ui/lib/pki/addon/routes/configuration/create/import-ca.js create mode 100644 ui/lib/pki/addon/routes/issuers/index.js create mode 100644 ui/lib/pki/addon/templates/configuration/create/generate-csr.hbs create mode 100644 ui/lib/pki/addon/templates/configuration/create/generate-root.hbs create mode 100644 ui/lib/pki/addon/templates/configuration/create/import-ca.hbs create mode 100644 ui/lib/pki/addon/templates/issuers/index.hbs diff --git a/ui/app/adapters/pki/pki-issuer-engine.js b/ui/app/adapters/pki/pki-issuer-engine.js new file mode 100644 index 0000000000..7f3e21c1ae --- /dev/null +++ b/ui/app/adapters/pki/pki-issuer-engine.js @@ -0,0 +1,28 @@ +import ApplicationAdapter from '../application'; +import { encodePath } from 'vault/utils/path-encoding-helpers'; + +export default class PkiIssuerEngineAdapter extends ApplicationAdapter { + namespace = 'v1'; + + optionsForQuery(id) { + let data = {}; + if (!id) { + data['list'] = true; + } + return { data }; + } + + urlForQuery(backend, id) { + let url = `${this.buildURL()}/${encodePath(backend)}/issuers`; + if (id) { + url = url + '/' + encodePath(id); + } + return url; + } + + async query(store, type, query) { + const { backend, id } = query; + let response = await this.ajax(this.urlForQuery(backend, id), 'GET', this.optionsForQuery(id)); + return response; + } +} diff --git a/ui/app/models/pki/pki-issuer-engine.js b/ui/app/models/pki/pki-issuer-engine.js new file mode 100644 index 0000000000..1324739cc0 --- /dev/null +++ b/ui/app/models/pki/pki-issuer-engine.js @@ -0,0 +1,51 @@ +import Model, { attr } from '@ember-data/model'; +import { expandAttributeMeta } from 'vault/utils/field-to-attrs'; +import { withModelValidations } from 'vault/decorators/model-validations'; + +const validations = { + name: [ + { type: 'presence', message: 'Name is required.' }, + { + type: 'containsWhiteSpace', + message: 'Name cannot contain whitespace.', + }, + ], +}; + +@withModelValidations(validations) +export default class PkiIssuersEngineModel extends Model { + @attr('string', { readOnly: true }) backend; + @attr('string', { + label: 'Issuer name', + fieldValue: 'id', + }) + name; + + get useOpenAPI() { + return true; + } + getHelpUrl(backend) { + return `/v1/${backend}/issuer/example?help=1`; + } + + @attr('boolean') isDefault; + @attr('string') issuerName; + + // Form Fields not hidden in toggle options + _attributeMeta = null; + get formFields() { + if (!this._attributeMeta) { + this._attributeMeta = expandAttributeMeta(this, [ + 'name', + 'leafNotAfterBehavior', + 'usage', + 'manualChain', + 'issuingCertifications', + 'crlDistributionPoints', + 'ocspServers', + 'deltaCrlUrls', // new endpoint, mentioned in RFC, but need to confirm it's there. + ]); + } + return this._attributeMeta; + } +} diff --git a/ui/app/models/pki/pki-role-engine.js b/ui/app/models/pki/pki-role-engine.js index f8aa946b6b..ec4ebfb29c 100644 --- a/ui/app/models/pki/pki-role-engine.js +++ b/ui/app/models/pki/pki-role-engine.js @@ -75,6 +75,7 @@ export default class PkiRolesEngineModel extends Model { { default: ['name'] }, { 'Domain handling': [ + 'allowedDomains', 'allowedDomainTemplate', 'allowBareDomains', 'allowSubdomains', @@ -93,21 +94,19 @@ export default class PkiRolesEngineModel extends Model { 'DigitalSignature', // ARG TODO: capitalized in the docs, but should confirm 'KeyAgreement', 'KeyEncipherment', + 'extKeyUsage', // ARG TODO: takes a list, but we have these as checkboxes from the options on the golang site: https://pkg.go.dev/crypto/x509#ExtKeyUsage ], }, - { 'Policy identifiers': ['policy_identifiers'] }, + { 'Policy identifiers': ['policyIdentifiers'] }, { - 'Subject Alternative Name (SAN) Options': [ - 'allow_ip_sans', - 'allowed_uri_sans', - 'allowed_other_sans', - ], + 'Subject Alternative Name (SAN) Options': ['allowIpSans', 'allowedUriSans', 'allowedOtherSans'], }, { 'Additional subject fields': [ 'allowed_serial_numbers', - 'require_cn', - 'use_csr_common_name', + 'requireCn', + 'useCsrCommonName', + 'useCsrSans', 'ou', 'organization', 'country', diff --git a/ui/app/serializers/pki/pki-issuer-engine.js b/ui/app/serializers/pki/pki-issuer-engine.js new file mode 100644 index 0000000000..545b742b17 --- /dev/null +++ b/ui/app/serializers/pki/pki-issuer-engine.js @@ -0,0 +1,15 @@ +import ApplicationSerializer from '../application'; + +export default class PkiIssuerEngineSerializer extends ApplicationSerializer { + // rehydrate each issuer 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) => ({ id: key, ...payload.data.key_info[key] })); + } + Object.assign(payload, payload.data); + delete payload.data; + } + return payload; + } +} diff --git a/ui/lib/pki/addon/controllers/issuers/index.js b/ui/lib/pki/addon/controllers/issuers/index.js new file mode 100644 index 0000000000..6f35c794a0 --- /dev/null +++ b/ui/lib/pki/addon/controllers/issuers/index.js @@ -0,0 +1,10 @@ +import Controller from '@ember/controller'; +import { action } from '@ember/object'; +import { next } from '@ember/runloop'; + +export default class PkiRolesIssuerController extends Controller { + // To prevent production build bug of passing D.actions to on "click": https://github.com/hashicorp/vault/pull/16983 + @action onLinkClick(D) { + next(() => D.actions.close()); + } +} diff --git a/ui/lib/pki/addon/controllers/roles/index.js b/ui/lib/pki/addon/controllers/roles/index.js index f18a6bf393..a399d8838c 100644 --- a/ui/lib/pki/addon/controllers/roles/index.js +++ b/ui/lib/pki/addon/controllers/roles/index.js @@ -1,7 +1,7 @@ import Controller from '@ember/controller'; import { getOwner } from '@ember/application'; -export default class BlogPostController extends Controller { +export default class PkiRolesIndexController extends Controller { get mountPoint() { return getOwner(this).mountPoint; } diff --git a/ui/lib/pki/addon/routes.js b/ui/lib/pki/addon/routes.js index c868bcd3c9..7e07ee1c89 100644 --- a/ui/lib/pki/addon/routes.js +++ b/ui/lib/pki/addon/routes.js @@ -15,7 +15,7 @@ export default buildRoutes(function () { this.route('details'); }); this.route('roles', function () { - this.route('index', { path: '/' }); // ARG TODO remove + this.route('index', { path: '/' }); this.route('create'); this.route('role', { path: '/:id' }, function () { this.route('details'); @@ -23,7 +23,7 @@ export default buildRoutes(function () { }); }); this.route('issuers', function () { - this.route('create'); + this.route('index', { path: '/' }); this.route('issuer', { path: '/:id' }, function () { this.route('details'); this.route('edit'); diff --git a/ui/lib/pki/addon/routes/configuration/create/generate-csr.js b/ui/lib/pki/addon/routes/configuration/create/generate-csr.js new file mode 100644 index 0000000000..bd35d7c0ef --- /dev/null +++ b/ui/lib/pki/addon/routes/configuration/create/generate-csr.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class PkiConfigurationCreateGenerateCsrRoute extends Route {} diff --git a/ui/lib/pki/addon/routes/configuration/create/generate-root.js b/ui/lib/pki/addon/routes/configuration/create/generate-root.js new file mode 100644 index 0000000000..4db5cef108 --- /dev/null +++ b/ui/lib/pki/addon/routes/configuration/create/generate-root.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class PkiConfigurationCreateGenerateRootRoute extends Route {} diff --git a/ui/lib/pki/addon/routes/configuration/create/import-ca.js b/ui/lib/pki/addon/routes/configuration/create/import-ca.js new file mode 100644 index 0000000000..34021583d6 --- /dev/null +++ b/ui/lib/pki/addon/routes/configuration/create/import-ca.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class PkiConfigurationCreateImportCaRoute extends Route {} diff --git a/ui/lib/pki/addon/routes/issuers/index.js b/ui/lib/pki/addon/routes/issuers/index.js new file mode 100644 index 0000000000..a425c1d3d0 --- /dev/null +++ b/ui/lib/pki/addon/routes/issuers/index.js @@ -0,0 +1,23 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; + +export default class PkiIssuersIndexRoute extends Route { + @service store; + @service secretMountPath; + @service pathHelp; + + model() { + // the pathHelp service is needed for adding openAPI to the model + this.pathHelp.getNewModel('pki/pki-issuer-engine', 'pki'); + + return this.store + .query('pki/pki-issuer-engine', { backend: this.secretMountPath.currentPath }) + .catch((err) => { + if (err.httpStatus === 404) { + return []; + } else { + throw err; + } + }); + } +} diff --git a/ui/lib/pki/addon/templates/configuration/create/generate-csr.hbs b/ui/lib/pki/addon/templates/configuration/create/generate-csr.hbs new file mode 100644 index 0000000000..69caa62151 --- /dev/null +++ b/ui/lib/pki/addon/templates/configuration/create/generate-csr.hbs @@ -0,0 +1 @@ +configuration.create.generate-csr \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/configuration/create/generate-root.hbs b/ui/lib/pki/addon/templates/configuration/create/generate-root.hbs new file mode 100644 index 0000000000..539da10703 --- /dev/null +++ b/ui/lib/pki/addon/templates/configuration/create/generate-root.hbs @@ -0,0 +1 @@ +configuration.create.generate-root \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/configuration/create/import-ca.hbs b/ui/lib/pki/addon/templates/configuration/create/import-ca.hbs new file mode 100644 index 0000000000..6627ec6760 --- /dev/null +++ b/ui/lib/pki/addon/templates/configuration/create/import-ca.hbs @@ -0,0 +1 @@ +configuration.create.import-ca \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/issuers.hbs b/ui/lib/pki/addon/templates/issuers.hbs index ed227a84ce..c1651378d9 100644 --- a/ui/lib/pki/addon/templates/issuers.hbs +++ b/ui/lib/pki/addon/templates/issuers.hbs @@ -7,4 +7,5 @@ model=this.model.id }} @isEngine={{true}} -/> \ No newline at end of file +/> +{{outlet}} \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/issuers/index.hbs b/ui/lib/pki/addon/templates/issuers/index.hbs new file mode 100644 index 0000000000..cd3c3fa175 --- /dev/null +++ b/ui/lib/pki/addon/templates/issuers/index.hbs @@ -0,0 +1,79 @@ + + + + Import + + + + Generate + + + + + + + + +{{#if (gt this.model.length 0)}} + {{#each this.model as |model|}} + +
+
+
+ + + {{or model.issuerName model.id}} + +
+ {{#if model.isDefault}} + default issuer + {{/if}} + {{#if model.issuerName}} + {{model.id}} + {{/if}} +
+
+
+
+
+ + + +
+
+
+
+ {{/each}} +{{else}} + + + Configure PKI + + +{{/if}} \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/roles/index.hbs b/ui/lib/pki/addon/templates/roles/index.hbs index 4fa9b0fca3..8db9e6a859 100644 --- a/ui/lib/pki/addon/templates/roles/index.hbs +++ b/ui/lib/pki/addon/templates/roles/index.hbs @@ -12,7 +12,7 @@
- + {{model.id}} @@ -24,22 +24,12 @@