1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[AC-2156] Billing State Provider Migration (#8133)

* Added billing account profile state service

* Update usages after removing state service functions

* Added migrator

* Updated bw.ts and main.background.ts

* Removed comment

* Updated state service dependencies to include billing service

* Added missing mv3 factory and updated MainContextMenuHandler

* updated autofill service and tests

* Updated the remaining extensions usages

* Updated desktop

* Removed subjects where they weren't needed

* Refactored billing service to have a single setter to avoid unecessary emissions

* Refactored has premium guard to return an observable

* Renamed services to match ADR

f633f2cdd8/docs/architecture/clients/presentation/angular.md (abstract--default-implementations)

* Updated property names to be a smidgen more descriptive and added jsdocs

* Updated setting of canAccessPremium to automatically update when the underlying observable emits

* Fixed build error after merge conflicts

* Another build error from conflict

* Removed autofill unit test changes from conflict

* Updated login strategy to not set premium field using state service

* Updated CLI to use billing state provider

* Shortened names a bit

* Fixed build
This commit is contained in:
Conner Turnbull
2024-03-15 15:53:05 -04:00
committed by GitHub
parent 65534a1323
commit b99153a016
85 changed files with 942 additions and 261 deletions

View File

@@ -41,6 +41,8 @@ import {
DefaultDomainSettingsService,
DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
import { ClientType } from "@bitwarden/common/enums";
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
@@ -221,6 +223,7 @@ export class Main {
avatarService: AvatarServiceAbstraction;
stateEventRunnerService: StateEventRunnerService;
biometricStateService: BiometricStateService;
billingAccountProfileStateService: BillingAccountProfileStateService;
constructor() {
let p = null;
@@ -456,6 +459,10 @@ export class Main {
this.stateService,
);
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
this.activeUserStateProvider,
);
this.loginStrategyService = new LoginStrategyService(
this.cryptoService,
this.apiService,
@@ -475,6 +482,7 @@ export class Main {
this.deviceTrustCryptoService,
this.authRequestService,
this.globalStateProvider,
this.billingAccountProfileStateService,
);
this.authService = new AuthService(
@@ -586,6 +594,7 @@ export class Main {
this.sendApiService,
this.avatarService,
async (expired: boolean) => await this.logout(),
this.billingAccountProfileStateService,
);
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);

View File

@@ -1,9 +1,12 @@
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EventType } from "@bitwarden/common/enums";
import { CardExport } from "@bitwarden/common/models/export/card.export";
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
@@ -57,6 +60,7 @@ export class GetCommand extends DownloadCommand {
private apiService: ApiService,
private organizationService: OrganizationService,
private eventCollectionService: EventCollectionService,
private accountProfileService: BillingAccountProfileStateService,
) {
super(cryptoService);
}
@@ -251,7 +255,9 @@ export class GetCommand extends DownloadCommand {
return Response.error("Couldn't generate TOTP code.");
}
const canAccessPremium = await this.stateService.getCanAccessPremium();
const canAccessPremium = await firstValueFrom(
this.accountProfileService.hasPremiumFromAnySource$,
);
if (!canAccessPremium) {
const originalCipher = await this.cipherService.get(cipher.id);
if (
@@ -334,7 +340,10 @@ export class GetCommand extends DownloadCommand {
return Response.multipleResults(attachments.map((a) => a.id));
}
if (!(await this.stateService.getCanAccessPremium())) {
const canAccessPremium = await firstValueFrom(
this.accountProfileService.hasPremiumFromAnySource$,
);
if (!canAccessPremium) {
const originalCipher = await this.cipherService.get(cipher.id);
if (originalCipher == null || originalCipher.organizationId == null) {
return Response.error("Premium status is required to use this feature.");

View File

@@ -68,6 +68,7 @@ export class ServeCommand {
this.main.apiService,
this.main.organizationService,
this.main.eventCollectionService,
this.main.billingAccountProfileStateService,
);
this.listCommand = new ListCommand(
this.main.cipherService,
@@ -82,10 +83,10 @@ export class ServeCommand {
this.createCommand = new CreateCommand(
this.main.cipherService,
this.main.folderService,
this.main.stateService,
this.main.cryptoService,
this.main.apiService,
this.main.folderApiService,
this.main.billingAccountProfileStateService,
);
this.editCommand = new EditCommand(
this.main.cipherService,
@@ -108,9 +109,9 @@ export class ServeCommand {
this.deleteCommand = new DeleteCommand(
this.main.cipherService,
this.main.folderService,
this.main.stateService,
this.main.apiService,
this.main.folderApiService,
this.main.billingAccountProfileStateService,
);
this.confirmCommand = new ConfirmCommand(
this.main.apiService,
@@ -135,9 +136,9 @@ export class ServeCommand {
this.sendCreateCommand = new SendCreateCommand(
this.main.sendService,
this.main.stateService,
this.main.environmentService,
this.main.sendApiService,
this.main.billingAccountProfileStateService,
);
this.sendDeleteCommand = new SendDeleteCommand(this.main.sendService, this.main.sendApiService);
this.sendGetCommand = new SendGetCommand(
@@ -148,9 +149,9 @@ export class ServeCommand {
);
this.sendEditCommand = new SendEditCommand(
this.main.sendService,
this.main.stateService,
this.sendGetCommand,
this.main.sendApiService,
this.main.billingAccountProfileStateService,
);
this.sendListCommand = new SendListCommand(
this.main.sendService,

View File

@@ -1,8 +1,10 @@
import * as fs from "fs";
import * as path from "path";
import { firstValueFrom } from "rxjs";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
@@ -16,9 +18,9 @@ import { SendResponse } from "../models/send.response";
export class SendCreateCommand {
constructor(
private sendService: SendService,
private stateService: StateService,
private environmentService: EnvironmentService,
private sendApiService: SendApiService,
private accountProfileService: BillingAccountProfileStateService,
) {}
async run(requestJson: any, cmdOptions: Record<string, any>) {
@@ -82,7 +84,7 @@ export class SendCreateCommand {
);
}
if (!(await this.stateService.getCanAccessPremium())) {
if (!(await firstValueFrom(this.accountProfileService.hasPremiumFromAnySource$))) {
return Response.error("Premium status is required to use this feature.");
}

View File

@@ -1,4 +1,6 @@
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { firstValueFrom } from "rxjs";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
@@ -12,9 +14,9 @@ import { SendGetCommand } from "./get.command";
export class SendEditCommand {
constructor(
private sendService: SendService,
private stateService: StateService,
private getCommand: SendGetCommand,
private sendApiService: SendApiService,
private accountProfileService: BillingAccountProfileStateService,
) {}
async run(requestJson: string, cmdOptions: Record<string, any>): Promise<Response> {
@@ -57,7 +59,10 @@ export class SendEditCommand {
return Response.badRequest("Cannot change a Send's type");
}
if (send.type === SendType.File && !(await this.stateService.getCanAccessPremium())) {
const canAccessPremium = await firstValueFrom(
this.accountProfileService.hasPremiumFromAnySource$,
);
if (send.type === SendType.File && !canAccessPremium) {
return Response.error("Premium status is required to use this feature.");
}

View File

@@ -153,6 +153,7 @@ export class SendProgram extends Program {
this.main.apiService,
this.main.organizationService,
this.main.eventCollectionService,
this.main.billingAccountProfileStateService,
);
const response = await cmd.run("template", object, null);
this.processResponse(response);
@@ -253,9 +254,9 @@ export class SendProgram extends Program {
);
const cmd = new SendEditCommand(
this.main.sendService,
this.main.stateService,
getCmd,
this.main.sendApiService,
this.main.billingAccountProfileStateService,
);
const response = await cmd.run(encodedJson, options);
this.processResponse(response);
@@ -323,9 +324,9 @@ export class SendProgram extends Program {
await this.exitIfLocked();
const cmd = new SendCreateCommand(
this.main.sendService,
this.main.stateService,
this.main.environmentService,
this.main.sendApiService,
this.main.billingAccountProfileStateService,
);
return await cmd.run(encodedJson, options);
}

View File

@@ -188,6 +188,7 @@ export class VaultProgram extends Program {
this.main.apiService,
this.main.organizationService,
this.main.eventCollectionService,
this.main.billingAccountProfileStateService,
);
const response = await command.run(object, id, cmd);
this.processResponse(response);
@@ -226,10 +227,10 @@ export class VaultProgram extends Program {
const command = new CreateCommand(
this.main.cipherService,
this.main.folderService,
this.main.stateService,
this.main.cryptoService,
this.main.apiService,
this.main.folderApiService,
this.main.billingAccountProfileStateService,
);
const response = await command.run(object, encodedJson, cmd);
this.processResponse(response);
@@ -313,9 +314,9 @@ export class VaultProgram extends Program {
const command = new DeleteCommand(
this.main.cipherService,
this.main.folderService,
this.main.stateService,
this.main.apiService,
this.main.folderApiService,
this.main.billingAccountProfileStateService,
);
const response = await command.run(object, id, cmd);
this.processResponse(response);

View File

@@ -1,13 +1,15 @@
import * as fs from "fs";
import * as path from "path";
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
import { FolderExport } from "@bitwarden/common/models/export/folder.export";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
@@ -26,10 +28,10 @@ export class CreateCommand {
constructor(
private cipherService: CipherService,
private folderService: FolderService,
private stateService: StateService,
private cryptoService: CryptoService,
private apiService: ApiService,
private folderApiService: FolderApiServiceAbstraction,
private accountProfileService: BillingAccountProfileStateService,
) {}
async run(
@@ -124,7 +126,10 @@ export class CreateCommand {
return Response.notFound();
}
if (cipher.organizationId == null && !(await this.stateService.getCanAccessPremium())) {
if (
cipher.organizationId == null &&
!(await firstValueFrom(this.accountProfileService.hasPremiumFromAnySource$))
) {
return Response.error("Premium status is required to use this feature.");
}

View File

@@ -1,5 +1,7 @@
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
@@ -12,9 +14,9 @@ export class DeleteCommand {
constructor(
private cipherService: CipherService,
private folderService: FolderService,
private stateService: StateService,
private apiService: ApiService,
private folderApiService: FolderApiServiceAbstraction,
private accountProfileService: BillingAccountProfileStateService,
) {}
async run(object: string, id: string, cmdOptions: Record<string, any>): Promise<Response> {
@@ -75,7 +77,10 @@ export class DeleteCommand {
return Response.error("Attachment `" + id + "` was not found.");
}
if (cipher.organizationId == null && !(await this.stateService.getCanAccessPremium())) {
const canAccessPremium = await firstValueFrom(
this.accountProfileService.hasPremiumFromAnySource$,
);
if (cipher.organizationId == null && !canAccessPremium) {
return Response.error("Premium status is required to use this feature.");
}