mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-26 00:51:08 +02:00
* UI: Move `wrapped_token` login functionality to route (#30465) * move token unwrap functionality to page component * update mfa test * remove wrapped_token logic from page component * more cleanup to relocate unwrap logic * move wrapped_token to route * move unwrap tests to acceptance * move mfa form back * add some padding * update mfa-form tests * get param from params * wait for auth form on back * run rests * UI: Add MFA support for SSO methods (#30489) * initial implementation of mfa validation for sso methods * update typescript interfaces * add stopgap changes to auth service * switch order backend is defined * update login form for tests even though it will be deleted * attempt to stabilize wrapped_query test * =update login form test why not * Update ui/app/components/auth/form/saml.ts Co-authored-by: lane-wetmore <lane.wetmore@hashicorp.com> --------- Co-authored-by: lane-wetmore <lane.wetmore@hashicorp.com> * Move CSP error to page component (#30492) * initial implementation of mfa validation for sso methods * update typescript interfaces * add stopgap changes to auth service * switch order backend is defined * update login form for tests even though it will be deleted * attempt to stabilize wrapped_query test * =update login form test why not * move csp error to page component * move csp error to page component * Move fetching unauthenticated mounts to the route (#30509) * rename namespace arg to namespaceQueryParam * move fetch mounts to route * add margin to sign in button spacing * update selectors for oidc provider test * add todo delete comments * fix arg typo in test * change method name * fix args handling tab click * remove tests that no longer relate to components functionality * add tests for preselectedAuthType functionality * move typescript interfaces, fix selector * add await * oops * move format method down, make private * move tab formatting to the route * move to page object * fix token unwrap aborting transition * not sure what that is doing there.. * add comments * rename to presetAuthType * use did-insert instead * UI: Implement `Auth::FormTemplate` (#30521) * replace Auth::LoginForm with Auth::FormTemplate * first round of test updates * return null if mounts object is empty * add comment and test for empty sys/internal/mounts data * more test updates * delete listing_visibility test, delete login-form component test * update divs to Hds::Card::Container * add overflow class * remove unused getters * move requesting stored auth type to page component * fix typo * Update ui/app/components/auth/form/oidc-jwt.ts make comment make more sense * small cleanup items, update imports * Delete old auth components (#30527) * delete old components * update codeowners * Update `with` query param functionality (#30537) * update path input to type=hidden * add test coverage * update page test * update auth route * delete login form * update ent test * consolidate logic in getter * add more comments * more comments.. * rename selector * refresh model as well * redirect for invalid query params * move unwrap to redirect * only redirect on invalid query params * add tests for query param * test selector updates * remove todos, update relevant ones with initials * add changelog --------- Co-authored-by: lane-wetmore <lane.wetmore@hashicorp.com>
125 lines
4.5 KiB
TypeScript
125 lines
4.5 KiB
TypeScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import Component from '@glimmer/component';
|
|
import { action } from '@ember/object';
|
|
import { service } from '@ember/service';
|
|
import { task } from 'ember-concurrency';
|
|
import { waitFor } from '@ember/test-waiters';
|
|
import { sanitizePath } from 'core/utils/sanitize-path';
|
|
import { POSSIBLE_FIELDS } from 'vault/utils/supported-login-methods';
|
|
|
|
import type AuthService from 'vault/vault/services/auth';
|
|
import type ClusterModel from 'vault/models/cluster';
|
|
import type FlagsService from 'vault/services/flags';
|
|
import type VersionService from 'vault/services/version';
|
|
import type { AuthResponse } from 'vault/vault/services/auth';
|
|
import type { HTMLElementEvent } from 'vault/forms';
|
|
import type { LoginFields } from 'vault/vault/auth/form';
|
|
import type { MfaRequirementApiResponse, ParsedMfaRequirement } from 'vault/vault/auth/mfa';
|
|
|
|
/**
|
|
* @module Auth::Base
|
|
*
|
|
* @param {string} authType - chosen login method type
|
|
* @param {object} cluster - The cluster model which contains information such as cluster id, name and boolean for if the cluster is in standby
|
|
* @param {function} onError - callback if there is a login error
|
|
* @param {function} onSuccess - calls onAuthResponse in auth/page redirects if successful
|
|
*/
|
|
|
|
interface Args {
|
|
authType: string;
|
|
cluster: ClusterModel;
|
|
onError: CallableFunction;
|
|
onSuccess: CallableFunction;
|
|
}
|
|
|
|
export default class AuthBase extends Component<Args> {
|
|
@service declare readonly auth: AuthService;
|
|
@service declare readonly flags: FlagsService;
|
|
@service declare readonly version: VersionService;
|
|
|
|
@action
|
|
onSubmit(event: HTMLElementEvent<HTMLFormElement>) {
|
|
event.preventDefault();
|
|
const formData = new FormData(event.target as HTMLFormElement);
|
|
const data = this.parseFormData(formData);
|
|
this.login.unlinked().perform(data);
|
|
}
|
|
|
|
login = task(
|
|
waitFor(async (formData) => {
|
|
try {
|
|
const authResponse = await this.auth.authenticate({
|
|
clusterId: this.args.cluster.id,
|
|
backend: this.args.authType,
|
|
data: formData,
|
|
selectedAuth: this.args.authType,
|
|
});
|
|
|
|
const path = formData?.path;
|
|
this.handleAuthResponse(authResponse, path);
|
|
} catch (error) {
|
|
this.onError(error as Error);
|
|
}
|
|
})
|
|
);
|
|
|
|
// Standard methods get mfa_requirements from the authenticate method in the auth service
|
|
// methodData is necessary if there's an MfaRequirement because persisting auth data happens after that
|
|
handleAuthResponse(authResponse: AuthResponse | ParsedMfaRequirement, path?: string) {
|
|
const methodData: { selectedAuth: string; path?: string } = { selectedAuth: this.args.authType, path };
|
|
// calls onAuthResponse in parent auth/page.js component
|
|
this.args.onSuccess(authResponse, methodData);
|
|
}
|
|
|
|
// SSO methods with a different token exchange workflow skip the auth service authenticate method
|
|
// and need mfa handle separately
|
|
handleMfa(mfaRequirement: MfaRequirementApiResponse, path: string) {
|
|
const parsedMfaAuthResponse = this.auth._parseMfaResponse(mfaRequirement);
|
|
this.handleAuthResponse(parsedMfaAuthResponse, path);
|
|
}
|
|
|
|
onError(error: Error | string) {
|
|
if (!this.auth.mfaErrors) {
|
|
const errorMessage = `Authentication failed: ${this.auth.handleError(error)}`;
|
|
this.args.onError(errorMessage);
|
|
}
|
|
}
|
|
|
|
parseFormData(formData: FormData) {
|
|
const data: LoginFields = {};
|
|
|
|
// iterate over method specific fields
|
|
for (const field of POSSIBLE_FIELDS) {
|
|
const value = formData.get(field);
|
|
if (value) {
|
|
data[field] = typeof value === 'string' ? value : undefined;
|
|
}
|
|
}
|
|
|
|
// path is supported by all auth methods except token
|
|
if (this.args.authType !== 'token') {
|
|
// strip leading or trailing slashes for consistency.
|
|
// fallback to auth type which is the default path Vault expects.
|
|
data['path'] = sanitizePath(formData?.get('path')) || this.args.authType;
|
|
}
|
|
|
|
if (this.version.isEnterprise) {
|
|
// strip leading or trailing slashes for consistency
|
|
let namespace = sanitizePath(formData?.get('namespace')) || '';
|
|
|
|
const hvdRootNs = this.flags.hvdManagedNamespaceRoot; // if HVD managed, this is "admin"
|
|
if (hvdRootNs) {
|
|
// HVD managed clusters can only input child namespaces, manually prepend with the hvd root
|
|
namespace = namespace ? `${hvdRootNs}/${namespace}` : hvdRootNs;
|
|
}
|
|
data['namespace'] = namespace;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
}
|