[UI] Ember Data Migration - Kubernetes Overview/Cleanup (#11430) (#11435)

* converts kubernetes overview page component to ts

* converts kubernetes role index controller to ts

* updates kubernetes overview to use api service

* removes store service from kubernetes engine

* removes kubernetes models, adapters and serializers

* removes unused types

* updates removed type references

* removes fetch-secrets-config decorator

Co-authored-by: Jordan Reimer <zofskeez@gmail.com>
This commit is contained in:
Vault Automation 2025-12-17 10:26:50 -07:00 committed by GitHub
parent 366e77bac5
commit 4900cbfe1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 61 additions and 1497 deletions

View file

@ -1,14 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import SecretsEnginePathAdapter from 'vault/adapters/secrets-engine-path';
export default class KubernetesConfigAdapter extends SecretsEnginePathAdapter {
path = 'config';
checkConfigVars(backend) {
return this.ajax(`${this._getURL(backend, 'check')}`, 'GET');
}
}

View file

@ -1,51 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import NamedPathAdapter from 'vault/adapters/named-path';
import { encodePath } from 'vault/utils/path-encoding-helpers';
export default class KubernetesRoleAdapter extends NamedPathAdapter {
getURL(backend, name) {
const base = `${this.buildURL()}/${encodePath(backend)}/roles`;
return name ? `${base}/${name}` : base;
}
urlForQuery({ backend }) {
return this.getURL(backend);
}
urlForUpdateRecord(name, modelName, snapshot) {
return this.getURL(snapshot.attr('backend'), name);
}
urlForDeleteRecord(name, modelName, snapshot) {
return this.getURL(snapshot.attr('backend'), name);
}
query(store, type, query) {
const { backend } = query;
return this.ajax(this.getURL(backend), 'GET', { data: { list: true } }).then((resp) => {
return resp.data.keys.map((name) => ({ name, backend }));
});
}
queryRecord(store, type, query) {
const { backend, name } = query;
return this.ajax(this.getURL(backend, name), 'GET').then((resp) => {
resp.data.backend = backend;
resp.data.name = name;
return resp.data;
});
}
generateCredentials(backend, data) {
const generateCredentialsUrl = `${this.buildURL()}/${encodePath(backend)}/creds/${data.role}`;
delete data.role;
return this.ajax(generateCredentialsUrl, 'POST', { data }).then((response) => {
const { lease_id, lease_duration, data } = response;
return {
lease_id,
lease_duration,
...data,
};
});
}
}

View file

@ -72,14 +72,7 @@ export default class App extends Application {
},
kubernetes: {
dependencies: {
services: [
{ 'app-router': 'router' },
'store',
'secret-mount-path',
'flash-messages',
'api',
'capabilities',
],
services: [{ 'app-router': 'router' }, 'secret-mount-path', 'flash-messages', 'api', 'capabilities'],
externalRoutes: {
secrets: 'vault.cluster.secrets.backends',
},

View file

@ -1,41 +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 { withModelValidations } from 'vault/decorators/model-validations';
const validations = {
kubernetesHost: [
{
validator: (model) => (model.disableLocalCaJwt && !model.kubernetesHost ? false : true),
message: 'Kubernetes host is required',
},
],
};
@withModelValidations(validations)
@withFormFields(['kubernetesHost', 'serviceAccountJwt', 'kubernetesCaCert'])
export default class KubernetesConfigModel extends Model {
@attr('string') backend; // dynamic path of secret -- set on response from value passed to queryRecord
@attr('string', {
label: 'Kubernetes host',
subText: 'Kubernetes API URL to connect to.',
})
kubernetesHost;
@attr('string', {
label: 'Service account JWT',
subText:
'The JSON web token of the service account used by the secret engine to manage Kubernetes roles. Defaults to the local pods JWT if found.',
})
serviceAccountJwt;
@attr('string', {
label: 'Kubernetes CA Certificate',
subText:
'PEM-encoded CA certificate to use by the secret engine to verify the Kubernetes API server certificate. Defaults to the local pods CA if found.',
editType: 'textarea',
})
kubernetesCaCert;
@attr('boolean', { defaultValue: false }) disableLocalCaJwt;
}

View file

@ -1,152 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Model, { attr } from '@ember-data/model';
import { withModelValidations } from 'vault/decorators/model-validations';
import { withFormFields } from 'vault/decorators/model-form-fields';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
import { tracked } from '@glimmer/tracking';
const validations = {
name: [{ type: 'presence', message: 'Name is required' }],
};
const formFieldProps = [
'name',
'serviceAccountName',
'kubernetesRoleType',
'kubernetesRoleName',
'allowedKubernetesNamespaces',
'tokenMaxTtl',
'tokenDefaultTtl',
'nameTemplate',
];
@withModelValidations(validations)
@withFormFields(formFieldProps)
export default class KubernetesRoleModel extends Model {
@attr('string') backend; // dynamic path of secret -- set on response from value passed to queryRecord
@attr('string', {
label: 'Role name',
subText: 'The roles name in Vault.',
})
name;
@attr('string', {
label: 'Service account name',
subText: 'Vault will use the default template when generating service accounts, roles and role bindings.',
})
serviceAccountName;
@attr('string', {
label: 'Kubernetes role type',
editType: 'radio',
possibleValues: ['Role', 'ClusterRole'],
})
kubernetesRoleType;
@attr('string', {
label: 'Kubernetes role name',
subText: 'Vault will use the default template when generating service accounts, roles and role bindings.',
})
kubernetesRoleName;
@attr('string', {
label: 'Allowed Kubernetes namespaces',
subText:
'A list of the valid Kubernetes namespaces in which this role can be used for creating service accounts. If set to "*" all namespaces are allowed.',
})
allowedKubernetesNamespaces;
@attr({
label: 'Max Lease TTL',
editType: 'ttl',
})
tokenMaxTtl;
@attr({
label: 'Default Lease TTL',
editType: 'ttl',
})
tokenDefaultTtl;
@attr('string', {
label: 'Name template',
editType: 'optionalText',
defaultSubText:
'Vault will use the default template when generating service accounts, roles and role bindings.',
subText: 'Vault will use the default template when generating service accounts, roles and role bindings.',
})
nameTemplate;
@attr extraAnnotations;
@attr extraLabels;
@attr('string') generatedRoleRules;
@tracked _generationPreference;
get generationPreference() {
// when the user interacts with the radio cards the value will be set to the pseudo prop which takes precedence
if (this._generationPreference) {
return this._generationPreference;
}
// for existing roles, default the value based on which model prop has value -- only one can be set
let pref = null;
if (this.serviceAccountName) {
pref = 'basic';
} else if (this.kubernetesRoleName) {
pref = 'expanded';
} else if (this.generatedRoleRules) {
pref = 'full';
}
return pref;
}
set generationPreference(pref) {
// unset model props specific to filteredFormFields when changing preference
// only one of service_account_name, kubernetes_role_name or generated_role_rules can be set
const props = {
basic: ['kubernetesRoleType', 'kubernetesRoleName', 'generatedRoleRules', 'nameTemplate'],
expanded: ['serviceAccountName', 'generatedRoleRules'],
full: ['serviceAccountName', 'kubernetesRoleName'],
}[pref];
props.forEach((prop) => (this[prop] = null));
this._generationPreference = pref;
}
get filteredFormFields() {
// return different form fields based on generationPreference
const hiddenFieldIndices = {
basic: [2, 3, 7], // kubernetesRoleType, kubernetesRoleName and nameTemplate
expanded: [1], // serviceAccountName
full: [1, 3], // serviceAccountName and kubernetesRoleName
}[this.generationPreference];
return hiddenFieldIndices
? this.formFields.filter((field, index) => !hiddenFieldIndices.includes(index))
: null;
}
@lazyCapabilities(apiPath`${'backend'}/roles/${'name'}`, 'backend', 'name') rolePath;
@lazyCapabilities(apiPath`${'backend'}/creds/${'name'}`, 'backend', 'name') credsPath;
@lazyCapabilities(apiPath`${'backend'}/roles`, 'backend') rolesPath;
get canCreate() {
return this.rolePath.get('canCreate');
}
get canDelete() {
return this.rolePath.get('canDelete');
}
get canEdit() {
return this.rolePath.get('canUpdate');
}
get canRead() {
return this.rolePath.get('canRead');
}
get canList() {
return this.rolesPath.get('canList');
}
get canGenerateCreds() {
return this.credsPath.get('canCreate');
}
}

View file

@ -1,23 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import ApplicationSerializer from '../application';
export default class KubernetesConfigSerializer extends ApplicationSerializer {
primaryKey = 'backend';
serialize() {
const json = super.serialize(...arguments);
// remove backend value from payload
delete json.backend;
// ensure that values from a previous manual configuration are unset
if (json.disable_local_ca_jwt === false) {
json.kubernetes_ca_cert = null;
json.kubernetes_host = null;
json.service_account_jwt = null;
}
return json;
}
}

View file

@ -1,14 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import ApplicationSerializer from '../application';
export default class KubernetesRoleSerializer extends ApplicationSerializer {
primaryKey = 'name';
attrs = {
backend: { serialize: false },
};
}

View file

@ -1,92 +0,0 @@
# Fetch Secrets Engine Configuration Decorator
The `fetch-secrets-engine-config` decorator is available in the core addon and can be used on a route that needs to be aware of the configuration details of a secrets engine prior to model hook execution. This is useful for conditionally displaying a call to action for the user to complete the configuration.
## API
The decorator accepts a single argument with the name of the Ember Data model to be fetched.
- **modelName** [string] - name of the Ember Data model to fetch which is passed to the `queryRecord` method.
With the provided model name, the decorator fetches the record using the store `queryRecord` method in the `beforeModel` route hook. Several properties are set on the route class based on the status of the request:
- **configModel** [Model | null] - set on success with resolved Ember Data model.
- **configError** [AdapterError | null] - set if the request errors with any status other than 404.
- **promptConfig** [boolean] - set to `true` if the request returns a 404, otherwise set to `false`. This is for convenience since checking for `(!this.configModel && !this.configError)` would result in the same value.
## Usage
### Configure route
```js
@withConfig('foo/config')
export default class FooConfigureRoute extends Route {
@service store;
@service secretMountPath;
model() {
const backend = this.secretMountPath.currentPath;
return this.configModel || this.store.createRecord('foo/config', { backend });
}
}
```
In the scenario of creating/updating the configuration, the model is used to populate the form if available, otherwise the form is presented in an empty state. Fetch errors are not a concern, nor is prompting the user to configure so only the `configModel` property is used.
### Configuration route
```js
@withConfig('foo/config')
export default class FooConfigurationRoute extends Route {
@service store;
@service secretMountPath;
model() {
// the error could also be thrown to display the error template
// in this example a component is used to display the error
return {
configModel: this.configModel,
configError: this.configError,
};
}
}
```
For configuration routes, the model and error properties may be used to determine what should be displayed to the user:
`configuration.hbs`
```hbs
{{#if @configModel}}
{{#each @configModel.fields as |field|}}
<InfoTableRow @label={{field.label}} @value={{field.value}} />
{{/each}}
{{else if @configError}}
<Page::Error @error={{@configError}} />
{{else}}
<ConfigCta />
{{/if}}
```
### Other routes (overview etc.)
This is the most basic usage where a route only needs to be aware of whether or not to show the config prompt:
```js
@withConfig('foo/config')
export default class FooOverviewRoute extends Route {
@service store;
@service secretMountPath;
model() {
const backend = this.secretMountPath.currentPath;
return hash({
promptConfig: this.promptConfig,
roles: this.store.query('foo/role', { backend }).catch(() => []),
libraries: this.store.query('foo/library', { backend }).catch(() => []),
});
}
}
```

View file

@ -13,7 +13,6 @@ These views are generated by files in the `docs/` directory.
- [Building and consuming components](./building-components.md)
- [CSS styling](./css.md)
- [Ember Engines](./ember-engines.md)
- [Fetch secrets engine config decorator](./fetch-secrets-engine-config.md)
- [Forms](./forms.md)
- [Model validations](./model-validations.md)
- [Models](./models.md)

View file

@ -1,73 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Route from '@ember/routing/route';
import type Store from '@ember-data/store';
import type SecretMountPath from 'vault/services/secret-mount-path';
import type Transition from '@ember/routing/transition';
import type MountConfigModel from 'vault/models/mount-config';
import type AdapterError from '@ember-data/adapter/error';
/**
* for use in routes that need to be aware of the config for a secrets engine
* if the user has not configured they are prompted to do so in each of the routes
* decorate the necessary routes to perform the check in the beforeModel hook since that may change what is returned for the model
*/
interface BaseRoute extends Route {
store: Store;
secretMountPath: SecretMountPath;
}
export function withConfig(modelName: string) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function <RouteClass extends new (...args: any[]) => BaseRoute>(SuperClass: RouteClass) {
if (!Object.prototype.isPrototypeOf.call(Route, SuperClass)) {
// eslint-disable-next-line
console.debug(
'withConfig decorator must be used on an instance of Ember Route class. Decorator not applied to returned class'
);
return SuperClass;
}
return class FetchSecretsEngineConfig extends SuperClass {
configModel: MountConfigModel | null = null;
configError: AdapterError | null = null;
promptConfig = false;
async beforeModel(transition: Transition) {
super.beforeModel(transition);
if (!this.secretMountPath) {
// eslint-disable-next-line
console.debug('secretMountPath service is required for withConfig decorator. Add it to the route');
}
const backend = this.secretMountPath.currentPath;
// check the store for record first
this.configModel = this.store.peekRecord(modelName, backend);
if (!this.configModel) {
return this.store
.queryRecord(modelName, { backend })
.then((record) => {
this.configModel = record;
this.promptConfig = false;
})
.catch((error) => {
// we need to ignore if the user does not have permission or other failures so as to not block the other operations
if (error.httpStatus === 404) {
this.promptConfig = true;
} else {
// not considering 404 an error since it triggers the cta
// this error is thrown in the configuration route so we can display the error in the view
this.configError = error;
}
});
} else {
this.promptConfig = false;
}
}
};
};
}

View file

@ -19,10 +19,9 @@
/>
</:action>
<:content>
<h2 class="title is-2 has-font-weight-normal has-top-margin-m" data-test-roles-card-overview-num>{{or
@roles.length
"None"
}}</h2>
<h2 class="title is-2 has-font-weight-normal has-top-margin-m" data-test-roles-card-overview-num>
{{or @roles.length "None"}}
</h2>
</:content>
</OverviewCard>
<OverviewCard @cardTitle="Generate credentials" @subText="Quickly generate credentials by typing the role name.">

View file

@ -8,6 +8,10 @@ import { service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import type RouterService from '@ember/routing/router-service';
import type { Breadcrumb, EngineOwner } from 'vault/vault/app-types';
import type SecretsEngineResource from 'vault/resources/secrets/engine';
/**
* @module Overview
* OverviewPage component is a child component to overview kubernetes secrets engine.
@ -18,21 +22,28 @@ import { action } from '@ember/object';
* @param {array} breadcrumbs - breadcrumbs as an array of objects that contain label and route
*/
export default class OverviewPageComponent extends Component {
@service('app-router') router;
interface Args {
promptConfig: boolean;
secretsEngine: SecretsEngineResource;
roles: string[];
breadcrumbs: Array<Breadcrumb>;
}
@tracked selectedRole = null;
@tracked roleOptions = [];
export default class OverviewPageComponent extends Component<Args> {
@service('app-router') declare readonly router: RouterService;
constructor() {
super(...arguments);
@tracked selectedRole = '';
@tracked roleOptions: Array<{ name: string; id: string }> = [];
constructor(owner: EngineOwner, args: Args) {
super(owner, args);
this.roleOptions = this.args.roles.map((role) => {
return { name: role.name, id: role.name };
return { name: role, id: role };
});
}
@action
selectRole([roleName]) {
selectRole([roleName]: [string]) {
this.selectedRole = roleName;
}

View file

@ -16,7 +16,7 @@ export default class KubernetesEngine extends Engine {
modulePrefix = modulePrefix;
Resolver = Resolver;
dependencies = {
services: ['app-router', 'store', 'secret-mount-path', 'flash-messages', 'api', 'capabilities'],
services: ['app-router', 'secret-mount-path', 'flash-messages', 'api', 'capabilities'],
externalRoutes: ['secrets'],
};
}

View file

@ -5,12 +5,12 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import { hash } from 'rsvp';
import { ModelFrom } from 'vault/route';
import { KubernetesListRolesListEnum } from '@hashicorp/vault-client-typescript';
import type { KubernetesApplicationModel } from './application';
import type SecretMountPath from 'vault/services/secret-mount-path';
import type Store from '@ember-data/store';
import type ApiService from 'vault/services/api';
import type Controller from '@ember/controller';
import type { Breadcrumb } from 'vault/app-types';
@ -22,17 +22,22 @@ interface RouteController extends Controller {
export type KubernetesOverviewModel = ModelFrom<KubernetesOverviewRoute>;
export default class KubernetesOverviewRoute extends Route {
@service declare readonly store: Store;
@service declare readonly api: ApiService;
@service declare readonly secretMountPath: SecretMountPath;
async model() {
const backend = this.secretMountPath.currentPath;
const { currentPath } = this.secretMountPath;
const { promptConfig, secretsEngine } = this.modelFor('application') as KubernetesApplicationModel;
return hash({
const { keys } = await this.api.secrets
.kubernetesListRoles(currentPath, KubernetesListRolesListEnum.TRUE)
.catch(() => ({ keys: [] }));
return {
promptConfig,
secretsEngine,
roles: this.store.query('kubernetes/role', { backend }).catch(() => []),
});
roles: keys,
};
}
setupController(controller: RouteController, resolvedModel: KubernetesOverviewModel) {

View file

@ -11,8 +11,7 @@ import { waitFor } from '@ember/test-waiters';
import type FlashMessageService from 'vault/services/flash-messages';
import type AuthService from 'vault/services/auth';
import type { LdapLibrary } from 'vault/secrets/ldap';
import type { LdapLibraryAccountStatus } from 'vault/adapters/ldap/library';
import type { LdapLibrary, LdapLibraryAccountStatus } from 'vault/secrets/ldap';
import type { CapabilitiesMap } from 'vault/app-types';
import type CapabilitiesService from 'vault/services/capabilities';
import type ApiService from 'vault/services/api';

View file

@ -6,13 +6,13 @@
import Controller from '@ember/controller';
import { tracked } from '@glimmer/tracking';
import type { Breadcrumb } from 'vault/vault/app-types';
import type LdapLibraryModel from 'vault/models/ldap/library';
import type { LdapLibrary } from 'vault/secrets/ldap';
import type SecretsEngineResource from 'vault/resources/secrets/engine';
interface RouteModel {
secretsEngine: SecretsEngineResource;
path_to_library: string;
libraries: Array<LdapLibraryModel>;
libraries: Array<LdapLibrary>;
}
export default class LdapLibrariesSubdirectoryController extends Controller {

View file

@ -13,7 +13,6 @@ import type { LdapLibraryRouteModel } from 'ldap/routes/libraries/library';
import type Controller from '@ember/controller';
import type Transition from '@ember/routing/transition';
import type { Breadcrumb } from 'vault/app-types';
import { LdapLibraryCheckOutCredentials } from 'vault/vault/adapters/ldap/library';
import { ldapBreadcrumbs, libraryRoutes } from 'ldap/utils/ldap-breadcrumbs';
import type ApiService from 'vault/services/api';
import type SecretMountPath from 'vault/services/secret-mount-path';
@ -68,7 +67,7 @@ export default class LdapLibraryCheckOutRoute extends Route {
}
setupController(
controller: LdapLibraryCheckOutController,
resolvedModel: LdapLibraryCheckOutCredentials,
resolvedModel: LdapLibraryCheckOutRouteModel,
transition: Transition
) {
super.setupController(controller, resolvedModel, transition);

View file

@ -10,7 +10,7 @@ import { ldapBreadcrumbs, libraryRoutes } from 'ldap/utils/ldap-breadcrumbs';
import type SecretMountPath from 'vault/services/secret-mount-path';
import type Transition from '@ember/routing/transition';
import type LdapLibraryModel from 'vault/models/ldap/library';
import type { LdapLibrary } from 'vault/secrets/ldap';
import type LdapLibrariesSubdirectoryController from 'ldap/controllers/libraries/subdirectory';
import type SecretsEngineResource from 'vault/resources/secrets/engine';
import type { LdapApplicationModel } from '../application';
@ -18,7 +18,7 @@ import type { LdapApplicationModel } from '../application';
interface RouteModel {
secretsEngine: SecretsEngineResource;
path_to_library: string;
libraries: Array<LdapLibraryModel>;
libraries: Array<LdapLibrary>;
}
type RouteController = LdapLibrariesSubdirectoryController;

View file

@ -6,16 +6,16 @@
import LdapRolesRoute from '../roles';
import type Transition from '@ember/routing/transition';
import type LdapRoleModel from 'vault/models/ldap/role';
import type { LdapRole } from 'vault/secrets/ldap';
import type Controller from '@ember/controller';
import type { Breadcrumb } from 'vault/vault/app-types';
import type { Breadcrumb } from 'vault/app-types';
import type SecretsEngineResource from 'vault/resources/secrets/engine';
import { LdapApplicationModel } from '../application';
interface RouteModel {
secretsEngine: SecretsEngineResource;
promptConfig: boolean;
roles: Array<LdapRoleModel>;
roles: Array<LdapRole>;
}
interface RouteController extends Controller {

View file

@ -8,7 +8,7 @@ import { ldapBreadcrumbs, roleRoutes } from 'ldap/utils/ldap-breadcrumbs';
import type { Breadcrumb } from 'vault/vault/app-types';
import type Controller from '@ember/controller';
import type LdapRoleModel from 'vault/models/ldap/role';
import type { LdapRole } from 'vault/secrets/ldap';
import type Transition from '@ember/routing/transition';
import type SecretsEngineResource from 'vault/resources/secrets/engine';
import type { LdapApplicationModel } from '../application';
@ -16,7 +16,7 @@ import type { LdapApplicationModel } from '../application';
interface RouteModel {
secretsEngine: SecretsEngineResource;
roleAncestry: { path_to_role: string; type: string };
roles: Array<LdapRoleModel>;
roles: Array<LdapRole>;
}
interface RouteController extends Controller {

View file

@ -16,10 +16,10 @@ import { toLabel } from 'core/helpers/to-label';
import type RouterService from '@ember/routing/router';
import type FlashMessageService from 'vault/services/flash-messages';
import type SecretMountPath from 'vault/services/secret-mount-path';
import type PkiIssuerModel from 'vault/models/pki/issuer';
import type { Breadcrumb, ValidationMap } from 'vault/vault/app-types';
import type {
PkiGenerateRootResponse,
PkiReadIssuerResponse,
PkiRotateRootExportedEnum,
PkiRotateRootRequest,
} from '@hashicorp/vault-client-typescript';
@ -27,7 +27,7 @@ import type { ParsedCertificateData } from 'vault/vault/utils/parse-pki-cert';
import type ApiService from 'vault/services/api';
interface Args {
oldRoot: PkiIssuerModel;
oldRoot: PkiReadIssuerResponse;
certData: ParsedCertificateData;
breadcrumbs: Breadcrumb;
parsingErrors: string;

View file

@ -11,8 +11,6 @@ import { PkiListIssuersListEnum } from '@hashicorp/vault-client-typescript';
* the overview, roles, issuers, certificates, and key routes all need to be aware of the whether there is a config for the engine
* if the user has not configured they are prompted to do so in each of the routes
* decorate the necessary routes to perform the check in the beforeModel hook since that may change what is returned for the model
*
* while this decorator is similar to fetch-secrets-engine-config in the core addon, the differences were enough to warrant a specific decorator for pki
*/
export function withConfig() {

View file

@ -11,24 +11,19 @@ import { paginate } from 'core/utils/paginate-list';
import type RouterService from '@ember/routing/router-service';
import type { ModelFrom } from 'vault/vault/route';
import type SyncDestinationModel from 'vault/vault/models/sync/destination';
import type Controller from '@ember/controller';
import type ApiService from 'vault/services/api';
import type CapabilitiesService from 'vault/services/capabilities';
import { ListDestination } from 'vault/vault/sync';
export type SyncSecretsDestinationsRouteModel = ModelFrom<SyncSecretsDestinationsIndexRoute>;
interface SyncSecretsDestinationsIndexRouteParams {
name?: string;
type?: string;
page?: string;
}
interface SyncSecretsDestinationsRouteModel {
destinations: SyncDestinationModel[];
nameFilter: string | undefined;
typeFilter: string | undefined;
}
interface SyncSecretsDestinationsController extends Controller {
model: SyncSecretsDestinationsRouteModel;
page: number | undefined;
@ -53,7 +48,7 @@ export default class SyncSecretsDestinationsIndexRoute extends Route {
},
};
redirect(model: ModelFrom<SyncSecretsDestinationsIndexRoute>) {
redirect(model: SyncSecretsDestinationsRouteModel) {
if (!model.destinations.meta.total) {
this.router.transitionTo('vault.cluster.sync.secrets.overview');
}

View file

@ -14,6 +14,7 @@ import hbs from 'htmlbars-inline-precompile';
import { setRunOptions } from 'ember-a11y-testing/test-support';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { KUBERNETES_OVERVIEW } from 'vault/tests/helpers/kubernetes/kubernetes-selectors';
import SecretsEngineResource from 'vault/resources/secrets/engine';
module('Integration | Component | kubernetes | Page::Overview', function (hooks) {
setupRenderingTest(hooks);
@ -21,35 +22,21 @@ module('Integration | Component | kubernetes | Page::Overview', function (hooks)
setupMirage(hooks);
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.store.pushPayload('secret-engine', {
modelName: 'secret-engine',
data: {
accessor: 'kubernetes_f3400dee',
path: 'kubernetes-test/',
type: 'kubernetes',
},
this.secretsEngine = new SecretsEngineResource({
accessor: 'kubernetes_f3400dee',
path: 'kubernetes-test/',
type: 'kubernetes',
});
this.store.pushPayload('kubernetes/role', {
modelName: 'kubernetes/role',
backend: 'kubernetes-test',
...this.server.create('kubernetes-role'),
});
this.store.pushPayload('kubernetes/role', {
modelName: 'kubernetes/role',
backend: 'kubernetes-test',
...this.server.create('kubernetes-role'),
});
this.backend = this.store.peekRecord('secret-engine', 'kubernetes-test');
this.roles = this.store.peekAll('kubernetes/role');
this.roles = ['role-0', 'role-1'];
this.breadcrumbs = [
{ label: 'Secrets', route: 'secrets', linkExternal: true },
{ label: this.backend.id },
{ label: 'kubernetes-test' },
];
this.promptConfig = false;
this.renderComponent = () => {
return render(
hbs`<Page::Overview @promptConfig={{this.promptConfig}} @secretsEngine={{this.backend}} @roles={{this.roles}} @breadcrumbs={{this.breadcrumbs}} />`,
hbs`<Page::Overview @promptConfig={{this.promptConfig}} @secretsEngine={{this.secretsEngine}} @roles={{this.roles}} @breadcrumbs={{this.breadcrumbs}} />`,
{ owner: this.engine }
);
};

View file

@ -1,71 +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 | kubernetes/config', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.store.unloadAll('kubernetes/config');
});
test('it should make request to correct endpoint when querying record', async function (assert) {
assert.expect(1);
this.server.get('/kubernetes-test/config', () => {
assert.ok('GET request made to correct endpoint when querying record');
});
await this.store.queryRecord('kubernetes/config', { backend: 'kubernetes-test' });
});
test('it should make request to correct endpoint when creating new record', async function (assert) {
assert.expect(1);
this.server.post('/kubernetes-test/config', () => {
assert.ok('POST request made to correct endpoint when creating new record');
});
const record = this.store.createRecord('kubernetes/config', { backend: 'kubernetes-test' });
await record.save();
});
test('it should make request to correct endpoint when updating record', async function (assert) {
assert.expect(1);
this.server.post('/kubernetes-test/config', () => {
assert.ok('POST request made to correct endpoint when updating record');
});
this.store.pushPayload('kubernetes/config', {
modelName: 'kubernetes/config',
backend: 'kubernetes-test',
});
const record = this.store.peekRecord('kubernetes/config', 'kubernetes-test');
await record.save();
});
test('it should make request to correct endpoint when deleting record', async function (assert) {
assert.expect(1);
this.server.delete('/kubernetes-test/config', () => {
assert.ok('DELETE request made to correct endpoint when deleting record');
});
this.store.pushPayload('kubernetes/config', {
modelName: 'kubernetes/config',
backend: 'kubernetes-test',
});
const record = this.store.peekRecord('kubernetes/config', 'kubernetes-test');
await record.destroyRecord();
});
test('it should check the config vars endpoint', async function (assert) {
assert.expect(1);
this.server.get('/kubernetes-test/check', () => {
assert.ok('GET request made to config vars check endpoint');
});
await this.store.adapterFor('kubernetes/config').checkConfigVars('kubernetes-test');
});
});

View file

@ -1,76 +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 | kubernetes/role', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.store.unloadAll('kubernetes/role');
});
test('it should make request to correct endpoint when listing records', async function (assert) {
assert.expect(1);
this.server.get('/kubernetes-test/roles', (schema, req) => {
assert.ok(req.queryParams.list, 'GET request made to correct endpoint when listing records');
return { data: { keys: ['test-role'] } };
});
await this.store.query('kubernetes/role', { backend: 'kubernetes-test' });
});
test('it should make request to correct endpoint when querying record', async function (assert) {
assert.expect(1);
this.server.get('/kubernetes-test/roles/test-role', () => {
assert.ok('GET request made to correct endpoint when querying record');
return { data: {} };
});
await this.store.queryRecord('kubernetes/role', { backend: 'kubernetes-test', name: 'test-role' });
});
test('it should make request to correct endpoint when creating new record', async function (assert) {
assert.expect(1);
this.server.post('/kubernetes-test/roles/test-role', () => {
assert.ok('POST request made to correct endpoint when creating new record');
});
const record = this.store.createRecord('kubernetes/role', {
backend: 'kubernetes-test',
name: 'test-role',
});
await record.save();
});
test('it should make request to correct endpoint when updating record', async function (assert) {
assert.expect(1);
this.server.post('/kubernetes-test/roles/test-role', () => {
assert.ok('POST request made to correct endpoint when updating record');
});
this.store.pushPayload('kubernetes/role', {
modelName: 'kubernetes/role',
backend: 'kubernetes-test',
name: 'test-role',
});
const record = this.store.peekRecord('kubernetes/role', 'test-role');
await record.save();
});
test('it should make request to correct endpoint when deleting record', async function (assert) {
assert.expect(1);
this.server.delete('/kubernetes-test/roles/test-role', () => {
assert.ok('DELETE request made to correct endpoint when deleting record');
});
this.store.pushPayload('kubernetes/role', {
modelName: 'kubernetes/role',
backend: 'kubernetes-test',
name: 'test-role',
});
const record = this.store.peekRecord('kubernetes/role', 'test-role');
await record.destroyRecord();
});
});

View file

@ -1,114 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import sinon from 'sinon';
import Route from '@ember/routing/route';
import { withConfig } from 'core/decorators/fetch-secrets-engine-config';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { service } from '@ember/service';
import { Response } from 'miragejs';
module('Unit | Decorators | fetch-secrets-engine-config', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
hooks.beforeEach(function () {
this.spy = sinon.spy(console, 'debug');
this.store = this.owner.lookup('service:store');
this.backend = 'test-path';
this.owner.lookup('service:secretMountPath').update(this.backend);
this.createClass = () => {
@withConfig('kubernetes/config')
class Foo extends Route {
@service store;
@service secretMountPath;
}
// service injection will fail if class is not instantiated with an owner
return new Foo(this.owner);
};
});
hooks.afterEach(function () {
this.spy.restore();
});
test('it should warn when applying decorator to class that does not extend Route', function (assert) {
@withConfig()
class Foo {} // eslint-disable-line
const message =
'withConfig decorator must be used on an instance of Ember Route class. Decorator not applied to returned class';
assert.ok(this.spy.calledWith(message), 'Error is printed to console');
});
test('it should return cached record from store if it exists', async function (assert) {
this.store.pushPayload('kubernetes/config', {
modelName: 'kubernetes/config',
backend: this.backend,
});
const peekSpy = sinon.spy(this.store, 'peekRecord');
const route = this.createClass();
await route.beforeModel();
assert.true(peekSpy.calledWith('kubernetes/config', this.backend), 'peekRecord called for config model');
assert.strictEqual(route.configModel.backend, this.backend, 'config model set on class');
assert.strictEqual(route.configError, null, 'error is unset when model is found');
assert.false(route.promptConfig, 'promptConfig is false when model is found');
});
test('it should fetch record when not in the store', async function (assert) {
assert.expect(4);
this.server.get('/test-path/config', () => {
assert.ok(true, 'fetch request is made');
return {};
});
const route = this.createClass();
await route.beforeModel();
assert.strictEqual(route.configModel.backend, this.backend, 'config model set on class');
assert.strictEqual(route.configError, null, 'error is unset when model is found');
assert.false(route.promptConfig, 'promptConfig is false when model is found');
});
test('it should set prompt value when fetch returns a 404', async function (assert) {
assert.expect(4);
this.server.get('/test-path/config', () => {
assert.ok(true, 'fetch request is made');
return new Response(404, {}, { errors: [] });
});
const route = this.createClass();
await route.beforeModel();
assert.strictEqual(route.configModel, null, 'config is not set when error is returned');
assert.strictEqual(route.configError, null, 'error is unset when 404 is returned');
assert.true(route.promptConfig, 'promptConfig is true when 404 is returned');
});
test('it should set error value when fetch returns error other than 404', async function (assert) {
assert.expect(4);
const error = { errors: ['Permission denied'] };
this.server.get('/test-path/config', () => {
assert.ok(true, 'fetch request is made');
return new Response(403, {}, error);
});
const route = this.createClass();
await route.beforeModel();
assert.strictEqual(route.configModel, null, 'config is not set when error is returned');
assert.deepEqual(
route.configError.errors,
error.errors,
'error is set when error other than 404 is returned'
);
assert.false(route.promptConfig, 'promptConfig is false when error other than 404 is returned');
});
});

View file

@ -1,29 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from '@ember-data/store';
import { AdapterRegistry } from 'ember-data/adapter';
export interface LdapLibraryAccountStatus {
account: string;
available: boolean;
library: string;
borrower_client_token?: string;
borrower_entity_id?: string;
}
export interface LdapLibraryCheckOutCredentials {
account: string;
password: string;
lease_id: string;
lease_duration: number;
renewable: boolean;
}
export default interface LdapLibraryAdapter extends AdapterRegistry {
fetchCheckOutStatus(backend: string, name: string): Promise<Array<LdapLibraryAccountStatus>>;
checkOutAccount(backend: string, name: string, ttl?: string): Promise<LdapLibraryCheckOutCredentials>;
checkInAccount(backend: string, name: string, service_account_names: Array<string>): Promise<void>;
}

View file

@ -1,12 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from '@ember-data/store';
import { AdapterRegistry } from 'ember-data/adapter';
export default interface LdapRoleAdapter extends AdapterRegistry {
fetchCredentials(backend: string, type: string, name: string);
rotateStaticPassword(backend: string, name: string);
}

View file

@ -1,12 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from '@ember-data/store';
import { AdapterRegistry } from 'ember-data/adapter';
export default interface PkiIssuerAdapter extends AdapterRegistry {
namespace: string;
deleteAllIssuers(backend: string);
}

View file

@ -1,13 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from '@ember-data/store';
import { AdapterRegistry } from 'ember-data/adapter';
export default interface PkiRoleAdapter extends AdapterRegistry {
namespace: string;
_urlForRole(backend: string, id: string): string;
_optionsForQuery(id: string): { data: unknown };
}

View file

@ -1,12 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from '@ember-data/store';
import { AdapterRegistry } from 'ember-data/adapter';
export default interface PkiTidyAdapter extends AdapterRegistry {
namespace: string;
cancelTidy(backend: string);
}

View file

@ -1,35 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from '@ember-data/store';
import { AdapterRegistry } from 'ember-data/adapter';
import type { SyncDestinationQueryData } from './destination';
export interface SyncStatus {
destinationType: string;
destinationName: string;
syncStatus: string;
updatedAt: string;
}
export interface SyncDestinationAssociationMetrics {
icon: string;
name: string;
associationCount: number;
status: string;
lastUpdated: Date;
}
export interface SyncAssociationMetrics {
total_associations: number;
total_secrets: number;
}
export default interface LdapLibraryAdapter extends AdapterRegistry {
queryAll(): Promise<SyncAssociationMetrics>;
fetchSyncStatus(mount: string, secretName: string): SyncStatus[];
fetchByDestinations(destinations: SyncDestinationQueryData[]): Promise<SyncDestinationAssociationMetrics[]>;
}

View file

@ -1,17 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Store from '@ember-data/store';
import { AdapterRegistry } from 'ember-data/adapter';
export interface SyncDestinationQueryData {
id: string;
name: string;
type: string;
}
export default interface LdapLibraryAdapter extends AdapterRegistry {
normalizedQuery(): Promise<SyncDestinationQueryData[]>;
}

View file

@ -1,37 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Model from '@ember-data/model';
import CapabilitiesModel from '../capabilities';
export default class KvSecretDataModel extends Model {
backend: string;
path: string;
secretData: object;
createdTime: string;
customMetadata: object;
deletionTime: string;
destroyed: boolean;
versions: object;
failReadErrorCode: number;
casVersion: number;
// apiPaths for capabilities
dataPath: Promise<CapabilitiesModel>;
metadataPath: Promise<CapabilitiesModel>;
deletePath: Promise<CapabilitiesModel>;
destroyPath: Promise<CapabilitiesModel>;
undeletePath: Promise<CapabilitiesModel>;
// Capabilities
get canDeleteLatestVersion(): boolean;
get canDeleteVersion(): boolean;
get canUndelete(): boolean;
get canDestroyVersion(): boolean;
get canEditData(): boolean;
get canReadData(): boolean;
get canReadMetadata(): boolean;
get canUpdateMetadata(): boolean;
get canListMetadata(): boolean;
}

View file

@ -1,35 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import Model from '@ember-data/model';
export default class KvSecretMetadataModel extends Model {
backend: string;
path: string;
fullSecretPath: string;
maxVersions: number;
casRequired: boolean;
deleteVersionAfter: string;
customMetadata: object;
createdTime: string;
currentVersion: number;
oldestVersion: number;
updatedTime: string;
versions: object;
// apiPaths for capabilities
dataPath: Promise<CapabilitiesModel>;
metadataPath: Promise<CapabilitiesModel>;
get pathIsDirectory(): boolean;
get isSecretDeleted(): boolean;
get sortedVersions(): number[];
get currentSecret(): { state: string; isDeactivated: boolean };
// Capabilities
get canDeleteMetadata(): boolean;
get canReadMetadata(): boolean;
get canUpdateMetadata(): boolean;
get canCreateVersionData(): boolean;
}

View file

@ -1,25 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithFormFieldsAndValidationsModel } from 'vault/app-types';
export default interface LdapConfigModel extends WithFormFieldsAndValidationsModel {
backend: string;
binddn: string;
bindpass: string;
url: string;
schema: string;
password_policy: string;
starttls: boolean;
insecure_tls: boolean;
certificate: string;
client_tls_cert: string;
client_tls_key: string;
userdn: string;
userattr: string;
upndomain: string;
connection_timeout: number;
request_timeout: number;
rotateRoot(): Promise;
}

View file

@ -1,38 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithFormFieldsAndValidationsModel } from 'vault/app-types';
import type { FormField } from 'vault/app-types';
import CapabilitiesModel from '../capabilities';
import type {
LdapLibraryAccountStatus,
LdapLibraryCheckOutCredentials,
} from 'vault/vault/adapters/ldap/library';
export default interface LdapLibraryModel extends WithFormFieldsAndValidationsModel {
backend: string;
name: string;
path_to_library: string;
service_account_names: string;
default_ttl: number;
max_ttl: number;
disable_check_in_enforcement: string;
get completeLibraryName(): string;
get displayFields(): Array<FormField>;
libraryPath: CapabilitiesModel;
statusPath: CapabilitiesModel;
checkOutPath: CapabilitiesModel;
checkInPath: CapabilitiesModel;
get canCreate(): boolean;
get canDelete(): boolean;
get canEdit(): boolean;
get canRead(): boolean;
get canList(): boolean;
get canReadStatus(): boolean;
get canCheckOut(): boolean;
get canCheckIn(): boolean;
fetchStatus(): Promise<Array<LdapLibraryAccountStatus>>;
checkOutAccount(ttl?: string): Promise<LdapLibraryCheckOutCredentials>;
checkInAccount(account: string): Promise<void>;
}

View file

@ -1,42 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithFormFieldsAndValidationsModel } from 'vault/app-types';
import type { FormField } from 'vault/app-types';
import CapabilitiesModel from '../capabilities';
import { LdapDynamicRoleCredentials, LdapStaticRoleCredentials } from 'ldap/routes/roles/role/credentials';
export default interface LdapRoleModel extends WithFormFieldsAndValidationsModel {
id: string;
type: string;
backend: string;
path_to_role: string;
name: string;
dn: string;
username: string;
rotation_period: string;
default_ttl: string;
max_ttl: string;
username_template: string;
creation_ldif: string;
rollback_ldif: string;
get completeRoleName(): string;
get isStatic(): string;
get isDynamic(): string;
get fieldsForType(): Array<string>;
get displayFields(): Array<FormField>;
get roleUri(): string;
get credsUri(): string;
rolePath: CapabilitiesModel;
credsPath: CapabilitiesModel;
staticRotateCredsPath: CapabilitiesModel;
get canCreate(): boolean;
get canDelete(): boolean;
get canEdit(): boolean;
get canRead(): boolean;
get canList(): boolean;
get canReadCreds(): boolean;
get canRotateStaticCreds(): boolean;
fetchCredentials(): Promise<LdapDynamicRoleCredentials | LdapStaticRoleCredentials>;
rotateStaticPassword(): Promise<void>;
}

View file

@ -1,60 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { StringMap, WithFormFieldsAndValidationsModel } from 'vault/app-types';
import type CapabilitiesModel from '../capabilities';
type PkiActionModel = WithFormFieldsAndValidationsModel & {
secretMountPath: unknown;
actionType: string | null;
pemBundle: string;
importedIssuers: string[];
importedKeys: string[];
mapping: StringMap;
type: string;
issuerName: string;
keyName: string;
keyRef: string;
commonName: string;
altNames: string[];
ipSans: string[];
uriSans: string[];
otherSans: string[];
format: string;
privateKeyFormat: string;
keyType: string;
keyBits: string;
maxPathLength: number;
excludeCnFromSans: boolean;
permittedDnsDomains: string;
ou: string[];
serialNumber: string;
addBasicConstraints: boolean;
notBeforeDuration: string;
managedKeyName: string;
managedKeyId: string;
customTtl: string;
ttl: string;
notAfter: string;
issuerId: string;
csr: string;
caChain: string;
keyId: string;
privateKey: string;
privateKeyType: string;
get backend(): string;
// apiPaths for capabilities
importBundlePath: Promise<CapabilitiesModel>;
generateIssuerRootPath: Promise<CapabilitiesModel>;
generateIssuerCsrPath: Promise<CapabilitiesModel>;
crossSignPath: string;
// Capabilities
get canImportBundle(): boolean;
get canGenerateIssuerRoot(): boolean;
get canGenerateIssuerIntermediate(): boolean;
get canCrossSign(): boolean;
};
export default PkiActionModel;

View file

@ -1,30 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithFormFieldsAndValidationsModel } from 'vault/app-types';
type PkiCertificateBaseModel = WithFormFieldsAndValidationsModel & {
secretMountPath: class;
get backend(): string;
altNames: string;
commonName: string;
caChain: string;
certificate: string;
excludeCnFromSans: boolean;
expiration: number;
ipSans: string;
issuingCa: string;
notValidAfter: date;
notValidBefore: date;
otherSans: string;
privateKey: string;
privateKeyType: string;
revokePath: string;
revocationTime: number;
serialNumber: string;
get canRevoke(): boolean;
};
export default PkiCertificateBaseModel;

View file

@ -1,12 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type PkiCertificateBaseModel from './base';
type PkiCertificateGenerateModel = PkiCertificateBaseModel & {
role: string;
};
export default PkiCertificateGenerateModel;

View file

@ -1,21 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type PkiCertificateBaseModel from './base';
type PkiCertificateSignIntermediateModel = PkiCertificateBaseModel & {
role: string;
csr: string;
issuerRef: string;
maxPathLength: string;
notBeforeDuration: string;
permittedDnsDomains: string;
useCsrValues: boolean;
usePss: boolean;
skid: string;
signatureBits: string;
};
export default PkiCertificateSignIntermediateModel;

View file

@ -1,14 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type PkiCertificateBaseModel from './base';
export type PkiCertificateSignModel = PkiCertificateBaseModel & {
role: string;
csr: string;
removeRootsFromChain: boolean;
};
export default PkiCertificateSignModel;

View file

@ -1,22 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { Model } from 'vault/app-types';
import type CapabilitiesModel from 'vault/models/capabilities';
type PkiConfigAcmeModel = Model & {
enabled: boolean;
defaultDirectoryPolicy: string;
allowedRoles: string[];
allowRoleExtKeyUsage: boolean;
allowedIssuers: string[];
eabPolicy: string;
dnsResolver: string;
maxTtl: string;
acmePath: CapabilitiesModel;
get canSet(): boolean;
};
export default PkiConfigAcmeModel;

View file

@ -1,16 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { Model } from 'vault/app-types';
import type CapabilitiesModel from 'vault/models/capabilities';
type PkiConfigClusterModel = Model & {
path: boolean;
aiaPath: string;
clusterPath: CapabilitiesModel;
get canSet(): boolean;
};
export default PkiConfigClusterModel;

View file

@ -1,21 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithFormFieldsModel } from 'vault/app-types';
type PkiConfigCrlModel = WithFormFieldsModel & {
autoRebuild: boolean;
autoRebuildGracePeriod: string;
enableDelta: boolean;
expiry: string;
deltaRebuildInterval: string;
disable: boolean;
ocspExpiry: string;
ocspDisable: boolean;
crlPath: string;
get canSet(): boolean;
};
export default PkiConfigCrlModel;

View file

@ -1,16 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { Model } from 'vault/app-types';
type PkiConfigUrlsModel = Model & {
issuingCertificates: array;
crlDistributionPoints: array;
ocspServers: array;
urlsPath: string;
get canSet(): boolean;
};
export default PkiConfigUrlsModel;

View file

@ -1,39 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithFormFieldsAndValidationsModel } from 'vault/app-types';
import type { ParsedCertificateData } from 'vault/utils/parse-pki-cert';
import type CapabilitiesModel from 'vault/models/capabilities';
type PkiIssuerModel = WithFormFieldsAndValidationsModel & {
secretMountPath: class;
get backend(): string;
get issuerRef(): string;
certificate: string;
issuerId: string;
issuerName: string;
keyId: string;
uriSans: string;
leafNotAfterBehavior: string;
usage: string;
manualChain: string;
issuingCertificates: string;
crlDistributionPoints: string;
ocspServers: string;
parsedCertificate: ParsedCertificateData;
rotateExported: CapabilitiesModel;
rotateInternal: CapabilitiesModel;
rotateExisting: CapabilitiesModel;
crossSignPath: CapabilitiesModel;
signIntermediate: CapabilitiesModel;
pemBundle: string;
importedIssuers: string[];
importedKeys: string[];
get canRotateIssuer(): boolean;
get canCrossSign(): boolean;
get canSignIntermediate(): boolean;
get canConfigure(): boolean;
};
export default PkiIssuerModel;

View file

@ -1,25 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithValidationsModel } from 'vault/app-types';
type PkiKeyModel = WithValidationsModel & {
secretMountPath: class;
keyId: string;
keyName: string;
type: string;
keyType: string;
keyBits: string;
pemBundle: string;
privateKey: string;
get backend(): string;
get canRead(): boolean;
get canEdit(): boolean;
get canDelete(): boolean;
get canGenerateKey(): boolean;
get canImportKey(): boolean;
};
export default PkiKeyModel;

View file

@ -1,15 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithValidationsModel } from 'vault/app-types';
type PkiRoleModel = WithValidationsModel & {
name: string;
issuerRef: string;
keyType: string;
keyBits: string | undefined;
};
export default PkiRoleModel;

View file

@ -1,34 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { FormField, FormFieldGroups, WithFormFieldsModel } from 'vault/app-types';
type PkiTidyModel = WithFormFieldsModel & {
version: string;
acmeAccountSafetyBuffer: string;
tidyAcme: boolean;
enabled: boolean;
intervalDuration: string;
minStartupBackoffDuration: string;
maxStartupBackoffDuration: string;
issuerSafetyBuffer: string;
pauseDuration: string;
revocationQueueSafetyBuffer: string;
safetyBuffer: string;
tidyCertMetadata: boolean;
tidyCertStore: boolean;
tidyCrossClusterRevokedCerts: boolean;
tidyExpiredIssuers: boolean;
tidyMoveLegacyCaBundle: boolean;
tidyRevocationQueue: boolean;
tidyRevokedCertIssuerAssociations: boolean;
tidyRevokedCerts: boolean;
allByKey: {
intervalDuration: FormField[];
};
get sharedFields(): FormFieldGroups[];
};
export default PkiTidyModel;

View file

@ -1,18 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { Model } from 'vault/app-types';
export default interface SyncAssocationModel extends Model {
mount: string;
secretName: string;
syncStatus: string;
updatedAt: string;
destinationName: string;
destinationType: string;
get canSync(): boolean;
get canUnsync(): boolean;
}

View file

@ -1,50 +0,0 @@
/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import type { WithFormFieldsAndValidationsModel } from 'vault/app-types';
export default interface SyncDestinationModel extends WithFormFieldsAndValidationsModel {
name: string;
type: string;
secretNameTemplate: string;
purgeInitiatedAt: string;
purgeError: string;
get icon(): string;
get typeDisplayName(): string;
get maskedParams(): string[];
// aws-sm
accessKeyId?: string;
secretAccessKey?: string;
region?: string;
// azure-kv
keyVaultUri?: string;
clientId?: string;
clientSecret?: string;
tenantId?: string;
cloud?: string;
// gcp
credentials?: string;
// gh
accessToken?: string;
repositoryOwner?: string;
repositoryName?: string;
// vercel project
accessToken?: string;
projectId?: string;
teamId?: string;
deploymentEnvironments?: array;
get canCreate(): boolean;
get canDelete(): boolean;
get canEdit(): boolean;
get canRead(): boolean;
get canSync(): boolean;
}