mirror of
https://github.com/bitwarden/browser
synced 2025-12-20 10:13:31 +00:00
refactor(set-change-password): [Auth/PM-18458] Create new ChangePasswordComponent (#14226)
This PR creates a new ChangePasswordComponent. The first use-case of the ChangePasswordComponent is to place it inside a new PasswordSettingsComponent, which is accessed by going to Account Settings > Security. The ChangePasswordComponent will be updated in future PRs to handle more change password scenarios. Feature Flags: PM16117_ChangeExistingPasswordRefactor
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export * from "./web-change-password.service";
|
||||
@@ -0,0 +1,63 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { ChangePasswordService } from "@bitwarden/auth/angular";
|
||||
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
import { UserKeyRotationService } from "@bitwarden/web-vault/app/key-management/key-rotation/user-key-rotation.service";
|
||||
|
||||
import { WebChangePasswordService } from "./web-change-password.service";
|
||||
|
||||
describe("WebChangePasswordService", () => {
|
||||
let keyService: MockProxy<KeyService>;
|
||||
let masterPasswordApiService: MockProxy<MasterPasswordApiService>;
|
||||
let masterPasswordService: MockProxy<InternalMasterPasswordServiceAbstraction>;
|
||||
let userKeyRotationService: MockProxy<UserKeyRotationService>;
|
||||
|
||||
let sut: ChangePasswordService;
|
||||
|
||||
const userId = "userId" as UserId;
|
||||
const user: Account = {
|
||||
id: userId,
|
||||
email: "email",
|
||||
emailVerified: false,
|
||||
name: "name",
|
||||
};
|
||||
|
||||
const currentPassword = "currentPassword";
|
||||
const newPassword = "newPassword";
|
||||
const newPasswordHint = "newPasswordHint";
|
||||
|
||||
beforeEach(() => {
|
||||
keyService = mock<KeyService>();
|
||||
masterPasswordApiService = mock<MasterPasswordApiService>();
|
||||
masterPasswordService = mock<InternalMasterPasswordServiceAbstraction>();
|
||||
userKeyRotationService = mock<UserKeyRotationService>();
|
||||
|
||||
sut = new WebChangePasswordService(
|
||||
keyService,
|
||||
masterPasswordApiService,
|
||||
masterPasswordService,
|
||||
userKeyRotationService,
|
||||
);
|
||||
});
|
||||
|
||||
describe("rotateUserKeyMasterPasswordAndEncryptedData()", () => {
|
||||
it("should call the method with the same name on the UserKeyRotationService with the correct arguments", async () => {
|
||||
// Arrange & Act
|
||||
await sut.rotateUserKeyMasterPasswordAndEncryptedData(
|
||||
currentPassword,
|
||||
newPassword,
|
||||
user,
|
||||
newPasswordHint,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(
|
||||
userKeyRotationService.rotateUserKeyMasterPasswordAndEncryptedData,
|
||||
).toHaveBeenCalledWith(currentPassword, newPassword, user, newPasswordHint);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ChangePasswordService, DefaultChangePasswordService } from "@bitwarden/auth/angular";
|
||||
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
import { UserKeyRotationService } from "@bitwarden/web-vault/app/key-management/key-rotation/user-key-rotation.service";
|
||||
|
||||
export class WebChangePasswordService
|
||||
extends DefaultChangePasswordService
|
||||
implements ChangePasswordService
|
||||
{
|
||||
constructor(
|
||||
protected keyService: KeyService,
|
||||
protected masterPasswordApiService: MasterPasswordApiService,
|
||||
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
private userKeyRotationService: UserKeyRotationService,
|
||||
) {
|
||||
super(keyService, masterPasswordApiService, masterPasswordService);
|
||||
}
|
||||
|
||||
override async rotateUserKeyMasterPasswordAndEncryptedData(
|
||||
currentPassword: string,
|
||||
newPassword: string,
|
||||
user: Account,
|
||||
newPasswordHint: string,
|
||||
): Promise<void> {
|
||||
await this.userKeyRotationService.rotateUserKeyMasterPasswordAndEncryptedData(
|
||||
currentPassword,
|
||||
newPassword,
|
||||
user,
|
||||
newPasswordHint,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./change-password";
|
||||
export * from "./login";
|
||||
export * from "./login-decryption-options";
|
||||
export * from "./webauthn-login";
|
||||
|
||||
@@ -185,11 +185,11 @@ describe("WebRegistrationFinishService", () => {
|
||||
emailVerificationToken = "emailVerificationToken";
|
||||
masterKey = new SymmetricCryptoKey(new Uint8Array(64).buffer as CsprngArray) as MasterKey;
|
||||
passwordInputResult = {
|
||||
masterKey: masterKey,
|
||||
serverMasterKeyHash: "serverMasterKeyHash",
|
||||
localMasterKeyHash: "localMasterKeyHash",
|
||||
newMasterKey: masterKey,
|
||||
newServerMasterKeyHash: "newServerMasterKeyHash",
|
||||
newLocalMasterKeyHash: "newLocalMasterKeyHash",
|
||||
kdfConfig: DEFAULT_KDF_CONFIG,
|
||||
hint: "hint",
|
||||
newPasswordHint: "newPasswordHint",
|
||||
newPassword: "newPassword",
|
||||
};
|
||||
|
||||
@@ -231,8 +231,8 @@ describe("WebRegistrationFinishService", () => {
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: emailVerificationToken,
|
||||
masterPasswordHash: passwordInputResult.serverMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.hint,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
@@ -267,8 +267,8 @@ describe("WebRegistrationFinishService", () => {
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.serverMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.hint,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
@@ -308,8 +308,8 @@ describe("WebRegistrationFinishService", () => {
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.serverMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.hint,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
@@ -351,8 +351,8 @@ describe("WebRegistrationFinishService", () => {
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.serverMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.hint,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
@@ -396,8 +396,8 @@ describe("WebRegistrationFinishService", () => {
|
||||
expect.objectContaining({
|
||||
email,
|
||||
emailVerificationToken: undefined,
|
||||
masterPasswordHash: passwordInputResult.serverMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.hint,
|
||||
masterPasswordHash: passwordInputResult.newServerMasterKeyHash,
|
||||
masterPasswordHint: passwordInputResult.newPasswordHint,
|
||||
userSymmetricKey: userKeyEncString.encryptedString,
|
||||
userAsymmetricKeys: {
|
||||
publicKey: userKeyPair[0],
|
||||
|
||||
@@ -29,6 +29,9 @@ import { KdfConfigService, KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { UserKeyRotationService } from "../../key-management/key-rotation/user-key-rotation.service";
|
||||
|
||||
/**
|
||||
* @deprecated use the auth `PasswordSettingsComponent` instead
|
||||
*/
|
||||
@Component({
|
||||
selector: "app-change-password",
|
||||
templateUrl: "change-password.component.html",
|
||||
@@ -132,7 +135,7 @@ export class ChangePasswordComponent
|
||||
content:
|
||||
this.i18nService.t("updateEncryptionKeyWarning") +
|
||||
" " +
|
||||
this.i18nService.t("updateEncryptionKeyExportWarning") +
|
||||
this.i18nService.t("updateEncryptionKeyAccountExportWarning") +
|
||||
" " +
|
||||
this.i18nService.t("rotateEncKeyConfirmation"),
|
||||
type: "warning",
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="tabbed-header">
|
||||
<h1>{{ "changeMasterPassword" | i18n }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="tw-max-w-lg tw-mb-12">
|
||||
<bit-callout type="warning">{{ "loggedOutWarning" | i18n }}</bit-callout>
|
||||
<auth-change-password [inputPasswordFlow]="inputPasswordFlow"></auth-change-password>
|
||||
</div>
|
||||
|
||||
<app-webauthn-login-settings></app-webauthn-login-settings>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ChangePasswordComponent, InputPasswordFlow } from "@bitwarden/auth/angular";
|
||||
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { CalloutModule } from "@bitwarden/components";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
import { WebauthnLoginSettingsModule } from "../../webauthn-login-settings";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: "app-password-settings",
|
||||
templateUrl: "password-settings.component.html",
|
||||
imports: [CalloutModule, ChangePasswordComponent, I18nPipe, WebauthnLoginSettingsModule],
|
||||
})
|
||||
export class PasswordSettingsComponent implements OnInit {
|
||||
inputPasswordFlow = InputPasswordFlow.ChangePasswordWithOptionalUserKeyRotation;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
const userHasMasterPassword = await firstValueFrom(
|
||||
this.userDecryptionOptionsService.hasMasterPassword$,
|
||||
);
|
||||
if (!userHasMasterPassword) {
|
||||
await this.router.navigate(["/settings/security/two-factor"]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
|
||||
import { ChangePasswordComponent } from "../change-password.component";
|
||||
import { TwoFactorSetupComponent } from "../two-factor/two-factor-setup.component";
|
||||
|
||||
import { DeviceManagementComponent } from "./device-management.component";
|
||||
import { PasswordSettingsComponent } from "./password-settings/password-settings.component";
|
||||
import { SecurityKeysComponent } from "./security-keys.component";
|
||||
import { SecurityComponent } from "./security.component";
|
||||
|
||||
@@ -14,10 +18,31 @@ const routes: Routes = [
|
||||
component: SecurityComponent,
|
||||
data: { titleId: "security" },
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", redirectTo: "change-password" },
|
||||
{ path: "", pathMatch: "full", redirectTo: "password" },
|
||||
{
|
||||
path: "change-password",
|
||||
component: ChangePasswordComponent,
|
||||
canActivate: [
|
||||
canAccessFeature(
|
||||
FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
|
||||
false,
|
||||
"/settings/security/password",
|
||||
false,
|
||||
),
|
||||
],
|
||||
data: { titleId: "masterPassword" },
|
||||
},
|
||||
{
|
||||
path: "password",
|
||||
component: PasswordSettingsComponent,
|
||||
canActivate: [
|
||||
canAccessFeature(
|
||||
FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
|
||||
true,
|
||||
"/settings/security/change-password",
|
||||
false,
|
||||
),
|
||||
],
|
||||
data: { titleId: "masterPassword" },
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<app-header>
|
||||
<bit-tab-nav-bar slot="tabs">
|
||||
<ng-container *ngIf="showChangePassword">
|
||||
<bit-tab-link route="change-password">{{ "masterPassword" | i18n }}</bit-tab-link>
|
||||
<bit-tab-link [route]="changePasswordRoute">{{ "masterPassword" | i18n }}</bit-tab-link>
|
||||
</ng-container>
|
||||
<bit-tab-link route="two-factor">{{ "twoStepLogin" | i18n }}</bit-tab-link>
|
||||
<bit-tab-link route="device-management">{{ "devices" | i18n }}</bit-tab-link>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
||||
@Component({
|
||||
@@ -10,6 +11,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
|
||||
})
|
||||
export class SecurityComponent implements OnInit {
|
||||
showChangePassword = true;
|
||||
changePasswordRoute = "change-password";
|
||||
|
||||
constructor(
|
||||
private userVerificationService: UserVerificationService,
|
||||
@@ -18,5 +20,12 @@ export class SecurityComponent implements OnInit {
|
||||
|
||||
async ngOnInit() {
|
||||
this.showChangePassword = await this.userVerificationService.hasMasterPassword();
|
||||
|
||||
const changePasswordRefreshFlag = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.PM16117_ChangeExistingPasswordRefactor,
|
||||
);
|
||||
if (changePasswordRefreshFlag) {
|
||||
this.changePasswordRoute = "password";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,100 @@
|
||||
<div *ngIf="!useTrialStepper">
|
||||
<auth-input-password
|
||||
[inputPasswordFlow]="InputPasswordFlow.SetInitialPassword"
|
||||
[email]="email"
|
||||
[masterPasswordPolicyOptions]="enforcedPolicyOptions"
|
||||
(onPasswordFormSubmit)="handlePasswordSubmit($event)"
|
||||
[primaryButtonText]="{ key: 'createAccount' }"
|
||||
></auth-input-password>
|
||||
</div>
|
||||
<div *ngIf="useTrialStepper">
|
||||
<app-vertical-stepper #stepper linear (selectionChange)="verticalStepChange($event)">
|
||||
<app-vertical-step label="Create Account" [editable]="false" [subLabel]="email">
|
||||
<auth-input-password
|
||||
[inputPasswordFlow]="InputPasswordFlow.SetInitialPassword"
|
||||
[email]="email"
|
||||
[masterPasswordPolicyOptions]="enforcedPolicyOptions"
|
||||
(onPasswordFormSubmit)="handlePasswordSubmit($event)"
|
||||
[primaryButtonText]="{ key: 'createAccount' }"
|
||||
></auth-input-password>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step label="Organization Information" [subLabel]="orgInfoSubLabel">
|
||||
<app-org-info [nameOnly]="true" [formGroup]="orgInfoFormGroup"></app-org-info>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[disabled]="orgInfoFormGroup.controls.name.invalid"
|
||||
[loading]="loading && (trialPaymentOptional$ | async)"
|
||||
(click)="orgNameEntrySubmit()"
|
||||
>
|
||||
{{ (trialPaymentOptional$ | async) ? ("startTrial" | i18n) : ("next" | i18n) }}
|
||||
</button>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step
|
||||
label="Billing"
|
||||
[subLabel]="billingSubLabel"
|
||||
*ngIf="!(trialPaymentOptional$ | async) && !isSecretsManagerFree"
|
||||
>
|
||||
<app-trial-billing-step
|
||||
*ngIf="stepper.selectedIndex === 2"
|
||||
[organizationInfo]="{
|
||||
name: orgInfoFormGroup.value.name,
|
||||
email: orgInfoFormGroup.value.billingEmail,
|
||||
type: trialOrganizationType,
|
||||
}"
|
||||
[subscriptionProduct]="
|
||||
product === ProductType.SecretsManager
|
||||
? SubscriptionProduct.SecretsManager
|
||||
: SubscriptionProduct.PasswordManager
|
||||
"
|
||||
[trialLength]="trialLength"
|
||||
(steppedBack)="previousStep()"
|
||||
(organizationCreated)="createdOrganization($event)"
|
||||
>
|
||||
</app-trial-billing-step>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step label="Confirmation Details" [applyBorder]="false">
|
||||
<app-trial-confirmation-details
|
||||
[email]="email"
|
||||
[orgLabel]="orgLabel"
|
||||
[product]="this.product"
|
||||
[trialLength]="trialLength"
|
||||
></app-trial-confirmation-details>
|
||||
<div class="tw-mb-3 tw-flex">
|
||||
<a
|
||||
@if (initializing) {
|
||||
<div class="tw-flex tw-items-center tw-justify-center">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-3x tw-text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</div>
|
||||
} @else {
|
||||
<div *ngIf="!useTrialStepper">
|
||||
<auth-input-password
|
||||
[flow]="inputPasswordFlow"
|
||||
[email]="email"
|
||||
[masterPasswordPolicyOptions]="enforcedPolicyOptions"
|
||||
(onPasswordFormSubmit)="handlePasswordSubmit($event)"
|
||||
[primaryButtonText]="{ key: 'createAccount' }"
|
||||
></auth-input-password>
|
||||
</div>
|
||||
<div *ngIf="useTrialStepper">
|
||||
<app-vertical-stepper #stepper linear (selectionChange)="verticalStepChange($event)">
|
||||
<app-vertical-step label="Create Account" [editable]="false" [subLabel]="email">
|
||||
<auth-input-password
|
||||
[flow]="inputPasswordFlow"
|
||||
[email]="email"
|
||||
[masterPasswordPolicyOptions]="enforcedPolicyOptions"
|
||||
(onPasswordFormSubmit)="handlePasswordSubmit($event)"
|
||||
[primaryButtonText]="{ key: 'createAccount' }"
|
||||
></auth-input-password>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step label="Organization Information" [subLabel]="orgInfoSubLabel">
|
||||
<app-org-info [nameOnly]="true" [formGroup]="orgInfoFormGroup"></app-org-info>
|
||||
<button
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[routerLink]="
|
||||
[disabled]="orgInfoFormGroup.controls.name.invalid"
|
||||
[loading]="loading && (trialPaymentOptional$ | async)"
|
||||
(click)="orgNameEntrySubmit()"
|
||||
>
|
||||
{{ (trialPaymentOptional$ | async) ? ("startTrial" | i18n) : ("next" | i18n) }}
|
||||
</button>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step
|
||||
label="Billing"
|
||||
[subLabel]="billingSubLabel"
|
||||
*ngIf="!(trialPaymentOptional$ | async) && !isSecretsManagerFree"
|
||||
>
|
||||
<app-trial-billing-step
|
||||
*ngIf="stepper.selectedIndex === 2"
|
||||
[organizationInfo]="{
|
||||
name: orgInfoFormGroup.value.name,
|
||||
email: orgInfoFormGroup.value.billingEmail,
|
||||
type: trialOrganizationType,
|
||||
}"
|
||||
[subscriptionProduct]="
|
||||
product === ProductType.SecretsManager
|
||||
? ['/sm', orgId]
|
||||
: ['/organizations', orgId, 'vault']
|
||||
? SubscriptionProduct.SecretsManager
|
||||
: SubscriptionProduct.PasswordManager
|
||||
"
|
||||
[trialLength]="trialLength"
|
||||
(steppedBack)="previousStep()"
|
||||
(organizationCreated)="createdOrganization($event)"
|
||||
>
|
||||
{{ "getStarted" | i18n | titlecase }}
|
||||
</a>
|
||||
<a
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
[routerLink]="['/organizations', orgId, 'members']"
|
||||
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
|
||||
>
|
||||
{{ "inviteUsers" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</app-vertical-step>
|
||||
</app-vertical-stepper>
|
||||
</div>
|
||||
</app-trial-billing-step>
|
||||
</app-vertical-step>
|
||||
<app-vertical-step label="Confirmation Details" [applyBorder]="false">
|
||||
<app-trial-confirmation-details
|
||||
[email]="email"
|
||||
[orgLabel]="orgLabel"
|
||||
[product]="this.product"
|
||||
[trialLength]="trialLength"
|
||||
></app-trial-confirmation-details>
|
||||
<div class="tw-mb-3 tw-flex">
|
||||
<a
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[routerLink]="
|
||||
product === ProductType.SecretsManager
|
||||
? ['/sm', orgId]
|
||||
: ['/organizations', orgId, 'vault']
|
||||
"
|
||||
>
|
||||
{{ "getStarted" | i18n | titlecase }}
|
||||
</a>
|
||||
<a
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="secondary"
|
||||
[routerLink]="['/organizations', orgId, 'members']"
|
||||
class="tw-ml-3 tw-inline-flex tw-items-center tw-px-3"
|
||||
>
|
||||
{{ "inviteUsers" | i18n }}
|
||||
</a>
|
||||
</div>
|
||||
</app-vertical-step>
|
||||
</app-vertical-stepper>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@ export type InitiationPath =
|
||||
export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
|
||||
|
||||
InputPasswordFlow = InputPasswordFlow;
|
||||
inputPasswordFlow = InputPasswordFlow.AccountRegistration;
|
||||
initializing = true;
|
||||
|
||||
/** Password Manager or Secrets Manager */
|
||||
product: ProductType;
|
||||
@@ -203,6 +204,8 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
||||
.subscribe(() => {
|
||||
this.orgInfoFormGroup.controls.name.markAsTouched();
|
||||
});
|
||||
|
||||
this.initializing = false;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
LoginDecryptionOptionsService,
|
||||
TwoFactorAuthComponentService,
|
||||
TwoFactorAuthDuoComponentService,
|
||||
ChangePasswordService,
|
||||
} from "@bitwarden/auth/angular";
|
||||
import {
|
||||
InternalUserDecryptionOptionsServiceAbstraction,
|
||||
@@ -110,6 +111,7 @@ import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarde
|
||||
import { flagEnabled } from "../../utils/flags";
|
||||
import { PolicyListService } from "../admin-console/core/policy-list.service";
|
||||
import {
|
||||
WebChangePasswordService,
|
||||
WebSetPasswordJitService,
|
||||
WebRegistrationFinishService,
|
||||
WebLoginComponentService,
|
||||
@@ -123,6 +125,7 @@ import { AcceptOrganizationInviteService } from "../auth/organization-invite/acc
|
||||
import { HtmlStorageService } from "../core/html-storage.service";
|
||||
import { I18nService } from "../core/i18n.service";
|
||||
import { WebFileDownloadService } from "../core/web-file-download.service";
|
||||
import { UserKeyRotationService } from "../key-management/key-rotation/user-key-rotation.service";
|
||||
import { WebLockComponentService } from "../key-management/lock/services/web-lock-component.service";
|
||||
import { WebProcessReloadService } from "../key-management/services/web-process-reload.service";
|
||||
import { WebBiometricsService } from "../key-management/web-biometric.service";
|
||||
@@ -373,6 +376,16 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: DefaultSshImportPromptService,
|
||||
deps: [DialogService, ToastService, PlatformUtilsService, I18nServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: ChangePasswordService,
|
||||
useClass: WebChangePasswordService,
|
||||
deps: [
|
||||
KeyServiceAbstraction,
|
||||
MasterPasswordApiService,
|
||||
InternalMasterPasswordServiceAbstraction,
|
||||
UserKeyRotationService,
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
Reference in New Issue
Block a user