mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +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({
|
||||
|
||||
@@ -4549,8 +4549,8 @@
|
||||
"updateEncryptionKeyWarning": {
|
||||
"message": "After updating your encryption key, you are required to log out and back in to all Bitwarden applications that you are currently using (such as the mobile app or browser extensions). Failure to log out and back in (which downloads your new encryption key) may result in data corruption. We will attempt to log you out automatically, however, it may be delayed."
|
||||
},
|
||||
"updateEncryptionKeyExportWarning": {
|
||||
"message": "Any encrypted exports that you have saved will also become invalid."
|
||||
"updateEncryptionKeyAccountExportWarning": {
|
||||
"message": "Any account restricted exports you have saved will become invalid."
|
||||
},
|
||||
"subscription": {
|
||||
"message": "Subscription"
|
||||
|
||||
Reference in New Issue
Block a user