mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-05 04:16:31 +02:00
[UI] Ember Data Migration - API Property Casing (#31325)
* updates api client vars to snake_case for custom messages * updates api client vars to snake_case for tools * updates api client vars to snake_case for sync * updates api client vars to snake_case for secrets engine * updates api client vars to snake_case for auth * updates api client vars to snake_case for usage * updates api client dep to point to gh repo * fixes custom-messages service unit tests * fixes configure-ssh test * fixes configure-ssh test...again
This commit is contained in:
parent
9190485ef6
commit
8700becc45
@ -121,8 +121,7 @@ export default ApplicationAdapter.extend({
|
||||
});
|
||||
},
|
||||
|
||||
mfaValidate(mfaRequirement) {
|
||||
const { mfaRequestId: mfa_request_id, mfaConstraints: mfa_constraints } = mfaRequirement;
|
||||
mfaValidate({ mfa_request_id, mfa_constraints }) {
|
||||
const options = {
|
||||
data: {
|
||||
mfa_request_id,
|
||||
|
||||
@ -19,16 +19,16 @@ export default class AuthMethodConfigurationComponent extends Component<Args> {
|
||||
'description',
|
||||
'accessor',
|
||||
'local',
|
||||
'sealWrap',
|
||||
'config.listingVisibility',
|
||||
'config.defaultLeaseTtl',
|
||||
'config.maxLeaseTtl',
|
||||
'config.tokenType',
|
||||
'config.auditNonHmacRequestKeys',
|
||||
'config.auditNonHmacResponseKeys',
|
||||
'config.passthroughRequestHeaders',
|
||||
'config.allowedResponseHeaders',
|
||||
'config.pluginVersion',
|
||||
'seal_wrap',
|
||||
'config.listing_visibility',
|
||||
'config.default_lease_ttl',
|
||||
'config.max_lease_ttl',
|
||||
'config.token_type',
|
||||
'config.audit_non_hmac_request_keys',
|
||||
'config.audit_non_hmac_response_keys',
|
||||
'config.passthrough_request_headers',
|
||||
'config.allowed_response_headers',
|
||||
'config.plugin_version',
|
||||
];
|
||||
|
||||
label = (field: string) => {
|
||||
@ -37,24 +37,24 @@ export default class AuthMethodConfigurationComponent extends Component<Args> {
|
||||
// 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',
|
||||
listing_visibility: 'Use as preferred UI login method',
|
||||
default_lease_ttl: 'Default Lease TTL',
|
||||
max_lease_ttl: 'Max Lease TTL',
|
||||
audit_non_hmac_request_keys: 'Request keys excluded from HMACing in audit',
|
||||
audit_non_hmac_response_keys: 'Response keys excluded from HMACing in audit',
|
||||
passthrough_request_headers: 'Allowed passthrough request headers',
|
||||
}[key] || label
|
||||
);
|
||||
};
|
||||
value = (field: string) => {
|
||||
const { method } = this.args;
|
||||
if (field === 'config.listingVisibility') {
|
||||
return method.config.listingVisibility === 'unauth';
|
||||
if (field === 'config.listing_visibility') {
|
||||
return method.config.listing_visibility === 'unauth';
|
||||
}
|
||||
return get(method, field);
|
||||
};
|
||||
|
||||
isTtl = (field: string) => {
|
||||
return ['config.defaultLeaseTtl', 'config.maxLeaseTtl'].includes(field);
|
||||
return ['config.default_lease_ttl', 'config.max_lease_ttl'].includes(field);
|
||||
};
|
||||
}
|
||||
|
||||
@ -13,12 +13,13 @@ import { POSSIBLE_FIELDS } from 'vault/utils/auth-form-helpers';
|
||||
import { ResponseError } from '@hashicorp/vault-client-typescript';
|
||||
|
||||
import type { HTMLElementEvent } from 'vault/forms';
|
||||
import type { LoginFields, NormalizedAuthData, NormalizeAuthResponseKeys } from 'vault/vault/auth/form';
|
||||
import type { AuthResponseAuthKey, AuthResponseDataKey } from 'vault/vault/auth/methods';
|
||||
import type { LoginFields, NormalizedAuthData, NormalizeAuthResponseKeys } from 'vault/auth/form';
|
||||
import type { AuthResponseAuthKey, AuthResponseDataKey } from 'vault/auth/methods';
|
||||
import type ApiService from 'vault/services/api';
|
||||
import type ClusterModel from 'vault/models/cluster';
|
||||
import type FlagsService from 'vault/services/flags';
|
||||
import type VersionService from 'vault/services/version';
|
||||
import type AuthService from 'vault/services/auth';
|
||||
|
||||
/**
|
||||
* @module Auth::Base
|
||||
@ -43,6 +44,7 @@ export default abstract class AuthBase extends Component<Args> {
|
||||
@service declare readonly api: ApiService;
|
||||
@service declare readonly flags: FlagsService;
|
||||
@service declare readonly version: VersionService;
|
||||
@service declare readonly auth: AuthService;
|
||||
|
||||
@action
|
||||
onSubmit(event: HTMLElementEvent<HTMLFormElement>) {
|
||||
@ -131,14 +133,13 @@ export default abstract class AuthBase extends Component<Args> {
|
||||
authResponse: AuthResponseAuthKey | AuthResponseDataKey,
|
||||
{ authMountPath, displayName, token, ttl }: NormalizeAuthResponseKeys
|
||||
) => {
|
||||
return {
|
||||
// authResponse will include enforcement data in the `mfaRequirement` key - if MFA is configured.
|
||||
...authResponse,
|
||||
// authResponse will include enforcement data in the `mfa_requirement` key - if MFA is configured.
|
||||
return this.auth.normalizeAuthData(authResponse, {
|
||||
authMethodType: this.args.authType,
|
||||
authMountPath,
|
||||
displayName,
|
||||
token,
|
||||
ttl,
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@ export default class AuthFormGithub extends AuthBase {
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
displayName,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@ export default class AuthFormLdap extends AuthBase {
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
displayName: auth?.metadata?.username,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ export default class AuthFormOidcJwt extends AuthBase {
|
||||
if (wait) await timeout(wait);
|
||||
|
||||
const { namespace = '', path = '', role = '' } = this.parseFormData(this._formData);
|
||||
const redirectUri = this.generateRedirectUri(namespace, path);
|
||||
const redirect_uri = this.generateRedirectUri(namespace, path);
|
||||
|
||||
// reset state
|
||||
this.authUrl = null;
|
||||
@ -116,9 +116,9 @@ export default class AuthFormOidcJwt extends AuthBase {
|
||||
try {
|
||||
const { data } = (await this.api.auth.jwtOidcRequestAuthorizationUrl(path, {
|
||||
role,
|
||||
redirectUri,
|
||||
redirect_uri,
|
||||
})) as JwtOidcAuthUrlResponse;
|
||||
this.authUrl = data.authUrl;
|
||||
this.authUrl = data.auth_url;
|
||||
this.isOIDC = true;
|
||||
} catch (e) {
|
||||
const { status, message } = await this.api.parseError(e);
|
||||
@ -158,8 +158,8 @@ export default class AuthFormOidcJwt extends AuthBase {
|
||||
// displayName is not returned by auth response and is set in persistAuthData
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
}
|
||||
|
||||
@ -175,8 +175,8 @@ export default class AuthFormOidcJwt extends AuthBase {
|
||||
// displayName is not returned by auth response and is set in persistAuthData
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
} finally {
|
||||
this.closeWindow(oidcWindow);
|
||||
|
||||
@ -42,8 +42,8 @@ export default class AuthFormOkta extends AuthBase {
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
displayName: auth?.metadata?.username,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ export default class AuthFormOkta extends AuthBase {
|
||||
async requestOktaVerify(nonce: string, mountPath: string) {
|
||||
try {
|
||||
const { data } = (await this.api.auth.oktaVerify(nonce, mountPath)) as OktaVerifyApiResponse;
|
||||
return data.correctAnswer;
|
||||
return data.correct_answer;
|
||||
} catch (e) {
|
||||
const { status, message } = await this.api.parseError(e);
|
||||
if (status === 404) {
|
||||
|
||||
@ -25,8 +25,8 @@ export default class AuthFormRadius extends AuthBase {
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
displayName: auth?.metadata?.username,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,9 +20,9 @@ import type { SamlLoginApiResponse, SamlSsoServiceUrlApiResponse } from 'vault/v
|
||||
*/
|
||||
|
||||
interface SamlRole {
|
||||
ssoServiceUrl: string;
|
||||
tokenPollId: string;
|
||||
clientVerifier: string;
|
||||
sso_service_url: string;
|
||||
token_poll_id: string;
|
||||
client_verifier: string;
|
||||
}
|
||||
export default class AuthFormSaml extends AuthBase {
|
||||
loginFields = [
|
||||
@ -48,7 +48,7 @@ export default class AuthFormSaml extends AuthBase {
|
||||
// either the default of auth type, or the custom inputted path
|
||||
const { namespace, path, role } = formData;
|
||||
const fetchedRole = await this.fetchSamlRole({ namespace, path, role });
|
||||
const samlWindow = await this.startSAMLAuth(fetchedRole.ssoServiceUrl);
|
||||
const samlWindow = await this.startSAMLAuth(fetchedRole.sso_service_url);
|
||||
if (samlWindow) {
|
||||
try {
|
||||
// start watching the popup window and the current one
|
||||
@ -60,8 +60,8 @@ export default class AuthFormSaml extends AuthBase {
|
||||
// displayName is not included in auth response - it is set in persistAuthData
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
} finally {
|
||||
this.closeWindow(samlWindow);
|
||||
@ -79,22 +79,22 @@ export default class AuthFormSaml extends AuthBase {
|
||||
async fetchSamlRole({ namespace = '', path = '', role = '' }): Promise<SamlRole> {
|
||||
// Create the client verifier and challenge
|
||||
const verifier = uuid();
|
||||
const clientChallenge = await this.generateClientChallenge(verifier);
|
||||
const acsUrl = this.generateAcsUrl(path, namespace);
|
||||
const clientType = SamlWriteSsoServiceUrlRequestClientTypeEnum.BROWSER; // 'browser'
|
||||
const client_challenge = await this.generateClientChallenge(verifier);
|
||||
const acs_url = this.generateAcsUrl(path, namespace);
|
||||
const client_type = SamlWriteSsoServiceUrlRequestClientTypeEnum.BROWSER; // 'browser'
|
||||
// Kick off the authentication flow by generating the SSO service URL
|
||||
// It requires the client challenge generated from the verifier. We'll
|
||||
// later provide the verifier to match up with the challenge on the server
|
||||
// when we poll for the Vault token by its returned token poll ID.
|
||||
const { data } = (await this.api.auth.samlWriteSsoServiceUrl(path, {
|
||||
acsUrl,
|
||||
clientChallenge,
|
||||
clientType,
|
||||
acs_url,
|
||||
client_challenge,
|
||||
client_type,
|
||||
role,
|
||||
})) as SamlSsoServiceUrlApiResponse;
|
||||
return {
|
||||
...data,
|
||||
clientVerifier: verifier,
|
||||
client_verifier: verifier,
|
||||
};
|
||||
}
|
||||
|
||||
@ -123,11 +123,11 @@ export default class AuthFormSaml extends AuthBase {
|
||||
await timeout(WAIT_TIME);
|
||||
|
||||
try {
|
||||
const { clientVerifier, tokenPollId } = fetchedRole;
|
||||
const { client_verifier, token_poll_id } = fetchedRole;
|
||||
// Exit loop if there's a response
|
||||
return (await this.api.auth.samlWriteToken(path, {
|
||||
clientVerifier,
|
||||
tokenPollId,
|
||||
client_verifier,
|
||||
token_poll_id,
|
||||
})) as SamlLoginApiResponse;
|
||||
} catch (e) {
|
||||
const { message, status } = await this.api.parseError(e);
|
||||
|
||||
@ -26,8 +26,8 @@ export default class AuthFormUserpass extends AuthBase {
|
||||
return this.normalizeAuthResponse(auth, {
|
||||
authMountPath: path,
|
||||
displayName: auth?.metadata?.username,
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
token: auth.client_token,
|
||||
ttl: auth.lease_duration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,17 +10,17 @@
|
||||
<:body as |B|>
|
||||
<B.Tr>
|
||||
<B.Td>API_ADDR</B.Td>
|
||||
<B.Td data-test-vault-config-details="api_addr">{{or @vaultConfiguration.apiAddr "None"}}</B.Td>
|
||||
<B.Td data-test-vault-config-details="api_addr">{{or @vaultConfiguration.api_addr "None"}}</B.Td>
|
||||
</B.Tr>
|
||||
<B.Tr>
|
||||
<B.Td>Default lease TTL</B.Td>
|
||||
<B.Td data-test-vault-config-details="default_lease_ttl">{{format-duration
|
||||
@vaultConfiguration.defaultLeaseTtl
|
||||
@vaultConfiguration.default_lease_ttl
|
||||
}}</B.Td>
|
||||
</B.Tr>
|
||||
<B.Tr>
|
||||
<B.Td>Max lease TTL</B.Td>
|
||||
<B.Td data-test-vault-config-details="max_lease_ttl">{{format-duration @vaultConfiguration.maxLeaseTtl}}</B.Td>
|
||||
<B.Td data-test-vault-config-details="max_lease_ttl">{{format-duration @vaultConfiguration.max_lease_ttl}}</B.Td>
|
||||
</B.Tr>
|
||||
<B.Tr>
|
||||
<B.Td>TLS</B.Td>
|
||||
@ -28,11 +28,11 @@
|
||||
</B.Tr>
|
||||
<B.Tr>
|
||||
<B.Td>Log format</B.Td>
|
||||
<B.Td data-test-vault-config-details="log_format">{{or @vaultConfiguration.logFormat "None"}}</B.Td>
|
||||
<B.Td data-test-vault-config-details="log_format">{{or @vaultConfiguration.log_format "None"}}</B.Td>
|
||||
</B.Tr>
|
||||
<B.Tr>
|
||||
<B.Td>Log level</B.Td>
|
||||
<B.Td data-test-vault-config-details="log_level">{{@vaultConfiguration.logLevel}}</B.Td>
|
||||
<B.Td data-test-vault-config-details="log_level">{{@vaultConfiguration.log_level}}</B.Td>
|
||||
</B.Tr>
|
||||
<B.Tr>
|
||||
<B.Td>Storage type</B.Td>
|
||||
|
||||
@ -18,11 +18,11 @@ import Component from '@glimmer/component';
|
||||
|
||||
export default class DashboardSecretsEnginesCard extends Component {
|
||||
get tls() {
|
||||
// since the default for tlsDisable is false it may not be in the config
|
||||
// consider tls enabled if tlsDisable is undefined or false AND both tlsCertFile and tlsKeyFile are defined
|
||||
// since the default for tls_disable is false it may not be in the config
|
||||
// consider tls enabled if tls_disable is undefined or false AND both tls_cert_file and tls_key_file are defined
|
||||
const tlsListener = this.args.vaultConfiguration?.listeners.find((listener) => {
|
||||
const { tlsDisable, tlsCertFile, tlsKeyFile } = listener.config || {};
|
||||
return !tlsDisable && tlsCertFile && tlsKeyFile;
|
||||
const { tls_disable, tls_cert_file, tls_key_file } = listener.config || {};
|
||||
return !tls_disable && tls_cert_file && tls_key_file;
|
||||
});
|
||||
|
||||
return tlsListener ? 'Enabled' : 'Disabled';
|
||||
|
||||
@ -47,7 +47,7 @@ export default class MfaForm extends Component {
|
||||
}
|
||||
|
||||
get constraints() {
|
||||
return this.args.authData.mfaRequirement.mfaConstraints;
|
||||
return this.args.authData.mfaRequirement.mfa_constraints;
|
||||
}
|
||||
get multiConstraint() {
|
||||
return this.constraints.length > 1;
|
||||
|
||||
@ -89,7 +89,7 @@ export default class MountBackendForm extends Component<Args> {
|
||||
typeChangeSideEffect(type: string) {
|
||||
if (this.args.mountCategory !== 'secret') return;
|
||||
// If type PKI, set max lease to ~10years
|
||||
this.args.mountModel.config.maxLeaseTtl = type === 'pki' ? '3650d' : 0;
|
||||
this.args.mountModel.config.max_lease_ttl = type === 'pki' ? '3650d' : 0;
|
||||
}
|
||||
|
||||
checkModelValidity(model: MountModel) {
|
||||
@ -114,16 +114,16 @@ export default class MountBackendForm extends Component<Args> {
|
||||
}
|
||||
|
||||
async saveKvConfig(path: string, formData: SecretsEngineForm['data']) {
|
||||
const { options, kvConfig = {} } = formData;
|
||||
const { maxVersions, casRequired, deleteVersionAfter } = kvConfig;
|
||||
const { options, kv_config = {} } = formData;
|
||||
const { max_versions, cas_required, delete_version_after } = kv_config;
|
||||
const isKvV2 = options?.version === 2 && ['kv', 'generic'].includes(this.args.mountModel.engineType);
|
||||
const hasConfig = maxVersions || casRequired || deleteVersionAfter;
|
||||
const hasConfig = max_versions || cas_required || delete_version_after;
|
||||
|
||||
if (isKvV2 && hasConfig) {
|
||||
try {
|
||||
const { canUpdate } = await this.capabilities.for('kvConfig', { path });
|
||||
if (canUpdate) {
|
||||
await this.api.secrets.kvV2Configure(path, kvConfig);
|
||||
await this.api.secrets.kvV2Configure(path, kv_config);
|
||||
} else {
|
||||
this.flashMessages.warning(
|
||||
'You do not have access to the config endpoint. The secret engine was mounted, but the configuration settings were not saved.'
|
||||
@ -209,6 +209,6 @@ export default class MountBackendForm extends Component<Args> {
|
||||
@action
|
||||
handleIdentityTokenKeyChange(value: string[] | string): void {
|
||||
// if array, it's coming from the search-select component, otherwise it hit the fallback component and will come in as a string.
|
||||
this.args.mountModel.config.identityTokenKey = Array.isArray(value) ? value[0] : value;
|
||||
this.args.mountModel.config.identity_token_key = Array.isArray(value) ? value[0] : value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,9 +6,9 @@
|
||||
{{#if @config}}
|
||||
{{#each this.displayFields as |field|}}
|
||||
{{! public key while not sensitive when editing/creating, should be hidden by default on viewing }}
|
||||
{{#if (eq field "publicKey")}}
|
||||
<InfoTableRow @label="Public key" @value={{@config.publicKey}}>
|
||||
<MaskedInput @value={{@config.publicKey}} @name={{field}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
{{#if (eq field "public_key")}}
|
||||
<InfoTableRow @label="Public key" @value={{@config.public_key}}>
|
||||
<MaskedInput @value={{@config.public_key}} @name={{field}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
|
||||
@ -15,33 +15,40 @@ type Args = {
|
||||
|
||||
export default class ConfigurationDetails extends Component<Args> {
|
||||
awsFields = [
|
||||
'roleArn',
|
||||
'identityTokenAudience',
|
||||
'identityTokenTtl',
|
||||
'accessKey',
|
||||
'role_arn',
|
||||
'identity_token_audience',
|
||||
'identity_token_ttl',
|
||||
'access_key',
|
||||
'region',
|
||||
'iamEndpoint',
|
||||
'stsEndpoint',
|
||||
'maxRetries',
|
||||
'iam_endpoint',
|
||||
'sts_endpoint',
|
||||
'max_retries',
|
||||
'lease',
|
||||
'leaseMax',
|
||||
'lease_max',
|
||||
'issuer',
|
||||
];
|
||||
|
||||
azureFields = [
|
||||
'subscriptionId',
|
||||
'tenantId',
|
||||
'clientId',
|
||||
'identityTokenAudience',
|
||||
'identityTokenTtl',
|
||||
'rootPasswordTtl',
|
||||
'subscription_id',
|
||||
'tenant_id',
|
||||
'client_id',
|
||||
'identity_token_audience',
|
||||
'identity_token_ttl',
|
||||
'root_password_ttl',
|
||||
'environment',
|
||||
'issuer',
|
||||
];
|
||||
|
||||
gcpFields = ['serviceAccountEmail', 'ttl', 'maxTtl', 'identityTokenAudience', 'identityTokenTtl', 'issuer'];
|
||||
gcpFields = [
|
||||
'service_account_email',
|
||||
'ttl',
|
||||
'max_ttl',
|
||||
'identity_token_audience',
|
||||
'identity_token_ttl',
|
||||
'issuer',
|
||||
];
|
||||
|
||||
sshFields = ['publicKey', 'generateSigningKey'];
|
||||
sshFields = ['public_key', 'generate_signing_key'];
|
||||
|
||||
get displayFields() {
|
||||
switch (this.args.typeDisplay) {
|
||||
@ -72,13 +79,15 @@ export default class ConfigurationDetails extends Component<Args> {
|
||||
return (
|
||||
{
|
||||
lease: 'Default Lease TTL',
|
||||
leaseMax: 'Max Lease TTL',
|
||||
lease_max: 'Max Lease TTL',
|
||||
ttl: 'Config TTL',
|
||||
}[field] || formattedLabel
|
||||
);
|
||||
};
|
||||
|
||||
isDuration = (field: string) => {
|
||||
return ['identityTokenTtl', 'rootPasswordTtl', 'lease', 'leaseMax', 'ttl', 'maxTtl'].includes(field);
|
||||
return ['identity_token_ttl', 'root_password_ttl', 'lease', 'lease_max', 'ttl', 'max_ttl'].includes(
|
||||
field
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@ -47,9 +47,9 @@
|
||||
</label>
|
||||
<div class="control">
|
||||
<MaskedInput
|
||||
@name="publickey"
|
||||
@name="publicKey"
|
||||
@id="publicKey"
|
||||
@value={{@configForm.publicKey}}
|
||||
@value={{@configForm.public_key}}
|
||||
@displayOnly={{true}}
|
||||
@allowCopy={{true}}
|
||||
data-test-input="public-key"
|
||||
@ -59,7 +59,7 @@
|
||||
<Hds::ButtonSet>
|
||||
<Hds::Copy::Button
|
||||
@text="Copy"
|
||||
@textToCopy={{@configForm.publicKey}}
|
||||
@textToCopy={{@configForm.public_key}}
|
||||
@onError={{fn (set-flash-message "Clipboard copy failed. The Clipboard API requires a secure context." "danger")}}
|
||||
class="primary"
|
||||
/>
|
||||
|
||||
@ -151,8 +151,8 @@ export default class ConfigureWif extends Component<Args> {
|
||||
if (type === 'aws') {
|
||||
await this.api.secrets.awsConfigureRootIamCredentials(backendPath, data);
|
||||
try {
|
||||
const { lease, leaseMax } = data as { lease: string; leaseMax: string };
|
||||
await this.api.secrets.awsConfigureLease(backendPath, { lease, leaseMax });
|
||||
const { lease, lease_max } = data as { lease: string; lease_max: string };
|
||||
await this.api.secrets.awsConfigureLease(backendPath, { lease, lease_max });
|
||||
} catch (e) {
|
||||
const { message } = await this.api.parseError(e);
|
||||
this.flashMessages.danger(`Error saving lease configuration: ${message}`);
|
||||
@ -182,14 +182,14 @@ export default class ConfigureWif extends Component<Args> {
|
||||
if (accessType === 'account') {
|
||||
// reset all "wif" attributes that are mutually exclusive with "account" attributes
|
||||
// these attributes are the same for each engine
|
||||
configForm.data.identityTokenAudience = configForm.data.identityTokenTtl = undefined;
|
||||
configForm.data.identity_token_audience = configForm.data.identity_token_ttl = undefined;
|
||||
} else if (accessType === 'wif') {
|
||||
// reset all "account" attributes that are mutually exclusive with "wif" attributes
|
||||
// these attributes are different for each engine
|
||||
if (type === 'azure') {
|
||||
(configForm as AzureConfigForm).data.clientSecret = undefined;
|
||||
(configForm as AzureConfigForm).data.client_secret = undefined;
|
||||
} else if (type === 'aws') {
|
||||
(configForm as AwsConfigForm).data.accessKey = undefined;
|
||||
(configForm as AwsConfigForm).data.access_key = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
</div>
|
||||
<code class="has-text-grey is-size-8" data-test-engine-accessor>
|
||||
{{backend.accessor}}
|
||||
{{backend.runningPluginVersion}}
|
||||
{{backend.running_plugin_version}}
|
||||
</code>
|
||||
{{#if backend.description}}
|
||||
<ReadMore>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
{{#if this.lookupData}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each-in this.lookupData as |key value|}}
|
||||
{{#let (if (eq key "creationTtl") "Creation TTL" (to-label key)) as |label|}}
|
||||
{{#let (if (eq key "creation_ttl") "Creation TTL" (to-label key)) as |label|}}
|
||||
<InfoTableRow @label={{label}} @value={{value}} />
|
||||
{{/let}}
|
||||
{{/each-in}}
|
||||
|
||||
@ -38,10 +38,10 @@ export default class ToolsLookup extends Component {
|
||||
|
||||
get expirationDate() {
|
||||
if (this.lookupData) {
|
||||
const { creationTime, creationTtl } = this.lookupData;
|
||||
if (creationTime && creationTtl) {
|
||||
const { creation_time, creation_ttl } = this.lookupData;
|
||||
if (creation_time && creation_ttl) {
|
||||
// returns new Date with seconds added.
|
||||
return addSeconds(creationTime, Number(creationTtl));
|
||||
return addSeconds(creation_time, Number(creation_ttl));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@ -47,8 +47,8 @@ export default class ToolsRandom extends Component {
|
||||
evt.preventDefault();
|
||||
const data = { bytes: Number(this.bytes), format: this.format };
|
||||
try {
|
||||
const { randomBytes } = await this.api.sys.generateRandom(data);
|
||||
this.randomBytes = randomBytes || '';
|
||||
const { random_bytes } = await this.api.sys.generateRandom(data);
|
||||
this.randomBytes = random_bytes || '';
|
||||
this.flashMessages.success('Generated random bytes successfully.');
|
||||
} catch (error) {
|
||||
const { message } = await this.api.parseError(error);
|
||||
|
||||
@ -41,8 +41,8 @@ export default class ToolsRewrap extends Component {
|
||||
const data = { token: this.originalToken.trim() };
|
||||
|
||||
try {
|
||||
const { wrapInfo } = await this.api.sys.rewrap(data);
|
||||
this.rewrappedToken = wrapInfo?.token || '';
|
||||
const { wrap_info } = await this.api.sys.rewrap(data);
|
||||
this.rewrappedToken = wrap_info?.token || '';
|
||||
this.flashMessages.success('Rewrap was successful.');
|
||||
} catch (error) {
|
||||
const { message } = await this.api.parseError(error);
|
||||
|
||||
@ -46,10 +46,10 @@ export default class ToolsUnwrap extends Component {
|
||||
const resp = await this.api.sys.unwrap(data);
|
||||
this.unwrapData = (resp && resp.data) || resp.auth;
|
||||
this.unwrapDetails = {
|
||||
'Request ID': resp.requestId,
|
||||
'Lease ID': resp.leaseId || 'None',
|
||||
'Request ID': resp.request_id,
|
||||
'Lease ID': resp.lease_id || 'None',
|
||||
Renewable: resp.renewable,
|
||||
'Lease Duration': resp.leaseDuration || 'None',
|
||||
'Lease Duration': resp.lease_duration || 'None',
|
||||
};
|
||||
this.flashMessages.success('Unwrap was successful.');
|
||||
} catch (error) {
|
||||
|
||||
@ -82,8 +82,8 @@ export default class ToolsWrap extends Component {
|
||||
const wrap = this.wrapTTL || '';
|
||||
|
||||
try {
|
||||
const { wrapInfo } = await this.api.sys.wrap(data, this.api.buildHeaders({ wrap }));
|
||||
this.token = wrapInfo?.token || '';
|
||||
const { wrap_info } = await this.api.sys.wrap(data, this.api.buildHeaders({ wrap }));
|
||||
this.token = wrap_info?.token || '';
|
||||
this.flashMessages.success('Wrap was successful.');
|
||||
} catch (error) {
|
||||
const { message } = await this.api.parseError(error);
|
||||
|
||||
@ -9,6 +9,7 @@ import { service } from '@ember/service';
|
||||
import type FlagsService from 'vault/services/flags';
|
||||
import type ApiService from 'vault/services/api';
|
||||
import type { getUsageDataFunction, UsageDashboardData } from '@hashicorp/vault-reporting/types/index';
|
||||
import type { UtilizationReport } from 'vault/usage';
|
||||
|
||||
/**
|
||||
* @module UsagePage
|
||||
@ -35,41 +36,38 @@ export default class UsagePage extends Component {
|
||||
* We should get typescript errors if top level interfaces in the API client or
|
||||
* the vault-reporting addon change.
|
||||
*/
|
||||
const response = await this.api.sys.generateUtilizationReport();
|
||||
const leaseCountQuotas = response.leaseCountQuotas as UsageDashboardData['leaseCountQuotas'];
|
||||
const replicationStatus = response.replicationStatus as UsageDashboardData['replicationStatus'];
|
||||
const pki = response.pki as UsageDashboardData['pki'];
|
||||
const secretSync = response.secretSync as UsageDashboardData['secretSync'];
|
||||
const response = (await this.api.sys.generateUtilizationReport()) as UtilizationReport;
|
||||
const { lease_count_quotas, replication_status, pki, secret_sync } = response;
|
||||
|
||||
const data: UsageDashboardData = {
|
||||
authMethods: (response.authMethods as Record<string, number>) || {},
|
||||
secretEngines: (response.secretEngines as Record<string, number>) || {},
|
||||
leasesByAuthMethod: (response.leasesByAuthMethod as Record<string, number>) || {},
|
||||
authMethods: (response.auth_methods as Record<string, number>) || {},
|
||||
secretEngines: (response.secret_engines as Record<string, number>) || {},
|
||||
leasesByAuthMethod: (response.leases_by_auth_method as Record<string, number>) || {},
|
||||
leaseCountQuotas: {
|
||||
globalLeaseCountQuota: {
|
||||
capacity: leaseCountQuotas?.globalLeaseCountQuota?.capacity || 0,
|
||||
count: leaseCountQuotas?.globalLeaseCountQuota?.count || 0,
|
||||
name: leaseCountQuotas?.globalLeaseCountQuota?.name || '',
|
||||
capacity: lease_count_quotas?.global_lease_count_quota?.capacity || 0,
|
||||
count: lease_count_quotas?.global_lease_count_quota?.count || 0,
|
||||
name: lease_count_quotas?.global_lease_count_quota?.name || '',
|
||||
},
|
||||
totalLeaseCountQuotas: leaseCountQuotas?.totalLeaseCountQuotas || 0,
|
||||
totalLeaseCountQuotas: lease_count_quotas?.total_lease_count_quotas || 0,
|
||||
},
|
||||
replicationStatus: {
|
||||
drState: replicationStatus?.drState || 'disabled',
|
||||
prState: replicationStatus?.prState || 'disabled',
|
||||
drPrimary: replicationStatus?.drPrimary ?? false,
|
||||
prPrimary: replicationStatus?.prPrimary ?? false,
|
||||
drState: replication_status?.dr_state || 'disabled',
|
||||
prState: replication_status?.pr_state || 'disabled',
|
||||
drPrimary: replication_status?.dr_primary ?? false,
|
||||
prPrimary: replication_status?.pr_primary ?? false,
|
||||
},
|
||||
kvv1Secrets: response.kvv1Secrets || 0,
|
||||
kvv2Secrets: response.kvv2Secrets || 0,
|
||||
kvv1Secrets: response.kvv1_secrets || 0,
|
||||
kvv2Secrets: response.kvv2_secrets || 0,
|
||||
namespaces: response.namespaces || 0,
|
||||
pki: {
|
||||
totalIssuers: pki?.totalIssuers || 0,
|
||||
totalRoles: pki?.totalRoles || 0,
|
||||
totalIssuers: pki?.total_issuers || 0,
|
||||
totalRoles: pki?.total_roles || 0,
|
||||
},
|
||||
secretSync: {
|
||||
totalDestinations: secretSync?.totalDestinations || 0,
|
||||
totalDestinations: secret_sync?.total_destinations || 0,
|
||||
},
|
||||
};
|
||||
return data as UsageDashboardData;
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
@ -10,24 +10,24 @@ import engineDisplayData from 'vault/helpers/engines-display-data';
|
||||
export default class SecretsBackendConfigurationController extends Controller {
|
||||
get displayFields() {
|
||||
const { engineType } = this.model.secretsEngine;
|
||||
const fields = ['type', 'path', 'description', 'accessor', 'local', 'sealWrap'];
|
||||
const fields = ['type', 'path', 'description', 'accessor', 'local', 'seal_wrap'];
|
||||
// no ttl options for keymgmt
|
||||
if (engineType !== 'keymgmt') {
|
||||
fields.push('config.defaultLeaseTtl', 'config.maxLeaseTtl');
|
||||
fields.push('config.default_lease_ttl', 'config.max_lease_ttl');
|
||||
}
|
||||
fields.push(
|
||||
'config.allowedManagedKeys',
|
||||
'config.auditNonHmacRequestKeys',
|
||||
'config.auditNonHmacResponseKeys',
|
||||
'config.passthroughRequestHeaders',
|
||||
'config.allowedResponseHeaders'
|
||||
'config.allowed_managed_keys',
|
||||
'config.audit_non_hmac_request_keys',
|
||||
'config.audit_non_hmac_response_keys',
|
||||
'config.passthrough_request_headers',
|
||||
'config.allowed_response_headers'
|
||||
);
|
||||
if (engineType === 'kv' || engineType === 'generic') {
|
||||
fields.push('version');
|
||||
}
|
||||
// For WIF Secret engines, allow users to set the identity token key when mounting the engine.
|
||||
if (engineDisplayData(engineType)?.isWIF) {
|
||||
fields.push('config.identityTokenKey');
|
||||
fields.push('config.identity_token_key');
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
@ -38,11 +38,11 @@ export default class SecretsBackendConfigurationController extends Controller {
|
||||
// map specific fields to custom labels
|
||||
return (
|
||||
{
|
||||
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',
|
||||
default_lease_ttl: 'Default Lease TTL',
|
||||
max_lease_ttl: 'Max Lease TTL',
|
||||
audit_non_hmac_request_keys: 'Request keys excluded from HMACing in audit',
|
||||
audit_non_hmac_response_keys: 'Response keys excluded from HMACing in audit',
|
||||
passthrough_request_headers: 'Allowed passthrough request headers',
|
||||
}[key] || label
|
||||
);
|
||||
};
|
||||
|
||||
@ -65,13 +65,13 @@ export default class CustomMessageForm extends Form<CustomMessageFormData> {
|
||||
allowWhiteSpace: true,
|
||||
}),
|
||||
|
||||
new FormField('startTime', 'dateTimeLocal', {
|
||||
new FormField('start_time', 'dateTimeLocal', {
|
||||
editType: 'dateTimeLocal',
|
||||
label: 'Message starts',
|
||||
subText: 'Defaults to 12:00 a.m. the following day (local timezone).',
|
||||
}),
|
||||
|
||||
new FormField('endTime', 'dateTimeLocal', {
|
||||
new FormField('end_time', 'dateTimeLocal', {
|
||||
editType: 'yield',
|
||||
label: 'Message expires',
|
||||
}),
|
||||
@ -91,20 +91,20 @@ export default class CustomMessageForm extends Form<CustomMessageFormData> {
|
||||
message: 'Link title and url are required.',
|
||||
},
|
||||
],
|
||||
startTime: [
|
||||
start_time: [
|
||||
{
|
||||
validator({ startTime, endTime }: CustomMessageFormData) {
|
||||
if (!startTime || !endTime) return true;
|
||||
return isBefore(new Date(startTime), new Date(endTime));
|
||||
validator({ start_time, end_time }: CustomMessageFormData) {
|
||||
if (!start_time || !end_time) return true;
|
||||
return isBefore(new Date(start_time), new Date(end_time));
|
||||
},
|
||||
message: 'Start time is after end time.',
|
||||
},
|
||||
],
|
||||
endTime: [
|
||||
end_time: [
|
||||
{
|
||||
validator({ startTime, endTime }: CustomMessageFormData) {
|
||||
if (!startTime || !endTime) return true;
|
||||
return isAfter(new Date(endTime), new Date(startTime));
|
||||
validator({ start_time, end_time }: CustomMessageFormData) {
|
||||
if (!start_time || !end_time) return true;
|
||||
return isAfter(new Date(end_time), new Date(start_time));
|
||||
},
|
||||
message: 'End time is before start time.',
|
||||
},
|
||||
@ -114,10 +114,10 @@ export default class CustomMessageForm extends Form<CustomMessageFormData> {
|
||||
toJSON() {
|
||||
// overriding to do some date serialization
|
||||
// form sets dates as strings but client expects Date objects
|
||||
const startTime = this.data.startTime ? new Date(this.data.startTime as unknown as string) : undefined;
|
||||
const endTime = this.data.endTime ? new Date(this.data.endTime as unknown as string) : undefined;
|
||||
const start_time = this.data.start_time ? new Date(this.data.start_time as unknown as string) : undefined;
|
||||
const end_time = this.data.end_time ? new Date(this.data.end_time as unknown as string) : undefined;
|
||||
// encode message to base64
|
||||
const message = this.data.message ? encodeString(this.data.message) : undefined;
|
||||
return super.toJSON({ ...this.data, startTime, endTime, message });
|
||||
return super.toJSON({ ...this.data, start_time, end_time, message });
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,8 +16,8 @@ export default class AwsConfigForm extends WifConfigForm<AwsConfigFormData> {
|
||||
lease: [
|
||||
{
|
||||
validator(data: AwsConfigForm['data']) {
|
||||
const { lease, leaseMax } = data;
|
||||
return (lease && leaseMax) || (!lease && !leaseMax) ? true : false;
|
||||
const { lease, lease_max } = data;
|
||||
return (lease && lease_max) || (!lease && !lease_max) ? true : false;
|
||||
},
|
||||
message: 'Lease TTL and Max Lease TTL are both required if one of them is set.',
|
||||
},
|
||||
@ -25,17 +25,17 @@ export default class AwsConfigForm extends WifConfigForm<AwsConfigFormData> {
|
||||
};
|
||||
|
||||
get isAccountPluginConfigured() {
|
||||
return !!this.data.accessKey;
|
||||
return !!this.data.access_key;
|
||||
}
|
||||
|
||||
get isWifPluginConfigured() {
|
||||
const { identityTokenAudience, identityTokenTtl, roleArn } = this.data;
|
||||
return !!identityTokenAudience || !!identityTokenTtl || !!roleArn;
|
||||
const { identity_token_audience, identity_token_ttl, role_arn } = this.data;
|
||||
return !!identity_token_audience || !!identity_token_ttl || !!role_arn;
|
||||
}
|
||||
|
||||
accountFields = [
|
||||
new FormField('accessKey', 'string'),
|
||||
new FormField('secretKey', 'string', { sensitive: true }),
|
||||
new FormField('access_key', 'string'),
|
||||
new FormField('secret_key', 'string', { sensitive: true }),
|
||||
];
|
||||
|
||||
optionFields = [
|
||||
@ -44,21 +44,21 @@ export default class AwsConfigForm extends WifConfigForm<AwsConfigFormData> {
|
||||
subText:
|
||||
'Specifies the AWS region. If not set it will use the AWS_REGION env var, AWS_DEFAULT_REGION env var, or us-east-1 in that order.',
|
||||
}),
|
||||
new FormField('iamEndpoint', 'string', { label: 'IAM endpoint' }),
|
||||
new FormField('stsEndpoint', 'string', { label: 'STS endpoint' }),
|
||||
new FormField('maxRetries', 'number', {
|
||||
new FormField('iam_endpoint', 'string', { label: 'IAM endpoint' }),
|
||||
new FormField('sts_endpoint', 'string', { label: 'STS endpoint' }),
|
||||
new FormField('max_retries', 'number', {
|
||||
subText: 'Number of max retries the client should use for recoverable errors. Default is -1.',
|
||||
}),
|
||||
];
|
||||
|
||||
wifFields = [
|
||||
this.commonWifFields.issuer,
|
||||
new FormField('roleArn', 'string', {
|
||||
new FormField('role_arn', 'string', {
|
||||
label: 'Role ARN',
|
||||
subText: 'Role ARN to assume for plugin workload identity federation.',
|
||||
}),
|
||||
this.commonWifFields.identityTokenAudience,
|
||||
this.commonWifFields.identityTokenTtl,
|
||||
this.commonWifFields.identity_token_audience,
|
||||
this.commonWifFields.identity_token_ttl,
|
||||
];
|
||||
|
||||
// formFieldGroups will render the default and root config option fields
|
||||
@ -77,7 +77,7 @@ export default class AwsConfigForm extends WifConfigForm<AwsConfigFormData> {
|
||||
label: 'Default Lease TTL',
|
||||
editType: 'ttl',
|
||||
}),
|
||||
new FormField('leaseMax', 'string', {
|
||||
new FormField('lease_max', 'string', {
|
||||
label: 'Max Lease TTL',
|
||||
editType: 'ttl',
|
||||
}),
|
||||
|
||||
@ -15,15 +15,15 @@ export default class AzureConfigForm extends WifConfigForm<AzureConfigFormData>
|
||||
isAccountPluginConfigured = false;
|
||||
|
||||
get isWifPluginConfigured() {
|
||||
const { identityTokenAudience, identityTokenTtl } = this.data;
|
||||
return !!identityTokenAudience || !!identityTokenTtl;
|
||||
const { identity_token_audience, identity_token_ttl } = this.data;
|
||||
return !!identity_token_audience || !!identity_token_ttl;
|
||||
}
|
||||
|
||||
accountFields = [
|
||||
new FormField('subscriptionId', 'string', { label: 'Subscription ID' }),
|
||||
new FormField('tenantId', 'string', { label: 'Tenant ID' }),
|
||||
new FormField('clientId', 'string', { label: 'Client ID' }),
|
||||
new FormField('clientSecret', 'string', { sensitive: true }),
|
||||
new FormField('subscription_id', 'string', { label: 'Subscription ID' }),
|
||||
new FormField('tenant_id', 'string', { label: 'Tenant ID' }),
|
||||
new FormField('client_id', 'string', { label: 'Client ID' }),
|
||||
new FormField('client_secret', 'string', { sensitive: true }),
|
||||
];
|
||||
|
||||
optionFields = [
|
||||
@ -31,7 +31,7 @@ export default class AzureConfigForm extends WifConfigForm<AzureConfigFormData>
|
||||
subText:
|
||||
'This value can also be provided with the AZURE_ENVIRONMENT environment variable. If not specified, Vault will use Azure Public Cloud.',
|
||||
}),
|
||||
new FormField('rootPasswordTtl', 'string', {
|
||||
new FormField('root_password_ttl', 'string', {
|
||||
label: 'Root password TTL',
|
||||
editType: 'ttl',
|
||||
// default is 15768000 sec. The api docs say 182 days, but this should be updated to 182.5 days.
|
||||
@ -46,8 +46,8 @@ export default class AzureConfigForm extends WifConfigForm<AzureConfigFormData>
|
||||
this.accountFields[0] as FormField,
|
||||
this.accountFields[1] as FormField,
|
||||
this.accountFields[2] as FormField,
|
||||
this.commonWifFields.identityTokenAudience,
|
||||
this.commonWifFields.identityTokenTtl,
|
||||
this.commonWifFields.identity_token_audience,
|
||||
this.commonWifFields.identity_token_ttl,
|
||||
];
|
||||
|
||||
get formFieldGroups() {
|
||||
|
||||
@ -25,7 +25,7 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
level: 'warn',
|
||||
},
|
||||
],
|
||||
'kvConfig.maxVersions': [
|
||||
'kv_config.max_versions': [
|
||||
{ type: 'number', message: 'Maximum versions must be a number.' },
|
||||
{ type: 'length', options: { min: 1, max: 16 }, message: 'You cannot go over 16 characters.' },
|
||||
],
|
||||
@ -34,7 +34,7 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
get coreOptionFields() {
|
||||
return [
|
||||
new FormField('description', 'string', { editType: 'textarea' }),
|
||||
new FormField('config.listingVisibility', 'boolean', {
|
||||
new FormField('config.listing_visibility', 'boolean', {
|
||||
label: 'Use as preferred UI login method',
|
||||
editType: 'toggleButton',
|
||||
helperTextEnabled:
|
||||
@ -46,7 +46,7 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
helpText:
|
||||
'When Replication is enabled, a local mount will not be replicated across clusters. This can only be specified at mount time.',
|
||||
}),
|
||||
new FormField('sealWrap', 'boolean', {
|
||||
new FormField('seal_wrap', 'boolean', {
|
||||
helpText:
|
||||
'When enabled - if a seal supporting seal wrapping is specified in the configuration, all critical security parameters (CSPs) in this backend will be seal wrapped. (For KV mounts, all values will be seal wrapped.) This can only be specified at mount time.',
|
||||
}),
|
||||
@ -55,9 +55,9 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
|
||||
get leaseConfigFields() {
|
||||
return [
|
||||
new FormField('config.defaultLeaseTtl', 'string', { label: 'Default Lease TTL', editType: 'ttl' }),
|
||||
new FormField('config.maxLeaseTtl', 'string', { label: 'Max Lease TTL', editType: 'ttl' }),
|
||||
new FormField('config.allowedManagedKeys', 'string', {
|
||||
new FormField('config.default_lease_ttl', 'string', { label: 'Default Lease TTL', editType: 'ttl' }),
|
||||
new FormField('config.max_lease_ttl', 'string', { label: 'Max Lease TTL', editType: 'ttl' }),
|
||||
new FormField('config.allowed_managed_keys', 'string', {
|
||||
label: 'Allowed managed keys',
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
@ -66,22 +66,22 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
|
||||
get standardConfigFields() {
|
||||
return [
|
||||
new FormField('config.auditNonHmacRequestKeys', 'string', {
|
||||
new FormField('config.audit_non_hmac_request_keys', 'string', {
|
||||
label: 'Request keys excluded from HMACing in audit',
|
||||
editType: 'stringArray',
|
||||
helpText: "Keys that will not be HMAC'd by audit devices in the request data object.",
|
||||
}),
|
||||
new FormField('config.auditNonHmacResponseKeys', 'string', {
|
||||
new FormField('config.audit_non_hmac_response_keys', 'string', {
|
||||
label: 'Response keys excluded from HMACing in audit',
|
||||
editType: 'stringArray',
|
||||
helpText: "Keys that will not be HMAC'd by audit devices in the response data object.",
|
||||
}),
|
||||
new FormField('config.passthroughRequestHeaders', 'string', {
|
||||
new FormField('config.passthrough_request_headers', 'string', {
|
||||
label: 'Allowed passthrough request headers',
|
||||
helpText: 'Headers to allow and pass from the request to the backend',
|
||||
editType: 'stringArray',
|
||||
}),
|
||||
new FormField('config.allowedResponseHeaders', 'string', {
|
||||
new FormField('config.allowed_response_headers', 'string', {
|
||||
label: 'Allowed response headers',
|
||||
helpText: 'Headers to allow, allowing a plugin to include them in the response.',
|
||||
editType: 'stringArray',
|
||||
@ -97,17 +97,17 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
const fields = [new FormField('path', 'string')];
|
||||
if (this.engineType === 'kv') {
|
||||
fields.push(
|
||||
new FormField('kvConfig.maxVersions', 'number', {
|
||||
new FormField('kv_config.max_versions', 'number', {
|
||||
label: 'Maximum number of versions',
|
||||
subText:
|
||||
'The number of versions to keep per key. Once the number of keys exceeds the maximum number set here, the oldest version will be permanently deleted. This value applies to all keys, but a key’s metadata settings can overwrite this value. When 0 is used or the value is unset, Vault will keep 10 versions.',
|
||||
}),
|
||||
new FormField('kvConfig.casRequired', 'boolean', {
|
||||
new FormField('kv_config.cas_required', 'boolean', {
|
||||
label: 'Require Check and Set',
|
||||
subText:
|
||||
'If checked, all keys will require the cas parameter to be set on all write requests. A key’s metadata settings can overwrite this value.',
|
||||
}),
|
||||
new FormField('kvConfig.deleteVersionAfter', 'string', {
|
||||
new FormField('kv_config.delete_version_after', 'string', {
|
||||
editType: 'ttl',
|
||||
label: 'Automate secret deletion',
|
||||
helperTextDisabled: 'A secret’s version must be manually deleted.',
|
||||
@ -139,7 +139,7 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
...this.coreOptionFields,
|
||||
defaultTtl,
|
||||
maxTtl,
|
||||
new FormField('config.identityTokenKey', undefined, {
|
||||
new FormField('config.identity_token_key', undefined, {
|
||||
label: 'Identity token key',
|
||||
subText: `A named key to sign tokens. If not provided, this will default to Vault's OIDC default key.`,
|
||||
editType: 'yield',
|
||||
@ -178,8 +178,8 @@ export default class SecretsEngineForm extends Form<SecretsEngineFormData> {
|
||||
...this.data,
|
||||
config: {
|
||||
...(config || {}),
|
||||
forceNoCache: config?.forceNoCache ?? false,
|
||||
listingVisibility: config?.listingVisibility ? 'unauth' : 'hidden',
|
||||
force_no_cache: config?.force_no_cache ?? false,
|
||||
listing_visibility: config?.listing_visibility ? 'unauth' : 'hidden',
|
||||
},
|
||||
};
|
||||
// options are only relevant for kv/generic engines
|
||||
|
||||
@ -16,8 +16,8 @@ export default class AzureConfigForm extends WifConfigForm<GcpConfigFormData> {
|
||||
isAccountPluginConfigured = false;
|
||||
|
||||
get isWifPluginConfigured() {
|
||||
const { identityTokenAudience, identityTokenTtl, serviceAccountEmail } = this.data;
|
||||
return !!identityTokenAudience || !!identityTokenTtl || !!serviceAccountEmail;
|
||||
const { identity_token_audience, identity_token_ttl, service_account_email } = this.data;
|
||||
return !!identity_token_audience || !!identity_token_ttl || !!service_account_email;
|
||||
}
|
||||
|
||||
accountFields = [
|
||||
@ -38,7 +38,7 @@ export default class AzureConfigForm extends WifConfigForm<GcpConfigFormData> {
|
||||
helperTextEnabled:
|
||||
'The default config TTL (time-to-live) for long-lived credentials (i.e. service account keys).',
|
||||
}),
|
||||
new FormField('maxTtl', 'string', {
|
||||
new FormField('max_ttl', 'string', {
|
||||
label: 'Max TTL',
|
||||
editType: 'ttl',
|
||||
helperTextDisabled:
|
||||
@ -50,11 +50,11 @@ export default class AzureConfigForm extends WifConfigForm<GcpConfigFormData> {
|
||||
|
||||
wifFields = [
|
||||
this.commonWifFields.issuer,
|
||||
this.commonWifFields.identityTokenAudience,
|
||||
new FormField('serviceAccountEmail', 'string', {
|
||||
this.commonWifFields.identity_token_audience,
|
||||
new FormField('service_account_email', 'string', {
|
||||
subText: 'Email ID for the Service Account to impersonate for Workload Identity Federation.',
|
||||
}),
|
||||
this.commonWifFields.identityTokenTtl,
|
||||
this.commonWifFields.identity_token_ttl,
|
||||
];
|
||||
|
||||
get formFieldGroups() {
|
||||
|
||||
@ -11,12 +11,12 @@ import type { SshConfigureCaRequest } from '@hashicorp/vault-client-typescript';
|
||||
|
||||
export default class SshConfigForm extends Form<SshConfigureCaRequest> {
|
||||
validations: Validations = {
|
||||
generateSigningKey: [
|
||||
generate_signing_key: [
|
||||
{
|
||||
validator(data: SshConfigForm['data']) {
|
||||
const { publicKey, privateKey, generateSigningKey } = data;
|
||||
const { public_key, private_key, generate_signing_key } = data;
|
||||
// if generateSigningKey is false, both public and private keys are required
|
||||
if (!generateSigningKey && (!publicKey || !privateKey)) {
|
||||
if (!generate_signing_key && (!public_key || !private_key)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -24,12 +24,12 @@ export default class SshConfigForm extends Form<SshConfigureCaRequest> {
|
||||
message: 'Provide a Public and Private key or set "Generate Signing Key" to true.',
|
||||
},
|
||||
],
|
||||
publicKey: [
|
||||
public_key: [
|
||||
{
|
||||
validator(data: SshConfigForm['data']) {
|
||||
const { publicKey, privateKey } = data;
|
||||
const { public_key, private_key } = data;
|
||||
// regardless of generateSigningKey, if one key is set they both need to be set.
|
||||
return publicKey || privateKey ? !!(publicKey && privateKey) : true;
|
||||
return public_key || private_key ? !!(public_key && private_key) : true;
|
||||
},
|
||||
message: 'You must provide a Public and Private keys or leave both unset.',
|
||||
},
|
||||
@ -37,8 +37,8 @@ export default class SshConfigForm extends Form<SshConfigureCaRequest> {
|
||||
};
|
||||
|
||||
formFields = [
|
||||
new FormField('privateKey', 'string', { sensitive: true }),
|
||||
new FormField('publicKey', 'string', { sensitive: true }),
|
||||
new FormField('generateSigningKey', 'boolean'),
|
||||
new FormField('private_key', 'string', { sensitive: true }),
|
||||
new FormField('public_key', 'string', { sensitive: true }),
|
||||
new FormField('generate_signing_key', 'boolean'),
|
||||
];
|
||||
}
|
||||
|
||||
@ -22,12 +22,12 @@ export default class WifConfigForm<T extends object> extends Form<T> {
|
||||
placeholder: 'https://vault-test.com',
|
||||
}),
|
||||
|
||||
identityTokenAudience: new FormField('identityTokenAudience', 'string', {
|
||||
identity_token_audience: new FormField('identity_token_audience', 'string', {
|
||||
subText:
|
||||
'The audience claim value for plugin identity tokens. Must match an allowed audience configured for the target IAM OIDC identity provider.',
|
||||
}),
|
||||
|
||||
identityTokenTtl: new FormField('identityTokenTtl', 'string', {
|
||||
identity_token_ttl: new FormField('identity_token_ttl', 'string', {
|
||||
label: 'Identity token TTL',
|
||||
helperTextDisabled:
|
||||
'The TTL of generated tokens. Defaults to 1 hour, turn on the toggle to specify a different value.',
|
||||
@ -35,7 +35,7 @@ export default class WifConfigForm<T extends object> extends Form<T> {
|
||||
editType: 'ttl',
|
||||
}),
|
||||
|
||||
serviceAccountEmail: new FormField('serviceAccountEmail', 'string', {
|
||||
service_account_email: new FormField('service_account_email', 'string', {
|
||||
subText: 'Email ID for the Service Account to impersonate for Workload Identity Federation.',
|
||||
}),
|
||||
};
|
||||
|
||||
@ -23,26 +23,26 @@ export default class AwsSmForm extends Form<AwsSmFormData> {
|
||||
'For AWS secrets manager, the name of the region must be supplied, something like “us-west-1.” If empty, Vault will use the AWS_REGION environment variable if configured.',
|
||||
editDisabled: true,
|
||||
}),
|
||||
new FormField('roleArn', 'string', {
|
||||
new FormField('role_arn', 'string', {
|
||||
label: 'Role ARN',
|
||||
subText:
|
||||
'Specifies a role to assume when connecting to AWS. When assuming a role, Vault uses temporary STS credentials to authenticate.',
|
||||
}),
|
||||
new FormField('externalId', 'string', {
|
||||
new FormField('external_id', 'string', {
|
||||
label: 'External ID',
|
||||
subText:
|
||||
'Optional extra protection that must match the trust policy granting access to the AWS IAM role ARN. We recommend using a different random UUID per destination.',
|
||||
}),
|
||||
]),
|
||||
new FormFieldGroup('Credentials', [
|
||||
new FormField('accessKeyId', 'string', {
|
||||
new FormField('access_key_id', 'string', {
|
||||
label: 'Access key ID',
|
||||
subText:
|
||||
'Access key ID to authenticate against the secrets manager. If empty, Vault will use the AWS_ACCESS_KEY_ID environment variable if configured.',
|
||||
sensitive: true,
|
||||
noCopy: true,
|
||||
}),
|
||||
new FormField('secretAccessKey', 'string', {
|
||||
new FormField('secret_access_key', 'string', {
|
||||
label: 'Secret access key',
|
||||
subText:
|
||||
'Secret access key to authenticate against the secrets manager. If empty, Vault will use the AWS_SECRET_ACCESS_KEY environment variable if configured.',
|
||||
|
||||
@ -18,13 +18,13 @@ export default class AzureKvForm extends Form<AzureKvFormData> {
|
||||
formFieldGroups = [
|
||||
new FormFieldGroup('default', [
|
||||
commonFields.name,
|
||||
new FormField('keyVaultUri', 'string', {
|
||||
new FormField('key_vault_uri', 'string', {
|
||||
label: 'Key Vault URI',
|
||||
subText:
|
||||
'URI of an existing Azure Key Vault instance. If empty, Vault will use the KEY_VAULT_URI environment variable if configured.',
|
||||
editDisabled: true,
|
||||
}),
|
||||
new FormField('tenantId', 'string', {
|
||||
new FormField('tenant_id', 'string', {
|
||||
label: 'Tenant ID',
|
||||
subText:
|
||||
'ID of the target Azure tenant. If empty, Vault will use the AZURE_TENANT_ID environment variable if configured.',
|
||||
@ -34,14 +34,14 @@ export default class AzureKvForm extends Form<AzureKvFormData> {
|
||||
subText: 'Specifies a cloud for the client. The default is Azure Public Cloud.',
|
||||
editDisabled: true,
|
||||
}),
|
||||
new FormField('clientId', 'string', {
|
||||
new FormField('client_id', 'string', {
|
||||
label: 'Client ID',
|
||||
subText:
|
||||
'Client ID of an Azure app registration. If empty, Vault will use the AZURE_CLIENT_ID environment variable if configured.',
|
||||
}),
|
||||
]),
|
||||
new FormFieldGroup('Credentials', [
|
||||
new FormField('clientSecret', 'string', {
|
||||
new FormField('client_secret', 'string', {
|
||||
subText:
|
||||
'Client secret of an Azure app registration. If empty, Vault will use the AZURE_CLIENT_SECRET environment variable if configured.',
|
||||
sensitive: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default class GcpSmForm extends Form<GcpSmFormData> {
|
||||
formFieldGroups = [
|
||||
new FormFieldGroup('default', [
|
||||
commonFields.name,
|
||||
new FormField('projectId', 'string', {
|
||||
new FormField('project_id', 'string', {
|
||||
label: 'Project ID',
|
||||
subText:
|
||||
'The target project to manage secrets in. If set, overrides the project derived from the service account JSON credentials or application default credentials.',
|
||||
|
||||
@ -18,19 +18,19 @@ export default class GcpSmForm extends Form<GhFormData> {
|
||||
formFieldGroups = [
|
||||
new FormFieldGroup('default', [
|
||||
commonFields.name,
|
||||
new FormField('repositoryOwner', 'string', {
|
||||
new FormField('repository_owner', 'string', {
|
||||
subText:
|
||||
'Github organization or username that owns the repository. If empty, Vault will use the GITHUB_REPOSITORY_OWNER environment variable if configured.',
|
||||
editDisabled: true,
|
||||
}),
|
||||
new FormField('repositoryName', 'string', {
|
||||
new FormField('repository_name', 'string', {
|
||||
subText:
|
||||
'The name of the Github repository to connect to. If empty, Vault will use the GITHUB_REPOSITORY_NAME environment variable if configured.',
|
||||
editDisabled: true,
|
||||
}),
|
||||
]),
|
||||
new FormFieldGroup('Credentials', [
|
||||
new FormField('accessToken', 'string', {
|
||||
new FormField('access_token', 'string', {
|
||||
subText:
|
||||
'Personal access token to authenticate to the GitHub repository. If empty, Vault will use the GITHUB_ACCESS_TOKEN environment variable if configured.',
|
||||
sensitive: true,
|
||||
|
||||
@ -36,16 +36,16 @@ export default function destinationFormResolver(type: DestinationType, data = {}
|
||||
return new GhForm(data, options, validations);
|
||||
}
|
||||
if (type === 'vercel-project') {
|
||||
const teamId = (data as VercelProjectForm['data'])['teamId'];
|
||||
validations['teamId'] = [
|
||||
const teamId = (data as VercelProjectForm['data'])['team_id'];
|
||||
validations['team_id'] = [
|
||||
{
|
||||
validator: (formData: VercelProjectForm['data']) =>
|
||||
!options?.isNew && formData['teamId'] !== teamId ? false : true,
|
||||
!options?.isNew && formData['team_id'] !== teamId ? false : true,
|
||||
message: 'Team ID should only be updated if the project was transferred to another account.',
|
||||
level: 'warn',
|
||||
},
|
||||
];
|
||||
validations['deploymentEnvironments'] = [
|
||||
validations['deployment_environments'] = [
|
||||
{ type: 'presence', message: 'At least one environment is required.' },
|
||||
];
|
||||
return new VercelProjectForm(data, options, validations);
|
||||
|
||||
@ -14,7 +14,7 @@ export const commonFields = {
|
||||
editDisabled: true,
|
||||
}),
|
||||
|
||||
secretNameTemplate: new FormField('secretNameTemplate', 'string', {
|
||||
secretNameTemplate: new FormField('secret_name_template', 'string', {
|
||||
subText:
|
||||
'Go-template string that indicates how to format the secret name at the destination. The default template varies by destination type but is generally in the form of "vault-{{ .MountAccessor }}-{{ .SecretPath }}" e.g. "vault-kv_9a8f68ad-my-secret-1". Optional.',
|
||||
}),
|
||||
@ -38,7 +38,7 @@ export const commonFields = {
|
||||
],
|
||||
}),
|
||||
|
||||
customTags: new FormField('customTags', 'object', {
|
||||
customTags: new FormField('custom_tags', 'object', {
|
||||
subText:
|
||||
'An optional set of informational key-value pairs added as additional metadata on secrets synced to this destination. Custom tags are merged with built-in tags.',
|
||||
editType: 'kv',
|
||||
|
||||
@ -18,23 +18,23 @@ export default class VercelProjectForm extends Form<VercelProjectFormData> {
|
||||
formFieldGroups = [
|
||||
new FormFieldGroup('default', [
|
||||
commonFields.name,
|
||||
new FormField('projectId', 'string', {
|
||||
new FormField('project_id', 'string', {
|
||||
label: 'Project ID',
|
||||
subText: 'Project ID where to manage environment variables.',
|
||||
editDisabled: true,
|
||||
}),
|
||||
new FormField('teamId', 'string', {
|
||||
new FormField('team_id', 'string', {
|
||||
label: 'Team ID',
|
||||
subText: 'Team ID the project belongs to. Optional.',
|
||||
}),
|
||||
new FormField('deploymentEnvironments', 'string', {
|
||||
new FormField('deployment_environments', 'string', {
|
||||
subText: 'Deployment environments where the environment variables are available.',
|
||||
editType: 'checkboxList',
|
||||
possibleValues: ['development', 'preview', 'production'],
|
||||
}),
|
||||
]),
|
||||
new FormFieldGroup('Credentials', [
|
||||
new FormField('accessToken', 'string', {
|
||||
new FormField('access_token', 'string', {
|
||||
subText: 'Vercel API access token with the permissions to manage environment variables.',
|
||||
sensitive: true,
|
||||
noCopy: true,
|
||||
|
||||
@ -80,13 +80,7 @@ export default class AuthRoute extends ClusterRouteBase {
|
||||
async unwrapToken(token, clusterId) {
|
||||
try {
|
||||
const { auth } = await this.api.sys.unwrap({}, this.api.buildHeaders({ token }));
|
||||
const authData = {
|
||||
...auth,
|
||||
authMethodType: 'token',
|
||||
authMountPath: '',
|
||||
token: auth.clientToken,
|
||||
ttl: auth.leaseDuration,
|
||||
};
|
||||
const authData = this.auth.normalizeAuthData(auth, { authMethodType: 'token', authMountPath: '' });
|
||||
return await this.auth.authSuccess(clusterId, authData);
|
||||
} catch (e) {
|
||||
const { message } = await this.api.parseError(e);
|
||||
|
||||
@ -55,7 +55,7 @@ export default class SecretsBackendConfigurationRoute extends Route {
|
||||
.catch(handleError);
|
||||
const { data: configLease } = await this.api.secrets.awsReadLeaseConfiguration(path).catch(handleError);
|
||||
|
||||
const WIF_FIELDS = ['roleArn', 'identityTokenAudience', 'identityTokenTtl'];
|
||||
const WIF_FIELDS = ['role_arn', 'identity_token_audience', 'identity_token_ttl'];
|
||||
const issuer = await this.checkIssuer(configRoot, WIF_FIELDS);
|
||||
|
||||
if (configRoot) {
|
||||
@ -66,7 +66,7 @@ export default class SecretsBackendConfigurationRoute extends Route {
|
||||
async fetchAzureConfig(path) {
|
||||
try {
|
||||
const { data: azureConfig } = await this.api.secrets.azureReadConfiguration(path);
|
||||
const WIF_FIELDS = ['identityTokenAudience', 'identityTokenTtl'];
|
||||
const WIF_FIELDS = ['identity_token_audience', 'identity_token_ttl'];
|
||||
const issuer = await this.checkIssuer(azureConfig, WIF_FIELDS);
|
||||
// azure config endpoint returns 200 with default values if engine has not been configured yet
|
||||
// all values happen to be falsy so we can just check if any are truthy
|
||||
@ -87,7 +87,7 @@ export default class SecretsBackendConfigurationRoute extends Route {
|
||||
async fetchGcpConfig(path) {
|
||||
try {
|
||||
const { data: gcpConfig } = await this.api.secrets.googleCloudReadConfiguration(path);
|
||||
const WIF_FIELDS = ['identityTokenAudience', 'identityTokenTtl', 'serviceAccountEmail'];
|
||||
const WIF_FIELDS = ['identity_token_audience', 'identity_token_ttl', 'service_account_email'];
|
||||
const issuer = await this.checkIssuer(gcpConfig, WIF_FIELDS);
|
||||
|
||||
if (gcpConfig) {
|
||||
|
||||
@ -43,7 +43,7 @@ export default class SecretsBackendConfigurationEdit extends Route {
|
||||
}[type];
|
||||
|
||||
const defaults = {
|
||||
ssh: { generateSigningKey: true, issuer: '' },
|
||||
ssh: { generate_signing_key: true, issuer: '' },
|
||||
}[type] || { issuer: '' };
|
||||
|
||||
// if the engine type is not configurable or a form class does not exist for the type return a 404.
|
||||
|
||||
@ -17,11 +17,11 @@ export default class VaultClusterSettingsMountSecretBackendRoute extends Route {
|
||||
|
||||
model() {
|
||||
const defaults = {
|
||||
config: { listingVisibility: false },
|
||||
kvConfig: {
|
||||
maxVersions: 0,
|
||||
casRequired: false,
|
||||
deleteVersionAfter: undefined,
|
||||
config: { listing_visibility: false },
|
||||
kv_config: {
|
||||
max_versions: 0,
|
||||
cas_required: false,
|
||||
delete_version_after: undefined,
|
||||
},
|
||||
options: { version: 2 },
|
||||
};
|
||||
|
||||
@ -421,13 +421,13 @@ export default Service.extend({
|
||||
},
|
||||
|
||||
parseMfaResponse(mfaRequirement) {
|
||||
// mfaRequirement response comes back in a shape that is not easy to work with
|
||||
// mfa_requirement response comes back in a shape that is not easy to work with
|
||||
// convert to array of objects and add necessary properties to satisfy the view
|
||||
if (mfaRequirement) {
|
||||
const { mfaRequestId, mfaConstraints } = mfaRequirement;
|
||||
const { mfa_request_id, mfa_constraints } = mfaRequirement;
|
||||
const constraints = [];
|
||||
for (const key in mfaConstraints) {
|
||||
const methods = mfaConstraints[key].any;
|
||||
for (const key in mfa_constraints) {
|
||||
const methods = mfa_constraints[key].any;
|
||||
const isMulti = methods.length > 1;
|
||||
|
||||
// friendly label for display in MfaForm
|
||||
@ -441,7 +441,7 @@ export default Service.extend({
|
||||
selectedMethod: isMulti ? null : methods[0],
|
||||
});
|
||||
}
|
||||
return { mfaRequestId, mfaConstraints: constraints };
|
||||
return { mfa_request_id, mfa_constraints: constraints };
|
||||
}
|
||||
return {};
|
||||
},
|
||||
@ -484,7 +484,7 @@ export default Service.extend({
|
||||
// Depending on where auth happens (mfa/validate, renew-self or the method's login) the auth data
|
||||
// varies slightly (i.e. "ttl" vs "lease_duration"). Normalize it so stored authData contains consistent keys.
|
||||
// (Also, the API service returns camel cased keys and raw ajax requests return snake cased params.)
|
||||
normalizeAuthData(authData, { authMethodType, authMountPath, displayName }) {
|
||||
normalizeAuthData(authData, { authMethodType, authMountPath, displayName, token, ttl }) {
|
||||
const displayNameFromMetadata = (metadata) =>
|
||||
metadata
|
||||
? ['org', 'username']
|
||||
@ -498,10 +498,11 @@ export default Service.extend({
|
||||
authMountPath,
|
||||
entityId: authData?.entity_id,
|
||||
expireTime: authData?.expire_time,
|
||||
token: authData?.client_token,
|
||||
token: token || authData?.client_token,
|
||||
renewable: authData?.renewable,
|
||||
ttl: authData?.lease_duration,
|
||||
ttl: ttl || authData?.lease_duration,
|
||||
policies: authData?.policies,
|
||||
mfaRequirement: authData?.mfa_requirement,
|
||||
// not all methods return a display name or metadata, if this is still empty it will be gleaned from lookup-self
|
||||
displayName: displayName || displayNameFromMetadata(authData?.metadata),
|
||||
};
|
||||
|
||||
@ -34,10 +34,10 @@ export default class CustomMessagesService extends Service {
|
||||
try {
|
||||
const type = this.auth.currentToken ? 'Authenticated' : 'Unauthenticated';
|
||||
const method = `internalUiRead${type}ActiveCustomMessages`;
|
||||
const { keys = [], keyInfo } = await this.api.sys[method]();
|
||||
const { keys = [], key_info } = await this.api.sys[method]();
|
||||
|
||||
this.messages = keys.map((key) => {
|
||||
const data = keyInfo[key];
|
||||
const data = key_info[key];
|
||||
return {
|
||||
id: key,
|
||||
...data,
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
{{#each this.displayFields as |field|}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{and (not (is-empty-value (get this.model.secretsEngine field))) (not-eq field "version")}}
|
||||
@formatTtl={{includes field (array "config.defaultLeaseTtl" "config.maxLeaseTtl")}}
|
||||
@formatTtl={{includes field (array "config.default_lease_ttl" "config.max_lease_ttl")}}
|
||||
@label={{this.label field}}
|
||||
@value={{get this.model.secretsEngine field}}
|
||||
/>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
id="never"
|
||||
value="never"
|
||||
@value="never"
|
||||
@onChange={{fn (mut @message.endTime) ""}}
|
||||
@onChange={{fn (mut @message.end_time) ""}}
|
||||
@groupValue={{this.groupValue}}
|
||||
/>
|
||||
<label for="never" class="has-left-margin-xs has-text-black is-size-7">
|
||||
@ -45,8 +45,8 @@
|
||||
@type="datetime-local"
|
||||
@value={{if this.messageEndTime (date-format this.messageEndTime this.datetimeLocalStringFormat) ""}}
|
||||
class="input has-top-margin-xs is-auto-width {{if this.validationError 'has-error-border'}}"
|
||||
name="endTime"
|
||||
data-test-input="endTime"
|
||||
name="end_time"
|
||||
data-test-input="end_time"
|
||||
{{on "focusout" this.onFocusOut}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -26,9 +26,9 @@ export default class MessageExpirationDateForm extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
if (this.args.message.endTime) {
|
||||
if (this.args.message.end_time) {
|
||||
this.groupValue = 'specificDate';
|
||||
this.messageEndTime = this.args.message.endTime;
|
||||
this.messageEndTime = this.args.message.end_time;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,13 +41,13 @@ export default class MessageExpirationDateForm extends Component {
|
||||
@action
|
||||
specificDateChange() {
|
||||
this.groupValue = 'specificDate';
|
||||
this.args.message.endTime = this.messageEndTime;
|
||||
this.args.message.end_time = this.messageEndTime;
|
||||
}
|
||||
|
||||
@action
|
||||
onFocusOut(e) {
|
||||
this.messageEndTime = e.target.value;
|
||||
this.args.message.endTime = this.messageEndTime;
|
||||
this.args.message.end_time = this.messageEndTime;
|
||||
this.groupValue = 'specificDate';
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,8 +46,8 @@ export default class MessagesList extends Component {
|
||||
get hasExpiredModalMessages() {
|
||||
const modalMessages = this.args.messages?.filter((message) => message.type === 'modal') || [];
|
||||
return modalMessages.every((message) => {
|
||||
if (!message.endTime) return false;
|
||||
return isAfter(timestamp.now(), new Date(message.endTime));
|
||||
if (!message.end_time) return false;
|
||||
return isAfter(timestamp.now(), new Date(message.end_time));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -32,12 +32,12 @@
|
||||
</Toolbar>
|
||||
|
||||
{{#each this.displayFields as |field|}}
|
||||
{{#if (or (eq field "endTime") (eq field "startTime"))}}
|
||||
{{! if the attr is an endTime and is falsy, we want to show a 'Never' text value }}
|
||||
{{#if (or (eq field "end_time") (eq field "start_time"))}}
|
||||
{{! if the attr is an end_time and is falsy, we want to show a 'Never' text value }}
|
||||
<InfoTableRow
|
||||
@label={{capitalize (humanize (dasherize field))}}
|
||||
@value={{if
|
||||
(and (eq field "endTime") (not (get @message field)))
|
||||
(and (eq field "end_time") (not (get @message field)))
|
||||
"Never"
|
||||
(date-format (get @message field) "MMM d, yyyy hh:mm aaa" withTimeZone=true)
|
||||
}}
|
||||
|
||||
@ -25,7 +25,7 @@ export default class MessageDetails extends Component {
|
||||
@service pagination;
|
||||
@service api;
|
||||
|
||||
displayFields = ['active', 'type', 'authenticated', 'title', 'message', 'startTime', 'endTime', 'link'];
|
||||
displayFields = ['active', 'type', 'authenticated', 'title', 'message', 'start_time', 'end_time', 'link'];
|
||||
|
||||
@action
|
||||
async deleteMessage() {
|
||||
|
||||
@ -35,7 +35,7 @@ export default class MessagesList extends Component {
|
||||
@tracked messageToDelete = null;
|
||||
|
||||
isStartTimeAfterToday = (message) => {
|
||||
return isAfter(message.startTime, timestamp.now());
|
||||
return isAfter(message.start_time, timestamp.now());
|
||||
};
|
||||
|
||||
get formattedMessages() {
|
||||
@ -44,8 +44,8 @@ export default class MessagesList extends Component {
|
||||
let badgeColor = 'neutral';
|
||||
|
||||
if (message.active) {
|
||||
if (message.endTime) {
|
||||
badgeDisplayText = `Active until ${dateFormat([message.endTime, 'MMM d, yyyy hh:mm aaa'], {
|
||||
if (message.end_time) {
|
||||
badgeDisplayText = `Active until ${dateFormat([message.end_time, 'MMM d, yyyy hh:mm aaa'], {
|
||||
withTimeZone: true,
|
||||
})}`;
|
||||
} else {
|
||||
@ -54,12 +54,12 @@ export default class MessagesList extends Component {
|
||||
badgeColor = 'success';
|
||||
} else {
|
||||
if (this.isStartTimeAfterToday(message)) {
|
||||
badgeDisplayText = `Scheduled: ${dateFormat([message.startTime, 'MMM d, yyyy hh:mm aaa'], {
|
||||
badgeDisplayText = `Scheduled: ${dateFormat([message.start_time, 'MMM d, yyyy hh:mm aaa'], {
|
||||
withTimeZone: true,
|
||||
})}`;
|
||||
badgeColor = 'highlight';
|
||||
} else {
|
||||
badgeDisplayText = `Inactive: ${dateFormat([message.startTime, 'MMM d, yyyy hh:mm aaa'], {
|
||||
badgeDisplayText = `Inactive: ${dateFormat([message.start_time, 'MMM d, yyyy hh:mm aaa'], {
|
||||
withTimeZone: true,
|
||||
})}`;
|
||||
badgeColor = 'neutral';
|
||||
|
||||
@ -20,8 +20,8 @@ export default class MessagesCreateRoute extends Route {
|
||||
|
||||
async getMessages(authenticated) {
|
||||
try {
|
||||
const { keyInfo } = await this.api.sys.uiConfigListCustomMessages(true, undefined, authenticated);
|
||||
return Object.values(keyInfo);
|
||||
const { key_info } = await this.api.sys.uiConfigListCustomMessages(true, undefined, authenticated);
|
||||
return Object.values(key_info);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@ -33,7 +33,7 @@ export default class MessagesCreateRoute extends Route {
|
||||
{
|
||||
authenticated,
|
||||
type: 'banner',
|
||||
startTime: addDays(startOfDay(timestamp.now()), 1).toISOString(),
|
||||
start_time: addDays(startOfDay(timestamp.now()), 1).toISOString(),
|
||||
},
|
||||
{ isNew: true }
|
||||
);
|
||||
|
||||
@ -37,22 +37,22 @@ export default class MessagesRoute extends Route {
|
||||
}[status];
|
||||
|
||||
try {
|
||||
const { keyInfo, keys } = await this.api.sys.uiConfigListCustomMessages(
|
||||
const { key_info, keys } = await this.api.sys.uiConfigListCustomMessages(
|
||||
true,
|
||||
active,
|
||||
authenticated,
|
||||
type
|
||||
);
|
||||
// ids are in the keys array and can be mapped to the object in keyInfo
|
||||
// map and set id property on keyInfo object
|
||||
// ids are in the keys array and can be mapped to the object in key_info
|
||||
// map and set id property on key_info object
|
||||
const data = keys.map((id) => {
|
||||
const { startTime, endTime, ...message } = keyInfo[id];
|
||||
const { start_time, end_time, ...message } = key_info[id];
|
||||
// dates returned from list endpoint are strings -- convert to date
|
||||
return {
|
||||
id,
|
||||
...message,
|
||||
startTime: startTime ? new Date(startTime) : startTime,
|
||||
endTime: endTime ? new Date(endTime) : endTime,
|
||||
start_time: start_time ? new Date(start_time) : start_time,
|
||||
end_time: end_time ? new Date(end_time) : end_time,
|
||||
};
|
||||
});
|
||||
const messages = paginate(data, {
|
||||
|
||||
@ -14,14 +14,14 @@ export default class MessagesMessageEditRoute extends Route {
|
||||
async model() {
|
||||
const { id } = this.paramsFor('messages.message');
|
||||
const data = await this.api.sys.uiConfigReadCustomMessage(id);
|
||||
const { keyInfo, keys } = await this.api.sys.uiConfigListCustomMessages(
|
||||
const { key_info, keys } = await this.api.sys.uiConfigListCustomMessages(
|
||||
true,
|
||||
undefined,
|
||||
data.authenticated
|
||||
);
|
||||
return {
|
||||
message: new CustomMessage({ ...data, message: decodeString(data.message) }),
|
||||
messages: keys.map((id) => ({ ...keyInfo[id], id })),
|
||||
messages: keys.map((id) => ({ ...key_info[id], id })),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
@modelValidations={{@modelValidations}}
|
||||
@showHelpText={{@showHelpText}}
|
||||
>
|
||||
{{#if (and (has-block "identityTokenKey") (eq attr.name "config.identityTokenKey"))}}
|
||||
{{#if (and (has-block "identityTokenKey") (eq attr.name "config.identity_token_key"))}}
|
||||
{{yield to="identityTokenKey"}}
|
||||
{{/if}}
|
||||
</FormField>
|
||||
|
||||
@ -37,10 +37,10 @@ export default class SecretsEngineMountConfig extends Component<Args> {
|
||||
{ label: 'Path', value: secretsEngine.path },
|
||||
{ label: 'Accessor', value: secretsEngine.accessor },
|
||||
{ label: 'Local', value: secretsEngine.local },
|
||||
{ label: 'Seal wrap', value: secretsEngine.sealWrap },
|
||||
{ label: 'Default Lease TTL', value: duration([secretsEngine.config.defaultLeaseTtl]) },
|
||||
{ label: 'Max Lease TTL', value: duration([secretsEngine.config.maxLeaseTtl]) },
|
||||
{ label: 'Identity token key', value: secretsEngine.config.identityTokenKey },
|
||||
{ label: 'Seal wrap', value: secretsEngine.seal_wrap },
|
||||
{ label: 'Default Lease TTL', value: duration([secretsEngine.config.default_lease_ttl]) },
|
||||
{ label: 'Max Lease TTL', value: duration([secretsEngine.config.max_lease_ttl]) },
|
||||
{ label: 'Identity token key', value: secretsEngine.config.identity_token_key },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ const SYNC_DESTINATIONS: Array<SyncDestination> = [
|
||||
type: 'aws-sm',
|
||||
icon: 'aws-color',
|
||||
category: 'cloud',
|
||||
maskedParams: ['accessKeyId', 'secretAccessKey'],
|
||||
maskedParams: ['access_key_id', 'secret_access_key'],
|
||||
readonlyParams: ['name', 'region'],
|
||||
defaultValues: {
|
||||
granularity: 'secret-path',
|
||||
@ -30,8 +30,8 @@ const SYNC_DESTINATIONS: Array<SyncDestination> = [
|
||||
type: 'azure-kv',
|
||||
icon: 'azure-color',
|
||||
category: 'cloud',
|
||||
maskedParams: ['clientSecret'],
|
||||
readonlyParams: ['name', 'keyVaultUri', 'tenantId', 'cloud'],
|
||||
maskedParams: ['client_secret'],
|
||||
readonlyParams: ['name', 'key_vault_uri', 'tenant_id', 'cloud'],
|
||||
defaultValues: {
|
||||
granularity: 'secret-path',
|
||||
},
|
||||
@ -52,8 +52,8 @@ const SYNC_DESTINATIONS: Array<SyncDestination> = [
|
||||
type: 'gh',
|
||||
icon: 'github-color',
|
||||
category: 'dev-tools',
|
||||
maskedParams: ['accessToken'],
|
||||
readonlyParams: ['name', 'repositoryOwner', 'repositoryName'],
|
||||
maskedParams: ['access_token'],
|
||||
readonlyParams: ['name', 'repository_owner', 'repository_name'],
|
||||
defaultValues: {
|
||||
granularity: 'secret-key',
|
||||
},
|
||||
@ -63,11 +63,11 @@ const SYNC_DESTINATIONS: Array<SyncDestination> = [
|
||||
type: 'vercel-project',
|
||||
icon: 'vercel-color',
|
||||
category: 'dev-tools',
|
||||
maskedParams: ['accessToken'],
|
||||
readonlyParams: ['name', 'projectId'],
|
||||
maskedParams: ['access_token'],
|
||||
readonlyParams: ['name', 'project_id'],
|
||||
defaultValues: {
|
||||
granularity: 'secret-key',
|
||||
deploymentEnvironments: [],
|
||||
deployment_environments: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@ -13,29 +13,29 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
{{#if @destination.purgeInitiatedAt}}
|
||||
{{#if @destination.purge_initiated_at}}
|
||||
<Hds::Alert
|
||||
data-test-delete-status-banner
|
||||
@type="inline"
|
||||
class="has-bottom-margin-m"
|
||||
@color={{if @destination.purgeError "critical" "neutral"}}
|
||||
@icon={{unless @destination.purgeError "loading-static"}}
|
||||
@color={{if @destination.purge_error "critical" "neutral"}}
|
||||
@icon={{unless @destination.purge_error "loading-static"}}
|
||||
as |A|
|
||||
>
|
||||
{{#if @destination.purgeError}}
|
||||
{{#if @destination.purge_error}}
|
||||
<A.Title>Deletion failed</A.Title>
|
||||
<A.Description>
|
||||
There was a problem with the delete purge initiated at
|
||||
{{date-format @destination.purgeInitiatedAt "MMM dd, yyyy 'at' hh:mm:ss aaa"}}.
|
||||
{{date-format @destination.purge_initiated_at "MMM dd, yyyy 'at' hh:mm:ss aaa"}}.
|
||||
</A.Description>
|
||||
<A.Description>
|
||||
{{@destination.purgeError}}
|
||||
{{@destination.purge_error}}
|
||||
</A.Description>
|
||||
{{else}}
|
||||
<A.Title>Deletion in progress</A.Title>
|
||||
<A.Description>
|
||||
Purge initiated on
|
||||
{{date-format @destination.purgeInitiatedAt "MMM dd, yyyy 'at' hh:mm:ss aaa"}}. This process may take some time
|
||||
{{date-format @destination.purge_initiated_at "MMM dd, yyyy 'at' hh:mm:ss aaa"}}. This process may take some time
|
||||
depending on how many secrets must be un-synced from this destination.
|
||||
</A.Description>
|
||||
{{/if}}
|
||||
|
||||
@ -31,13 +31,13 @@ export default class DestinationsTabsToolbar extends Component<Args> {
|
||||
get showSyncBtn() {
|
||||
const { destination, capabilities } = this.args;
|
||||
const path = this.capabilities.pathFor('syncSetAssociation', destination);
|
||||
return capabilities[path]?.canUpdate && !destination.purgeInitiatedAt;
|
||||
return capabilities[path]?.canUpdate && !destination.purge_initiated_at;
|
||||
}
|
||||
|
||||
get showEditBtn() {
|
||||
const { destination, capabilities } = this.args;
|
||||
const path = this.capabilities.pathFor('syncDestination', destination);
|
||||
return capabilities[path]?.canUpdate && !destination.purgeInitiatedAt;
|
||||
return capabilities[path]?.canUpdate && !destination.purge_initiated_at;
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<code class="has-text-grey is-size-8" data-test-destination-type={{index}}>
|
||||
{{destination.typeDisplayName}}
|
||||
{{destination.type_display_name}}
|
||||
</code>
|
||||
</Item.content>
|
||||
|
||||
|
||||
@ -39,10 +39,10 @@ export default class DestinationsCreateForm extends Component<Args> {
|
||||
super(owner, args);
|
||||
// cache initial custom tags value to compare against updates
|
||||
// tags that are removed when editing need to be added to the payload
|
||||
// cast type here since not all types have customTags
|
||||
const { customTags } = args.form.data as unknown as Record<string, unknown>;
|
||||
if (customTags) {
|
||||
this.initialCustomTags = { ...customTags };
|
||||
// cast type here since not all types have custom_tags
|
||||
const { custom_tags } = args.form.data as unknown as Record<string, unknown>;
|
||||
if (custom_tags) {
|
||||
this.initialCustomTags = { ...custom_tags };
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,13 +92,15 @@ export default class DestinationsCreateForm extends Component<Args> {
|
||||
diffCustomTags(payload: Record<string, unknown>) {
|
||||
// if tags were removed we need to add them to the payload
|
||||
const { isNew } = this.args.form;
|
||||
const { customTags } = payload;
|
||||
if (!isNew && customTags && this.initialCustomTags) {
|
||||
// compare the new and old keys of customTags object to determine which need to be removed
|
||||
const oldKeys = Object.keys(this.initialCustomTags).filter((k) => !Object.keys(customTags).includes(k));
|
||||
// add tagsToRemove to the payload if there is a diff
|
||||
const { custom_tags } = payload;
|
||||
if (!isNew && custom_tags && this.initialCustomTags) {
|
||||
// compare the new and old keys of custom_tags object to determine which need to be removed
|
||||
const oldKeys = Object.keys(this.initialCustomTags).filter(
|
||||
(k) => !Object.keys(custom_tags).includes(k)
|
||||
);
|
||||
// add tags_to_remove to the payload if there is a diff
|
||||
if (oldKeys.length > 0) {
|
||||
payload['tagsToRemove'] = oldKeys;
|
||||
payload['tags_to_remove'] = oldKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<InfoTableRow @label={{this.fieldLabel field}}>
|
||||
<Hds::Badge @text={{this.credentialValue fieldValue}} @icon="check-circle" @color="success" />
|
||||
</InfoTableRow>
|
||||
{{else if (eq field "options.customTags")}}
|
||||
{{else if (eq field "options.custom_tags")}}
|
||||
{{#unless (is-empty-value fieldValue)}}
|
||||
<Hds::Text::Display @tag="h3" @size="300" @weight="semibold" class="has-top-margin-l" data-test-section-header>
|
||||
Custom tags
|
||||
|
||||
@ -17,45 +17,50 @@ interface Args {
|
||||
|
||||
export default class DestinationDetailsPage extends Component<Args> {
|
||||
connectionDetailsMap = {
|
||||
'aws-sm': ['region', 'accessKeyId', 'secretAccessKey', 'roleArn', 'externalId'],
|
||||
'azure-kv': ['keyVaultUri', 'tenantId', 'cloud', 'clientId', 'clientSecret'],
|
||||
'gcp-sm': ['projectId', 'credentials'],
|
||||
gh: ['repositoryOwner', 'repositoryName', 'accessToken'],
|
||||
'vercel-project': ['accessToken', 'projectId', 'teamId', 'deploymentEnvironments'],
|
||||
'aws-sm': ['region', 'access_key_id', 'secret_access_key', 'role_arn', 'external_id'],
|
||||
'azure-kv': ['key_vault_uri', 'tenant_id', 'cloud', 'client_id', 'client_secret'],
|
||||
'gcp-sm': ['project_id', 'credentials'],
|
||||
gh: ['repository_owner', 'repository_name', 'access_token'],
|
||||
'vercel-project': ['access_token', 'project_id', 'team_id', 'deployment_environments'],
|
||||
};
|
||||
|
||||
get displayFields() {
|
||||
const { destination } = this.args;
|
||||
const type = destination.type as keyof typeof this.connectionDetailsMap;
|
||||
const connectionDetails = this.connectionDetailsMap[type].map((field) => `connectionDetails.${field}`);
|
||||
const fields = ['name', ...connectionDetails, 'options.granularityLevel', 'options.secretNameTemplate'];
|
||||
const connectionDetails = this.connectionDetailsMap[type].map((field) => `connection_details.${field}`);
|
||||
const fields = [
|
||||
'name',
|
||||
...connectionDetails,
|
||||
'options.granularity_level',
|
||||
'options.secret_name_template',
|
||||
];
|
||||
|
||||
if (!['gh', 'vercel-project'].includes(type)) {
|
||||
fields.push('options.customTags');
|
||||
fields.push('options.custom_tags');
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
// remove connectionDetails or options from the field name
|
||||
// remove connection_details or options from the field name
|
||||
fieldName(field: string) {
|
||||
return field.replace(/(connectionDetails|options)\./, '');
|
||||
return field.replace(/(connection_details|options)\./, '');
|
||||
}
|
||||
|
||||
fieldLabel = (field: string) => {
|
||||
const fieldName = this.fieldName(field);
|
||||
// some fields have a specific label that cannot be converted from key name
|
||||
const customLabel = {
|
||||
granularityLevel: 'Secret sync granularity',
|
||||
accessKeyId: 'Access key ID',
|
||||
roleArn: 'Role ARN',
|
||||
externalId: 'External ID',
|
||||
keyVaultUri: 'Key Vault URI',
|
||||
clientId: 'Client ID',
|
||||
tenantId: 'Tenant ID',
|
||||
projectId: 'Project ID',
|
||||
granularity_level: 'Secret sync granularity',
|
||||
access_key_id: 'Access key ID',
|
||||
role_arn: 'Role ARN',
|
||||
external_id: 'External ID',
|
||||
key_vault_uri: 'Key Vault URI',
|
||||
client_id: 'Client ID',
|
||||
tenant_id: 'Tenant ID',
|
||||
project_id: 'Project ID',
|
||||
credentials: 'JSON credentials',
|
||||
teamId: 'Team ID',
|
||||
team_id: 'Team ID',
|
||||
}[fieldName];
|
||||
|
||||
return customLabel || toLabel([fieldName]);
|
||||
|
||||
@ -22,18 +22,18 @@
|
||||
data-test-association-name={{index}}
|
||||
class="has-text-black has-text-weight-semibold"
|
||||
@route="kvSecretOverview"
|
||||
@models={{array association.mount association.secretName}}
|
||||
@models={{array association.mount association.secret_name}}
|
||||
>
|
||||
{{association.secretName}}
|
||||
{{association.secret_name}}
|
||||
</LinkToExternal>
|
||||
{{#if association.subKey}}
|
||||
<Hds::Badge @text="secret key: {{association.subKey}}/" />
|
||||
{{#if association.sub_key}}
|
||||
<Hds::Badge @text="secret key: {{association.sub_key}}/" />
|
||||
{{/if}}
|
||||
<div>
|
||||
<SyncStatusBadge @status={{association.syncStatus}} data-test-association-status={{index}} />
|
||||
<SyncStatusBadge @status={{association.sync_status}} data-test-association-status={{index}} />
|
||||
<code class="has-text-grey is-size-8" data-test-association-updated={{index}}>
|
||||
last updated on
|
||||
{{date-format association.updatedAt "MMMM do yyyy, h:mm:ss a"}}
|
||||
{{date-format association.updated_at "MMMM do yyyy, h:mm:ss a"}}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
@ -47,9 +47,9 @@
|
||||
@hasChevron={{false}}
|
||||
data-test-popup-menu-trigger
|
||||
/>
|
||||
{{#if (eq @destination.options.granularityLevel "secret-key")}}
|
||||
{{#if (eq @destination.options.granularity_level "secret-key")}}
|
||||
<dd.Description
|
||||
@text='Sync or unsync actions will apply to the secret "{{association.secretName}}" and not this individual key.'
|
||||
@text='Sync or unsync actions will apply to the secret "{{association.secret_name}}" and not this individual key.'
|
||||
/>
|
||||
<dd.Separator />
|
||||
{{/if}}
|
||||
@ -62,7 +62,7 @@
|
||||
data-test-association-action="view"
|
||||
@route="kvSecretOverview"
|
||||
@isRouteExternal={{true}}
|
||||
@models={{array association.mount association.secretName}}
|
||||
@models={{array association.mount association.secret_name}}
|
||||
>
|
||||
View secret
|
||||
</dd.Interactive>
|
||||
@ -107,7 +107,7 @@
|
||||
{{#if this.secretToUnsync}}
|
||||
<ConfirmModal
|
||||
@color="critical"
|
||||
@confirmMessage='The secret "{{this.secretToUnsync.secretName}}" will be unsynced from this destination.'
|
||||
@confirmMessage='The secret "{{this.secretToUnsync.secret_name}}" will be unsynced from this destination.'
|
||||
@onClose={{fn (mut this.secretToUnsync) null}}
|
||||
@onConfirm={{fn this.update this.secretToUnsync "remove"}}
|
||||
/>
|
||||
|
||||
@ -51,8 +51,8 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
|
||||
async update(association: AssociatedSecret, operation: string) {
|
||||
try {
|
||||
const { name, type } = this.args.destination;
|
||||
const { mount, secretName } = association;
|
||||
const body = { mount, secretName };
|
||||
const { mount, secret_name } = association;
|
||||
const body = { mount, secret_name };
|
||||
|
||||
if (operation === 'set') {
|
||||
await this.api.sys.systemWriteSyncDestinationsTypeNameAssociationsSet(name, type, body);
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
<p class="is-label">Which secrets would you like us to sync?</p>
|
||||
<p class="sub-text">
|
||||
Select a KV engine mount and path to sync a secret to the
|
||||
{{@destination.typeDisplayName}}
|
||||
{{@destination.type_display_name}}
|
||||
destination. Selecting a previously synced secret will re-sync that secret.
|
||||
{{#if (eq @destination.granularity "secret-key")}}
|
||||
This destination is configured to sync with
|
||||
|
||||
@ -87,7 +87,7 @@ export default class DestinationSyncPageComponent extends Component<Args> {
|
||||
this.syncedSecret = '';
|
||||
const { name, type } = this.args.destination;
|
||||
const mount = keyIsFolder(this.mountPath) ? this.mountPath.slice(0, -1) : this.mountPath; // strip trailing slash from mount path
|
||||
const payload = { mount, secretName: this.secretPath };
|
||||
const payload = { mount, secret_name: this.secretPath };
|
||||
await this.api.sys.systemWriteSyncDestinationsTypeNameAssociationsSet(name, type, payload);
|
||||
this.syncedSecret = this.secretPath;
|
||||
// reset the secret path to help make it clear that the sync was successful
|
||||
|
||||
@ -67,9 +67,9 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
|
||||
responses: SystemReadSyncDestinationsTypeNameAssociationsResponse[]
|
||||
): DestinationMetrics[] {
|
||||
return responses.map((response) => {
|
||||
const { storeName, storeType, associatedSecrets } = response;
|
||||
const type = storeType as DestinationType;
|
||||
const secrets = associatedSecrets as Record<string, AssociatedSecret>;
|
||||
const { store_name, store_type, associated_secrets } = response;
|
||||
const type = store_type as DestinationType;
|
||||
const secrets = associated_secrets as Record<string, AssociatedSecret>;
|
||||
const unsynced = [];
|
||||
let lastUpdated;
|
||||
|
||||
@ -77,11 +77,11 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
|
||||
const association = secrets[key];
|
||||
// for display purposes, any status other than SYNCED is considered unsynced
|
||||
if (association) {
|
||||
if (association.syncStatus !== 'SYNCED') {
|
||||
unsynced.push(association.syncStatus);
|
||||
if (association.sync_status !== 'SYNCED') {
|
||||
unsynced.push(association.sync_status);
|
||||
}
|
||||
// use the most recent updated_at value as the last synced date
|
||||
const updated = new Date(association.updatedAt);
|
||||
const updated = new Date(association.updated_at);
|
||||
if (!lastUpdated || updated > lastUpdated) {
|
||||
lastUpdated = updated;
|
||||
}
|
||||
@ -91,7 +91,7 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
|
||||
const associationCount = Object.entries(secrets).length;
|
||||
return {
|
||||
icon: findDestination(type).icon,
|
||||
name: storeName,
|
||||
name: store_name,
|
||||
type,
|
||||
associationCount,
|
||||
status: associationCount ? (unsynced.length ? `${unsynced.length} Unsynced` : 'All synced') : null,
|
||||
|
||||
@ -58,7 +58,7 @@ export default class SyncSecretsDestinationsDestinationRoute extends Route {
|
||||
const baseRoute = 'vault.cluster.sync.secrets.destinations.destination';
|
||||
const routes = [`${baseRoute}.edit`, `${baseRoute}.sync`];
|
||||
const toRoute = transition.to?.name;
|
||||
if (toRoute && routes.includes(toRoute) && destination.purgeInitiatedAt) {
|
||||
if (toRoute && routes.includes(toRoute) && destination.purge_initiated_at) {
|
||||
const action = transition.to?.localName === 'edit' ? 'Editing a destination' : 'Syncing secrets';
|
||||
this.flashMessages.info(`${action} is not permitted once a purge has been initiated.`);
|
||||
this.router.replaceWith('vault.cluster.sync.secrets.destinations.destination.secrets');
|
||||
|
||||
@ -14,17 +14,17 @@ import type { DestinationRouteModel } from '../destination';
|
||||
export default class SyncSecretsDestinationsDestinationEditRoute extends Route {
|
||||
model() {
|
||||
const { destination } = this.modelFor('secrets.destinations.destination') as DestinationRouteModel;
|
||||
const { type, name, connectionDetails, options } = destination;
|
||||
const { type, name, connection_details, options } = destination;
|
||||
// granularity is returned as granularityLevel in the response but expected as granularity in the request
|
||||
const { granularityLevel, ...partialOptions } = options;
|
||||
const { granularity_level, ...partialOptions } = options;
|
||||
|
||||
return {
|
||||
type,
|
||||
form: formResolver(type, {
|
||||
name,
|
||||
...connectionDetails,
|
||||
...connection_details,
|
||||
...partialOptions,
|
||||
granularity: granularityLevel,
|
||||
granularity: granularity_level,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -30,14 +30,14 @@ export default class SyncDestinationSecretsRoute extends Route {
|
||||
) as DestinationRouteModel;
|
||||
|
||||
const {
|
||||
associatedSecrets = {},
|
||||
storeName,
|
||||
storeType,
|
||||
associated_secrets = {},
|
||||
store_name,
|
||||
store_type,
|
||||
} = await this.api.sys.systemReadSyncDestinationsTypeNameAssociations(destination.name, destination.type);
|
||||
|
||||
const associations = Object.values(associatedSecrets).map((association) => ({
|
||||
destinationName: storeName,
|
||||
destinationType: storeType,
|
||||
const associations = Object.values(associated_secrets).map((association) => ({
|
||||
destination_name: store_name,
|
||||
destination_type: store_type,
|
||||
...association,
|
||||
}));
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ export default class SyncSecretsOverviewRoute extends Route {
|
||||
]
|
||||
: [capabilitiesReq, [], []];
|
||||
|
||||
const [{ canCreate, canUpdate }, { totalSecrets }, destinations] = (await Promise.all(requests)) as [
|
||||
const [{ canCreate, canUpdate }, { total_secrets }, destinations] = (await Promise.all(requests)) as [
|
||||
Capabilities,
|
||||
SystemListSyncAssociationsResponse,
|
||||
SystemListSyncDestinationsResponse,
|
||||
@ -48,7 +48,7 @@ export default class SyncSecretsOverviewRoute extends Route {
|
||||
|
||||
return {
|
||||
canActivateSecretsSync: canCreate || canUpdate,
|
||||
totalSecrets,
|
||||
total_secrets,
|
||||
destinations: listDestinationsTransform(destinations),
|
||||
};
|
||||
}
|
||||
|
||||
@ -17,20 +17,20 @@ export const listDestinationsTransform = (
|
||||
nameFilter?: string,
|
||||
typeFilter?: string
|
||||
) => {
|
||||
const { keyInfo } = response;
|
||||
const { key_info } = response;
|
||||
const destinations: ListDestination[] = [];
|
||||
// build ListDestination objects from keyInfo
|
||||
for (const key in keyInfo) {
|
||||
for (const key in key_info) {
|
||||
// iterate through each type's destination names
|
||||
const names = (keyInfo as Record<string, string[]>)[key];
|
||||
const names = (key_info as Record<string, string[]>)[key];
|
||||
// remove trailing slash from key
|
||||
const type = key.replace(/\/$/, '') as DestinationType;
|
||||
|
||||
names?.forEach((name: string) => {
|
||||
const id = `${type}/${name}`;
|
||||
const { icon, name: typeDisplayName } = findDestination(type);
|
||||
const { icon, name: type_display_name } = findDestination(type);
|
||||
// create object with destination's id and attributes
|
||||
destinations.push({ id, name, type, icon, typeDisplayName });
|
||||
destinations.push({ id, name, type, icon, type_display_name });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -217,7 +217,7 @@
|
||||
"dependencies": {
|
||||
"@babel/core": "7.26.10",
|
||||
"@hashicorp/design-system-components": "4.18.2",
|
||||
"@hashicorp/vault-client-typescript": "portal:./api-client",
|
||||
"@hashicorp/vault-client-typescript": "hashicorp/vault-client-typescript",
|
||||
"@hashicorp/vault-reporting": "portal:./vault-reporting",
|
||||
"ember-auto-import": "2.10.0",
|
||||
"handlebars": "4.7.8",
|
||||
|
||||
@ -107,13 +107,13 @@ module('Acceptance | Enterprise | config-ui/message', function (hooks) {
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
|
||||
);
|
||||
await fillIn(
|
||||
CUSTOM_MESSAGES.input('startTime'),
|
||||
CUSTOM_MESSAGES.input('start_time'),
|
||||
format(addDays(startOfDay(new Date('2023-12-12')), 1), datetimeLocalStringFormat)
|
||||
);
|
||||
if (end_time) {
|
||||
await click('#specificDate');
|
||||
await fillIn(
|
||||
CUSTOM_MESSAGES.input('endTime'),
|
||||
CUSTOM_MESSAGES.input('end_time'),
|
||||
format(addDays(startOfDay(new Date('2023-12-12')), 10), datetimeLocalStringFormat)
|
||||
);
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ module('Acceptance | auth custom messages auth tests', function (hooks) {
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
|
||||
);
|
||||
await fillIn(
|
||||
CUSTOM_MESSAGES.input('startTime'),
|
||||
CUSTOM_MESSAGES.input('start_time'),
|
||||
format(addDays(startOfDay(new Date('2023-12-12')), 1), datetimeLocalStringFormat)
|
||||
);
|
||||
await fillIn('[data-test-kv-key="0"]', 'Learn more');
|
||||
|
||||
@ -121,46 +121,46 @@ module('Acceptance | landing page dashboard', function (hooks) {
|
||||
module('configuration details card', function (hooks) {
|
||||
hooks.beforeEach(async function () {
|
||||
this.data = {
|
||||
apiAddr: 'http://127.0.0.1:8200',
|
||||
cacheSize: 0,
|
||||
clusterAddr: 'https://127.0.0.1:8201',
|
||||
clusterCipherSuites: '',
|
||||
clusterName: '',
|
||||
defaultLeaseTtl: 0,
|
||||
defaultMaxRequestDuration: 0,
|
||||
detectDeadlocks: '',
|
||||
disableCache: false,
|
||||
disableClustering: false,
|
||||
disableIndexing: false,
|
||||
disableMlock: true,
|
||||
disablePerformanceStandby: false,
|
||||
disablePrintableCheck: false,
|
||||
disableSealwrap: false,
|
||||
disableSentinelTrace: false,
|
||||
enableResponseHeaderHostname: false,
|
||||
enableResponseHeaderRaftNodeId: false,
|
||||
enableUi: true,
|
||||
api_addr: 'http://127.0.0.1:8200',
|
||||
cache_size: 0,
|
||||
cluster_addr: 'https://127.0.0.1:8201',
|
||||
cluster_cipher_suites: '',
|
||||
cluster_name: '',
|
||||
default_lease_ttl: 0,
|
||||
default_max_request_duration: 0,
|
||||
detect_deadlocks: '',
|
||||
disable_cache: false,
|
||||
disable_clustering: false,
|
||||
disable_indexing: false,
|
||||
disable_mlock: true,
|
||||
disable_performance_standby: false,
|
||||
disable_printable_check: false,
|
||||
disable_sealwrap: false,
|
||||
disable_sentinel_trace: false,
|
||||
enable_response_header_hostname: false,
|
||||
enable_response_header_raft_node_id: false,
|
||||
enable_ui: true,
|
||||
experiments: null,
|
||||
introspectionEndpoint: false,
|
||||
introspection_endpoint: false,
|
||||
listeners: [
|
||||
{
|
||||
config: {
|
||||
address: '0.0.0.0:8200',
|
||||
clusterAddress: '0.0.0.0:8201',
|
||||
tlsDisable: true,
|
||||
cluster_address: '0.0.0.0:8201',
|
||||
tls_disable: true,
|
||||
},
|
||||
type: 'tcp',
|
||||
},
|
||||
],
|
||||
logFormat: '',
|
||||
logLevel: 'debug',
|
||||
logRequestsLevel: '',
|
||||
maxLeaseTtl: '48h',
|
||||
pidFile: '',
|
||||
pluginDirectory: '',
|
||||
pluginFilePermissions: 0,
|
||||
pluginFileUid: 0,
|
||||
rawStorageEndpoint: true,
|
||||
log_format: '',
|
||||
log_level: 'debug',
|
||||
log_requests_level: '',
|
||||
max_lease_ttl: '48h',
|
||||
pid_file: '',
|
||||
plugin_directory: '',
|
||||
plugin_file_permissions: 0,
|
||||
plugin_file_uid: 0,
|
||||
raw_storage_endpoint: true,
|
||||
seals: [
|
||||
{
|
||||
disabled: false,
|
||||
@ -168,44 +168,44 @@ module('Acceptance | landing page dashboard', function (hooks) {
|
||||
},
|
||||
],
|
||||
storage: {
|
||||
clusterAddr: 'https://127.0.0.1:8201',
|
||||
disableClustering: false,
|
||||
cluster_addr: 'https://127.0.0.1:8201',
|
||||
disable_clustering: false,
|
||||
raft: {
|
||||
maxEntrySize: '',
|
||||
max_entry_size: '',
|
||||
},
|
||||
redirectAddr: 'http://127.0.0.1:8200',
|
||||
redirect_addr: 'http://127.0.0.1:8200',
|
||||
type: 'raft',
|
||||
},
|
||||
telemetry: {
|
||||
addLeaseMetricsNamespaceLabels: false,
|
||||
circonusApiApp: '',
|
||||
circonusApiToken: '',
|
||||
circonusApiUrl: '',
|
||||
circonusBrokerId: '',
|
||||
circonusBrokerSelectTag: '',
|
||||
circonusCheckDisplayName: '',
|
||||
circonusCheckForceMetricActivation: '',
|
||||
circonusCheckId: '',
|
||||
circonusCheckInstanceId: '',
|
||||
circonusCheckSearchTag: '',
|
||||
circonusCheckTags: '',
|
||||
circonusSubmissionInterval: '',
|
||||
circonusSubmissionUrl: '',
|
||||
disableHostname: true,
|
||||
dogstatsdAddr: '',
|
||||
dogstatsdTags: null,
|
||||
leaseMetricsEpsilon: 3600000000000,
|
||||
maximumGaugeCardinality: 500,
|
||||
metricsPrefix: '',
|
||||
numLeaseMetricsBuckets: 168,
|
||||
prometheusRetentionTime: 86400000000000,
|
||||
stackdriverDebugLogs: false,
|
||||
stackdriverLocation: '',
|
||||
stackdriverNamespace: '',
|
||||
stackdriverProjectId: '',
|
||||
statsdAddress: '',
|
||||
statsiteAddress: '',
|
||||
usageGaugePeriod: 5000000000,
|
||||
add_lease_metrics_namespace_labels: false,
|
||||
circonus_api_app: '',
|
||||
circonus_api_token: '',
|
||||
circonus_api_url: '',
|
||||
circonus_broker_id: '',
|
||||
circonus_broker_select_tag: '',
|
||||
circonus_check_display_name: '',
|
||||
circonus_check_force_metric_activation: '',
|
||||
circonus_check_id: '',
|
||||
circonus_check_instance_id: '',
|
||||
circonus_check_search_tag: '',
|
||||
circonus_check_tags: '',
|
||||
circonus_submission_interval: '',
|
||||
circonus_submission_url: '',
|
||||
disable_hostname: true,
|
||||
dogstatsd_addr: '',
|
||||
dogstatsd_tags: null,
|
||||
lease_metrics_epsilon: 3600000000000,
|
||||
maximum_gauge_cardinality: 500,
|
||||
metrics_prefix: '',
|
||||
num_lease_metrics_buckets: 168,
|
||||
prometheus_retention_time: 86400000000000,
|
||||
stackdriver_debug_logs: false,
|
||||
stackdriver_location: '',
|
||||
stackdriver_namespace: '',
|
||||
stackdriver_project_id: '',
|
||||
statsd_address: '',
|
||||
statsite_address: '',
|
||||
usage_gauge_period: 5000000000,
|
||||
},
|
||||
};
|
||||
|
||||
@ -252,9 +252,9 @@ module('Acceptance | landing page dashboard', function (hooks) {
|
||||
|
||||
test('it should show tls as enabled if tls_disable, tls_cert_file and tls_key_file are in the config', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.data.listeners[0].config.tlsDisable = false;
|
||||
this.data.listeners[0].config.tlsCertFile = './cert.pem';
|
||||
this.data.listeners[0].config.tlsKeyFile = './key.pem';
|
||||
this.data.listeners[0].config.tls_disable = false;
|
||||
this.data.listeners[0].config.tls_cert_file = './cert.pem';
|
||||
this.data.listeners[0].config.tls_key_file = './key.pem';
|
||||
|
||||
await login();
|
||||
await visit('/vault/dashboard');
|
||||
@ -263,9 +263,9 @@ module('Acceptance | landing page dashboard', function (hooks) {
|
||||
|
||||
test('it should show tls as enabled if only cert and key exist in config', async function (assert) {
|
||||
assert.expect(1);
|
||||
delete this.data.listeners[0].config.tlsDisable;
|
||||
this.data.listeners[0].config.tlsCertFile = './cert.pem';
|
||||
this.data.listeners[0].config.tlsKeyFile = './key.pem';
|
||||
delete this.data.listeners[0].config.tls_disable;
|
||||
this.data.listeners[0].config.tls_cert_file = './cert.pem';
|
||||
this.data.listeners[0].config.tls_key_file = './key.pem';
|
||||
await login();
|
||||
await visit('/vault/dashboard');
|
||||
assert.dom(DASHBOARD.vaultConfigurationCard.configDetailsField('tls')).hasText('Enabled');
|
||||
|
||||
@ -173,17 +173,17 @@ module('Acceptance | aws | configuration', function (hooks) {
|
||||
await runCmd(`delete sys/mounts/${path}`);
|
||||
});
|
||||
|
||||
test('it should show identityTokenTtl or maxRetries even if they have not been set', async function (assert) {
|
||||
test('it should show identity_token_ttl or maxRetries even if they have not been set', async function (assert) {
|
||||
// documenting the intention that we show fields that have not been set but are returned by the api due to defaults
|
||||
const path = `aws-${this.uid}`;
|
||||
await enablePage.enable('aws', path);
|
||||
|
||||
await click(SES.configTab);
|
||||
await click(SES.configure);
|
||||
// manually fill in attrs without using helper so we can exclude identityTokenTtl and maxRetries.
|
||||
// manually fill in attrs without using helper so we can exclude identity_token_ttl and max_retries.
|
||||
await click(SES.wif.accessType('wif')); // toggle to wif
|
||||
await fillIn(GENERAL.inputByAttr('roleArn'), 'foo-role');
|
||||
await fillIn(GENERAL.inputByAttr('identityTokenAudience'), 'foo-audience');
|
||||
await fillIn(GENERAL.inputByAttr('role_arn'), 'foo-role');
|
||||
await fillIn(GENERAL.inputByAttr('identity_token_audience'), 'foo-audience');
|
||||
// manually fill in non-access type specific fields on root config so we can exclude Max Retries.
|
||||
await click(GENERAL.button('Root config options'));
|
||||
await fillIn(GENERAL.inputByAttr('region'), 'eu-central-1');
|
||||
@ -231,7 +231,7 @@ module('Acceptance | aws | configuration', function (hooks) {
|
||||
const path = `aws-${this.uid}`;
|
||||
const type = 'aws';
|
||||
await enablePage.enable(type, path);
|
||||
// create accessKey with value foo and confirm it shows up in the details page.
|
||||
// create access_key with value foo and confirm it shows up in the details page.
|
||||
await click(SES.configTab);
|
||||
await click(SES.configure);
|
||||
await fillInAwsConfig('withAccess');
|
||||
@ -243,7 +243,7 @@ module('Acceptance | aws | configuration', function (hooks) {
|
||||
// edit root config details and lease config details and confirm the configuration.index page is updated.
|
||||
await click(SES.configure);
|
||||
// edit root config details
|
||||
await fillIn(GENERAL.inputByAttr('accessKey'), 'not-foo');
|
||||
await fillIn(GENERAL.inputByAttr('access_key'), 'not-foo');
|
||||
await click(GENERAL.button('Root config options'));
|
||||
await fillIn(GENERAL.inputByAttr('region'), 'ap-southeast-2');
|
||||
// add lease config details
|
||||
|
||||
@ -176,8 +176,8 @@ module('Acceptance | Azure | configuration', function (hooks) {
|
||||
'subscription_id is included with updated value in the payload'
|
||||
);
|
||||
});
|
||||
await fillIn(GENERAL.inputByAttr('subscriptionId'), 'subscription-id-updated');
|
||||
await click(GENERAL.enableField('clientSecret'));
|
||||
await fillIn(GENERAL.inputByAttr('subscription_id'), 'subscription-id-updated');
|
||||
await click(GENERAL.enableField('client_secret'));
|
||||
await click(GENERAL.submitButton);
|
||||
// cleanup
|
||||
await runCmd(`delete sys/mounts/${path}`);
|
||||
@ -201,10 +201,10 @@ module('Acceptance | Azure | configuration', function (hooks) {
|
||||
'subscription_id is included with updated value in the payload'
|
||||
);
|
||||
});
|
||||
await fillIn(GENERAL.inputByAttr('subscriptionId'), 'subscription-id-updated-again');
|
||||
await click(GENERAL.enableField('clientSecret'));
|
||||
await fillIn(GENERAL.inputByAttr('subscription_id'), 'subscription-id-updated-again');
|
||||
await click(GENERAL.enableField('client_secret'));
|
||||
await click(GENERAL.button('toggle-masked'));
|
||||
await fillIn(GENERAL.inputByAttr('clientSecret'), 'client-secret-updated');
|
||||
await fillIn(GENERAL.inputByAttr('client_secret'), 'client-secret-updated');
|
||||
await click(GENERAL.submitButton);
|
||||
// cleanup
|
||||
await runCmd(`delete sys/mounts/${path}`);
|
||||
@ -449,7 +449,7 @@ module('Acceptance | Azure | configuration', function (hooks) {
|
||||
.dom(GENERAL.infoRowValue('Identity token audience'))
|
||||
.hasText('azure-audience', `value for identity token audience shows on the config details view.`);
|
||||
await click(SES.configure);
|
||||
await fillIn(GENERAL.inputByAttr('identityTokenAudience'), 'new-audience');
|
||||
await fillIn(GENERAL.inputByAttr('identity_token_audience'), 'new-audience');
|
||||
await click(GENERAL.submitButton);
|
||||
assert
|
||||
.dom(GENERAL.infoRowValue('Identity token audience'))
|
||||
|
||||
@ -50,8 +50,8 @@ module('Acceptance | secrets/secret/create, read, delete', function (hooks) {
|
||||
await mountSecrets.visit();
|
||||
await click(MOUNT_BACKEND_FORM.mountType('kv'));
|
||||
await fillIn(GENERAL.inputByAttr('path'), enginePath);
|
||||
await fillIn('[data-test-input="kvConfig.maxVersions"]', maxVersion);
|
||||
await click('[data-test-input="kvConfig.casRequired"]');
|
||||
await fillIn('[data-test-input="kv_config.max_versions"]', maxVersion);
|
||||
await click('[data-test-input="kv_config.cas_required"]');
|
||||
await click('[data-test-toggle-label="Automate secret deletion"]');
|
||||
await fillIn('[data-test-select="ttl-unit"]', 's');
|
||||
await fillIn('[data-test-ttl-value="Automate secret deletion"]', '1');
|
||||
|
||||
@ -84,9 +84,9 @@ module('Acceptance | ssh | configuration', function (hooks) {
|
||||
`/vault/secrets/${sshPath}/configuration/edit`,
|
||||
'after deleting public key stays on edit page'
|
||||
);
|
||||
assert.dom(GENERAL.inputByAttr('privateKey')).hasNoText('Private key is empty and reset');
|
||||
assert.dom(GENERAL.inputByAttr('publicKey')).hasNoText('Public key is empty and reset');
|
||||
assert.dom(GENERAL.inputByAttr('generateSigningKey')).isChecked('Generate signing key is checked');
|
||||
assert.dom(GENERAL.inputByAttr('private_key')).hasNoText('Private key is empty and reset');
|
||||
assert.dom(GENERAL.inputByAttr('public_key')).hasNoText('Public key is empty and reset');
|
||||
assert.dom(GENERAL.inputByAttr('generate_signing_key')).isChecked('Generate signing key is checked');
|
||||
await click(SES.viewBackend);
|
||||
await click(SES.configTab);
|
||||
assert
|
||||
@ -101,11 +101,13 @@ module('Acceptance | ssh | configuration', function (hooks) {
|
||||
await enablePage.enable('ssh', path);
|
||||
await click(SES.configTab);
|
||||
await click(SES.configure);
|
||||
assert.dom(GENERAL.inputByAttr('generateSigningKey')).isChecked('generate_signing_key defaults to true');
|
||||
await click(GENERAL.inputByAttr('generateSigningKey'));
|
||||
assert
|
||||
.dom(GENERAL.inputByAttr('generate_signing_key'))
|
||||
.isChecked('generate_signing_key defaults to true');
|
||||
await click(GENERAL.inputByAttr('generate_signing_key'));
|
||||
await click(GENERAL.submitButton);
|
||||
assert
|
||||
.dom(GENERAL.validationErrorByAttr('generateSigningKey'))
|
||||
.dom(GENERAL.validationErrorByAttr('generate_signing_key'))
|
||||
.hasText('Provide a Public and Private key or set "Generate Signing Key" to true.');
|
||||
// visit the details page and confirm the public key is not shown
|
||||
await visit(`/vault/secrets/${path}/configuration`);
|
||||
|
||||
@ -101,23 +101,23 @@ module('Acceptance | settings/mount-secret-backend', function (hooks) {
|
||||
await page.visit();
|
||||
assert.strictEqual(currentRouteName(), 'vault.cluster.settings.mount-secret-backend');
|
||||
await click(MOUNT_BACKEND_FORM.mountType('pki'));
|
||||
assert.dom('[data-test-input="config.maxLeaseTtl"]').exists();
|
||||
assert.dom('[data-test-input="config.max_lease_ttl"]').exists();
|
||||
assert
|
||||
.dom('[data-test-input="config.maxLeaseTtl"] [data-test-ttl-toggle]')
|
||||
.dom('[data-test-input="config.max_lease_ttl"] [data-test-ttl-toggle]')
|
||||
.isChecked('Toggle is checked by default');
|
||||
assert.dom('[data-test-input="config.maxLeaseTtl"] [data-test-ttl-value]').hasValue('3650');
|
||||
assert.dom('[data-test-input="config.maxLeaseTtl"] [data-test-select="ttl-unit"]').hasValue('d');
|
||||
assert.dom('[data-test-input="config.max_lease_ttl"] [data-test-ttl-value]').hasValue('3650');
|
||||
assert.dom('[data-test-input="config.max_lease_ttl"] [data-test-select="ttl-unit"]').hasValue('d');
|
||||
|
||||
// Go back and choose a different type
|
||||
await click(GENERAL.backButton);
|
||||
await click(MOUNT_BACKEND_FORM.mountType('database'));
|
||||
assert.dom('[data-test-input="config.maxLeaseTtl"]').exists('3650');
|
||||
assert.dom('[data-test-input="config.max_lease_ttl"]').exists('3650');
|
||||
assert
|
||||
.dom('[data-test-input="config.maxLeaseTtl"] [data-test-ttl-toggle]')
|
||||
.dom('[data-test-input="config.max_lease_ttl"] [data-test-ttl-toggle]')
|
||||
.isNotChecked('Toggle is unchecked by default');
|
||||
await click(GENERAL.toggleInput('Max Lease TTL'));
|
||||
assert.dom('[data-test-input="config.maxLeaseTtl"] [data-test-ttl-value]').hasValue('');
|
||||
assert.dom('[data-test-input="config.maxLeaseTtl"] [data-test-select="ttl-unit"]').hasValue('s');
|
||||
assert.dom('[data-test-input="config.max_lease_ttl"] [data-test-ttl-value]').hasValue('');
|
||||
assert.dom('[data-test-input="config.max_lease_ttl"] [data-test-select="ttl-unit"]').hasValue('s');
|
||||
});
|
||||
|
||||
test('it throws error if setting duplicate path name', async function (assert) {
|
||||
|
||||
@ -82,16 +82,16 @@ module('Acceptance | sync | destination (singular)', function (hooks) {
|
||||
apiStub.resolves(response);
|
||||
|
||||
await visit('vault/sync/secrets/destinations/vercel-project/destination-vercel/edit');
|
||||
await fillIn(GENERAL.inputByAttr('teamId'), 'team-id');
|
||||
await fillIn(GENERAL.inputByAttr('team_id'), 'team-id');
|
||||
await click(GENERAL.submitButton);
|
||||
assert.false('accessToken' in apiStub.lastCall.args[1], 'access_token not sent in request');
|
||||
assert.false('access_token' in apiStub.lastCall.args[1], 'access_token not sent in request');
|
||||
|
||||
await click(ts.toolbar('Edit destination'));
|
||||
await click(ts.enableField('accessToken'));
|
||||
await fillIn(GENERAL.inputByAttr('accessToken'), 'foobar');
|
||||
await click(ts.enableField('access_token'));
|
||||
await fillIn(GENERAL.inputByAttr('access_token'), 'foobar');
|
||||
await click(GENERAL.submitButton);
|
||||
assert.strictEqual(
|
||||
apiStub.lastCall.args[1].accessToken,
|
||||
apiStub.lastCall.args[1].access_token,
|
||||
'foobar',
|
||||
'Updated access token sent in patch request'
|
||||
);
|
||||
|
||||
@ -8,7 +8,7 @@ import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { toolsActions } from 'vault/helpers/tools-actions';
|
||||
import { login } from 'vault/tests/helpers/auth/auth-helpers';
|
||||
import { capitalize, camelize } from '@ember/string';
|
||||
import { capitalize } from '@ember/string';
|
||||
import codemirror from 'vault/tests/helpers/codemirror';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
@ -166,11 +166,11 @@ module('Acceptance | tools', function (hooks) {
|
||||
await click(GENERAL.submitButton);
|
||||
await waitUntil(() => find('.CodeMirror'));
|
||||
|
||||
const expected = Object.keys(AUTH_RESPONSE.auth).reduce((obj, auth) => {
|
||||
obj[camelize(auth)] = AUTH_RESPONSE.auth[auth];
|
||||
return obj;
|
||||
}, {});
|
||||
assert.deepEqual(expected, JSON.parse(codemirror().getValue()), 'unwrapped data equals input data');
|
||||
assert.deepEqual(
|
||||
AUTH_RESPONSE.auth,
|
||||
JSON.parse(codemirror().getValue()),
|
||||
'unwrapped data equals input data'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
import { click, fillIn, find } from '@ember/test-helpers';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
|
||||
import { stringArrayToCamelCase } from 'vault/helpers/string-array-to-camel';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import SecretsEngineResource from 'vault/resources/secrets/engine';
|
||||
|
||||
@ -58,9 +57,9 @@ export function configUrl(type, backend) {
|
||||
const createAwsRootConfig = (accessType = 'iam') => {
|
||||
if (accessType === 'wif') {
|
||||
return {
|
||||
roleArn: '123-role',
|
||||
identityTokenAudience: '123-audience',
|
||||
identityTokenTtl: 7200,
|
||||
role_arn: '123-role',
|
||||
identity_token_audience: '123-audience',
|
||||
identity_token_ttl: 7200,
|
||||
};
|
||||
} else if (accessType === 'no-access') {
|
||||
// set root config options that are not associated with accessType 'wif' or 'iam'
|
||||
@ -70,10 +69,10 @@ const createAwsRootConfig = (accessType = 'iam') => {
|
||||
} else {
|
||||
return {
|
||||
region: 'us-west-2',
|
||||
accessKey: '123-key',
|
||||
iamEndpoint: 'iam-endpoint',
|
||||
stsEndpoint: 'sts-endpoint',
|
||||
maxRetries: 1,
|
||||
access_key: '123-key',
|
||||
iam_endpoint: 'iam-endpoint',
|
||||
sts_endpoint: 'sts-endpoint',
|
||||
max_retries: 1,
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -81,14 +80,14 @@ const createAwsRootConfig = (accessType = 'iam') => {
|
||||
const createAwsLeaseConfig = () => {
|
||||
return {
|
||||
lease: '50s',
|
||||
leaseMax: '55s',
|
||||
lease_max: '55s',
|
||||
};
|
||||
};
|
||||
|
||||
const createSshCaConfig = () => {
|
||||
return {
|
||||
publicKey: 'public-key',
|
||||
generateSigningKey: true,
|
||||
public_key: 'public-key',
|
||||
generate_signing_key: true,
|
||||
};
|
||||
};
|
||||
|
||||
@ -96,30 +95,30 @@ const createAzureConfig = (accessType = 'generic') => {
|
||||
// note: allowed "environment" params for testing https://github.com/hashicorp/vault-plugin-secrets-azure/blob/main/client.go#L35-L37
|
||||
if (accessType === 'azure') {
|
||||
return {
|
||||
clientSecret: 'client-secret',
|
||||
subscriptionId: 'subscription-id',
|
||||
tenantId: 'tenant-id',
|
||||
clientId: 'client-id',
|
||||
rootPasswordTtl: '1800000s',
|
||||
client_secret: 'client-secret',
|
||||
subscription_id: 'subscription-id',
|
||||
tenant_id: 'tenant-id',
|
||||
client_id: 'client-id',
|
||||
root_password_ttl: '1800000s',
|
||||
environment: 'AZUREPUBLICCLOUD',
|
||||
};
|
||||
} else if (accessType === 'wif') {
|
||||
return {
|
||||
subscriptionId: 'subscription-id',
|
||||
tenantId: 'tenant-id',
|
||||
clientId: 'client-id',
|
||||
identityTokenAudience: 'audience',
|
||||
identityTokenTtl: 7200,
|
||||
rootPasswordTtl: '1800000s',
|
||||
subscription_id: 'subscription-id',
|
||||
tenant_id: 'tenant-id',
|
||||
client_id: 'client-id',
|
||||
identity_token_audience: 'audience',
|
||||
identity_token_ttl: 7200,
|
||||
root_password_ttl: '1800000s',
|
||||
environment: 'AZUREPUBLICCLOUD',
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
subscriptionId: 'subscription-id-2',
|
||||
tenantId: 'tenant-id-2',
|
||||
clientId: 'client-id-2',
|
||||
subscription_id: 'subscription-id-2',
|
||||
tenant_id: 'tenant-id-2',
|
||||
client_id: 'client-id-2',
|
||||
environment: 'AZUREPUBLICCLOUD',
|
||||
rootPasswordTtl: '1800000s',
|
||||
root_password_ttl: '1800000s',
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -127,15 +126,15 @@ const createAzureConfig = (accessType = 'generic') => {
|
||||
const createGcpConfig = (accessType = 'gcp') => {
|
||||
if (accessType === 'wif') {
|
||||
return {
|
||||
serviceAccountEmail: 'service-email',
|
||||
identityTokenAudience: 'audience',
|
||||
identityTokenTtl: 7200,
|
||||
service_account_email: 'service-email',
|
||||
identity_token_audience: 'audience',
|
||||
identity_token_ttl: 7200,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
credentials: '{"some-key":"some-value"}',
|
||||
ttl: '100s',
|
||||
maxTtl: '101s',
|
||||
max_ttl: '101s',
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -169,15 +168,15 @@ export const createConfig = (type) => {
|
||||
/* Manually create the configuration by filling in the configuration form */
|
||||
export const fillInAwsConfig = async (situation = 'withAccess') => {
|
||||
if (situation === 'withAccess') {
|
||||
await fillIn(GENERAL.inputByAttr('accessKey'), 'foo');
|
||||
await fillIn(GENERAL.inputByAttr('secretKey'), 'bar');
|
||||
await fillIn(GENERAL.inputByAttr('access_key'), 'foo');
|
||||
await fillIn(GENERAL.inputByAttr('secret_key'), 'bar');
|
||||
}
|
||||
if (situation === 'withAccessOptions') {
|
||||
await click(GENERAL.button('Root config options'));
|
||||
await fillIn(GENERAL.inputByAttr('region'), 'ca-central-1');
|
||||
await fillIn(GENERAL.inputByAttr('iamEndpoint'), 'iam-endpoint');
|
||||
await fillIn(GENERAL.inputByAttr('stsEndpoint'), 'sts-endpoint');
|
||||
await fillIn(GENERAL.inputByAttr('maxRetries'), '3');
|
||||
await fillIn(GENERAL.inputByAttr('iam_endpoint'), 'iam-endpoint');
|
||||
await fillIn(GENERAL.inputByAttr('sts_endpoint'), 'sts-endpoint');
|
||||
await fillIn(GENERAL.inputByAttr('max_retries'), '3');
|
||||
}
|
||||
if (situation === 'withLease') {
|
||||
await click(GENERAL.ttl.toggle('Default Lease TTL'));
|
||||
@ -188,17 +187,17 @@ export const fillInAwsConfig = async (situation = 'withAccess') => {
|
||||
if (situation === 'withWif') {
|
||||
await click(SES.wif.accessType('wif')); // toggle to wif
|
||||
await fillIn(GENERAL.inputByAttr('issuer'), `http://bar.${uuidv4()}`); // make random because global setting
|
||||
await fillIn(GENERAL.inputByAttr('roleArn'), 'foo-role');
|
||||
await fillIn(GENERAL.inputByAttr('identityTokenAudience'), 'foo-audience');
|
||||
await fillIn(GENERAL.inputByAttr('role_arn'), 'foo-role');
|
||||
await fillIn(GENERAL.inputByAttr('identity_token_audience'), 'foo-audience');
|
||||
await click(GENERAL.ttl.toggle('Identity token TTL'));
|
||||
await fillIn(GENERAL.ttl.input('Identity token TTL'), '7200');
|
||||
}
|
||||
};
|
||||
|
||||
export const fillInAzureConfig = async (withWif = false) => {
|
||||
await fillIn(GENERAL.inputByAttr('subscriptionId'), 'subscription-id');
|
||||
await fillIn(GENERAL.inputByAttr('tenantId'), 'tenant-id');
|
||||
await fillIn(GENERAL.inputByAttr('clientId'), 'client-id');
|
||||
await fillIn(GENERAL.inputByAttr('subscription_id'), 'subscription-id');
|
||||
await fillIn(GENERAL.inputByAttr('tenant_id'), 'tenant-id');
|
||||
await fillIn(GENERAL.inputByAttr('client_id'), 'client-id');
|
||||
// options may already be toggled so check before clicking
|
||||
if (!find(GENERAL.inputByAttr('environment'))) {
|
||||
await click(GENERAL.button('More options'));
|
||||
@ -212,21 +211,21 @@ export const fillInAzureConfig = async (withWif = false) => {
|
||||
|
||||
if (withWif) {
|
||||
await click(SES.wif.accessType('wif')); // toggle to wif
|
||||
await fillIn(GENERAL.inputByAttr('identityTokenAudience'), 'azure-audience');
|
||||
await fillIn(GENERAL.inputByAttr('identity_token_audience'), 'azure-audience');
|
||||
await click(GENERAL.ttl.toggle('Identity token TTL'));
|
||||
await fillIn(GENERAL.ttl.input('Identity token TTL'), '7200');
|
||||
} else {
|
||||
await fillIn(GENERAL.inputByAttr('clientSecret'), 'client-secret');
|
||||
await fillIn(GENERAL.inputByAttr('client_secret'), 'client-secret');
|
||||
}
|
||||
};
|
||||
|
||||
export const fillInGcpConfig = async (withWif = false) => {
|
||||
if (withWif) {
|
||||
await click(SES.wif.accessType('wif')); // toggle to wif
|
||||
await fillIn(GENERAL.inputByAttr('identityTokenAudience'), 'azure-audience');
|
||||
await fillIn(GENERAL.inputByAttr('identity_token_audience'), 'azure-audience');
|
||||
await click(GENERAL.ttl.toggle('Identity token TTL'));
|
||||
await fillIn(GENERAL.ttl.input('Identity token TTL'), '7200');
|
||||
await fillIn(GENERAL.inputByAttr('serviceAccountEmail'), 'some@email.com');
|
||||
await fillIn(GENERAL.inputByAttr('service_account_email'), 'some@email.com');
|
||||
} else {
|
||||
await click(GENERAL.button('More options'));
|
||||
await click(GENERAL.ttl.toggle('Config TTL'));
|
||||
@ -259,24 +258,26 @@ const gcpWifKeys = [...genericWifKeys, 'Service account email'];
|
||||
// SSH specific keys
|
||||
const sshKeys = ['Private key', 'Public key', 'Generate signing key'];
|
||||
|
||||
export const expectedConfigKeys = (type, camelCase = false) => {
|
||||
export const expectedConfigKeys = (type, snake_case = false) => {
|
||||
const getKeys = (keys) => (snake_case ? keys.map((str) => str.replace(/\s+/g, '_').toLowerCase()) : keys);
|
||||
|
||||
switch (type) {
|
||||
case 'aws':
|
||||
return camelCase ? stringArrayToCamelCase(awsKeys) : awsKeys;
|
||||
return getKeys(awsKeys);
|
||||
case 'aws-wif':
|
||||
return camelCase ? stringArrayToCamelCase(awsWifKeys) : awsWifKeys;
|
||||
return getKeys(awsWifKeys);
|
||||
case 'aws-lease':
|
||||
return camelCase ? stringArrayToCamelCase(awsLeaseKeys) : awsLeaseKeys;
|
||||
return getKeys(awsLeaseKeys);
|
||||
case 'azure':
|
||||
return camelCase ? stringArrayToCamelCase(azureKeys) : azureKeys;
|
||||
return getKeys(azureKeys);
|
||||
case 'azure-wif':
|
||||
return camelCase ? stringArrayToCamelCase(azureWifKeys) : azureWifKeys;
|
||||
return getKeys(azureWifKeys);
|
||||
case 'gcp':
|
||||
return camelCase ? stringArrayToCamelCase(gcpKeys) : gcpKeys;
|
||||
return getKeys(gcpKeys);
|
||||
case 'gcp-wif':
|
||||
return camelCase ? stringArrayToCamelCase(gcpWifKeys) : gcpWifKeys;
|
||||
return getKeys(gcpWifKeys);
|
||||
case 'ssh':
|
||||
return camelCase ? stringArrayToCamelCase(sshKeys) : sshKeys;
|
||||
return getKeys(sshKeys);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
import camelizeKeys from 'vault/utils/camelize-object-keys';
|
||||
|
||||
// creates destination and association model for use in sync integration tests
|
||||
// ensure that setupMirage is used prior to setupModels since this.server is used
|
||||
@ -26,14 +25,14 @@ export function setupDataStubs(hooks) {
|
||||
this.destination = {
|
||||
name,
|
||||
type,
|
||||
connectionDetails: camelizeKeys(connection_details),
|
||||
connection_details,
|
||||
options: {
|
||||
granularityLevel: granularity,
|
||||
secretNameTemplate: secret_name_template,
|
||||
customTags: custom_tags,
|
||||
granularity_level: granularity,
|
||||
secret_name_template,
|
||||
custom_tags,
|
||||
},
|
||||
purgeInitiatedAt: purge_initiated_at,
|
||||
purgeError: purge_error,
|
||||
purge_initiated_at,
|
||||
purge_error,
|
||||
};
|
||||
|
||||
this.destinations = [this.destination];
|
||||
@ -52,9 +51,9 @@ export function setupDataStubs(hooks) {
|
||||
updated_at: '2023-09-20T10:51:53.961861096', // removed tz offset so time is consistently displayed
|
||||
});
|
||||
this.association = {
|
||||
...camelizeKeys(association),
|
||||
destinationType: this.destination.type,
|
||||
destinationName: this.destination.name,
|
||||
...association,
|
||||
destination_type: this.destination.type,
|
||||
destination_name: this.destination.name,
|
||||
};
|
||||
this.associations = [this.association];
|
||||
this.associations.meta = {
|
||||
|
||||
@ -93,13 +93,13 @@ export const PAGE = {
|
||||
case 'credentials':
|
||||
await click(GENERAL.textToggle);
|
||||
return fillIn(GENERAL.maskedInput, value);
|
||||
case 'customTags':
|
||||
case 'custom_tags':
|
||||
await fillIn('[data-test-kv-key="0"]', 'foo');
|
||||
return fillIn('[data-test-kv-value="0"]', value);
|
||||
case 'deploymentEnvironments':
|
||||
await click(`${GENERAL.inputGroupByAttr('deploymentEnvironments')} input#development`);
|
||||
await click(`${GENERAL.inputGroupByAttr('deploymentEnvironments')} input#preview`);
|
||||
return await click(`${GENERAL.inputGroupByAttr('deploymentEnvironments')} input#production`);
|
||||
case 'deployment_environments':
|
||||
await click(`${GENERAL.inputGroupByAttr('deployment_environments')} input#development`);
|
||||
await click(`${GENERAL.inputGroupByAttr('deployment_environments')} input#preview`);
|
||||
return await click(`${GENERAL.inputGroupByAttr('deployment_environments')} input#production`);
|
||||
default:
|
||||
return fillIn(`[data-test-input="${attr}"]`, value);
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ module('Integration | Component | auth-method/configuration', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
this.createMethod = (path, type) => {
|
||||
this.method = new AuthMethodResource({ path, type, config: { listingVisibility: 'hidden' } }, this);
|
||||
this.method = new AuthMethodResource({ path, type, config: { listing_visibility: 'hidden' } }, this);
|
||||
};
|
||||
this.renderComponent = () => render(hbs`<AuthMethod::Configuration @method={{this.method}} />`);
|
||||
});
|
||||
|
||||
@ -35,7 +35,7 @@ const authUrlRequestTests = (test) => {
|
||||
const { role } = JSON.parse(req.requestBody);
|
||||
assert.true(true, 'it makes request to auth_url');
|
||||
assert.strictEqual(role, '', 'role is empty');
|
||||
return { data: { authUrl: '123-example.com' } };
|
||||
return { data: { auth_url: '123-example.com' } };
|
||||
});
|
||||
await this.renderComponent();
|
||||
});
|
||||
@ -100,7 +100,7 @@ const oidcLoginTests = (test) => {
|
||||
// true success has to be asserted in acceptance tests because it's not possible to mock a trusted message event
|
||||
test('it opens the popup window on submit', async function (assert) {
|
||||
this.server.post(`/auth/${this.authType}/oidc/auth_url`, () => {
|
||||
return { data: { authUrl: '123-example.com' } };
|
||||
return { data: { auth_url: '123-example.com' } };
|
||||
});
|
||||
sinon.replaceGetter(window, 'screen', () => ({ height: 600, width: 500 }));
|
||||
await this.renderComponent();
|
||||
@ -142,7 +142,7 @@ const oidcLoginTests = (test) => {
|
||||
});
|
||||
|
||||
test('it fires onError callback on submit when auth_url request is successful but missing auth_url', async function (assert) {
|
||||
this.server.post('/auth/:path/oidc/auth_url', () => ({ data: { authUrl: '' } }));
|
||||
this.server.post('/auth/:path/oidc/auth_url', () => ({ data: { auth_url: '' } }));
|
||||
await this.renderComponent();
|
||||
await click(GENERAL.submitButton);
|
||||
|
||||
|
||||
@ -52,12 +52,12 @@ module('Integration | Component | auth | form | saml', function (hooks) {
|
||||
});
|
||||
|
||||
this.assertSubmit = (assert, loginRequestArgs, loginData) => {
|
||||
const [path, { clientVerifier, tokenPollId }] = loginRequestArgs;
|
||||
const [path, { client_verifier, token_poll_id }] = loginRequestArgs;
|
||||
// if path is included in loginData, a custom path was submitted
|
||||
const expectedPath = loginData?.path || this.authType;
|
||||
assert.strictEqual(path, expectedPath, 'it calls samlWriteToken with expected path');
|
||||
assert.strictEqual(clientVerifier, this.verifier, 'it calls samlWriteToken with verifier');
|
||||
assert.strictEqual(tokenPollId, this.tokenPollId, 'it calls samlWriteToken with tokenPollId');
|
||||
assert.strictEqual(client_verifier, this.verifier, 'it calls samlWriteToken with verifier');
|
||||
assert.strictEqual(token_poll_id, this.tokenPollId, 'it calls samlWriteToken with tokenPollId');
|
||||
};
|
||||
|
||||
this.renderComponent = ({ yieldBlock = false } = {}) => {
|
||||
|
||||
@ -30,7 +30,7 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
{
|
||||
authenticated: true,
|
||||
type: 'banner',
|
||||
startTime: addDays(startOfDay(timestamp.now()), 1).toISOString(),
|
||||
start_time: addDays(startOfDay(timestamp.now()), 1).toISOString(),
|
||||
},
|
||||
{ isNew: true }
|
||||
);
|
||||
@ -60,14 +60,14 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
assert.dom('[data-test-kv-key="0"]').exists();
|
||||
assert.dom('[data-test-kv-value="0"]').exists();
|
||||
assert
|
||||
.dom(CUSTOM_MESSAGES.input('startTime'))
|
||||
.dom(CUSTOM_MESSAGES.input('start_time'))
|
||||
.hasValue(
|
||||
format(addDays(startOfDay(timestamp.now()), 1), datetimeLocalStringFormat),
|
||||
`message startTime defaults to midnight of following day. test context startTime: ${
|
||||
this.message.startTime
|
||||
`message start_time defaults to midnight of following day. test context start_time: ${
|
||||
this.message.start_time
|
||||
}, now: ${timestamp.now().toISOString()}`
|
||||
);
|
||||
assert.dom(CUSTOM_MESSAGES.input('endTime')).hasValue('');
|
||||
assert.dom(CUSTOM_MESSAGES.input('end_time')).hasValue('');
|
||||
});
|
||||
|
||||
test('it should display validation errors for invalid form fields', async function (assert) {
|
||||
@ -75,8 +75,8 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
|
||||
await this.renderComponent();
|
||||
|
||||
await fillIn(CUSTOM_MESSAGES.input('startTime'), '2024-01-20T00:00');
|
||||
await fillIn(CUSTOM_MESSAGES.input('endTime'), '2024-01-01T00:00');
|
||||
await fillIn(CUSTOM_MESSAGES.input('start_time'), '2024-01-20T00:00');
|
||||
await fillIn(CUSTOM_MESSAGES.input('end_time'), '2024-01-01T00:00');
|
||||
await click(GENERAL.submitButton);
|
||||
assert
|
||||
.dom(GENERAL.validationErrorByAttr('title'))
|
||||
@ -87,12 +87,12 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
.exists('Validation error for field `message` renders')
|
||||
.hasText('Message is required.');
|
||||
assert
|
||||
.dom(GENERAL.validationErrorByAttr('startTime'))
|
||||
.exists('Validation error for field `startTime` renders')
|
||||
.dom(GENERAL.validationErrorByAttr('start_time'))
|
||||
.exists('Validation error for field `start_time` renders')
|
||||
.hasText('Start time is after end time.');
|
||||
assert
|
||||
.dom(GENERAL.validationErrorByAttr('endTime'))
|
||||
.exists('Validation error for field `endTime` renders')
|
||||
.dom(GENERAL.validationErrorByAttr('end_time'))
|
||||
.exists('Validation error for field `end_time` renders')
|
||||
.hasText('End time is before start time.');
|
||||
});
|
||||
|
||||
@ -111,12 +111,12 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
|
||||
);
|
||||
await fillIn(
|
||||
CUSTOM_MESSAGES.input('startTime'),
|
||||
CUSTOM_MESSAGES.input('start_time'),
|
||||
format(addDays(startOfDay(new Date('2023-12-12')), 1), datetimeLocalStringFormat)
|
||||
);
|
||||
await click('#specificDate');
|
||||
await fillIn(
|
||||
CUSTOM_MESSAGES.input('endTime'),
|
||||
CUSTOM_MESSAGES.input('end_time'),
|
||||
format(addDays(startOfDay(new Date('2023-12-12')), 10), datetimeLocalStringFormat)
|
||||
);
|
||||
await fillIn('[data-test-kv-key="0"]', 'Learn more');
|
||||
@ -143,8 +143,8 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
authenticated: false,
|
||||
title: 'Hello world',
|
||||
message: 'Blah blah blah. Some super long message.',
|
||||
startTime: new Date('2023-12-12T08:00:00.000Z'),
|
||||
endTime: new Date('2023-12-21T08:00:00.000Z'),
|
||||
start_time: new Date('2023-12-12T08:00:00.000Z'),
|
||||
end_time: new Date('2023-12-21T08:00:00.000Z'),
|
||||
link: { 'Learn more': 'www.learnmore.com' },
|
||||
});
|
||||
|
||||
@ -163,11 +163,11 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
assert.dom('[data-test-kv-value="0"]').hasValue('www.learnmore.com');
|
||||
await click('#specificDate');
|
||||
assert
|
||||
.dom(CUSTOM_MESSAGES.input('startTime'))
|
||||
.hasValue(format(new Date(this.message.startTime), datetimeLocalStringFormat));
|
||||
.dom(CUSTOM_MESSAGES.input('start_time'))
|
||||
.hasValue(format(new Date(this.message.start_time), datetimeLocalStringFormat));
|
||||
assert
|
||||
.dom(CUSTOM_MESSAGES.input('endTime'))
|
||||
.hasValue(format(new Date(this.message.endTime), datetimeLocalStringFormat));
|
||||
.dom(CUSTOM_MESSAGES.input('end_time'))
|
||||
.hasValue(format(new Date(this.message.end_time), datetimeLocalStringFormat));
|
||||
});
|
||||
|
||||
test('it should show a preview image modal when preview is clicked', async function (assert) {
|
||||
@ -220,8 +220,8 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
title: 'Message title 1',
|
||||
message: 'Some long long long message',
|
||||
link: { here: 'www.example.com' },
|
||||
startTime: new Date('2021-08-01T00:00:00Z'),
|
||||
endTime: '',
|
||||
start_time: new Date('2021-08-01T00:00:00Z'),
|
||||
end_time: '',
|
||||
},
|
||||
{
|
||||
id: '01234567-89ab-vvvv-0123-456789abcdef',
|
||||
@ -231,8 +231,8 @@ module('Integration | Component | messages/page/create-and-edit', function (hook
|
||||
title: 'Message title 2',
|
||||
message: 'Some long long long message',
|
||||
link: { here: 'www.example.com' },
|
||||
startTime: new Date('2021-08-01T00:00:00Z'),
|
||||
endTime: new Date('2090-08-01T00:00:00Z'),
|
||||
start_time: new Date('2021-08-01T00:00:00Z'),
|
||||
end_time: new Date('2090-08-01T00:00:00Z'),
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -18,8 +18,8 @@ const allFields = [
|
||||
{ label: 'Authenticated', key: 'authenticated' },
|
||||
{ label: 'Title', key: 'title' },
|
||||
{ label: 'Message', key: 'message' },
|
||||
{ label: 'Start time', key: 'startTime' },
|
||||
{ label: 'End time', key: 'endTime' },
|
||||
{ label: 'Start time', key: 'start_time' },
|
||||
{ label: 'End time', key: 'end_time' },
|
||||
{ label: 'Link', key: 'link' },
|
||||
];
|
||||
|
||||
@ -45,9 +45,8 @@ module('Integration | Component | messages/page/details', function (hooks) {
|
||||
title: 'Message title 1',
|
||||
message: 'Some long long long message',
|
||||
link: { here: 'www.example.com' },
|
||||
startTime: new Date('2021-08-01T00:00:00Z'),
|
||||
endTime: undefined,
|
||||
canEditCustomMessages: true,
|
||||
start_time: new Date('2021-08-01T00:00:00Z'),
|
||||
end_time: undefined,
|
||||
};
|
||||
this.capabilities = { canDelete: true, canUpdate: true };
|
||||
});
|
||||
@ -64,7 +63,7 @@ module('Integration | Component | messages/page/details', function (hooks) {
|
||||
|
||||
allFields.forEach((field) => {
|
||||
assert.dom(GENERAL.infoRowLabel(field.label)).hasText(field.label, `${field.label} label renders`);
|
||||
if (field.key === 'startTime' || field.key === 'endTime') {
|
||||
if (field.key === 'start_time' || field.key === 'end_time') {
|
||||
const formattedDate = dateFormat([this.message[field.key], 'MMM d, yyyy hh:mm aaa'], {
|
||||
withTimeZone: true,
|
||||
});
|
||||
|
||||
@ -38,8 +38,8 @@ module('Integration | Component | messages/page/list', function (hooks) {
|
||||
title: 'Message title 1',
|
||||
message: 'Some long long long message',
|
||||
link: { title: 'here', href: 'www.example.com' },
|
||||
startTime: new Date('2021-08-01T00:00:00Z'),
|
||||
endTime: undefined,
|
||||
start_time: new Date('2021-08-01T00:00:00Z'),
|
||||
end_time: undefined,
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
@ -49,8 +49,8 @@ module('Integration | Component | messages/page/list', function (hooks) {
|
||||
title: 'Message title 2',
|
||||
message: 'Some long long long message blah blah blah',
|
||||
link: { title: 'here', href: 'www.example2.com' },
|
||||
startTime: new Date('2023-07-01T00:00:00Z'),
|
||||
endTime: new Date('2023-08-01T00:00:00Z'),
|
||||
start_time: new Date('2023-07-01T00:00:00Z'),
|
||||
end_time: new Date('2023-08-01T00:00:00Z'),
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
@ -111,7 +111,7 @@ module('Integration | Component | messages/page/list', function (hooks) {
|
||||
title: `Message title ${i}`,
|
||||
message: 'Some long long long message',
|
||||
link: { title: 'here', href: 'www.example.com' },
|
||||
startTime: new Date('2021-08-01T00:00:00Z'),
|
||||
start_time: new Date('2021-08-01T00:00:00Z'),
|
||||
});
|
||||
}
|
||||
this.messages.meta.total = this.messages.length;
|
||||
|
||||
@ -29,8 +29,8 @@ module('Integration | Component | mfa-form', function (hooks) {
|
||||
// override in tests that require different scenarios
|
||||
this.totpConstraint = this.server.create('mfa-method', { type: 'totp' });
|
||||
const mfaRequirement = this.authService.parseMfaResponse({
|
||||
mfaRequestId: 'test-mfa-id',
|
||||
mfaConstraints: { test_mfa: { any: [this.totpConstraint] } },
|
||||
mfa_request_id: 'test-mfa-id',
|
||||
mfa_constraints: { test_mfa: { any: [this.totpConstraint] } },
|
||||
});
|
||||
this.mfaAuthData.mfaRequirement = mfaRequirement;
|
||||
});
|
||||
@ -41,8 +41,8 @@ module('Integration | Component | mfa-form', function (hooks) {
|
||||
const duoConstraint = this.server.create('mfa-method', { type: 'duo' });
|
||||
|
||||
this.mfaAuthData.mfaRequirement = this.authService.parseMfaResponse({
|
||||
mfaRequestId: 'test-mfa-id',
|
||||
mfaConstraints: { test_mfa_1: { any: [totpConstraint] } },
|
||||
mfa_request_id: 'test-mfa-id',
|
||||
mfa_constraints: { test_mfa_1: { any: [totpConstraint] } },
|
||||
});
|
||||
|
||||
await render(
|
||||
@ -61,8 +61,8 @@ module('Integration | Component | mfa-form', function (hooks) {
|
||||
);
|
||||
|
||||
this.mfaAuthData.mfaRequirement = this.authService.parseMfaResponse({
|
||||
mfaRequestId: 'test-mfa-id',
|
||||
mfaConstraints: { test_mfa_1: { any: [duoConstraint, oktaConstraint] } },
|
||||
mfa_request_id: 'test-mfa-id',
|
||||
mfa_constraints: { test_mfa_1: { any: [duoConstraint, oktaConstraint] } },
|
||||
});
|
||||
|
||||
await render(
|
||||
@ -81,8 +81,8 @@ module('Integration | Component | mfa-form', function (hooks) {
|
||||
);
|
||||
|
||||
this.mfaAuthData.mfaRequirement = this.authService.parseMfaResponse({
|
||||
mfaRequestId: 'test-mfa-id',
|
||||
mfaConstraints: { test_mfa_1: { any: [oktaConstraint] }, test_mfa_2: { any: [duoConstraint] } },
|
||||
mfa_request_id: 'test-mfa-id',
|
||||
mfa_constraints: { test_mfa_1: { any: [oktaConstraint] }, test_mfa_2: { any: [duoConstraint] } },
|
||||
});
|
||||
|
||||
await render(
|
||||
@ -117,8 +117,8 @@ module('Integration | Component | mfa-form', function (hooks) {
|
||||
const oktaConstraint = this.server.create('mfa-method', { type: 'okta' });
|
||||
const pingidConstraint = this.server.create('mfa-method', { type: 'pingid' });
|
||||
const mfaRequirement = this.authService.parseMfaResponse({
|
||||
mfaRequestId: 'test-mfa-id',
|
||||
mfaConstraints: {
|
||||
mfa_request_id: 'test-mfa-id',
|
||||
mfa_constraints: {
|
||||
test_mfa_1: {
|
||||
any: [pingidConstraint, oktaConstraint],
|
||||
},
|
||||
|
||||
@ -137,11 +137,11 @@ module('Integration | Component | mount backend form', function (hooks) {
|
||||
module('secrets engine', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
const defaults = {
|
||||
config: { listingVisibility: false },
|
||||
kvConfig: {
|
||||
maxVersions: 0,
|
||||
casRequired: false,
|
||||
deleteVersionAfter: 0,
|
||||
config: { listing_visibility: false },
|
||||
kv_config: {
|
||||
max_versions: 0,
|
||||
cas_required: false,
|
||||
delete_version_after: 0,
|
||||
},
|
||||
options: { version: 2 },
|
||||
};
|
||||
@ -218,7 +218,7 @@ module('Integration | Component | mount backend form', function (hooks) {
|
||||
});
|
||||
|
||||
module('WIF secret engines', function () {
|
||||
test('it shows identityTokenKey when type is a WIF engine and hides when its not', async function (assert) {
|
||||
test('it shows identity_token_key when type is a WIF engine and hides when its not', async function (assert) {
|
||||
await render(
|
||||
hbs`<MountBackendForm @mountCategory="secret" @mountModel={{this.model}} @onMountSuccess={{this.onMountSuccess}} />`
|
||||
);
|
||||
@ -226,7 +226,7 @@ module('Integration | Component | mount backend form', function (hooks) {
|
||||
await click(MOUNT_BACKEND_FORM.mountType(engine));
|
||||
await click(GENERAL.button('Method Options'));
|
||||
assert
|
||||
.dom(GENERAL.fieldByAttr('config.identityTokenKey'))
|
||||
.dom(GENERAL.fieldByAttr('config.identity_token_key'))
|
||||
.exists(`Identity token key field shows when type=${this.model.type}`);
|
||||
await click(GENERAL.backButton);
|
||||
}
|
||||
@ -238,20 +238,20 @@ module('Integration | Component | mount backend form', function (hooks) {
|
||||
await click(MOUNT_BACKEND_FORM.mountType(engine.type));
|
||||
await click(GENERAL.button('Method Options'));
|
||||
assert
|
||||
.dom(GENERAL.fieldByAttr('config.identityTokenKey'))
|
||||
.dom(GENERAL.fieldByAttr('config.identity_token_key'))
|
||||
.doesNotExist(`Identity token key field hidden when type=${this.model.type}`);
|
||||
await click(GENERAL.backButton);
|
||||
}
|
||||
});
|
||||
|
||||
test('it updates identityTokenKey if user has changed it', async function (assert) {
|
||||
test('it updates identity_token_key if user has changed it', async function (assert) {
|
||||
await render(
|
||||
hbs`<MountBackendForm @mountCategory="secret" @mountModel={{this.model}} @onMountSuccess={{this.onMountSuccess}} />`
|
||||
);
|
||||
assert.strictEqual(
|
||||
this.model.config.identityTokenKey,
|
||||
this.model.config.identity_token_key,
|
||||
undefined,
|
||||
`On init identityTokenKey is not set on the model`
|
||||
`On init identity_token_key is not set on the model`
|
||||
);
|
||||
for (const engine of WIF_ENGINES) {
|
||||
await click(MOUNT_BACKEND_FORM.mountType(engine));
|
||||
@ -259,9 +259,9 @@ module('Integration | Component | mount backend form', function (hooks) {
|
||||
await typeIn(GENERAL.inputSearch('key'), `${engine}+specialKey`); // set to something else
|
||||
|
||||
assert.strictEqual(
|
||||
this.model.config.identityTokenKey,
|
||||
this.model.config.identity_token_key,
|
||||
`${engine}+specialKey`,
|
||||
`updates ${engine} model with custom identityTokenKey`
|
||||
`updates ${engine} model with custom identity_token_key`
|
||||
);
|
||||
await click(GENERAL.backButton);
|
||||
}
|
||||
|
||||
@ -22,27 +22,27 @@ module('Integration | Component | SecretEngine::ConfigurationDetails', function
|
||||
this.configs = {
|
||||
aws: {
|
||||
region: 'us-west-2',
|
||||
accessKey: '123-key',
|
||||
iamEndpoint: 'iam-endpoint',
|
||||
stsEndpoint: 'sts-endpoint',
|
||||
maxRetries: 1,
|
||||
access_key: '123-key',
|
||||
iam_endpoint: 'iam-endpoint',
|
||||
sts_endpoint: 'sts-endpoint',
|
||||
max_retries: 1,
|
||||
},
|
||||
azure: {
|
||||
clientSecret: 'client-secret',
|
||||
subscriptionId: 'subscription-id',
|
||||
tenantId: 'tenant-id',
|
||||
clientId: 'client-id',
|
||||
rootPasswordTtl: '1800000s',
|
||||
client_secret: 'client-secret',
|
||||
subscription_id: 'subscription-id',
|
||||
tenant_id: 'tenant-id',
|
||||
client_id: 'client-id',
|
||||
root_password_ttl: '1800000s',
|
||||
environment: 'AZUREPUBLICCLOUD',
|
||||
},
|
||||
gcp: {
|
||||
credentials: '{"some-key":"some-value"}',
|
||||
ttl: '100s',
|
||||
maxTtl: '101s',
|
||||
max_ttl: '101s',
|
||||
},
|
||||
ssh: {
|
||||
publicKey: 'public-key',
|
||||
generateSigningKey: true,
|
||||
public_key: 'public-key',
|
||||
generate_signing_key: true,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@ -20,7 +20,7 @@ module('Integration | Component | SecretEngine/configure-ssh', function (hooks)
|
||||
hooks.beforeEach(function () {
|
||||
const router = this.owner.lookup('service:router');
|
||||
this.id = 'ssh-test';
|
||||
this.form = new SshConfigForm({ generateSigningKey: true }, { isNew: true });
|
||||
this.form = new SshConfigForm({ generate_signing_key: true }, { isNew: true });
|
||||
this.transitionStub = sinon.stub(router, 'transitionTo');
|
||||
this.refreshStub = sinon.stub(router, 'refresh');
|
||||
});
|
||||
@ -32,10 +32,10 @@ module('Integration | Component | SecretEngine/configure-ssh', function (hooks)
|
||||
@id={{this.id}}
|
||||
/>
|
||||
`);
|
||||
assert.dom(GENERAL.inputByAttr('privateKey')).hasNoText('Private key is empty and reset');
|
||||
assert.dom(GENERAL.inputByAttr('publicKey')).hasNoText('Public key is empty and reset');
|
||||
assert.dom(GENERAL.inputByAttr('private_key')).hasNoText('Private key is empty and reset');
|
||||
assert.dom(GENERAL.inputByAttr('public_key')).hasNoText('Public key is empty and reset');
|
||||
assert
|
||||
.dom(GENERAL.inputByAttr('generateSigningKey'))
|
||||
.dom(GENERAL.inputByAttr('generate_signing_key'))
|
||||
.isChecked('Generate signing key is checked by default');
|
||||
});
|
||||
|
||||
@ -62,19 +62,19 @@ module('Integration | Component | SecretEngine/configure-ssh', function (hooks)
|
||||
@id={{this.id}}
|
||||
/>
|
||||
`);
|
||||
await fillIn(GENERAL.inputByAttr('publicKey'), 'hello');
|
||||
await fillIn(GENERAL.inputByAttr('public_key'), 'hello');
|
||||
await click(GENERAL.submitButton);
|
||||
assert
|
||||
.dom(GENERAL.validationErrorByAttr('publicKey'))
|
||||
.dom(GENERAL.validationErrorByAttr('public_key'))
|
||||
.hasText(
|
||||
'You must provide a Public and Private keys or leave both unset.',
|
||||
'Public key validation error renders.'
|
||||
);
|
||||
|
||||
await click(GENERAL.inputByAttr('generateSigningKey'));
|
||||
await click(GENERAL.inputByAttr('generate_signing_key'));
|
||||
await click(GENERAL.submitButton);
|
||||
assert
|
||||
.dom(GENERAL.validationErrorByAttr('generateSigningKey'))
|
||||
.dom(GENERAL.validationErrorByAttr('generate_signing_key'))
|
||||
.hasText(
|
||||
'Provide a Public and Private key or set "Generate Signing Key" to true.',
|
||||
'Generate signing key validation message shows.'
|
||||
@ -107,10 +107,10 @@ module('Integration | Component | SecretEngine/configure-ssh', function (hooks)
|
||||
module('editing', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.editId = 'ssh-edit-me';
|
||||
this.publicKey = 'public-key';
|
||||
this.public_key = 'public-key';
|
||||
this.form = new SshConfigForm({
|
||||
publicKey: this.publicKey,
|
||||
generateSigningKey: true,
|
||||
public_key: this.public_key,
|
||||
generate_signing_key: true,
|
||||
});
|
||||
});
|
||||
test('it populates fields when editing', async function (assert) {
|
||||
@ -127,7 +127,7 @@ module('Integration | Component | SecretEngine/configure-ssh', function (hooks)
|
||||
await click(GENERAL.button('toggle-masked'));
|
||||
assert
|
||||
.dom(GENERAL.inputByAttr('public-key'))
|
||||
.hasText(this.publicKey, 'public key is unmasked and shows the actual value');
|
||||
.hasText(this.public_key, 'public key is unmasked and shows the actual value');
|
||||
});
|
||||
|
||||
test('it allows you to delete a public key', async function (assert) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user