mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 04:16:31 +02:00
[UI] Ember Data Migration - Auth Method List/Config (#31203)
* updates auth method list and config views to use api service * adds capabilities checks to auth methods route * fixes auth method config tests * updates SecretsEngine type to Mount * updates listingVisibility value in config test * adds missing copyright header
This commit is contained in:
parent
2f4b1c493e
commit
75e1108750
@ -8,6 +8,7 @@ import { service } from '@ember/service';
|
||||
import { sanitizePath } from 'core/utils/sanitize-path';
|
||||
import { encodePath } from 'vault/utils/path-encoding-helpers';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { getOwner } from '@ember/owner';
|
||||
|
||||
export default class GeneratedItemListAdapter extends ApplicationAdapter {
|
||||
@service store;
|
||||
@ -28,8 +29,10 @@ export default class GeneratedItemListAdapter extends ApplicationAdapter {
|
||||
return this.paths.deletePath || '';
|
||||
}
|
||||
|
||||
getDynamicApiPath(id) {
|
||||
const result = this.store.peekRecord('auth-method', id);
|
||||
getDynamicApiPath() {
|
||||
const result = getOwner(this)
|
||||
.lookup('route:vault.cluster.access.method')
|
||||
.modelFor('vault.cluster.access.method');
|
||||
this.apiPath = result.apiPath;
|
||||
return result.apiPath;
|
||||
}
|
||||
|
||||
20
ui/app/components/auth-method/configuration.hbs
Normal file
20
ui/app/components/auth-method/configuration.hbs
Normal file
@ -0,0 +1,20 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#if @method.directLoginLink}}
|
||||
<InfoTableRow @alwaysRender={{true}} @label="UI login link">
|
||||
<Hds::Copy::Snippet @textToCopy={{@method.directLoginLink}} />
|
||||
</InfoTableRow>
|
||||
{{/if}}
|
||||
{{#each this.displayFields as |field|}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{not (is-empty-value (get @method field))}}
|
||||
@label={{this.label field}}
|
||||
@value={{this.value field}}
|
||||
@formatTtl={{this.isTtl field}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
60
ui/app/components/auth-method/configuration.ts
Normal file
60
ui/app/components/auth-method/configuration.ts
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { toLabel } from 'core/helpers/to-label';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
import type AuthMethodResource from 'vault/resources/auth/method';
|
||||
|
||||
interface Args {
|
||||
method: AuthMethodResource;
|
||||
}
|
||||
export default class AuthMethodConfigurationComponent extends Component<Args> {
|
||||
displayFields = [
|
||||
'type',
|
||||
'path',
|
||||
'description',
|
||||
'accessor',
|
||||
'local',
|
||||
'sealWrap',
|
||||
'config.listingVisibility',
|
||||
'config.defaultLeaseTtl',
|
||||
'config.maxLeaseTtl',
|
||||
'config.tokenType',
|
||||
'config.auditNonHmacRequestKeys',
|
||||
'config.auditNonHmacResponseKeys',
|
||||
'config.passthroughRequestHeaders',
|
||||
'config.allowedResponseHeaders',
|
||||
'config.pluginVersion',
|
||||
];
|
||||
|
||||
label = (field: string) => {
|
||||
const key = field.replace('config.', '');
|
||||
const label = toLabel([key]);
|
||||
// map specific fields to custom labels
|
||||
return (
|
||||
{
|
||||
listingVisibility: 'Use as preferred UI login method',
|
||||
defaultLeaseTtl: 'Default Lease TTL',
|
||||
maxLeaseTtl: 'Max Lease TTL',
|
||||
auditNonHmacRequestKeys: 'Request keys excluded from HMACing in audit',
|
||||
auditNonHmacResponseKeys: 'Response keys excluded from HMACing in audit',
|
||||
passthroughRequestHeaders: 'Allowed passthrough request headers',
|
||||
}[key] || label
|
||||
);
|
||||
};
|
||||
value = (field: string) => {
|
||||
const { method } = this.args;
|
||||
if (field === 'config.listingVisibility') {
|
||||
return method.config.listingVisibility === 'unauth';
|
||||
}
|
||||
return get(method, field);
|
||||
};
|
||||
|
||||
isTtl = (field: string) => {
|
||||
return ['config.defaultLeaseTtl', 'config.maxLeaseTtl'].includes(field);
|
||||
};
|
||||
}
|
||||
@ -26,23 +26,24 @@ export default class VaultClusterAccessMethodsController extends Controller {
|
||||
|
||||
// list returned by getter is sorted in template
|
||||
get authMethodList() {
|
||||
const { methods } = this.model;
|
||||
// return an options list to filter by engine type, ex: 'kv'
|
||||
if (this.selectedAuthType) {
|
||||
// check first if the user has also filtered by name.
|
||||
// names are individualized across type so you can't have the same name for an aws auth method and userpass.
|
||||
// this means it's fine to filter by first type and then name or just name.
|
||||
if (this.selectedAuthName) {
|
||||
return this.model.filter((method) => this.selectedAuthName === method.id);
|
||||
return methods.filter((method) => this.selectedAuthName === method.id);
|
||||
}
|
||||
// otherwise filter by auth type
|
||||
return this.model.filter((method) => this.selectedAuthType === method.type);
|
||||
return methods.filter((method) => this.selectedAuthType === method.type);
|
||||
}
|
||||
// return an options list to filter by auth name, ex: 'my-userpass'
|
||||
if (this.selectedAuthName) {
|
||||
return this.model.filter((method) => this.selectedAuthName === method.id);
|
||||
return methods.filter((method) => this.selectedAuthName === method.id);
|
||||
}
|
||||
// no filters, return full list
|
||||
return this.model;
|
||||
return methods;
|
||||
}
|
||||
|
||||
get authMethodArrayByType() {
|
||||
|
||||
63
ui/app/resources/auth/method.ts
Normal file
63
ui/app/resources/auth/method.ts
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { baseResourceFactory } from 'vault/resources/base-factory';
|
||||
import { service } from '@ember/service';
|
||||
import { supportedTypes } from 'vault/utils/supported-login-methods';
|
||||
import engineDisplayData from 'vault/helpers/engines-display-data';
|
||||
|
||||
import type { Mount } from 'vault/mount';
|
||||
import type VersionService from 'vault/services/version';
|
||||
import type NamespaceService from 'vault/services/namespace';
|
||||
import type { PathInfo } from 'vault/utils/openapi-helpers';
|
||||
|
||||
export default class AuthMethodResource extends baseResourceFactory<Mount>() {
|
||||
@service declare readonly version: VersionService;
|
||||
@service declare readonly namespace: NamespaceService;
|
||||
|
||||
id: string;
|
||||
declare paths: PathInfo;
|
||||
|
||||
constructor(data: Mount, context: unknown) {
|
||||
super(data, context);
|
||||
// strip trailing slash from path for id since it is used in routing
|
||||
this.id = data.path.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
// namespaces introduced types with a `ns_` prefix for built-in engines
|
||||
// so we need to strip that to normalize the type
|
||||
get methodType() {
|
||||
return this.type.replace(/^ns_/, '');
|
||||
}
|
||||
|
||||
get icon() {
|
||||
// methodType refers to the backend type (e.g., "aws", "azure")
|
||||
const engineData = engineDisplayData(this.methodType);
|
||||
return engineData?.glyph || 'users';
|
||||
}
|
||||
|
||||
get directLoginLink() {
|
||||
const ns = this.namespace.path;
|
||||
const nsQueryParam = ns ? `namespace=${encodeURIComponent(ns)}&` : '';
|
||||
const isSupported = supportedTypes(this.version.isEnterprise).includes(this.methodType);
|
||||
return isSupported
|
||||
? `${window.origin}/ui/vault/auth?${nsQueryParam}with=${encodeURIComponent(this.path)}`
|
||||
: '';
|
||||
}
|
||||
|
||||
// used when the `auth` prefix is important,
|
||||
// currently only when setting perf mount filtering
|
||||
get apiPath() {
|
||||
return `auth/${this.path}`;
|
||||
}
|
||||
|
||||
get localDisplay() {
|
||||
return this.local ? 'local' : 'replicated';
|
||||
}
|
||||
|
||||
get supportsUserLockoutConfig() {
|
||||
return ['approle', 'ldap', 'userpass'].includes(this.methodType);
|
||||
}
|
||||
}
|
||||
@ -3,18 +3,27 @@
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { getOwner, setOwner } from '@ember/owner';
|
||||
|
||||
import type Owner from '@ember/owner';
|
||||
|
||||
abstract class BaseResource<T> {
|
||||
// pass data that the resource should represent (typically from an API response) to constructor
|
||||
// object properties will be assigned to class instance
|
||||
// extending classes can define getters and additional properties/methods that are required widely across the app
|
||||
constructor(readonly data: T) {
|
||||
constructor(data: T, context?: unknown) {
|
||||
Object.assign(this, data) as T;
|
||||
// pass in context (this) of Ember class (route, component etc.) where the resource is being constructed
|
||||
// this will be used to set the owner on the class so that services can be injected (if required)
|
||||
if (context) {
|
||||
setOwner(this, getOwner(context) as Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// factory that allows for the BaseResource class to be casted to the specific type provided
|
||||
// factory that allows for the BaseResource class to be cast to the specific type provided
|
||||
// without this the compiler is not aware of the properties set on the class via Object.assign
|
||||
// example usage -> export default class SecretsEngineResource extends baseResourceFactory<SecretsEngine>() { ... }
|
||||
// example usage -> export default class SecretsEngineResource extends baseResourceFactory<Mount>() { ... }
|
||||
export function baseResourceFactory<T>() {
|
||||
return BaseResource as new (data: T) => T;
|
||||
return BaseResource as new (data: T, context?: unknown) => T;
|
||||
}
|
||||
|
||||
@ -8,14 +8,14 @@ import { supportedSecretBackends } from 'vault/helpers/supported-secret-backends
|
||||
import { isAddonEngine } from 'vault/utils/all-engines-metadata';
|
||||
import engineDisplayData from 'vault/helpers/engines-display-data';
|
||||
|
||||
import type { SecretsEngine } from 'vault/secrets/engine';
|
||||
import type { Mount } from 'vault/mount';
|
||||
|
||||
export default class SecretsEngineResource extends baseResourceFactory<SecretsEngine>() {
|
||||
export default class SecretsEngineResource extends baseResourceFactory<Mount>() {
|
||||
id: string;
|
||||
|
||||
#LIST_EXCLUDED_BACKENDS = ['system', 'identity'];
|
||||
|
||||
constructor(data: SecretsEngine) {
|
||||
constructor(data: Mount) {
|
||||
super(data);
|
||||
// strip trailing slash from path for id since it is used in routing
|
||||
this.id = data.path.replace(/\/$/, '');
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import AdapterError from '@ember-data/adapter/error';
|
||||
import { set } from '@ember/object';
|
||||
import Route from '@ember/routing/route';
|
||||
import { service } from '@ember/service';
|
||||
import { supportedManagedAuthBackends } from 'vault/helpers/supported-managed-auth-backends';
|
||||
|
||||
export default Route.extend({
|
||||
store: service(),
|
||||
pathHelp: service('path-help'),
|
||||
|
||||
model(params) {
|
||||
const { path } = params;
|
||||
return this.store.query('auth-method', {}).then((modelArray) => {
|
||||
const model = modelArray.find((m) => m.id === path);
|
||||
if (!model) {
|
||||
const error = new AdapterError();
|
||||
set(error, 'httpStatus', 404);
|
||||
throw error;
|
||||
}
|
||||
const supportManaged = supportedManagedAuthBackends();
|
||||
if (!supportManaged.includes(model.methodType)) {
|
||||
// do not fetch path-help for unmanaged auth types
|
||||
model.set('paths', {
|
||||
apiPath: model.apiPath,
|
||||
paths: [],
|
||||
});
|
||||
return model;
|
||||
}
|
||||
return this.pathHelp.getPaths(model.apiPath, path).then((paths) => {
|
||||
model.set('paths', paths);
|
||||
return model;
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
42
ui/app/routes/vault/cluster/access/method.ts
Normal file
42
ui/app/routes/vault/cluster/access/method.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { service } from '@ember/service';
|
||||
import { supportedManagedAuthBackends } from 'vault/helpers/supported-managed-auth-backends';
|
||||
import AuthMethodResource from 'vault/resources/auth/method';
|
||||
|
||||
import type ApiService from 'vault/services/api';
|
||||
import type PathHelpService from 'vault/services/path-help';
|
||||
|
||||
export default class VaultClusterAccessMethodRoute extends Route {
|
||||
@service declare readonly api: ApiService;
|
||||
@service declare readonly pathHelp: PathHelpService;
|
||||
|
||||
async model(params: { path: string }) {
|
||||
const { path } = params;
|
||||
const { auth } = await this.api.sys.internalUiListEnabledVisibleMounts();
|
||||
const methods = this.api
|
||||
.responseObjectToArray(auth, 'path')
|
||||
.map((method) => new AuthMethodResource(method, this));
|
||||
const method = methods.find((m) => m.id === path);
|
||||
// the user could have entered a random path in the URL that doesn't correspond to an existing method
|
||||
if (method) {
|
||||
const supportManaged = supportedManagedAuthBackends();
|
||||
// do not fetch path-help for unmanaged auth types
|
||||
if (!supportManaged.includes(method.methodType)) {
|
||||
method.paths = { apiPath: method.apiPath, paths: [], itemTypes: [] };
|
||||
return method;
|
||||
}
|
||||
return this.pathHelp.getPaths(method.apiPath, path, '', '').then((pathInfo) => {
|
||||
method.paths = pathInfo;
|
||||
return method;
|
||||
});
|
||||
} else {
|
||||
// throw a 404 if the path doesn't match any of the fetched methods
|
||||
throw { httpStatus: 404, path };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,14 +18,13 @@ export default Route.extend({
|
||||
return this.modelFor('vault.cluster.access.method');
|
||||
},
|
||||
|
||||
setupController(controller) {
|
||||
setupController(controller, model) {
|
||||
const { section_name: section } = this.paramsFor(this.routeName);
|
||||
this._super(...arguments);
|
||||
controller.set('section', section);
|
||||
const method = this.modelFor('vault.cluster.access.method');
|
||||
controller.set(
|
||||
'paths',
|
||||
method.paths.paths.filter((path) => path.navigation)
|
||||
model.paths.paths.filter((path) => path.navigation)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { service } from '@ember/service';
|
||||
|
||||
export default class VaultClusterAccessMethodsRoute extends Route {
|
||||
@service store;
|
||||
|
||||
queryParams = {
|
||||
page: {
|
||||
refreshModel: true,
|
||||
},
|
||||
pageFilter: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
model() {
|
||||
return this.store.query('auth-method', {});
|
||||
}
|
||||
}
|
||||
48
ui/app/routes/vault/cluster/access/methods.ts
Normal file
48
ui/app/routes/vault/cluster/access/methods.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { service } from '@ember/service';
|
||||
import AuthMethodResource from 'vault/resources/auth/method';
|
||||
|
||||
import type ApiService from 'vault/services/api';
|
||||
import type Capabilities from 'vault/services/capabilities';
|
||||
|
||||
export default class VaultClusterAccessMethodsRoute extends Route {
|
||||
@service declare readonly api: ApiService;
|
||||
@service declare readonly capabilities: Capabilities;
|
||||
|
||||
queryParams = {
|
||||
page: {
|
||||
refreshModel: true,
|
||||
},
|
||||
pageFilter: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
async model() {
|
||||
const { auth } = await this.api.sys.internalUiListEnabledVisibleMounts();
|
||||
|
||||
const methods = this.api
|
||||
.responseObjectToArray(auth, 'path')
|
||||
.map((method) => new AuthMethodResource(method, this));
|
||||
|
||||
const paths = methods.reduce((paths: string[], { path, methodType }) => {
|
||||
paths.push(
|
||||
this.capabilities.pathFor('authMethodConfig', { path }),
|
||||
this.capabilities.pathFor('authMethodDelete', { path })
|
||||
);
|
||||
if (methodType === 'aws') {
|
||||
paths.push(this.capabilities.pathFor('authMethodConfigAws', { path }));
|
||||
}
|
||||
return paths;
|
||||
}, []);
|
||||
|
||||
const capabilities = this.capabilities.fetch(paths);
|
||||
|
||||
return { methods, capabilities };
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#if @model.directLoginLink}}
|
||||
<InfoTableRow @alwaysRender={{true}} @label="UI login link">
|
||||
<Hds::Copy::Snippet @textToCopy={{@model.directLoginLink}} />
|
||||
</InfoTableRow>
|
||||
{{/if}}
|
||||
{{#each @model.attrs as |attr|}}
|
||||
{{#if (eq attr.type "object")}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{not (is-empty-value (get @model attr.name))}}
|
||||
@label={{or attr.options.label (to-label attr.name)}}
|
||||
@value={{stringify (get @model attr.name)}}
|
||||
@formatTtl={{eq attr.options.editType "ttl"}}
|
||||
/>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{not (is-empty-value (get @model attr.name))}}
|
||||
@label={{or attr.options.label (to-label attr.name)}}
|
||||
@value={{get @model attr.name}}
|
||||
@formatTtl={{eq attr.options.editType "ttl"}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
@ -36,4 +36,4 @@
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
{{component (concat "auth-method/" this.section) model=this.model}}
|
||||
{{component (concat "auth-method/" this.section) method=this.model}}
|
||||
@ -83,16 +83,28 @@
|
||||
<dd.Interactive @route="vault.cluster.access.method.section" @models={{array method.id "configuration"}}>
|
||||
View configuration
|
||||
</dd.Interactive>
|
||||
{{#if (or method.canEdit (and (eq method.methodType "aws") method.canEditAws))}}
|
||||
{{#if
|
||||
(or
|
||||
(has-capability this.model.capabilities "update" pathKey="authMethodConfig" params=method.id)
|
||||
(and
|
||||
(eq method.methodType "aws")
|
||||
(has-capability this.model.capabilities "update" pathKey="authMethodConfigAws" params=method.id)
|
||||
)
|
||||
)
|
||||
}}
|
||||
<dd.Interactive @route="vault.cluster.settings.auth.configure" @model={{method.id}}>
|
||||
Edit configuration
|
||||
</dd.Interactive>
|
||||
{{/if}}
|
||||
{{#if (and (not-eq method.methodType "token") method.canDisable)}}
|
||||
<dd.Interactive
|
||||
@color="critical"
|
||||
{{on "click" (fn (mut this.methodToDisable) method)}}
|
||||
>Disable</dd.Interactive>
|
||||
{{#if
|
||||
(and
|
||||
(not-eq method.methodType "token")
|
||||
(has-capability this.model.capabilities "delete" pathKey="authMethodDelete" params=method.id)
|
||||
)
|
||||
}}
|
||||
<dd.Interactive @color="critical" {{on "click" (fn (mut this.methodToDisable) method)}}>
|
||||
Disable
|
||||
</dd.Interactive>
|
||||
{{/if}}
|
||||
</Hds::Dropdown>
|
||||
</div>
|
||||
|
||||
@ -23,4 +23,7 @@ export const PATH_MAP = {
|
||||
syncSetAssociation: apiPath`sys/sync/destinations/${'type'}/${'name'}/associations/set`,
|
||||
syncRemoveAssociation: apiPath`sys/sync/destinations/${'type'}/${'name'}/associations/remove`,
|
||||
kvConfig: apiPath`${'path'}/config`,
|
||||
authMethodConfig: apiPath`auth/${'path'}/config`,
|
||||
authMethodConfigAws: apiPath`auth/${'path'}/config/client`,
|
||||
authMethodDelete: apiPath`sys/auth/${'path'}`,
|
||||
};
|
||||
|
||||
@ -16,12 +16,13 @@ interface Path {
|
||||
navigation: boolean;
|
||||
param: string | false;
|
||||
}
|
||||
interface PathsInfo {
|
||||
export type PathInfo = {
|
||||
apiPath: string;
|
||||
itemType: string;
|
||||
itemTypes: string[];
|
||||
paths: Path[];
|
||||
}
|
||||
itemTypes: string[];
|
||||
itemType?: string;
|
||||
itemID?: string;
|
||||
};
|
||||
|
||||
interface OpenApiParameter {
|
||||
description?: string;
|
||||
@ -53,7 +54,7 @@ interface OpenApiPath {
|
||||
}
|
||||
|
||||
// Take object entries from the OpenAPI response and consolidate them into an object which includes itemTypes, operations, and paths
|
||||
export function reducePathsByPathName(pathsInfo: PathsInfo, currentPath: [string, OpenApiPath]): PathsInfo {
|
||||
export function reducePathsByPathName(pathsInfo: PathInfo, currentPath: [string, OpenApiPath]): PathInfo {
|
||||
const pathName = currentPath[0];
|
||||
const pathDetails = currentPath[1];
|
||||
const displayAttrs = pathDetails['x-vault-displayAttrs'];
|
||||
@ -123,7 +124,7 @@ export function pathToHelpUrlSegment(path: string): string {
|
||||
return path.replaceAll(apiPathRegex, 'example');
|
||||
}
|
||||
|
||||
export function filterPathsByItemType(pathInfo: PathsInfo, itemType: string): Path[] {
|
||||
export function filterPathsByItemType(pathInfo: PathInfo, itemType: string): Path[] {
|
||||
if (!itemType) {
|
||||
return pathInfo.paths;
|
||||
}
|
||||
|
||||
@ -8,34 +8,34 @@ import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import AuthMethodResource from 'vault/resources/auth/method';
|
||||
|
||||
module('Integration | Component | auth-method/configuration', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.createModel = (path, type) => {
|
||||
this.model = this.store.createRecord('auth-method', { path, type });
|
||||
this.model.set('config', this.store.createRecord('mount-config'));
|
||||
this.createMethod = (path, type) => {
|
||||
this.method = new AuthMethodResource({ path, type, config: { listingVisibility: 'hidden' } }, this);
|
||||
};
|
||||
this.renderComponent = async () => await render(hbs`<AuthMethod::Configuration @model={{this.model}} />`);
|
||||
this.renderComponent = () => render(hbs`<AuthMethod::Configuration @method={{this.method}} />`);
|
||||
});
|
||||
|
||||
test('it renders direct link for supported method', async function (assert) {
|
||||
this.createModel('token/', 'token');
|
||||
this.createMethod('token/', 'token');
|
||||
await this.renderComponent();
|
||||
assert.dom(GENERAL.infoRowValue('UI login link')).hasText(`${window.origin}/ui/vault/auth?with=token%2F`);
|
||||
});
|
||||
|
||||
test('it does not render direct link for unsupported method', async function (assert) {
|
||||
this.createModel('my-approle/', 'approle');
|
||||
this.createMethod('my-approle/', 'approle');
|
||||
await this.renderComponent();
|
||||
assert.dom(GENERAL.infoRowValue('UI login link')).doesNotExist();
|
||||
});
|
||||
|
||||
test('it renders direct link if within a namespace', async function (assert) {
|
||||
this.owner.lookup('service:namespace').set('path', 'foo/bar');
|
||||
this.createModel('token/', 'token');
|
||||
this.createMethod('token/', 'token');
|
||||
await this.renderComponent();
|
||||
assert
|
||||
.dom(GENERAL.infoRowValue('UI login link'))
|
||||
|
||||
37
ui/types/vault/mount.d.ts
vendored
Normal file
37
ui/types/vault/mount.d.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
export type MountConfig = {
|
||||
forceNoCache?: boolean;
|
||||
listingVisibility?: string | boolean;
|
||||
defaultLeaseTtl?: number;
|
||||
maxLeaseTtl?: number;
|
||||
allowedManagedKeys?: string[];
|
||||
auditNonHmacRequestKeys?: string[];
|
||||
auditNonHmacResponseKeys?: string[];
|
||||
passthroughRequestHeaders?: string[];
|
||||
allowedResponseHeaders?: string[];
|
||||
identityTokenKey?: string;
|
||||
};
|
||||
|
||||
export type MountOptions = {
|
||||
version: number;
|
||||
};
|
||||
|
||||
export type Mount = {
|
||||
path: string;
|
||||
accessor: string;
|
||||
config: MountConfig;
|
||||
description: string;
|
||||
externalEntropyAccess: boolean;
|
||||
local: boolean;
|
||||
options?: MountOptions;
|
||||
pluginVersion: string;
|
||||
runningPluginVersion: string;
|
||||
runningSha256: string;
|
||||
sealWrap: boolean;
|
||||
type: string;
|
||||
uuid: string;
|
||||
};
|
||||
34
ui/types/vault/secrets/engine.d.ts
vendored
34
ui/types/vault/secrets/engine.d.ts
vendored
@ -10,39 +10,7 @@ import type {
|
||||
AzureConfigureRequest,
|
||||
GoogleCloudConfigureRequest,
|
||||
} from '@hashicorp/vault-client-typescript';
|
||||
|
||||
export type EngineConfig = {
|
||||
forceNoCache?: boolean;
|
||||
listingVisibility?: string | boolean;
|
||||
defaultLeaseTtl?: number;
|
||||
maxLeaseTtl?: number;
|
||||
allowedManagedKeys?: string[];
|
||||
auditNonHmacRequestKeys?: string[];
|
||||
auditNonHmacResponseKeys?: string[];
|
||||
passthroughRequestHeaders?: string[];
|
||||
allowedResponseHeaders?: string[];
|
||||
identityTokenKey?: string;
|
||||
};
|
||||
|
||||
export type EngineOptions = {
|
||||
version: number;
|
||||
};
|
||||
|
||||
export type SecretsEngine = {
|
||||
path: string;
|
||||
accessor: string;
|
||||
config: EngineConfig;
|
||||
description: string;
|
||||
externalEntropyAccess: boolean;
|
||||
local: boolean;
|
||||
options?: EngineOptions;
|
||||
pluginVersion: string;
|
||||
runningPluginVersion: string;
|
||||
runningSha256: string;
|
||||
sealWrap: boolean;
|
||||
type: string;
|
||||
uuid: string;
|
||||
};
|
||||
import type { MountConfig, MountOptions } from 'vault/mount';
|
||||
|
||||
type CommonConfigParams = {
|
||||
rotationPeriod: number;
|
||||
|
||||
12
ui/types/vault/services/path-help.d.ts
vendored
Normal file
12
ui/types/vault/services/path-help.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Service from '@ember/service';
|
||||
|
||||
import type { PathInfo } from 'vault/utils/openapi-helpers';
|
||||
|
||||
export default class PathHelpService extends Service {
|
||||
getPaths(apiPath: string, backend: string, itemType?: string, itemID?: string): Promise<PathInfo>;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user