1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-28 02:23:25 +00:00

Merge branch 'master' into auth/pm-3797/emergency-access-refactor

This commit is contained in:
Jacob Fink
2023-10-16 12:48:11 -04:00
422 changed files with 10697 additions and 1589 deletions

View File

@@ -45,10 +45,20 @@ const routes: Routes = [
},
{
path: "tools",
loadChildren: () =>
import("../tools/import-export/org-import-export.module").then(
(m) => m.OrganizationImportExportModule
),
children: [
{
path: "import",
loadChildren: () =>
import("../tools/import/org-import.module").then((m) => m.OrganizationImportModule),
},
{
path: "export",
loadChildren: () =>
import("../tools/vault-export/org-vault-export.module").then(
(m) => m.OrganizationVaultExportModule
),
},
],
},
],
},

View File

@@ -15,15 +15,17 @@ import { ValidationService } from "@bitwarden/common/platform/abstractions/valid
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
import { OrganizationPlansComponent } from "../../../billing/settings/organization-plans.component";
import { OrganizationPlansComponent } from "../../../billing";
import { SharedModule } from "../../../shared";
import {
DeleteOrganizationDialogResult,
openDeleteOrganizationDialog,
} from "../settings/components";
@Component({
selector: "families-for-enterprise-setup",
templateUrl: "families-for-enterprise-setup.component.html",
standalone: true,
imports: [SharedModule, OrganizationPlansComponent],
})
export class FamiliesForEnterpriseSetupComponent implements OnInit, OnDestroy {
@ViewChild(OrganizationPlansComponent, { static: false })

View File

@@ -6,9 +6,9 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
// eslint-disable-next-line no-restricted-imports
import { ExposedPasswordsReportComponent as BaseExposedPasswordsReportComponent } from "../../../reports/pages/exposed-passwords-report.component";

View File

@@ -3,14 +3,13 @@ import { RouterModule, Routes } from "@angular/router";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { OrganizationPermissionsGuard } from "../../../../admin-console/organizations/guards/org-permissions.guard";
import { OrganizationPermissionsGuard } from "../../guards/org-permissions.guard";
import { OrganizationExportComponent } from "./org-export.component";
import { OrganizationImportComponent } from "./org-import.component";
const routes: Routes = [
{
path: "import",
path: "",
component: OrganizationImportComponent,
canActivate: [OrganizationPermissionsGuard],
data: {
@@ -18,18 +17,9 @@ const routes: Routes = [
organizationPermissions: (org: Organization) => org.canAccessImportExport,
},
},
{
path: "export",
component: OrganizationExportComponent,
canActivate: [OrganizationPermissionsGuard],
data: {
titleId: "exportVault",
organizationPermissions: (org: Organization) => org.canAccessImportExport,
},
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
})
export class OrganizationImportExportRoutingModule {}
export class OrganizationImportRoutingModule {}

View File

@@ -18,11 +18,11 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv
import { DialogService } from "@bitwarden/components";
import { ImportServiceAbstraction } from "@bitwarden/importer";
import { ImportComponent } from "../../../../tools/import-export/import.component";
import { ImportComponent } from "../../../../tools/import/import.component";
@Component({
selector: "app-org-import",
templateUrl: "../../../../tools/import-export/import.component.html",
templateUrl: "../../../../tools/import/import.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class OrganizationImportComponent extends ImportComponent {

View File

@@ -15,13 +15,12 @@ import {
import { LooseComponentsModule, SharedModule } from "../../../../shared";
import { OrganizationExportComponent } from "./org-export.component";
import { OrganizationImportExportRoutingModule } from "./org-import-export-routing.module";
import { OrganizationImportRoutingModule } from "./org-import-routing.module";
import { OrganizationImportComponent } from "./org-import.component";
@NgModule({
imports: [SharedModule, LooseComponentsModule, OrganizationImportExportRoutingModule],
declarations: [OrganizationImportComponent, OrganizationExportComponent],
imports: [SharedModule, LooseComponentsModule, OrganizationImportRoutingModule],
declarations: [OrganizationImportComponent],
providers: [
{
provide: ImportApiServiceAbstraction,
@@ -42,4 +41,4 @@ import { OrganizationImportComponent } from "./org-import.component";
},
],
})
export class OrganizationImportExportModule {}
export class OrganizationImportModule {}

View File

@@ -6,8 +6,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
// eslint-disable-next-line no-restricted-imports
import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponent } from "../../../reports/pages/inactive-two-factor-report.component";

View File

@@ -6,9 +6,9 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
// eslint-disable-next-line no-restricted-imports
import { ReusedPasswordsReportComponent as BaseReusedPasswordsReportComponent } from "../../../reports/pages/reused-passwords-report.component";

View File

@@ -5,8 +5,8 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
// eslint-disable-next-line no-restricted-imports
import { UnsecuredWebsitesReportComponent as BaseUnsecuredWebsitesReportComponent } from "../../../reports/pages/unsecured-websites-report.component";

View File

@@ -0,0 +1,25 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { OrganizationPermissionsGuard } from "../../guards/org-permissions.guard";
import { OrganizationVaultExportComponent } from "./org-vault-export.component";
const routes: Routes = [
{
path: "",
component: OrganizationVaultExportComponent,
canActivate: [OrganizationPermissionsGuard],
data: {
titleId: "exportVault",
organizationPermissions: (org: Organization) => org.canAccessImportExport,
},
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
})
export class OrganizationVaultExportRoutingModule {}

View File

@@ -14,14 +14,14 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { DialogService } from "@bitwarden/components";
import { VaultExportServiceAbstraction } from "@bitwarden/exporter/vault-export";
import { ExportComponent } from "../../../../tools/import-export/export.component";
import { ExportComponent } from "../../../../tools/vault-export/export.component";
@Component({
selector: "app-org-export",
templateUrl: "../../../../tools/import-export/export.component.html",
templateUrl: "../../../../tools/vault-export/export.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class OrganizationExportComponent extends ExportComponent {
export class OrganizationVaultExportComponent extends ExportComponent {
constructor(
cryptoService: CryptoService,
i18nService: I18nService,

View File

@@ -0,0 +1,12 @@
import { NgModule } from "@angular/core";
import { LooseComponentsModule, SharedModule } from "../../../../shared";
import { OrganizationVaultExportRoutingModule } from "./org-vault-export-routing.module";
import { OrganizationVaultExportComponent } from "./org-vault-export.component";
@NgModule({
imports: [SharedModule, LooseComponentsModule, OrganizationVaultExportRoutingModule],
declarations: [OrganizationVaultExportComponent],
})
export class OrganizationVaultExportModule {}

View File

@@ -6,9 +6,9 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
// eslint-disable-next-line no-restricted-imports
import { WeakPasswordsReportComponent as BaseWeakPasswordsReportComponent } from "../../../reports/pages/weak-passwords-report.component";

View File

@@ -5,11 +5,13 @@ import { first } from "rxjs/operators";
import { PlanType } from "@bitwarden/common/billing/enums";
import { ProductType } from "@bitwarden/common/enums";
import { OrganizationPlansComponent } from "../../billing/settings/organization-plans.component";
import { OrganizationPlansComponent } from "../../billing";
import { SharedModule } from "../../shared";
@Component({
selector: "app-create-organization",
templateUrl: "create-organization.component.html",
standalone: true,
imports: [SharedModule, OrganizationPlansComponent],
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class CreateOrganizationComponent implements OnInit {

View File

@@ -1,12 +1,11 @@
import { NgModule } from "@angular/core";
import { CoreAuthModule } from "./core";
import { SettingsModule } from "./settings/settings.module";
import { AuthSettingsModule } from "./settings/settings.module";
@NgModule({
imports: [CoreAuthModule, SettingsModule],
imports: [AuthSettingsModule],
declarations: [],
providers: [],
exports: [SettingsModule],
exports: [AuthSettingsModule],
})
export class AuthModule {}

View File

@@ -1,2 +1 @@
export * from "./services";
export * from "./core.module";

View File

@@ -1,25 +1,20 @@
import { Injectable } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { Verification } from "@bitwarden/common/types/verification";
import { SaveCredentialRequest } from "./request/save-credential.request";
import { WebauthnLoginCredentialCreateOptionsResponse } from "./response/webauthn-login-credential-create-options.response";
import { WebauthnLoginCredentialResponse } from "./response/webauthn-login-credential.response";
@Injectable()
@Injectable({ providedIn: "root" })
export class WebauthnLoginApiService {
constructor(
private apiService: ApiService,
private userVerificationService: UserVerificationService
) {}
constructor(private apiService: ApiService) {}
async getCredentialCreateOptions(
verification: Verification
request: SecretVerificationRequest
): Promise<WebauthnLoginCredentialCreateOptionsResponse> {
const request = await this.userVerificationService.buildRequest(verification);
const response = await this.apiService.send("POST", "/webauthn/options", request, true, true);
return new WebauthnLoginCredentialCreateOptionsResponse(response);
}
@@ -33,8 +28,7 @@ export class WebauthnLoginApiService {
return this.apiService.send("GET", "/webauthn", null, true, true);
}
async deleteCredential(credentialId: string, verification: Verification): Promise<void> {
const request = await this.userVerificationService.buildRequest(verification);
async deleteCredential(credentialId: string, request: SecretVerificationRequest): Promise<void> {
await this.apiService.send("POST", `/webauthn/${credentialId}/delete`, request, true, true);
}
}

View File

@@ -1,5 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { CredentialCreateOptionsView } from "../../views/credential-create-options.view";
import { WebauthnLoginApiService } from "./webauthn-login-api.service";
@@ -7,6 +9,7 @@ import { WebauthnLoginService } from "./webauthn-login.service";
describe("WebauthnService", () => {
let apiService!: MockProxy<WebauthnLoginApiService>;
let userVerificationService!: MockProxy<UserVerificationService>;
let credentials: MockProxy<CredentialsContainer>;
let webauthnService!: WebauthnLoginService;
@@ -15,8 +18,9 @@ describe("WebauthnService", () => {
window.PublicKeyCredential = class {} as any;
window.AuthenticatorAttestationResponse = class {} as any;
apiService = mock<WebauthnLoginApiService>();
userVerificationService = mock<UserVerificationService>();
credentials = mock<CredentialsContainer>();
webauthnService = new WebauthnLoginService(apiService, credentials);
webauthnService = new WebauthnLoginService(apiService, userVerificationService, credentials);
});
describe("createCredential", () => {

View File

@@ -1,6 +1,7 @@
import { Injectable, Optional } from "@angular/core";
import { BehaviorSubject, filter, from, map, Observable, shareReplay, switchMap, tap } from "rxjs";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Verification } from "@bitwarden/common/types/verification";
@@ -11,8 +12,10 @@ import { SaveCredentialRequest } from "./request/save-credential.request";
import { WebauthnLoginAttestationResponseRequest } from "./request/webauthn-login-attestation-response.request";
import { WebauthnLoginApiService } from "./webauthn-login-api.service";
@Injectable()
@Injectable({ providedIn: "root" })
export class WebauthnLoginService {
static readonly MaxCredentialCount = 5;
private navigatorCredentials: CredentialsContainer;
private _refresh$ = new BehaviorSubject<void>(undefined);
private _loading$ = new BehaviorSubject<boolean>(true);
@@ -27,6 +30,7 @@ export class WebauthnLoginService {
constructor(
private apiService: WebauthnLoginApiService,
private userVerificationService: UserVerificationService,
@Optional() navigatorCredentials?: CredentialsContainer,
@Optional() private logService?: LogService
) {
@@ -37,7 +41,8 @@ export class WebauthnLoginService {
async getCredentialCreateOptions(
verification: Verification
): Promise<CredentialCreateOptionsView> {
const response = await this.apiService.getCredentialCreateOptions(verification);
const request = await this.userVerificationService.buildRequest(verification);
const response = await this.apiService.getCredentialCreateOptions(request);
return new CredentialCreateOptionsView(response.options, response.token);
}
@@ -95,7 +100,8 @@ export class WebauthnLoginService {
}
async deleteCredential(credentialId: string, verification: Verification): Promise<void> {
await this.apiService.deleteCredential(credentialId, verification);
const request = await this.userVerificationService.buildRequest(verification);
await this.apiService.deleteCredential(credentialId, request);
this.refresh();
}

View File

@@ -15,9 +15,9 @@ import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.s
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
import { AddEditComponent as BaseAddEditComponent } from "../../../vault/individual-vault/add-edit.component";

View File

@@ -1,14 +1,16 @@
import { NgModule } from "@angular/core";
import { PasswordCalloutComponent } from "@bitwarden/auth";
import { SharedModule } from "../../shared";
import { ChangePasswordComponent } from "./change-password.component";
import { WebauthnLoginSettingsModule } from "./webauthn-login-settings";
@NgModule({
imports: [SharedModule, WebauthnLoginSettingsModule],
imports: [SharedModule, WebauthnLoginSettingsModule, PasswordCalloutComponent],
declarations: [ChangePasswordComponent],
providers: [],
exports: [WebauthnLoginSettingsModule, ChangePasswordComponent],
exports: [ChangePasswordComponent],
})
export class SettingsModule {}
export class AuthSettingsModule {}

View File

@@ -1,5 +1,5 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog dialogSize="large">
<bit-dialog dialogSize="large" [loading]="loading$ | async">
<span bitDialogTitle
>{{ "loginWithPasskey" | i18n }}
<span class="tw-text-sm tw-normal-case tw-text-muted">{{ "newPasskey" | i18n }}</span>

View File

@@ -46,6 +46,7 @@ export class CreateCredentialDialogComponent implements OnInit {
protected credentialOptions?: CredentialCreateOptionsView;
protected deviceResponse?: PublicKeyCredential;
protected hasPasskeys$?: Observable<boolean>;
protected loading$ = this.webauthnService.loading$;
constructor(
private formBuilder: FormBuilder,
@@ -144,7 +145,7 @@ export class CreateCredentialDialogComponent implements OnInit {
return;
}
if (firstValueFrom(this.hasPasskeys$)) {
if (await firstValueFrom(this.hasPasskeys$)) {
this.platformUtilsService.showToast(
"success",
null,

View File

@@ -1,5 +1,5 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog dialogSize="large">
<bit-dialog dialogSize="large" [loading]="loading$ | async">
<span bitDialogTitle
>{{ "removePasskey" | i18n }}
<span *ngIf="credential" class="tw-text-sm tw-normal-case tw-text-muted">{{

View File

@@ -27,6 +27,7 @@ export class DeleteCredentialDialogComponent implements OnInit, OnDestroy {
masterPassword: ["", [Validators.required]],
});
protected credential?: WebauthnCredentialView;
protected loading$ = this.webauthnService.loading$;
constructor(
@Inject(DIALOG_DATA) private params: DeleteCredentialDialogParams,

View File

@@ -22,7 +22,7 @@
<table *ngIf="hasCredentials" class="tw-mb-5">
<tr *ngFor="let credential of credentials">
<td class="tw-p-2 tw-pl-0 tw-font-semibold">{{ credential.name }}</td>
<td class="tw-p-2 tw-pr-0">
<td class="tw-p-2 tw-pr-10">
<ng-container *ngIf="credential.prfSupport">
<i class="bwi bwi-lock-encrypted"></i>
{{ "supportsEncryption" | i18n }}
@@ -31,7 +31,7 @@
{{ "encryptionNotSupported" | i18n }}
</span>
</td>
<td class="tw-py-2 tw-pl-10 tw-pr-0">
<td class="tw-py-2">
<button
type="button"
bitLink

View File

@@ -19,7 +19,7 @@ import { openDeleteCredentialDialogComponent } from "./delete-credential-dialog/
export class WebauthnLoginSettingsComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
protected readonly MaxCredentialCount = 5;
protected readonly MaxCredentialCount = WebauthnLoginService.MaxCredentialCount;
protected credentials?: WebauthnCredentialView[];
protected loading = true;

View File

@@ -6,9 +6,10 @@ import { FormFieldModule } from "@bitwarden/components";
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
import { RegisterFormModule } from "../../auth/register-form/register-form.module";
import { PaymentComponent, TaxInfoComponent } from "../../billing";
import { BillingComponent } from "../../billing/accounts/trial-initiation/billing.component";
import { EnvironmentSelectorModule } from "../../components/environment-selector/environment-selector.module";
import { LooseComponentsModule, SharedModule } from "../../shared";
import { SharedModule } from "../../shared";
import { ConfirmationDetailsComponent } from "./confirmation-details.component";
import { AbmEnterpriseContentComponent } from "./content/abm-enterprise-content.component";
@@ -37,8 +38,9 @@ import { VerticalStepperModule } from "./vertical-stepper/vertical-stepper.modul
FormFieldModule,
RegisterFormModule,
OrganizationCreateModule,
LooseComponentsModule,
EnvironmentSelectorModule,
PaymentComponent,
TaxInfoComponent,
],
declarations: [
TrialInitiationComponent,

View File

@@ -92,7 +92,7 @@
<div id="duo-frame" class="mb-3">
<iframe
id="duo_iframe"
sandbox="allow-scripts allow-forms allow-same-origin"
sandbox="allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox"
></iframe>
</div>
</ng-container>

View File

@@ -3,7 +3,7 @@ import { FormGroup } from "@angular/forms";
import { ProductType } from "@bitwarden/common/enums";
import { OrganizationPlansComponent } from "../../settings/organization-plans.component";
import { OrganizationPlansComponent } from "../../organizations";
@Component({
selector: "app-billing",

View File

@@ -0,0 +1,2 @@
export { OrganizationPlansComponent } from "./organizations";
export { PaymentComponent, TaxInfoComponent } from "./shared";

View File

@@ -6,7 +6,6 @@ import { BillingHistoryResponse } from "@bitwarden/common/billing/models/respons
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@Component({
selector: "app-billing-history-view",
templateUrl: "billing-history-view.component.html",
})
export class BillingHistoryViewComponent implements OnInit {

View File

@@ -1,12 +1,12 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { BillingHistoryViewComponent } from "../../billing/settings/billing-history-view.component";
import { PaymentMethodComponent } from "../../billing/settings/payment-method.component";
import { UserSubscriptionComponent } from "../../billing/settings/user-subscription.component";
import { PremiumComponent } from "../settings/premium.component";
import { PaymentMethodComponent } from "../shared";
import { BillingHistoryViewComponent } from "./billing-history-view.component";
import { PremiumComponent } from "./premium.component";
import { SubscriptionComponent } from "./subscription.component";
import { UserSubscriptionComponent } from "./user-subscription.component";
const routes: Routes = [
{
@@ -43,4 +43,4 @@ const routes: Routes = [
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class SubscriptionRoutingModule {}
export class IndividualBillingRoutingModule {}

View File

@@ -0,0 +1,20 @@
import { NgModule } from "@angular/core";
import { BillingSharedModule } from "../shared";
import { BillingHistoryViewComponent } from "./billing-history-view.component";
import { IndividualBillingRoutingModule } from "./individual-billing-routing.module";
import { PremiumComponent } from "./premium.component";
import { SubscriptionComponent } from "./subscription.component";
import { UserSubscriptionComponent } from "./user-subscription.component";
@NgModule({
imports: [IndividualBillingRoutingModule, BillingSharedModule],
declarations: [
SubscriptionComponent,
BillingHistoryViewComponent,
UserSubscriptionComponent,
PremiumComponent,
],
})
export class IndividualBillingModule {}

View File

@@ -11,11 +11,9 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { PaymentComponent } from "./payment.component";
import { TaxInfoComponent } from "./tax-info.component";
import { PaymentComponent, TaxInfoComponent } from "../shared";
@Component({
selector: "app-premium",
templateUrl: "premium.component.html",
})
export class PremiumComponent implements OnInit {

View File

@@ -4,7 +4,6 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
@Component({
selector: "app-subscription",
templateUrl: "subscription.component.html",
})
export class SubscriptionComponent {

View File

@@ -12,7 +12,6 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv
import { DialogService } from "@bitwarden/components";
@Component({
selector: "app-user-subscription",
templateUrl: "user-subscription.component.html",
})
export class UserSubscriptionComponent implements OnInit {

View File

@@ -14,7 +14,7 @@
required
/>
<small class="d-block text-muted mb-4">
<strong>{{ "total" | i18n }}:</strong> {{ newSeatCount || 0 }} &times;
<strong>{{ "total" | i18n }}:</strong> {{ additionalSeatCount || 0 }} &times;
{{ seatPrice | currency : "$" }} = {{ adjustedSeatTotal | currency : "$" }} /
{{ interval | i18n }}
</small>
@@ -50,7 +50,7 @@
[required]="limitSubscription"
/>
<small class="d-block text-muted">
<strong>{{ "maxSeatCost" | i18n }}:</strong> {{ newMaxSeats || 0 }} &times;
<strong>{{ "maxSeatCost" | i18n }}:</strong> {{ additionalMaxSeatCount || 0 }} &times;
{{ seatPrice | currency : "$" }} = {{ maxSeatTotal | currency : "$" }} /
{{ interval | i18n }}
</small>

View File

@@ -38,8 +38,10 @@ export class AdjustSubscription {
async submit() {
try {
const seatAdjustment = this.newSeatCount - this.currentSeatCount;
const request = new OrganizationSubscriptionUpdateRequest(seatAdjustment, this.newMaxSeats);
const request = new OrganizationSubscriptionUpdateRequest(
this.additionalSeatCount,
this.newMaxSeats
);
this.formPromise = this.organizationApiService.updatePasswordManagerSeats(
this.organizationId,
request
@@ -64,11 +66,19 @@ export class AdjustSubscription {
}
}
get additionalSeatCount(): number {
return this.newSeatCount ? this.newSeatCount - this.currentSeatCount : 0;
}
get additionalMaxSeatCount(): number {
return this.newMaxSeats ? this.newMaxSeats - this.currentSeatCount : 0;
}
get adjustedSeatTotal(): number {
return this.newSeatCount * this.seatPrice;
return this.additionalSeatCount * this.seatPrice;
}
get maxSeatTotal(): number {
return this.newMaxSeats * this.seatPrice;
return this.additionalMaxSeatCount * this.seatPrice;
}
}

View File

@@ -1,117 +1,75 @@
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="billingSyncApiKeyTitle">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<form
class="modal-content"
#form
(ngSubmit)="submit()"
[appApiAction]="formPromise"
ngNativeValidate
>
<div class="modal-header">
<h1 class="modal-title" id="billingSyncApiKeyTitle">
{{ (hasBillingToken ? "viewBillingSyncToken" : "generateBillingSyncToken") | i18n }}
</h1>
<button
type="button"
class="close"
data-dismiss="modal"
appA11yTitle="{{ 'close' | i18n }}"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<app-user-verification
[(ngModel)]="masterPassword"
ngDefaultControl
name="secret"
*ngIf="!clientSecret"
>
</app-user-verification>
<ng-container *ngIf="clientSecret && showRotateScreen">
<p>{{ "rotateBillingSyncTokenTitle" | i18n }}</p>
<app-callout type="warning">
{{ "rotateBillingSyncTokenWarning" | i18n }}
</app-callout>
</ng-container>
<form [formGroup]="formGroup" [bitSubmit]="submit">
<bit-dialog>
<h1 bitDialogTitle>
{{ (hasBillingToken ? "viewBillingSyncToken" : "generateBillingSyncToken") | i18n }}
</h1>
<div bitDialogContent>
<app-user-verification formControlName="verification" *ngIf="!clientSecret">
</app-user-verification>
<div *ngIf="clientSecret && !showRotateScreen">
<p>{{ "copyPasteBillingSync" | i18n }}</p>
<label for="clientSecret">{{ "billingSyncKey" | i18n }}</label>
<div class="input-group">
<input
id="clientSecret"
class="form-control text-monospace"
type="text"
[(ngModel)]="clientSecret"
name="clientSecret"
disabled
/>
<div class="input-group-append">
<button
type="button"
class="btn btn-outline-secondary"
(click)="copy()"
[appA11yTitle]="'copy' | i18n"
>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>
<div class="small text-muted mt-2" *ngIf="showLastSyncText">
<b class="font-weight-semibold">{{ "lastSync" | i18n }}:</b>
{{ lastSyncDate | date : "medium" }}
</div>
<div class="small text-danger mt-2" *ngIf="showAwaitingSyncText">
<i class="bwi bwi-error"></i>
{{
(daysBetween === 1 ? "awaitingSyncSingular" : "awaitingSyncPlural")
| i18n : daysBetween
}}
</div>
<ng-container *ngIf="clientSecret && showRotateScreen">
<p>{{ "rotateBillingSyncTokenTitle" | i18n }}</p>
<bit-callout type="warning">
{{ "rotateBillingSyncTokenWarning" | i18n }}
</bit-callout>
</ng-container>
<div *ngIf="clientSecret && !showRotateScreen">
<p>{{ "copyPasteBillingSync" | i18n }}</p>
<bit-form-field>
<bit-label>{{ "billingSyncKey" | i18n }}</bit-label>
<input
bitInput
id="clientSecret"
type="text"
[value]="clientSecret"
class="tw-font-mono"
disabled
/>
<button
bitIconButton="bwi-clone"
bitSuffix
type="button"
[appCopyClick]="clientSecret"
[appA11yTitle]="'copyValue' | i18n"
></button>
</bit-form-field>
<div class="tw-mt-2 tw-text-sm tw-text-muted" *ngIf="showLastSyncText">
<b class="tw-font-semibold">{{ "lastSync" | i18n }}:</b>
{{ lastSyncDate | date : "medium" }}
</div>
<div class="tw-mt-2 tw-text-sm tw-text-danger" *ngIf="showAwaitingSyncText">
<i class="bwi bwi-error"></i>
{{
(daysBetween === 1 ? "awaitingSyncSingular" : "awaitingSyncPlural") | i18n : daysBetween
}}
</div>
</div>
<div class="modal-footer">
<button
type="submit"
class="btn btn-primary btn-submit"
[disabled]="form.loading"
*ngIf="!clientSecret || showRotateScreen"
>
<i
class="bwi bwi-spinner bwi-spin"
title="{{ 'loading' | i18n }}"
*ngIf="form.loading"
></i>
<span>
{{ submitButtonText }}
</span>
</button>
<button
type="button"
class="btn btn-outline-secondary"
data-dismiss="modal"
*ngIf="!showRotateScreen"
>
{{ "close" | i18n }}
</button>
<button
type="button"
class="btn btn-outline-secondary"
*ngIf="showRotateScreen"
(click)="cancelRotate()"
>
{{ "cancel" | i18n }}
</button>
<button
type="button"
class="btn btn-outline-secondary"
*ngIf="clientSecret && !showRotateScreen"
(click)="rotateToken()"
>
{{ "rotateToken" | i18n }}
</button>
</div>
</form>
</div>
</div>
</div>
<ng-container bitDialogFooter>
<button
type="submit"
bitButton
bitFormButton
buttonType="primary"
*ngIf="!clientSecret || showRotateScreen"
>
{{ submitButtonText }}
</button>
<button bitButton bitDialogClose type="button" *ngIf="!showRotateScreen">
{{ "close" | i18n }}
</button>
<button bitButton type="button" *ngIf="showRotateScreen" (click)="cancelRotate()">
{{ "cancel" | i18n }}
</button>
<button
bitButton
type="button"
*ngIf="clientSecret && !showRotateScreen"
(click)="rotateToken()"
>
{{ "rotateToken" | i18n }}
</button>
</ng-container>
</bit-dialog>
</form>

View File

@@ -1,6 +1,7 @@
import { Component } from "@angular/core";
import { DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { ModalConfig } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationApiKeyType } from "@bitwarden/common/admin-console/enums";
@@ -8,8 +9,10 @@ import { OrganizationApiKeyRequest } from "@bitwarden/common/admin-console/model
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { ApiKeyResponse } from "@bitwarden/common/auth/models/response/api-key.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Verification } from "@bitwarden/common/types/verification";
import { DialogService } from "@bitwarden/components";
export interface BillingSyncApiModalData {
organizationId: string;
@@ -17,63 +20,69 @@ export interface BillingSyncApiModalData {
}
@Component({
selector: "app-billing-sync-api-key",
templateUrl: "billing-sync-api-key.component.html",
})
export class BillingSyncApiKeyComponent {
organizationId: string;
hasBillingToken: boolean;
protected organizationId: string;
protected hasBillingToken: boolean;
protected formGroup = new FormGroup({
verification: new FormControl<Verification>(null, Validators.required),
});
showRotateScreen: boolean;
masterPassword: Verification;
formPromise: Promise<ApiKeyResponse>;
clientSecret?: string;
keyRevisionDate?: Date;
lastSyncDate?: Date = null;
lastSyncDate?: Date;
constructor(
@Inject(DIALOG_DATA) protected data: BillingSyncApiModalData,
private userVerificationService: UserVerificationService,
private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private organizationApiService: OrganizationApiServiceAbstraction,
modalConfig: ModalConfig<BillingSyncApiModalData>
private logService: LogService
) {
this.organizationId = modalConfig.data.organizationId;
this.hasBillingToken = modalConfig.data.hasBillingToken;
this.organizationId = data.organizationId;
this.hasBillingToken = data.hasBillingToken;
}
copy() {
this.platformUtilsService.copyToClipboard(this.clientSecret);
}
async submit() {
if (this.showRotateScreen) {
this.formPromise = this.userVerificationService
.buildRequest(this.masterPassword, OrganizationApiKeyRequest)
submit = async () => {
try {
const request = this.userVerificationService
.buildRequest(this.formGroup.value.verification, OrganizationApiKeyRequest)
.then((request) => {
request.type = OrganizationApiKeyType.BillingSync;
return request;
});
if (this.showRotateScreen) {
const response = await request.then((request) => {
return this.organizationApiService.rotateApiKey(this.organizationId, request);
});
const response = await this.formPromise;
await this.load(response);
this.showRotateScreen = false;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("billingSyncApiKeyRotated")
);
} else {
this.formPromise = this.userVerificationService
.buildRequest(this.masterPassword, OrganizationApiKeyRequest)
.then((request) => {
request.type = OrganizationApiKeyType.BillingSync;
await this.load(response);
this.showRotateScreen = false;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("billingSyncApiKeyRotated")
);
} else {
const response = await request.then((request) => {
return this.organizationApiService.getOrCreateApiKey(this.organizationId, request);
});
const response = await this.formPromise;
await this.load(response);
await this.load(response);
}
} catch (e) {
this.logService.error(e);
throw e;
}
}
};
async load(response: ApiKeyResponse) {
this.clientSecret = response.apiKey;
@@ -117,4 +126,8 @@ export class BillingSyncApiKeyComponent {
get daysBetween(): number {
return this.dayDiff(this.keyRevisionDate, new Date());
}
static open(dialogService: DialogService, data: BillingSyncApiModalData) {
return dialogService.open(BillingSyncApiKeyComponent, { data });
}
}

View File

@@ -18,7 +18,6 @@ export interface BillingSyncKeyModalData {
}
@Component({
selector: "app-billing-sync-key",
templateUrl: "billing-sync-key.component.html",
})
export class BillingSyncKeyComponent {

View File

@@ -0,0 +1 @@
export * from "./organization-plans.component";

View File

@@ -6,7 +6,6 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso
import { BillingHistoryResponse } from "@bitwarden/common/billing/models/response/billing-history.response";
@Component({
selector: "app-org-billing-history-view",
templateUrl: "organization-billing-history-view.component.html",
})
export class OrgBillingHistoryViewComponent implements OnInit, OnDestroy {

View File

@@ -5,8 +5,8 @@ import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstraction
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { OrganizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard";
import { PaymentMethodComponent } from "../../billing/settings/payment-method.component";
import { WebPlatformUtilsService } from "../../core/web-platform-utils.service";
import { PaymentMethodComponent } from "../shared";
import { OrgBillingHistoryViewComponent } from "./organization-billing-history-view.component";
import { OrganizationBillingTabComponent } from "./organization-billing-tab.component";

View File

@@ -6,7 +6,6 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@Component({
selector: "app-org-billing-tab",
templateUrl: "organization-billing-tab.component.html",
})
export class OrganizationBillingTabComponent implements OnInit {

View File

@@ -1,37 +1,42 @@
import { NgModule } from "@angular/core";
import { UserVerificationModule } from "../../auth/shared/components/user-verification";
import { LooseComponentsModule, SharedModule } from "../../shared";
import { BillingSharedModule } from "../shared";
import { AdjustSubscription } from "./adjust-subscription.component";
import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component";
import { BillingSyncKeyComponent } from "./billing-sync-key.component";
import { ChangePlanComponent } from "./change-plan.component";
import { DownloadLicenseComponent } from "./download-license.component";
import { OrgBillingHistoryViewComponent } from "./organization-billing-history-view.component";
import { OrganizationBillingRoutingModule } from "./organization-billing-routing.module";
import { OrganizationBillingTabComponent } from "./organization-billing-tab.component";
import { OrganizationPlansComponent } from "./organization-plans.component";
import { OrganizationSubscriptionCloudComponent } from "./organization-subscription-cloud.component";
import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscription-selfhost.component";
import { SecretsManagerBillingModule } from "./secrets-manager/sm-billing.module";
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
import { SubscriptionHiddenComponent } from "./subscription-hidden.component";
@NgModule({
imports: [
SharedModule,
LooseComponentsModule,
OrganizationBillingRoutingModule,
UserVerificationModule,
SecretsManagerBillingModule,
BillingSharedModule,
OrganizationPlansComponent,
],
declarations: [
AdjustSubscription,
BillingSyncApiKeyComponent,
BillingSyncKeyComponent,
ChangePlanComponent,
DownloadLicenseComponent,
OrganizationBillingTabComponent,
OrgBillingHistoryViewComponent,
OrganizationSubscriptionSelfhostComponent,
OrganizationSubscriptionCloudComponent,
OrganizationSubscriptionSelfhostComponent,
OrgBillingHistoryViewComponent,
SecretsManagerAdjustSubscriptionComponent,
SecretsManagerSubscribeStandaloneComponent,
SubscriptionHiddenComponent,
],
})

View File

@@ -276,7 +276,7 @@
<!-- Secrets Manager -->
<div class="tw-my-10">
<sm-subscribe
*ngIf="planOffersSecretsManager"
*ngIf="planOffersSecretsManager && !hasProvider"
[formGroup]="formGroup.controls.secretsManager"
[selectedPlan]="selectedSecretsManagerPlan"
[upgradeOrganization]="!createOrganization"

View File

@@ -35,10 +35,10 @@ import {
} from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { secretsManagerSubscribeFormFactory } from "../organizations/secrets-manager/sm-subscribe.component";
import { PaymentComponent } from "./payment.component";
import { TaxInfoComponent } from "./tax-info.component";
import { OrganizationCreateModule } from "../../admin-console/organizations/create/organization-create.module";
import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared";
import { PaymentComponent } from "../shared/payment.component";
import { TaxInfoComponent } from "../shared/tax-info.component";
interface OnSuccessArgs {
organizationId: string;
@@ -47,6 +47,8 @@ interface OnSuccessArgs {
@Component({
selector: "app-organization-plans",
templateUrl: "organization-plans.component.html",
standalone: true,
imports: [BillingSharedModule, OrganizationCreateModule],
})
export class OrganizationPlansComponent implements OnInit, OnDestroy {
@ViewChild(PaymentComponent) paymentComponent: PaymentComponent;
@@ -146,7 +148,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
}
}
if (this.providerId) {
if (this.hasProvider) {
this.formGroup.controls.businessOwned.setValue(true);
this.changedOwnedBusiness();
}
@@ -174,7 +176,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
}
get singleOrgPolicyBlock() {
return this.singleOrgPolicyAppliesToActiveUser && this.providerId == null;
return this.singleOrgPolicyAppliesToActiveUser && !this.hasProvider;
}
get createOrganization() {
@@ -233,6 +235,10 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
);
}
get hasProvider() {
return this.providerId != null;
}
additionalStoragePriceMonthly(selectedPlan: PlanResponse) {
if (!selectedPlan.isAnnual) {
return selectedPlan.additionalStoragePricePerGb;
@@ -538,7 +544,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
// Secrets Manager
this.buildSecretsManagerRequest(request);
if (this.providerId) {
if (this.hasProvider) {
const providerRequest = new ProviderOrganizationCreateRequest(
this.formGroup.controls.clientOwnerEmail.value,
request

View File

@@ -1,9 +1,8 @@
import { DatePipe } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { concatMap, Subject, takeUntil } from "rxjs";
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
import { ModalConfig, ModalService } from "@bitwarden/angular/services/modal.service";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@@ -18,14 +17,10 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { DialogService } from "@bitwarden/components";
import {
BillingSyncApiKeyComponent,
BillingSyncApiModalData,
} from "./billing-sync-api-key.component";
import { SecretsManagerSubscriptionOptions } from "./secrets-manager/sm-adjust-subscription.component";
import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component";
import { SecretsManagerSubscriptionOptions } from "./sm-adjust-subscription.component";
@Component({
selector: "app-org-subscription-cloud",
templateUrl: "organization-subscription-cloud.component.html",
})
export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy {
@@ -55,7 +50,6 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private logService: LogService,
private modalService: ModalService,
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction,
private route: ActivatedRoute,
@@ -116,6 +110,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
this.showSecretsManagerSubscribe =
this.userOrg.canEditSubscription &&
!this.userOrg.hasProvider &&
!this.userOrg.useSecretsManager &&
!this.subscription?.cancelled &&
!this.subscriptionMarkedForCancel;
@@ -330,22 +325,13 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
}
async manageBillingSync() {
const modalConfig: ModalConfig<BillingSyncApiModalData> = {
data: {
organizationId: this.organizationId,
hasBillingToken: this.hasBillingSyncToken,
},
};
const modalRef = this.modalService.open(BillingSyncApiKeyComponent, modalConfig);
const dialogRef = BillingSyncApiKeyComponent.open(this.dialogService, {
organizationId: this.organizationId,
hasBillingToken: this.hasBillingSyncToken,
});
modalRef.onClosed
.pipe(
concatMap(async () => {
this.load();
}),
takeUntil(this.destroy$)
)
.subscribe();
await firstValueFrom(dialogRef.closed);
this.load();
}
closeDownloadLicense() {

View File

@@ -17,10 +17,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import {
BillingSyncKeyComponent,
BillingSyncKeyModalData,
} from "../../billing/settings/billing-sync-key.component";
import { BillingSyncKeyComponent, BillingSyncKeyModalData } from "./billing-sync-key.component";
enum LicenseOptions {
SYNC = 0,
@@ -28,7 +25,6 @@ enum LicenseOptions {
}
@Component({
selector: "app-org-subscription-selfhost",
templateUrl: "organization-subscription-selfhost.component.html",
})
export class OrganizationSubscriptionSelfhostComponent implements OnInit, OnDestroy {

View File

@@ -1,3 +0,0 @@
export * from "./sm-billing.module";
export * from "./sm-subscribe.component";
export * from "./sm-subscribe-standalone.component";

View File

@@ -1,22 +0,0 @@
import { NgModule } from "@angular/core";
import { SharedModule } from "../../../shared";
import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component";
import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component";
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
@NgModule({
imports: [SharedModule],
declarations: [
SecretsManagerSubscribeComponent,
SecretsManagerSubscribeStandaloneComponent,
SecretsManagerAdjustSubscriptionComponent,
],
exports: [
SecretsManagerSubscribeComponent,
SecretsManagerSubscribeStandaloneComponent,
SecretsManagerAdjustSubscriptionComponent,
],
})
export class SecretsManagerBillingModule {}

View File

@@ -10,7 +10,7 @@ import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.res
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { secretsManagerSubscribeFormFactory } from "./sm-subscribe.component";
import { secretsManagerSubscribeFormFactory } from "../shared";
@Component({
selector: "sm-subscribe-standalone",

View File

@@ -9,7 +9,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { PaymentComponent } from "../billing/settings/payment.component";
import { PaymentComponent } from "./payment.component";
@Component({
selector: "app-adjust-storage",

View File

@@ -0,0 +1,37 @@
import { NgModule } from "@angular/core";
import { SharedModule } from "../../shared";
import { AddCreditComponent } from "./add-credit.component";
import { AdjustPaymentComponent } from "./adjust-payment.component";
import { AdjustStorageComponent } from "./adjust-storage.component";
import { BillingHistoryComponent } from "./billing-history.component";
import { PaymentMethodComponent } from "./payment-method.component";
import { PaymentComponent } from "./payment.component";
import { SecretsManagerSubscribeComponent } from "./sm-subscribe.component";
import { TaxInfoComponent } from "./tax-info.component";
import { UpdateLicenseComponent } from "./update-license.component";
@NgModule({
imports: [SharedModule, PaymentComponent, TaxInfoComponent],
declarations: [
AddCreditComponent,
AdjustPaymentComponent,
AdjustStorageComponent,
BillingHistoryComponent,
PaymentMethodComponent,
SecretsManagerSubscribeComponent,
UpdateLicenseComponent,
],
exports: [
SharedModule,
PaymentComponent,
TaxInfoComponent,
AdjustStorageComponent,
BillingHistoryComponent,
SecretsManagerSubscribeComponent,
UpdateLicenseComponent,
],
})
export class BillingSharedModule {}

View File

@@ -0,0 +1,5 @@
export * from "./billing-shared.module";
export * from "./payment-method.component";
export * from "./payment.component";
export * from "./sm-subscribe.component";
export * from "./tax-info.component";

View File

@@ -17,7 +17,6 @@ import { DialogService } from "@bitwarden/components";
import { TaxInfoComponent } from "./tax-info.component";
@Component({
selector: "app-payment-method",
templateUrl: "payment-method.component.html",
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil

View File

@@ -6,9 +6,13 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SharedModule } from "../../shared";
@Component({
selector: "app-payment",
templateUrl: "payment.component.html",
standalone: true,
imports: [SharedModule],
})
export class PaymentComponent implements OnInit, OnDestroy {
@Input() showMethods = true;

View File

@@ -40,7 +40,7 @@
<bit-form-control>
<input type="checkbox" bitCheckbox formControlName="enabled" />
<bit-label>{{ "addSecretsManager" | i18n }}</bit-label>
<bit-label>{{ "subscribeToSecretsManager" | i18n }}</bit-label>
<bit-hint *ngIf="upgradeOrganization">{{ "addSecretsManagerUpgradeDesc" | i18n }}</bit-hint>
</bit-form-control>

View File

@@ -7,7 +7,7 @@ import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.res
import { ProductType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SecretsManagerLogo } from "../../../layouts/secrets-manager-logo";
import { SecretsManagerLogo } from "../../layouts/secrets-manager-logo";
export interface SecretsManagerSubscription {
enabled: boolean;

View File

@@ -9,6 +9,8 @@ import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-i
import { TaxRateResponse } from "@bitwarden/common/billing/models/response/tax-rate.response";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SharedModule } from "../../shared";
type TaxInfoView = Omit<TaxInfoResponse, "taxIdType"> & {
includeTaxId: boolean;
[key: string]: unknown;
@@ -17,6 +19,8 @@ type TaxInfoView = Omit<TaxInfoResponse, "taxIdType"> & {
@Component({
selector: "app-tax-info",
templateUrl: "tax-info.component.html",
standalone: true,
imports: [SharedModule],
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class TaxInfoComponent {

View File

@@ -21,13 +21,11 @@ import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/p
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { PolicyListService } from "../admin-console/core/policy-list.service";
import { HtmlStorageService } from "../core/html-storage.service";
import { I18nService } from "../core/i18n.service";
import { CollectionAdminService } from "../vault/core/collection-admin.service";
import { PasswordRepromptService } from "../vault/core/password-reprompt.service";
import { BroadcasterMessagingService } from "./broadcaster-messaging.service";
import { EventService } from "./event.service";
@@ -87,10 +85,6 @@ import { WebPlatformUtilsService } from "./web-platform-utils.service";
provide: BaseStateServiceAbstraction,
useExisting: StateService,
},
{
provide: PasswordRepromptServiceAbstraction,
useClass: PasswordRepromptService,
},
{
provide: FileDownloadService,
useClass: WebFileDownloadService,

View File

@@ -47,9 +47,10 @@ export class ProductSwitcherContentComponent {
map(([orgs, paramMap]) => {
const routeOrg = orgs.find((o) => o.id === paramMap.get("organizationId"));
// If the active route org doesn't have access to SM, find the first org that does.
const smOrg = routeOrg?.canAccessSecretsManager
? routeOrg
: orgs.find((o) => o.canAccessSecretsManager);
const smOrg =
routeOrg?.canAccessSecretsManager && routeOrg?.enabled == true
? routeOrg
: orgs.find((o) => o.canAccessSecretsManager && o.enabled == true);
/**
* We can update this to the "satisfies" type upon upgrading to TypeScript 4.9

View File

@@ -131,5 +131,5 @@ OrgWithoutSecretsManager.args = {
export const OrgWithSecretsManager = Template.bind({});
OrgWithSecretsManager.args = {
mockOrgs: [{ id: "b", canAccessSecretsManager: true }],
mockOrgs: [{ id: "b", canAccessSecretsManager: true, enabled: true }],
};

View File

@@ -11,7 +11,6 @@ import {
import { canAccessFeature } from "@bitwarden/angular/guard/feature-flag.guard";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { SubscriptionRoutingModule } from "../app/billing/settings/subscription-routing.module";
import { flagEnabled, Flags } from "../utils/flags";
import { AcceptFamilySponsorshipComponent } from "./admin-console/organizations/sponsorships/accept-family-sponsorship.component";
@@ -223,7 +222,10 @@ const routes: Routes = [
},
{
path: "subscription",
loadChildren: () => SubscriptionRoutingModule,
loadChildren: () =>
import("./billing/individual/individual-billing.module").then(
(m) => m.IndividualBillingModule
),
},
{
path: "emergency-access",
@@ -254,11 +256,13 @@ const routes: Routes = [
children: [
{ path: "", pathMatch: "full", redirectTo: "generator" },
{
path: "",
path: "import",
loadChildren: () => import("./tools/import/import.module").then((m) => m.ImportModule),
},
{
path: "export",
loadChildren: () =>
import("./tools/import-export/import-export.module").then(
(m) => m.ImportExportModule
),
import("./tools/vault-export/export.module").then((m) => m.ExportModule),
},
{
path: "generator",

View File

@@ -1,6 +1,5 @@
import { NgModule } from "@angular/core";
import { OrganizationCreateModule } from "./admin-console/organizations/create/organization-create.module";
import { OrganizationUserModule } from "./admin-console/organizations/users/organization-user.module";
import { AuthModule } from "./auth";
import { LoginModule } from "./auth/login/login.module";
@@ -17,7 +16,6 @@ import { VaultFilterModule } from "./vault/individual-vault/vault-filter/vault-f
VaultFilterModule,
OrganizationBadgeModule,
OrganizationUserModule,
OrganizationCreateModule,
LoginModule,
AuthModule,
],

View File

@@ -3,9 +3,9 @@ import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
import { AddEditComponent } from "../../vault/individual-vault/add-edit.component";
import { AddEditComponent as OrgAddEditComponent } from "../../vault/org-vault/add-edit.component";

View File

@@ -4,9 +4,9 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
import { CipherReportComponent } from "./cipher-report.component";

View File

@@ -5,9 +5,9 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
import { CipherReportComponent } from "./cipher-report.component";

View File

@@ -4,9 +4,9 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
import { CipherReportComponent } from "./cipher-report.component";

View File

@@ -3,9 +3,9 @@ import { Component, OnInit } from "@angular/core";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PasswordRepromptService } from "@bitwarden/vault";
import { CipherReportComponent } from "./cipher-report.component";

View File

@@ -4,10 +4,10 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { PasswordRepromptService } from "@bitwarden/common/vault/abstractions/password-reprompt.service";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { BadgeTypes } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
import { CipherReportComponent } from "./cipher-report.component";

View File

@@ -3,12 +3,10 @@ import { NgModule } from "@angular/core";
import { PasswordCalloutComponent } from "@bitwarden/auth";
import { OrganizationSwitcherComponent } from "../admin-console/components/organization-switcher.component";
import { OrganizationCreateModule } from "../admin-console/organizations/create/organization-create.module";
import { OrganizationLayoutComponent } from "../admin-console/organizations/layouts/organization-layout.component";
import { EventsComponent as OrgEventsComponent } from "../admin-console/organizations/manage/events.component";
import { UserConfirmComponent as OrgUserConfirmComponent } from "../admin-console/organizations/manage/user-confirm.component";
import { AcceptFamilySponsorshipComponent } from "../admin-console/organizations/sponsorships/accept-family-sponsorship.component";
import { FamiliesForEnterpriseSetupComponent } from "../admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
import { ExposedPasswordsReportComponent as OrgExposedPasswordsReportComponent } from "../admin-console/organizations/tools/exposed-passwords-report.component";
import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "../admin-console/organizations/tools/inactive-two-factor-report.component";
import { ReusedPasswordsReportComponent as OrgReusedPasswordsReportComponent } from "../admin-console/organizations/tools/reused-passwords-report.component";
@@ -16,7 +14,6 @@ import { ToolsComponent as OrgToolsComponent } from "../admin-console/organizati
import { UnsecuredWebsitesReportComponent as OrgUnsecuredWebsitesReportComponent } from "../admin-console/organizations/tools/unsecured-websites-report.component";
import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from "../admin-console/organizations/tools/weak-passwords-report.component";
import { ProvidersComponent } from "../admin-console/providers/providers.component";
import { CreateOrganizationComponent } from "../admin-console/settings/create-organization.component";
import { SponsoredFamiliesComponent } from "../admin-console/settings/sponsored-families.component";
import { SponsoringOrgRowComponent } from "../admin-console/settings/sponsoring-org-row.component";
import { AuthModule } from "../auth";
@@ -53,19 +50,6 @@ import { UpdatePasswordComponent } from "../auth/update-password.component";
import { UpdateTempPasswordComponent } from "../auth/update-temp-password.component";
import { VerifyEmailTokenComponent } from "../auth/verify-email-token.component";
import { VerifyRecoverDeleteComponent } from "../auth/verify-recover-delete.component";
import { SecretsManagerBillingModule } from "../billing/organizations/secrets-manager/sm-billing.module";
import { AddCreditComponent } from "../billing/settings/add-credit.component";
import { AdjustPaymentComponent } from "../billing/settings/adjust-payment.component";
import { BillingHistoryViewComponent } from "../billing/settings/billing-history-view.component";
import { BillingHistoryComponent } from "../billing/settings/billing-history.component";
import { BillingSyncKeyComponent } from "../billing/settings/billing-sync-key.component";
import { OrganizationPlansComponent } from "../billing/settings/organization-plans.component";
import { PaymentMethodComponent } from "../billing/settings/payment-method.component";
import { PaymentComponent } from "../billing/settings/payment.component";
import { PremiumComponent } from "../billing/settings/premium.component";
import { SubscriptionComponent } from "../billing/settings/subscription.component";
import { TaxInfoComponent } from "../billing/settings/tax-info.component";
import { UserSubscriptionComponent } from "../billing/settings/user-subscription.component";
import { DynamicAvatarComponent } from "../components/dynamic-avatar.component";
import { SelectableAvatarComponent } from "../components/selectable-avatar.component";
import { FooterComponent } from "../layouts/footer.component";
@@ -74,7 +58,6 @@ import { NavbarComponent } from "../layouts/navbar.component";
import { ProductSwitcherModule } from "../layouts/product-switcher/product-switcher.module";
import { UserLayoutComponent } from "../layouts/user-layout.component";
import { AccountComponent } from "../settings/account.component";
import { AdjustStorageComponent } from "../settings/adjust-storage.component";
import { ApiKeyComponent } from "../settings/api-key.component";
import { ChangeAvatarComponent } from "../settings/change-avatar.component";
import { ChangeEmailComponent } from "../settings/change-email.component";
@@ -88,14 +71,12 @@ import { PurgeVaultComponent } from "../settings/purge-vault.component";
import { SecurityKeysComponent } from "../settings/security-keys.component";
import { SecurityComponent } from "../settings/security.component";
import { SettingsComponent } from "../settings/settings.component";
import { UpdateLicenseComponent } from "../settings/update-license.component";
import { VaultTimeoutInputComponent } from "../settings/vault-timeout-input.component";
import { GeneratorComponent } from "../tools/generator.component";
import { PasswordGeneratorHistoryComponent } from "../tools/password-generator-history.component";
import { AccessComponent } from "../tools/send/access.component";
import { AddEditComponent as SendAddEditComponent } from "../tools/send/add-edit.component";
import { ToolsComponent } from "../tools/tools.component";
import { PasswordRepromptComponent } from "../vault/components/password-reprompt.component";
import { PremiumBadgeComponent } from "../vault/components/premium-badge.component";
import { AddEditCustomFieldsComponent } from "../vault/individual-vault/add-edit-custom-fields.component";
import { AddEditComponent } from "../vault/individual-vault/add-edit.component";
@@ -116,7 +97,6 @@ import { SharedModule } from "./shared.module";
@NgModule({
imports: [
SharedModule,
OrganizationCreateModule,
RegisterFormModule,
ProductSwitcherModule,
UserVerificationModule,
@@ -128,9 +108,6 @@ import { SharedModule } from "./shared.module";
AuthModule,
EnvironmentSelectorModule,
AccountFingerprintComponent,
// To be removed when OrganizationPlansComponent is moved to its own module (see AC-1453)
SecretsManagerBillingModule,
PasswordCalloutComponent,
],
declarations: [
@@ -138,18 +115,13 @@ import { SharedModule } from "./shared.module";
AcceptOrganizationComponent,
AccessComponent,
AccountComponent,
AddCreditComponent,
AddEditComponent,
AddEditCustomFieldsComponent,
AddEditCustomFieldsComponent,
AdjustPaymentComponent,
AdjustStorageComponent,
ApiKeyComponent,
AttachmentsComponent,
BillingSyncKeyComponent,
ChangeEmailComponent,
CollectionsComponent,
CreateOrganizationComponent,
DeauthorizeSessionsComponent,
DeleteAccountComponent,
DomainRulesComponent,
@@ -160,7 +132,6 @@ import { SharedModule } from "./shared.module";
EmergencyAccessTakeoverComponent,
EmergencyAccessViewComponent,
EmergencyAddEditComponent,
FamiliesForEnterpriseSetupComponent,
FolderAddEditComponent,
FooterComponent,
FrontendLayoutComponent,
@@ -170,7 +141,6 @@ import { SharedModule } from "./shared.module";
OrganizationSwitcherComponent,
OrgAddEditComponent,
OrganizationLayoutComponent,
OrganizationPlansComponent,
OrgAttachmentsComponent,
OrgCollectionsComponent,
OrgEventsComponent,
@@ -183,12 +153,8 @@ import { SharedModule } from "./shared.module";
OrgWeakPasswordsReportComponent,
GeneratorComponent,
PasswordGeneratorHistoryComponent,
PasswordRepromptComponent,
PaymentComponent,
PaymentMethodComponent,
PreferencesComponent,
PremiumBadgeComponent,
PremiumComponent,
ProfileComponent,
ChangeAvatarComponent,
ProvidersComponent,
@@ -206,8 +172,6 @@ import { SharedModule } from "./shared.module";
SponsoredFamiliesComponent,
SponsoringOrgRowComponent,
SsoComponent,
SubscriptionComponent,
TaxInfoComponent,
ToolsComponent,
TwoFactorAuthenticatorComponent,
TwoFactorComponent,
@@ -219,13 +183,9 @@ import { SharedModule } from "./shared.module";
TwoFactorVerifyComponent,
TwoFactorWebAuthnComponent,
TwoFactorYubiKeyComponent,
UpdateLicenseComponent,
UpdatePasswordComponent,
UpdateTempPasswordComponent,
BillingHistoryComponent,
BillingHistoryViewComponent,
UserLayoutComponent,
UserSubscriptionComponent,
VaultTimeoutInputComponent,
VerifyEmailComponent,
VerifyEmailTokenComponent,
@@ -238,17 +198,13 @@ import { SharedModule } from "./shared.module";
AcceptOrganizationComponent,
AccessComponent,
AccountComponent,
AddCreditComponent,
AddEditComponent,
AddEditCustomFieldsComponent,
AddEditCustomFieldsComponent,
AdjustPaymentComponent,
AdjustStorageComponent,
ApiKeyComponent,
AttachmentsComponent,
ChangeEmailComponent,
CollectionsComponent,
CreateOrganizationComponent,
DeauthorizeSessionsComponent,
DeleteAccountComponent,
DomainRulesComponent,
@@ -260,7 +216,6 @@ import { SharedModule } from "./shared.module";
EmergencyAccessTakeoverComponent,
EmergencyAccessViewComponent,
EmergencyAddEditComponent,
FamiliesForEnterpriseSetupComponent,
FolderAddEditComponent,
FooterComponent,
FrontendLayoutComponent,
@@ -270,7 +225,6 @@ import { SharedModule } from "./shared.module";
OrganizationSwitcherComponent,
OrgAddEditComponent,
OrganizationLayoutComponent,
OrganizationPlansComponent,
OrgAttachmentsComponent,
OrgCollectionsComponent,
OrgEventsComponent,
@@ -283,12 +237,8 @@ import { SharedModule } from "./shared.module";
OrgWeakPasswordsReportComponent,
GeneratorComponent,
PasswordGeneratorHistoryComponent,
PasswordRepromptComponent,
PaymentComponent,
PaymentMethodComponent,
PreferencesComponent,
PremiumBadgeComponent,
PremiumComponent,
ProfileComponent,
ChangeAvatarComponent,
ProvidersComponent,
@@ -306,8 +256,6 @@ import { SharedModule } from "./shared.module";
SponsoredFamiliesComponent,
SponsoringOrgRowComponent,
SsoComponent,
SubscriptionComponent,
TaxInfoComponent,
ToolsComponent,
TwoFactorAuthenticatorComponent,
TwoFactorComponent,
@@ -319,13 +267,9 @@ import { SharedModule } from "./shared.module";
TwoFactorVerifyComponent,
TwoFactorWebAuthnComponent,
TwoFactorYubiKeyComponent,
UpdateLicenseComponent,
UpdatePasswordComponent,
UpdateTempPasswordComponent,
BillingHistoryComponent,
BillingHistoryViewComponent,
UserLayoutComponent,
UserSubscriptionComponent,
VaultTimeoutInputComponent,
VerifyEmailComponent,
VerifyEmailTokenComponent,

Some files were not shown because too many files have changed in this diff Show More