1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-02-09 00:11:30 +01:00

Merge branch 'main' into autofill/pm-6546-blurring-of-autofilled-elements-causes-problems-in-blur-event-listeners

This commit is contained in:
Cesar Gonzalez 2024-03-19 10:55:52 -05:00 committed by GitHub
commit 57464c41f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
222 changed files with 7097 additions and 1676 deletions

72
.github/workflows/scan.yml vendored Normal file
View File

@ -0,0 +1,72 @@
name: Scan
on:
workflow_dispatch:
push:
branches:
- "main"
- "rc"
- "hotfix-rc"
pull_request_target:
types: [opened, synchronize]
permissions: read-all
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
sast:
name: SAST scan
runs-on: ubuntu-22.04
needs: check-run
permissions:
security-events: write
steps:
- name: Check out repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with Checkmarx
uses: checkmarx/ast-github-action@749fec53e0db0f6404a97e2e0807c3e80e3583a7 #2.0.23
env:
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
with:
project_name: ${{ github.repository }}
cx_tenant: ${{ secrets.CHECKMARX_TENANT }}
base_uri: https://ast.checkmarx.net/
cx_client_id: ${{ secrets.CHECKMARX_CLIENT_ID }}
cx_client_secret: ${{ secrets.CHECKMARX_SECRET }}
additional_params: --report-format sarif --output-path . ${{ env.INCREMENTAL }}
- name: Upload Checkmarx results to GitHub
uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
with:
sarif_file: cx_result.sarif
quality:
name: Quality scan
runs-on: ubuntu-22.04
needs: check-run
steps:
- name: Check out repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with SonarCloud
uses: sonarsource/sonarcloud-github-action@49e6cd3b187936a73b8280d59ffd9da69df63ec9 # v2.1.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: >
-Dsonar.organization=${{ github.repository_owner }}
-Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }}
-Dsonar.test.inclusions=**/*.spec.ts
-Dsonar.tests=.

View File

@ -1,4 +1,5 @@
import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
import {
FactoryOptions,
@ -6,11 +7,7 @@ import {
factory,
} from "../../../platform/background/service-factories/factory-options";
import { stateProviderFactory } from "../../../platform/background/service-factories/state-provider.factory";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../../platform/background/service-factories/state-service.factory";
import { BrowserOrganizationService } from "../../services/browser-organization.service";
import { StateServiceInitOptions } from "../../../platform/background/service-factories/state-service.factory";
type OrganizationServiceFactoryOptions = FactoryOptions;
@ -25,10 +22,6 @@ export function organizationServiceFactory(
cache,
"organizationService",
opts,
async () =>
new BrowserOrganizationService(
await stateServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
),
async () => new OrganizationService(await stateProviderFactory(cache, opts)),
);
}

View File

@ -1,12 +0,0 @@
import { BehaviorSubject } from "rxjs";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
import { browserSession, sessionSync } from "../../platform/decorators/session-sync-observable";
@browserSession
export class BrowserOrganizationService extends OrganizationService {
@sessionSync({ initializer: Organization.fromJSON, initializeAs: "array" })
protected _organizations: BehaviorSubject<Organization[]>;
}

View File

@ -9,6 +9,7 @@ import {
ApiServiceInitOptions,
} from "../../../platform/background/service-factories/api-service.factory";
import { appIdServiceFactory } from "../../../platform/background/service-factories/app-id-service.factory";
import { billingAccountProfileStateServiceFactory } from "../../../platform/background/service-factories/billing-account-profile-state-service.factory";
import {
CryptoServiceInitOptions,
cryptoServiceFactory,
@ -119,6 +120,7 @@ export function loginStrategyServiceFactory(
await deviceTrustCryptoServiceFactory(cache, opts),
await authRequestServiceFactory(cache, opts),
await globalStateProviderFactory(cache, opts),
await billingAccountProfileStateServiceFactory(cache, opts),
),
);
}

View File

@ -7,13 +7,29 @@ import {
factory,
} from "../../../platform/background/service-factories/factory-options";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../../platform/background/service-factories/state-service.factory";
GlobalStateProviderInitOptions,
globalStateProviderFactory,
} from "../../../platform/background/service-factories/global-state-provider.factory";
import {
PlatformUtilsServiceInitOptions,
platformUtilsServiceFactory,
} from "../../../platform/background/service-factories/platform-utils-service.factory";
import {
SingleUserStateProviderInitOptions,
singleUserStateProviderFactory,
} from "../../../platform/background/service-factories/single-user-state-provider.factory";
import {
SecureStorageServiceInitOptions,
secureStorageServiceFactory,
} from "../../../platform/background/service-factories/storage-service.factory";
type TokenServiceFactoryOptions = FactoryOptions;
export type TokenServiceInitOptions = TokenServiceFactoryOptions & StateServiceInitOptions;
export type TokenServiceInitOptions = TokenServiceFactoryOptions &
SingleUserStateProviderInitOptions &
GlobalStateProviderInitOptions &
PlatformUtilsServiceInitOptions &
SecureStorageServiceInitOptions;
export function tokenServiceFactory(
cache: { tokenService?: AbstractTokenService } & CachedServices,
@ -23,6 +39,12 @@ export function tokenServiceFactory(
cache,
"tokenService",
opts,
async () => new TokenService(await stateServiceFactory(cache, opts)),
async () =>
new TokenService(
await singleUserStateProviderFactory(cache, opts),
await globalStateProviderFactory(cache, opts),
(await platformUtilsServiceFactory(cache, opts)).supportsSecureStorage(),
await secureStorageServiceFactory(cache, opts),
),
);
}

View File

@ -91,6 +91,8 @@ export class LoginComponent extends BaseLoginComponent {
}
async launchSsoBrowser() {
// Save off email for SSO
await this.ssoLoginService.setSsoEmail(this.formGroup.value.email);
await this.loginService.saveEmailSettings();
// Generate necessary sso params
const passwordOptions: any = {

View File

@ -8,11 +8,21 @@ import {
AutofillOverlayVisibility,
} from "@bitwarden/common/autofill/constants";
import { AutofillSettingsService } from "@bitwarden/common/autofill/services/autofill-settings.service";
import {
DefaultDomainSettingsService,
DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
import { I18nService } from "@bitwarden/common/platform/services/i18n.service";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { SettingsService } from "@bitwarden/common/services/settings.service";
import {
FakeStateProvider,
FakeAccountService,
mockAccountServiceWith,
} from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@ -41,6 +51,10 @@ import OverlayBackground from "./overlay.background";
const iconServerUrl = "https://icons.bitwarden.com/";
describe("OverlayBackground", () => {
const mockUserId = Utils.newGuid() as UserId;
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
const fakeStateProvider: FakeStateProvider = new FakeStateProvider(accountService);
let domainSettingsService: DomainSettingsService;
let buttonPortSpy: chrome.runtime.Port;
let listPortSpy: chrome.runtime.Port;
let overlayBackground: OverlayBackground;
@ -50,7 +64,6 @@ describe("OverlayBackground", () => {
const environmentService = mock<EnvironmentService>({
getIconsUrl: () => iconServerUrl,
});
const settingsService = mock<SettingsService>();
const stateService = mock<BrowserStateService>();
const autofillSettingsService = mock<AutofillSettingsService>();
const i18nService = mock<I18nService>();
@ -72,12 +85,13 @@ describe("OverlayBackground", () => {
};
beforeEach(() => {
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider);
overlayBackground = new OverlayBackground(
cipherService,
autofillService,
authService,
environmentService,
settingsService,
domainSettingsService,
stateService,
autofillSettingsService,
i18nService,
@ -90,6 +104,7 @@ describe("OverlayBackground", () => {
.mockResolvedValue(AutofillOverlayVisibility.OnFieldFocus);
themeStateService.selectedTheme$ = of(ThemeType.Light);
domainSettingsService.showFavicons$ = of(true);
void overlayBackground.init();
});
@ -274,7 +289,7 @@ describe("OverlayBackground", () => {
card: { subTitle: "Mastercard, *1234" },
});
it("formats and returns the cipher data", () => {
it("formats and returns the cipher data", async () => {
overlayBackground["overlayLoginCiphers"] = new Map([
["overlay-cipher-0", cipher2],
["overlay-cipher-1", cipher1],
@ -282,7 +297,7 @@ describe("OverlayBackground", () => {
["overlay-cipher-3", cipher4],
]);
const overlayCipherData = overlayBackground["getOverlayCipherData"]();
const overlayCipherData = await overlayBackground["getOverlayCipherData"]();
expect(overlayCipherData).toStrictEqual([
{

View File

@ -1,10 +1,10 @@
import { firstValueFrom } from "rxjs";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { SHOW_AUTOFILL_BUTTON } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -92,7 +92,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
private autofillService: AutofillService,
private authService: AuthService,
private environmentService: EnvironmentService,
private settingsService: SettingsService,
private domainSettingsService: DomainSettingsService,
private stateService: StateService,
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private i18nService: I18nService,
@ -145,7 +145,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
this.overlayLoginCiphers.set(`overlay-cipher-${cipherIndex}`, ciphersViews[cipherIndex]);
}
const ciphers = this.getOverlayCipherData();
const ciphers = await this.getOverlayCipherData();
this.overlayListPort?.postMessage({ command: "updateOverlayListCiphers", ciphers });
await BrowserApi.tabSendMessageData(currentTab, "updateIsOverlayCiphersPopulated", {
isOverlayCiphersPopulated: Boolean(ciphers.length),
@ -156,8 +156,8 @@ class OverlayBackground implements OverlayBackgroundInterface {
* Strips out unnecessary data from the ciphers and returns an array of
* objects that contain the cipher data needed for the overlay list.
*/
private getOverlayCipherData(): OverlayCipherData[] {
const isFaviconDisabled = this.settingsService.getDisableFavicon();
private async getOverlayCipherData(): Promise<OverlayCipherData[]> {
const showFavicons = await firstValueFrom(this.domainSettingsService.showFavicons$);
const overlayCiphersArray = Array.from(this.overlayLoginCiphers);
const overlayCipherData = [];
let loginCipherIcon: WebsiteIconData;
@ -165,7 +165,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
for (let cipherIndex = 0; cipherIndex < overlayCiphersArray.length; cipherIndex++) {
const [overlayCipherId, cipher] = overlayCiphersArray[cipherIndex];
if (!loginCipherIcon && cipher.type === CipherType.Login) {
loginCipherIcon = buildCipherIcon(this.iconsServerUrl, cipher, isFaviconDisabled);
loginCipherIcon = buildCipherIcon(this.iconsServerUrl, cipher, showFavicons);
}
overlayCipherData.push({
@ -177,7 +177,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
icon:
cipher.type === CipherType.Login
? loginCipherIcon
: buildCipherIcon(this.iconsServerUrl, cipher, isFaviconDisabled),
: buildCipherIcon(this.iconsServerUrl, cipher, showFavicons),
login: cipher.type === CipherType.Login ? { username: cipher.login.username } : null,
card: cipher.type === CipherType.Card ? cipher.card.subTitle : null,
});
@ -699,7 +699,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
styleSheetUrl: chrome.runtime.getURL(`overlay/${isOverlayListPort ? "list" : "button"}.css`),
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
translations: this.getTranslations(),
ciphers: isOverlayListPort ? this.getOverlayCipherData() : null,
ciphers: isOverlayListPort ? await this.getOverlayCipherData() : null,
});
this.updateOverlayPosition({
overlayElement: isOverlayListPort

View File

@ -6,6 +6,7 @@ import {
EventCollectionServiceInitOptions,
eventCollectionServiceFactory,
} from "../../../background/service-factories/event-collection-service.factory";
import { billingAccountProfileStateServiceFactory } from "../../../platform/background/service-factories/billing-account-profile-state-service.factory";
import {
CachedServices,
factory,
@ -69,6 +70,7 @@ export function autofillServiceFactory(
await logServiceFactory(cache, opts),
await domainSettingsServiceFactory(cache, opts),
await userVerificationServiceFactory(cache, opts),
await billingAccountProfileStateServiceFactory(cache, opts),
),
);
}

View File

@ -3,6 +3,7 @@ import { of } from "rxjs";
import { NOOP_COMMAND_SUFFIX } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { CipherType } from "@bitwarden/common/vault/enums";
@ -18,6 +19,7 @@ describe("context-menu", () => {
let autofillSettingsService: MockProxy<AutofillSettingsServiceAbstraction>;
let i18nService: MockProxy<I18nService>;
let logService: MockProxy<LogService>;
let billingAccountProfileStateService: MockProxy<BillingAccountProfileStateService>;
let removeAllSpy: jest.SpyInstance<void, [callback?: () => void]>;
let createSpy: jest.SpyInstance<
@ -32,6 +34,7 @@ describe("context-menu", () => {
autofillSettingsService = mock();
i18nService = mock();
logService = mock();
billingAccountProfileStateService = mock();
removeAllSpy = jest
.spyOn(chrome.contextMenus, "removeAll")
@ -50,6 +53,7 @@ describe("context-menu", () => {
autofillSettingsService,
i18nService,
logService,
billingAccountProfileStateService,
);
autofillSettingsService.enableContextMenu$ = of(true);
});
@ -66,7 +70,7 @@ describe("context-menu", () => {
});
it("has menu enabled, but does not have premium", async () => {
stateService.getCanAccessPremium.mockResolvedValue(false);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(false);
const createdMenu = await sut.init();
expect(createdMenu).toBeTruthy();
@ -74,7 +78,7 @@ describe("context-menu", () => {
});
it("has menu enabled and has premium", async () => {
stateService.getCanAccessPremium.mockResolvedValue(true);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
const createdMenu = await sut.init();
expect(createdMenu).toBeTruthy();
@ -128,7 +132,7 @@ describe("context-menu", () => {
});
it("create entry for each cipher piece", async () => {
stateService.getCanAccessPremium.mockResolvedValue(true);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
await sut.loadOptions("TEST_TITLE", "1", createCipher());
@ -137,7 +141,7 @@ describe("context-menu", () => {
});
it("creates a login/unlock item for each context menu action option when user is not authenticated", async () => {
stateService.getCanAccessPremium.mockResolvedValue(true);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
await sut.loadOptions("TEST_TITLE", "NOOP");

View File

@ -17,6 +17,7 @@ import {
SEPARATOR_ID,
} from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
@ -27,6 +28,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { autofillSettingsServiceFactory } from "../../autofill/background/service_factories/autofill-settings-service.factory";
import { Account } from "../../models/account";
import { billingAccountProfileStateServiceFactory } from "../../platform/background/service-factories/billing-account-profile-state-service.factory";
import { CachedServices } from "../../platform/background/service-factories/factory-options";
import {
i18nServiceFactory,
@ -163,6 +165,7 @@ export class MainContextMenuHandler {
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private i18nService: I18nService,
private logService: LogService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {}
static async mv3Create(cachedServices: CachedServices) {
@ -184,6 +187,11 @@ export class MainContextMenuHandler {
stateServiceOptions: {
stateFactory: stateFactory,
},
platformUtilsServiceOptions: {
clipboardWriteCallback: () => Promise.resolve(),
biometricCallback: () => Promise.resolve(false),
win: self,
},
};
return new MainContextMenuHandler(
@ -191,6 +199,7 @@ export class MainContextMenuHandler {
await autofillSettingsServiceFactory(cachedServices, serviceOptions),
await i18nServiceFactory(cachedServices, serviceOptions),
await logServiceFactory(cachedServices, serviceOptions),
await billingAccountProfileStateServiceFactory(cachedServices, serviceOptions),
);
}
@ -212,7 +221,10 @@ export class MainContextMenuHandler {
try {
for (const options of this.initContextMenuItems) {
if (options.checkPremiumAccess && !(await this.stateService.getCanAccessPremium())) {
if (
options.checkPremiumAccess &&
!(await firstValueFrom(this.billingAccountProfileStateService.hasPremiumFromAnySource$))
) {
continue;
}
@ -307,7 +319,9 @@ export class MainContextMenuHandler {
await createChildItem(COPY_USERNAME_ID);
}
const canAccessPremium = await this.stateService.getCanAccessPremium();
const canAccessPremium = await firstValueFrom(
this.billingAccountProfileStateService.hasPremiumFromAnySource$,
);
if (canAccessPremium && (!cipher || !Utils.isNullOrEmpty(cipher.login?.totp))) {
await createChildItem(COPY_VERIFICATION_CODE_ID);
}

View File

@ -8,6 +8,7 @@ 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 { EventType } from "@bitwarden/common/enums";
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@ -72,6 +73,7 @@ describe("AutofillService", () => {
const eventCollectionService = mock<EventCollectionService>();
const logService = mock<LogService>();
const userVerificationService = mock<UserVerificationService>();
const billingAccountProfileStateService = mock<BillingAccountProfileStateService>();
beforeEach(() => {
autofillService = new AutofillService(
@ -83,6 +85,7 @@ describe("AutofillService", () => {
logService,
domainSettingsService,
userVerificationService,
billingAccountProfileStateService,
);
domainSettingsService = new DefaultDomainSettingsService(fakeStateProvider);
@ -476,6 +479,7 @@ describe("AutofillService", () => {
it("throws an error if an autofill did not occur for any of the passed pages", async () => {
autofillOptions.tab.url = "https://a-different-url.com";
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
try {
await autofillService.doAutoFill(autofillOptions);
@ -487,7 +491,6 @@ describe("AutofillService", () => {
});
it("will autofill login data for a page", async () => {
jest.spyOn(stateService, "getCanAccessPremium");
jest.spyOn(autofillService as any, "generateFillScript");
jest.spyOn(autofillService as any, "generateLoginFillScript");
jest.spyOn(logService, "info");
@ -497,8 +500,6 @@ describe("AutofillService", () => {
const autofillResult = await autofillService.doAutoFill(autofillOptions);
const currentAutofillPageDetails = autofillOptions.pageDetails[0];
expect(stateService.getCanAccessPremium).toHaveBeenCalled();
expect(autofillService["getDefaultUriMatchStrategy"]).toHaveBeenCalled();
expect(autofillService["generateFillScript"]).toHaveBeenCalledWith(
currentAutofillPageDetails.details,
{
@ -660,7 +661,7 @@ describe("AutofillService", () => {
it("returns a TOTP value", async () => {
const totpCode = "123456";
autofillOptions.cipher.login.totp = "totp";
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValue(true);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
jest.spyOn(autofillService, "getShouldAutoCopyTotp").mockResolvedValue(true);
jest.spyOn(totpService, "getCode").mockResolvedValue(totpCode);
@ -673,7 +674,7 @@ describe("AutofillService", () => {
it("does not return a TOTP value if the user does not have premium features", async () => {
autofillOptions.cipher.login.totp = "totp";
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValue(false);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(false);
jest.spyOn(autofillService, "getShouldAutoCopyTotp").mockResolvedValue(true);
const autofillResult = await autofillService.doAutoFill(autofillOptions);
@ -707,7 +708,7 @@ describe("AutofillService", () => {
it("returns a null value if the user cannot access premium and the organization does not use TOTP", async () => {
autofillOptions.cipher.login.totp = "totp";
autofillOptions.cipher.organizationUseTotp = false;
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValueOnce(false);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(false);
const autofillResult = await autofillService.doAutoFill(autofillOptions);
@ -717,13 +718,12 @@ describe("AutofillService", () => {
it("returns a null value if the user has disabled `auto TOTP copy`", async () => {
autofillOptions.cipher.login.totp = "totp";
autofillOptions.cipher.organizationUseTotp = true;
jest.spyOn(stateService, "getCanAccessPremium").mockResolvedValue(true);
billingAccountProfileStateService.hasPremiumFromAnySource$ = of(true);
jest.spyOn(autofillService, "getShouldAutoCopyTotp").mockResolvedValue(false);
jest.spyOn(totpService, "getCode");
const autofillResult = await autofillService.doAutoFill(autofillOptions);
expect(stateService.getCanAccessPremium).toHaveBeenCalled();
expect(autofillService.getShouldAutoCopyTotp).toHaveBeenCalled();
expect(totpService.getCode).not.toHaveBeenCalled();
expect(autofillResult).toBeNull();

View File

@ -5,6 +5,7 @@ import { UserVerificationService } from "@bitwarden/common/auth/abstractions/use
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EventType } from "@bitwarden/common/enums";
import {
UriMatchStrategySetting,
@ -55,6 +56,7 @@ export default class AutofillService implements AutofillServiceInterface {
private logService: LogService,
private domainSettingsService: DomainSettingsService,
private userVerificationService: UserVerificationService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {}
/**
@ -240,7 +242,9 @@ export default class AutofillService implements AutofillServiceInterface {
let totp: string | null = null;
const canAccessPremium = await this.stateService.getCanAccessPremium();
const canAccessPremium = await firstValueFrom(
this.billingAccountProfileStateService.hasPremiumFromAnySource$,
);
const defaultUriMatch = await this.getDefaultUriMatchStrategy();
if (!canAccessPremium) {

View File

@ -14,12 +14,12 @@ import { EventCollectionService as EventCollectionServiceAbstraction } from "@bi
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
@ -64,6 +64,8 @@ import {
UserNotificationSettingsService,
UserNotificationSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/user-notification-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 { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
@ -180,7 +182,6 @@ import {
VaultExportServiceAbstraction,
} from "@bitwarden/vault-export-core";
import { BrowserOrganizationService } from "../admin-console/services/browser-organization.service";
import ContextMenusBackground from "../autofill/background/context-menus.background";
import NotificationBackground from "../autofill/background/notification.background";
import OverlayBackground from "../autofill/background/overlay.background";
@ -211,7 +212,6 @@ import { BrowserPlatformUtilsService } from "../platform/services/platform-utils
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserSendService } from "../services/browser-send.service";
import { BrowserSettingsService } from "../services/browser-settings.service";
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
import FilelessImporterBackground from "../tools/background/fileless-importer.background";
import { BrowserFido2UserInterfaceService } from "../vault/fido2/browser-fido2-user-interface.service";
@ -240,7 +240,6 @@ export default class MainBackground {
appIdService: AppIdServiceAbstraction;
apiService: ApiServiceAbstraction;
environmentService: BrowserEnvironmentService;
settingsService: SettingsServiceAbstraction;
cipherService: CipherServiceAbstraction;
folderService: InternalFolderServiceAbstraction;
collectionService: CollectionServiceAbstraction;
@ -311,6 +310,7 @@ export default class MainBackground {
biometricStateService: BiometricStateService;
stateEventRunnerService: StateEventRunnerService;
ssoLoginService: SsoLoginServiceAbstraction;
billingAccountProfileStateService: BillingAccountProfileStateService;
onUpdatedRan: boolean;
onReplacedRan: boolean;
@ -427,6 +427,21 @@ export default class MainBackground {
);
this.biometricStateService = new DefaultBiometricStateService(this.stateProvider);
this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);
this.platformUtilsService = new BackgroundPlatformUtilsService(
this.messagingService,
(clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
async () => this.biometricUnlock(),
self,
);
this.tokenService = new TokenService(
this.singleUserStateProvider,
this.globalStateProvider,
this.platformUtilsService.supportsSecureStorage(),
this.secureStorageService,
);
const migrationRunner = new MigrationRunner(
this.storageService,
this.logService,
@ -441,15 +456,9 @@ export default class MainBackground {
new StateFactory(GlobalState, Account),
this.accountService,
this.environmentService,
this.tokenService,
migrationRunner,
);
this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);
this.platformUtilsService = new BackgroundPlatformUtilsService(
this.messagingService,
(clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
async () => this.biometricUnlock(),
self,
);
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
@ -465,17 +474,17 @@ export default class MainBackground {
this.stateProvider,
this.biometricStateService,
);
this.tokenService = new TokenService(this.stateService);
this.appIdService = new AppIdService(this.globalStateProvider);
this.apiService = new ApiService(
this.tokenService,
this.platformUtilsService,
this.environmentService,
this.appIdService,
this.stateService,
(expired: boolean) => this.logout(expired),
);
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
this.settingsService = new BrowserSettingsService(this.stateService);
this.fileUploadService = new FileUploadService(this.logService);
this.cipherFileUploadService = new CipherFileUploadService(
this.apiService,
@ -489,10 +498,7 @@ export default class MainBackground {
this.stateProvider,
);
this.syncNotifierService = new SyncNotifierService();
this.organizationService = new BrowserOrganizationService(
this.stateService,
this.stateProvider,
);
this.organizationService = new OrganizationService(this.stateProvider);
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
this.autofillSettingsService = new AutofillSettingsService(
this.stateProvider,
@ -562,6 +568,10 @@ export default class MainBackground {
this.stateService,
);
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
this.activeUserStateProvider,
);
this.loginStrategyService = new LoginStrategyService(
this.cryptoService,
this.apiService,
@ -581,6 +591,7 @@ export default class MainBackground {
this.deviceTrustCryptoService,
this.authRequestService,
this.globalStateProvider,
this.billingAccountProfileStateService,
);
this.ssoLoginService = new SsoLoginService(this.stateProvider);
@ -708,17 +719,20 @@ export default class MainBackground {
this.sendApiService,
this.avatarService,
logoutCallback,
this.billingAccountProfileStateService,
);
this.eventUploadService = new EventUploadService(
this.apiService,
this.stateService,
this.stateProvider,
this.logService,
this.accountService,
);
this.eventCollectionService = new EventCollectionService(
this.cipherService,
this.stateService,
this.stateProvider,
this.organizationService,
this.eventUploadService,
this.accountService,
);
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
@ -731,6 +745,7 @@ export default class MainBackground {
this.logService,
this.domainSettingsService,
this.userVerificationService,
this.billingAccountProfileStateService,
);
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);
@ -791,7 +806,6 @@ export default class MainBackground {
this.fido2AuthenticatorService,
this.configService,
this.authService,
this.stateService,
this.vaultSettingsService,
this.domainSettingsService,
this.logService,
@ -839,7 +853,6 @@ export default class MainBackground {
this.cryptoService,
this.cryptoFunctionService,
this.runtimeBackground,
this.i18nService,
this.messagingService,
this.appIdService,
this.platformUtilsService,
@ -873,7 +886,7 @@ export default class MainBackground {
this.autofillService,
this.authService,
this.environmentService,
this.settingsService,
this.domainSettingsService,
this.stateService,
this.autofillSettingsService,
this.i18nService,
@ -953,6 +966,7 @@ export default class MainBackground {
this.autofillSettingsService,
this.i18nService,
this.logService,
this.billingAccountProfileStateService,
);
this.cipherContextMenuHandler = new CipherContextMenuHandler(
@ -1092,7 +1106,7 @@ export default class MainBackground {
async logout(expired: boolean, userId?: UserId) {
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
await this.eventUploadService.uploadEvents(userId);
await this.eventUploadService.uploadEvents(userId as UserId);
await Promise.all([
this.syncService.setLastSync(new Date(0), userId),

View File

@ -5,7 +5,6 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -75,7 +74,6 @@ export class NativeMessagingBackground {
private cryptoService: CryptoService,
private cryptoFunctionService: CryptoFunctionService,
private runtimeBackground: RuntimeBackground,
private i18nService: I18nService,
private messagingService: MessagingService,
private appIdService: AppIdService,
private platformUtilsService: PlatformUtilsService,

View File

@ -5,15 +5,14 @@ import {
organizationServiceFactory,
OrganizationServiceInitOptions,
} from "../../admin-console/background/service-factories/organization-service.factory";
import { accountServiceFactory } from "../../auth/background/service-factories/account-service.factory";
import {
FactoryOptions,
CachedServices,
factory,
} from "../../platform/background/service-factories/factory-options";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../platform/background/service-factories/state-service.factory";
import { stateProviderFactory } from "../../platform/background/service-factories/state-provider.factory";
import { StateServiceInitOptions } from "../../platform/background/service-factories/state-service.factory";
import {
cipherServiceFactory,
CipherServiceInitOptions,
@ -43,9 +42,10 @@ export function eventCollectionServiceFactory(
async () =>
new EventCollectionService(
await cipherServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
await organizationServiceFactory(cache, opts),
await eventUploadServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
),
);
}

View File

@ -1,6 +1,7 @@
import { EventUploadService as AbstractEventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
import { accountServiceFactory } from "../../auth/background/service-factories/account-service.factory";
import {
ApiServiceInitOptions,
apiServiceFactory,
@ -14,10 +15,8 @@ import {
logServiceFactory,
LogServiceInitOptions,
} from "../../platform/background/service-factories/log-service.factory";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../platform/background/service-factories/state-service.factory";
import { stateProviderFactory } from "../../platform/background/service-factories/state-provider.factory";
import { StateServiceInitOptions } from "../../platform/background/service-factories/state-service.factory";
type EventUploadServiceOptions = FactoryOptions;
@ -37,8 +36,9 @@ export function eventUploadServiceFactory(
async () =>
new EventUploadService(
await apiServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
await stateProviderFactory(cache, opts),
await logServiceFactory(cache, opts),
await accountServiceFactory(cache, opts),
),
);
}

View File

@ -1,28 +0,0 @@
import { SettingsService as AbstractSettingsService } from "@bitwarden/common/abstractions/settings.service";
import {
FactoryOptions,
CachedServices,
factory,
} from "../../platform/background/service-factories/factory-options";
import {
stateServiceFactory,
StateServiceInitOptions,
} from "../../platform/background/service-factories/state-service.factory";
import { BrowserSettingsService } from "../../services/browser-settings.service";
type SettingsServiceFactoryOptions = FactoryOptions;
export type SettingsServiceInitOptions = SettingsServiceFactoryOptions & StateServiceInitOptions;
export function settingsServiceFactory(
cache: { settingsService?: AbstractSettingsService } & CachedServices,
opts: SettingsServiceInitOptions,
): Promise<AbstractSettingsService> {
return factory(
cache,
"settingsService",
opts,
async () => new BrowserSettingsService(await stateServiceFactory(cache, opts)),
);
}

View File

@ -20,6 +20,7 @@ import {
PlatformUtilsServiceInitOptions,
platformUtilsServiceFactory,
} from "./platform-utils-service.factory";
import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory";
type ApiServiceFactoryOptions = FactoryOptions & {
apiServiceOptions: {
@ -32,7 +33,8 @@ export type ApiServiceInitOptions = ApiServiceFactoryOptions &
TokenServiceInitOptions &
PlatformUtilsServiceInitOptions &
EnvironmentServiceInitOptions &
AppIdServiceInitOptions;
AppIdServiceInitOptions &
StateServiceInitOptions;
export function apiServiceFactory(
cache: { apiService?: AbstractApiService } & CachedServices,
@ -48,6 +50,7 @@ export function apiServiceFactory(
await platformUtilsServiceFactory(cache, opts),
await environmentServiceFactory(cache, opts),
await appIdServiceFactory(cache, opts),
await stateServiceFactory(cache, opts),
opts.apiServiceOptions.logoutCallback,
opts.apiServiceOptions.customUserAgent,
),

View File

@ -0,0 +1,28 @@
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 { activeUserStateProviderFactory } from "./active-user-state-provider.factory";
import { FactoryOptions, CachedServices, factory } from "./factory-options";
import { StateProviderInitOptions } from "./state-provider.factory";
type BillingAccountProfileStateServiceFactoryOptions = FactoryOptions;
export type BillingAccountProfileStateServiceInitOptions =
BillingAccountProfileStateServiceFactoryOptions & StateProviderInitOptions;
export function billingAccountProfileStateServiceFactory(
cache: {
billingAccountProfileStateService?: BillingAccountProfileStateService;
} & CachedServices,
opts: BillingAccountProfileStateServiceInitOptions,
): Promise<BillingAccountProfileStateService> {
return factory(
cache,
"billingAccountProfileStateService",
opts,
async () =>
new DefaultBillingAccountProfileStateService(
await activeUserStateProviderFactory(cache, opts),
),
);
}

View File

@ -5,6 +5,10 @@ import {
accountServiceFactory,
AccountServiceInitOptions,
} from "../../../auth/background/service-factories/account-service.factory";
import {
tokenServiceFactory,
TokenServiceInitOptions,
} from "../../../auth/background/service-factories/token-service.factory";
import { Account } from "../../../models/account";
import { BrowserStateService } from "../../services/browser-state.service";
@ -38,6 +42,7 @@ export type StateServiceInitOptions = StateServiceFactoryOptions &
LogServiceInitOptions &
AccountServiceInitOptions &
EnvironmentServiceInitOptions &
TokenServiceInitOptions &
MigrationRunnerInitOptions;
export async function stateServiceFactory(
@ -57,6 +62,7 @@ export async function stateServiceFactory(
opts.stateServiceOptions.stateFactory,
await accountServiceFactory(cache, opts),
await environmentServiceFactory(cache, opts),
await tokenServiceFactory(cache, opts),
await migrationRunnerFactory(cache, opts),
opts.stateServiceOptions.useAccountCache,
),

View File

@ -7,6 +7,7 @@ import localeBn from "@angular/common/locales/bn";
import localeBs from "@angular/common/locales/bs";
import localeCa from "@angular/common/locales/ca";
import localeCs from "@angular/common/locales/cs";
import localeCy from "@angular/common/locales/cy";
import localeDa from "@angular/common/locales/da";
import localeDe from "@angular/common/locales/de";
import localeEl from "@angular/common/locales/el";
@ -19,6 +20,7 @@ import localeFa from "@angular/common/locales/fa";
import localeFi from "@angular/common/locales/fi";
import localeFil from "@angular/common/locales/fil";
import localeFr from "@angular/common/locales/fr";
import localeGl from "@angular/common/locales/gl";
import localeHe from "@angular/common/locales/he";
import localeHi from "@angular/common/locales/hi";
import localeHr from "@angular/common/locales/hr";
@ -33,9 +35,13 @@ import localeKo from "@angular/common/locales/ko";
import localeLt from "@angular/common/locales/lt";
import localeLv from "@angular/common/locales/lv";
import localeMl from "@angular/common/locales/ml";
import localeMr from "@angular/common/locales/mr";
import localeMy from "@angular/common/locales/my";
import localeNb from "@angular/common/locales/nb";
import localeNe from "@angular/common/locales/ne";
import localeNl from "@angular/common/locales/nl";
import localeNn from "@angular/common/locales/nn";
import localeOr from "@angular/common/locales/or";
import localePl from "@angular/common/locales/pl";
import localePtBr from "@angular/common/locales/pt";
import localePtPt from "@angular/common/locales/pt-PT";
@ -46,6 +52,7 @@ import localeSk from "@angular/common/locales/sk";
import localeSl from "@angular/common/locales/sl";
import localeSr from "@angular/common/locales/sr";
import localeSv from "@angular/common/locales/sv";
import localeTe from "@angular/common/locales/te";
import localeTh from "@angular/common/locales/th";
import localeTr from "@angular/common/locales/tr";
import localeUk from "@angular/common/locales/uk";
@ -61,6 +68,7 @@ registerLocaleData(localeBn, "bn");
registerLocaleData(localeBs, "bs");
registerLocaleData(localeCa, "ca");
registerLocaleData(localeCs, "cs");
registerLocaleData(localeCy, "cy");
registerLocaleData(localeDa, "da");
registerLocaleData(localeDe, "de");
registerLocaleData(localeEl, "el");
@ -73,6 +81,7 @@ registerLocaleData(localeFa, "fa");
registerLocaleData(localeFi, "fi");
registerLocaleData(localeFil, "fil");
registerLocaleData(localeFr, "fr");
registerLocaleData(localeGl, "gl");
registerLocaleData(localeHe, "he");
registerLocaleData(localeHi, "hi");
registerLocaleData(localeHr, "hr");
@ -87,9 +96,13 @@ registerLocaleData(localeKo, "ko");
registerLocaleData(localeLt, "lt");
registerLocaleData(localeLv, "lv");
registerLocaleData(localeMl, "ml");
registerLocaleData(localeMr, "mr");
registerLocaleData(localeMy, "my");
registerLocaleData(localeNb, "nb");
registerLocaleData(localeNe, "ne");
registerLocaleData(localeNl, "nl");
registerLocaleData(localeNn, "nn");
registerLocaleData(localeOr, "or");
registerLocaleData(localePl, "pl");
registerLocaleData(localePtBr, "pt-BR");
registerLocaleData(localePtPt, "pt-PT");
@ -100,6 +113,7 @@ registerLocaleData(localeSk, "sk");
registerLocaleData(localeSl, "sl");
registerLocaleData(localeSr, "sr");
registerLocaleData(localeSv, "sv");
registerLocaleData(localeTe, "te");
registerLocaleData(localeTh, "th");
registerLocaleData(localeTr, "tr");
registerLocaleData(localeUk, "uk");

View File

@ -1,5 +1,6 @@
import { mock, MockProxy } from "jest-mock-extended";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
@ -32,6 +33,7 @@ describe("Browser State Service", () => {
let stateFactory: MockProxy<StateFactory<GlobalState, Account>>;
let useAccountCache: boolean;
let environmentService: MockProxy<EnvironmentService>;
let tokenService: MockProxy<TokenService>;
let migrationRunner: MockProxy<MigrationRunner>;
let state: State<GlobalState, Account>;
@ -46,6 +48,7 @@ describe("Browser State Service", () => {
logService = mock();
stateFactory = mock();
environmentService = mock();
tokenService = mock();
migrationRunner = mock();
// turn off account cache for tests
useAccountCache = false;
@ -77,6 +80,7 @@ describe("Browser State Service", () => {
stateFactory,
accountService,
environmentService,
tokenService,
migrationRunner,
useAccountCache,
);

View File

@ -1,6 +1,7 @@
import { BehaviorSubject } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
@ -45,6 +46,7 @@ export class BrowserStateService
stateFactory: StateFactory<GlobalState, Account>,
accountService: AccountService,
environmentService: EnvironmentService,
tokenService: TokenService,
migrationRunner: MigrationRunner,
useAccountCache = true,
) {
@ -56,6 +58,7 @@ export class BrowserStateService
stateFactory,
accountService,
environmentService,
tokenService,
migrationRunner,
useAccountCache,
);

View File

@ -17,11 +17,8 @@ import {
LoginStrategyServiceAbstraction,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
@ -42,6 +39,10 @@ import {
AutofillSettingsService,
AutofillSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/autofill-settings.service";
import {
DefaultDomainSettingsService,
DomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import {
UserNotificationSettingsService,
UserNotificationSettingsServiceAbstraction,
@ -98,7 +99,6 @@ import { DialogService } from "@bitwarden/components";
import { ImportServiceAbstraction } from "@bitwarden/importer/core";
import { VaultExportServiceAbstraction } from "@bitwarden/vault-export-core";
import { BrowserOrganizationService } from "../../admin-console/services/browser-organization.service";
import { UnauthGuardService } from "../../auth/popup/services";
import { AutofillService } from "../../autofill/services/abstractions/autofill.service";
import MainBackground from "../../background/main.background";
@ -118,7 +118,6 @@ import { ForegroundPlatformUtilsService } from "../../platform/services/platform
import { ForegroundDerivedStateProvider } from "../../platform/state/foreground-derived-state.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
import { BrowserSendService } from "../../services/browser-send.service";
import { BrowserSettingsService } from "../../services/browser-settings.service";
import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service";
import { VaultFilterService } from "../../vault/services/vault-filter.service";
@ -233,7 +232,6 @@ function getBgService<T>(service: keyof MainBackground) {
deps: [],
},
{ provide: TotpService, useFactory: getBgService<TotpService>("totpService"), deps: [] },
{ provide: TokenService, useFactory: getBgService<TokenService>("tokenService"), deps: [] },
{
provide: I18nServiceAbstraction,
useFactory: (globalStateProvider: GlobalStateProvider) => {
@ -265,16 +263,6 @@ function getBgService<T>(service: keyof MainBackground) {
useFactory: getBgService<DevicesServiceAbstraction>("devicesService"),
deps: [],
},
{
provide: EventUploadService,
useFactory: getBgService<EventUploadService>("eventUploadService"),
deps: [],
},
{
provide: EventCollectionService,
useFactory: getBgService<EventCollectionService>("eventCollectionService"),
deps: [],
},
{
provide: PlatformUtilsService,
useExisting: ForegroundPlatformUtilsService,
@ -348,11 +336,9 @@ function getBgService<T>(service: keyof MainBackground) {
},
{ provide: SyncService, useFactory: getBgService<SyncService>("syncService"), deps: [] },
{
provide: SettingsService,
useFactory: (stateService: StateServiceAbstraction) => {
return new BrowserSettingsService(stateService);
},
deps: [StateServiceAbstraction],
provide: DomainSettingsService,
useClass: DefaultDomainSettingsService,
deps: [StateProvider],
},
{
provide: AbstractStorageService,
@ -399,13 +385,6 @@ function getBgService<T>(service: keyof MainBackground) {
useFactory: getBgService<NotificationsService>("notificationsService"),
deps: [],
},
{
provide: OrganizationService,
useFactory: (stateService: StateServiceAbstraction, stateProvider: StateProvider) => {
return new BrowserOrganizationService(stateService, stateProvider);
},
deps: [StateServiceAbstraction, StateProvider],
},
{
provide: VaultFilterService,
useClass: VaultFilterService,
@ -445,6 +424,7 @@ function getBgService<T>(service: keyof MainBackground) {
logService: LogServiceAbstraction,
accountService: AccountServiceAbstraction,
environmentService: EnvironmentService,
tokenService: TokenService,
migrationRunner: MigrationRunner,
) => {
return new BrowserStateService(
@ -455,6 +435,7 @@ function getBgService<T>(service: keyof MainBackground) {
new StateFactory(GlobalState, Account),
accountService,
environmentService,
tokenService,
migrationRunner,
);
},
@ -465,6 +446,7 @@ function getBgService<T>(service: keyof MainBackground) {
LogServiceAbstraction,
AccountServiceAbstraction,
EnvironmentService,
TokenService,
MigrationRunner,
],
},

View File

@ -6,7 +6,6 @@ import { DomainSettingsService } from "@bitwarden/common/autofill/services/domai
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { BrowserApi } from "../../platform/browser/browser-api";
@ -31,7 +30,6 @@ export class ExcludedDomainsComponent implements OnInit, OnDestroy {
accountSwitcherEnabled = false;
constructor(
private stateService: StateService,
private domainSettingsService: DomainSettingsService,
private i18nService: I18nService,
private router: Router,

View File

@ -1,7 +1,6 @@
import { Component, OnInit } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
@ -13,7 +12,6 @@ import {
} from "@bitwarden/common/models/domain/domain-service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
@ -51,14 +49,12 @@ export class OptionsComponent implements OnInit {
constructor(
private messagingService: MessagingService,
private stateService: StateService,
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private domainSettingsService: DomainSettingsService,
private badgeSettingsService: BadgeSettingsServiceAbstraction,
i18nService: I18nService,
private themeStateService: ThemeStateService,
private settingsService: SettingsService,
private vaultSettingsService: VaultSettingsService,
) {
this.themeOptions = [
@ -121,7 +117,7 @@ export class OptionsComponent implements OnInit {
this.enableAutoTotpCopy = await firstValueFrom(this.autofillSettingsService.autoCopyTotp$);
this.enableFavicon = !this.settingsService.getDisableFavicon();
this.enableFavicon = await firstValueFrom(this.domainSettingsService.showFavicons$);
this.enableBadgeCounter = await firstValueFrom(this.badgeSettingsService.enableBadgeCounter$);
@ -171,7 +167,7 @@ export class OptionsComponent implements OnInit {
}
async updateFavicon() {
await this.settingsService.setDisableFavicon(!this.enableFavicon);
await this.domainSettingsService.setShowFavicons(this.enableFavicon);
}
async updateBadgeCounter() {

View File

@ -12,7 +12,7 @@
</header>
<main tabindex="-1">
<div class="content">
<ng-container *ngIf="!isPremium">
<ng-container *ngIf="!(isPremium$ | async)">
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
<ul class="bwi-ul">
@ -61,7 +61,7 @@
></i>
</button>
</ng-container>
<ng-container *ngIf="isPremium">
<ng-container *ngIf="isPremium$ | async">
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
<button type="button" class="btn block primary" (click)="manage()">

View File

@ -3,6 +3,7 @@ import { Component } from "@angular/core";
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@ -27,6 +28,7 @@ export class PremiumComponent extends BasePremiumComponent {
private currencyPipe: CurrencyPipe,
dialogService: DialogService,
environmentService: EnvironmentService,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
i18nService,
@ -36,6 +38,7 @@ export class PremiumComponent extends BasePremiumComponent {
stateService,
dialogService,
environmentService,
billingAccountProfileStateService,
);
// Support old price string. Can be removed in future once all translations are properly updated.

View File

@ -16,7 +16,6 @@ import {
takeUntil,
} from "rxjs";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { FingerprintDialogComponent } from "@bitwarden/auth/angular";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
@ -98,7 +97,6 @@ export class SettingsComponent implements OnInit {
private environmentService: EnvironmentService,
private cryptoService: CryptoService,
private stateService: StateService,
private modalService: ModalService,
private userVerificationService: UserVerificationService,
private dialogService: DialogService,
private changeDetectorRef: ChangeDetectorRef,

View File

@ -1,11 +0,0 @@
import { BehaviorSubject } from "rxjs";
import { SettingsService } from "@bitwarden/common/services/settings.service";
import { browserSession, sessionSync } from "../platform/decorators/session-sync-observable";
@browserSession
export class BrowserSettingsService extends SettingsService {
@sessionSync({ initializer: (b: boolean) => b })
protected _disableFavicon: BehaviorSubject<boolean>;
}

View File

@ -6,6 +6,7 @@ import { first } from "rxjs/operators";
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@ -49,6 +50,7 @@ export class SendAddEditComponent extends BaseAddEditComponent {
dialogService: DialogService,
formBuilder: FormBuilder,
private filePopoutUtilsService: FilePopoutUtilsService,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
i18nService,
@ -63,6 +65,7 @@ export class SendAddEditComponent extends BaseAddEditComponent {
sendApiService,
dialogService,
formBuilder,
billingAccountProfileStateService,
);
}

View File

@ -1,10 +1,11 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { Subject, takeUntil } from "rxjs";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EventType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
@ -15,7 +16,7 @@ import { PasswordRepromptService } from "@bitwarden/vault";
selector: "app-action-buttons",
templateUrl: "action-buttons.component.html",
})
export class ActionButtonsComponent {
export class ActionButtonsComponent implements OnInit, OnDestroy {
@Output() onView = new EventEmitter<CipherView>();
@Output() launchEvent = new EventEmitter<CipherView>();
@Input() cipher: CipherView;
@ -24,17 +25,28 @@ export class ActionButtonsComponent {
cipherType = CipherType;
userHasPremiumAccess = false;
private componentIsDestroyed$ = new Subject<boolean>();
constructor(
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private eventCollectionService: EventCollectionService,
private totpService: TotpService,
private stateService: StateService,
private passwordRepromptService: PasswordRepromptService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {}
async ngOnInit() {
this.userHasPremiumAccess = await this.stateService.getCanAccessPremium();
ngOnInit() {
this.billingAccountProfileStateService.hasPremiumFromAnySource$
.pipe(takeUntil(this.componentIsDestroyed$))
.subscribe((canAccessPremium: boolean) => {
this.userHasPremiumAccess = canAccessPremium;
});
}
ngOnDestroy() {
this.componentIsDestroyed$.next(true);
this.componentIsDestroyed$.complete();
}
launchCipher() {

View File

@ -6,7 +6,6 @@ import { firstValueFrom } from "rxjs";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { BrowserFido2UserInterfaceSession } from "../../../fido2/browser-fido2-user-interface.service";
@ -52,7 +51,6 @@ export class Fido2UseBrowserLinkComponent {
protected fido2PopoutSessionData$ = fido2PopoutSessionData$();
constructor(
private stateService: StateService,
private domainSettingsService: DomainSettingsService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,

View File

@ -5,6 +5,7 @@ import { first } from "rxjs/operators";
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -34,6 +35,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
logService: LogService,
fileDownloadService: FileDownloadService,
dialogService: DialogService,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
cipherService,
@ -46,6 +48,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
stateService,
fileDownloadService,
dialogService,
billingAccountProfileStateService,
);
}

View File

@ -275,7 +275,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
const dontShowIdentities = !(await firstValueFrom(
this.vaultSettingsService.showIdentitiesCurrentTab$,
));
this.showOrganizations = this.organizationService.hasOrganizations();
this.showOrganizations = await this.organizationService.hasOrganizations();
if (!dontShowCards) {
otherTypes.push(CipherType.Card);
}

View File

@ -74,7 +74,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
async ngOnInit() {
this.searchTypeSearch = !this.platformUtilsService.isSafari();
this.showOrganizations = this.organizationService.hasOrganizations();
this.showOrganizations = await this.organizationService.hasOrganizations();
this.vaultFilter = this.vaultFilterService.getVaultFilter();
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.route.queryParams.pipe(first()).subscribe(async (params) => {

View File

@ -9,6 +9,7 @@ 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 { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
@ -95,6 +96,7 @@ export class ViewComponent extends BaseViewComponent {
fileDownloadService: FileDownloadService,
dialogService: DialogService,
datePipe: DatePipe,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
cipherService,
@ -117,6 +119,7 @@ export class ViewComponent extends BaseViewComponent {
fileDownloadService,
dialogService,
datePipe,
billingAccountProfileStateService,
);
}

View File

@ -203,6 +203,7 @@ export class LoginCommand {
ssoCodeVerifier,
this.ssoRedirectUri,
orgIdentifier,
undefined, // email to look up 2FA token not required as CLI can't remember 2FA token
twoFactor,
),
);

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";
@ -88,7 +90,6 @@ import { AuditService } from "@bitwarden/common/services/audit.service";
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
import { SearchService } from "@bitwarden/common/services/search.service";
import { SettingsService } from "@bitwarden/common/services/settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
import {
@ -157,7 +158,6 @@ export class Main {
appIdService: AppIdService;
apiService: NodeApiService;
environmentService: EnvironmentService;
settingsService: SettingsService;
cipherService: CipherService;
folderService: InternalFolderService;
organizationUserService: OrganizationUserService;
@ -221,6 +221,7 @@ export class Main {
avatarService: AvatarServiceAbstraction;
stateEventRunnerService: StateEventRunnerService;
biometricStateService: BiometricStateService;
billingAccountProfileStateService: BillingAccountProfileStateService;
constructor() {
let p = null;
@ -310,6 +311,13 @@ export class Main {
this.environmentService = new EnvironmentService(this.stateProvider, this.accountService);
this.tokenService = new TokenService(
this.singleUserStateProvider,
this.globalStateProvider,
this.platformUtilsService.supportsSecureStorage(),
this.secureStorageService,
);
const migrationRunner = new MigrationRunner(
this.storageService,
this.logService,
@ -324,6 +332,7 @@ export class Main {
new StateFactory(GlobalState, Account),
this.accountService,
this.environmentService,
this.tokenService,
migrationRunner,
);
@ -341,7 +350,6 @@ export class Main {
);
this.appIdService = new AppIdService(this.globalStateProvider);
this.tokenService = new TokenService(this.stateService);
const customUserAgent =
"Bitwarden_CLI/" +
@ -354,6 +362,7 @@ export class Main {
this.platformUtilsService,
this.environmentService,
this.appIdService,
this.stateService,
async (expired: boolean) => await this.logout(),
customUserAgent,
);
@ -364,7 +373,6 @@ export class Main {
this.containerService = new ContainerService(this.cryptoService, this.encryptService);
this.settingsService = new SettingsService(this.stateService);
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
this.fileUploadService = new FileUploadService(this.logService);
@ -399,7 +407,7 @@ export class Main {
this.providerService = new ProviderService(this.stateProvider);
this.organizationService = new OrganizationService(this.stateService, this.stateProvider);
this.organizationService = new OrganizationService(this.stateProvider);
this.organizationUserService = new OrganizationUserServiceImplementation(this.apiService);
@ -448,6 +456,10 @@ export class Main {
this.stateService,
);
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
this.activeUserStateProvider,
);
this.loginStrategyService = new LoginStrategyService(
this.cryptoService,
this.apiService,
@ -467,6 +479,7 @@ export class Main {
this.deviceTrustCryptoService,
this.authRequestService,
this.globalStateProvider,
this.billingAccountProfileStateService,
);
this.authService = new AuthService(
@ -578,6 +591,7 @@ export class Main {
this.sendApiService,
this.avatarService,
async (expired: boolean) => await this.logout(),
this.billingAccountProfileStateService,
);
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
@ -624,15 +638,17 @@ export class Main {
this.eventUploadService = new EventUploadService(
this.apiService,
this.stateService,
this.stateProvider,
this.logService,
this.accountService,
);
this.eventCollectionService = new EventCollectionService(
this.cipherService,
this.stateService,
this.stateProvider,
this.organizationService,
this.eventUploadService,
this.accountService,
);
}
@ -656,6 +672,7 @@ export class Main {
});
const userId = await this.stateService.getUserId();
await Promise.all([
this.eventUploadService.uploadEvents(userId as UserId),
this.syncService.setLastSync(new Date(0)),
this.cryptoService.clearKeys(),
this.cipherService.clear(userId),

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

@ -6,6 +6,7 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ApiService } from "@bitwarden/common/services/api.service";
(global as any).fetch = fe.default;
@ -20,6 +21,7 @@ export class NodeApiService extends ApiService {
platformUtilsService: PlatformUtilsService,
environmentService: EnvironmentService,
appIdService: AppIdService,
stateService: StateService,
logoutCallback: (expired: boolean) => Promise<void>,
customUserAgent: string = null,
) {
@ -28,6 +30,7 @@ export class NodeApiService extends ApiService {
platformUtilsService,
environmentService,
appIdService,
stateService,
logoutCallback,
customUserAgent,
);

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.");
}

View File

@ -130,12 +130,6 @@ version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cbc"
version = "0.1.2"
@ -333,7 +327,6 @@ dependencies = [
"security-framework-sys",
"sha2",
"thiserror",
"tokio",
"typenum",
"widestring",
"windows",
@ -377,7 +370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -772,17 +765,6 @@ dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
dependencies = [
"libc",
"wasi",
"windows-sys 0.48.0",
]
[[package]]
name = "napi"
version = "2.13.3"
@ -925,7 +907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -1114,7 +1096,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -1204,15 +1186,6 @@ dependencies = [
"digest",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
@ -1228,16 +1201,6 @@ version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "socket2"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "str-buf"
version = "1.0.6"
@ -1295,7 +1258,7 @@ dependencies = [
"fastrand",
"redox_syscall",
"rustix",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
@ -1329,32 +1292,13 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.32.0"
version = "1.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
@ -1606,15 +1550,6 @@ dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"

View File

@ -25,7 +25,6 @@ retry = "=2.0.0"
scopeguard = "=1.2.0"
sha2 = "=0.10.8"
thiserror = "=1.0.51"
tokio = { version = "=1.32.0", features = ["full"] }
typenum = "=1.17.0"
[build-dependencies]

View File

@ -24,7 +24,7 @@
"**/node_modules/argon2/package.json",
"**/node_modules/argon2/lib/binding/napi-v3/argon2.node"
],
"electronVersion": "28.2.6",
"electronVersion": "28.2.7",
"generateUpdatesFilesForAllChannels": true,
"publish": {
"provider": "generic",

View File

@ -3,13 +3,12 @@ import { FormBuilder } from "@angular/forms";
import { BehaviorSubject, firstValueFrom, Observable, Subject } from "rxjs";
import { concatMap, debounceTime, filter, map, switchMap, takeUntil, tap } from "rxjs/operators";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { DeviceType } from "@bitwarden/common/enums";
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
@ -115,9 +114,8 @@ export class SettingsComponent implements OnInit {
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private messagingService: MessagingService,
private cryptoService: CryptoService,
private modalService: ModalService,
private themeStateService: ThemeStateService,
private settingsService: SettingsService,
private domainSettingsService: DomainSettingsService,
private dialogService: DialogService,
private userVerificationService: UserVerificationServiceAbstraction,
private biometricStateService: BiometricStateService,
@ -251,7 +249,7 @@ export class SettingsComponent implements OnInit {
approveLoginRequests: (await this.stateService.getApproveLoginRequests()) ?? false,
clearClipboard: await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$),
minimizeOnCopyToClipboard: await this.stateService.getMinimizeOnCopyToClipboard(),
enableFavicons: !(await this.stateService.getDisableFavicon()),
enableFavicons: await firstValueFrom(this.domainSettingsService.showFavicons$),
enableTray: await this.stateService.getEnableTray(),
enableMinToTray: await this.stateService.getEnableMinimizeToTray(),
enableCloseToTray: await this.stateService.getEnableCloseToTray(),
@ -498,7 +496,7 @@ export class SettingsComponent implements OnInit {
}
async saveFavicons() {
await this.settingsService.setDisableFavicon(!this.form.value.enableFavicons);
await this.domainSettingsService.setShowFavicons(this.form.value.enableFavicons);
this.messagingService.send("refreshCiphers");
}

View File

@ -19,9 +19,9 @@ import { FingerprintDialogComponent } from "@bitwarden/auth/angular";
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
@ -122,7 +122,6 @@ export class AppComponent implements OnInit, OnDestroy {
constructor(
private broadcasterService: BroadcasterService,
private folderService: InternalFolderService,
private settingsService: SettingsService,
private syncService: SyncService,
private passwordGenerationService: PasswordGenerationServiceAbstraction,
private cipherService: CipherService,
@ -153,6 +152,7 @@ export class AppComponent implements OnInit, OnDestroy {
private biometricStateService: BiometricStateService,
private stateEventRunnerService: StateEventRunnerService,
private providerService: ProviderService,
private organizationService: InternalOrganizationServiceAbstraction,
) {}
ngOnInit() {
@ -574,7 +574,8 @@ export class AppComponent implements OnInit, OnDestroy {
let preLogoutActiveUserId;
try {
await this.eventUploadService.uploadEvents(userBeingLoggedOut);
// Provide the userId of the user to upload events for
await this.eventUploadService.uploadEvents(userBeingLoggedOut as UserId);
await this.syncService.setLastSync(new Date(0), userBeingLoggedOut);
await this.cryptoService.clearKeys(userBeingLoggedOut);
await this.cipherService.clear(userBeingLoggedOut);

View File

@ -10,6 +10,7 @@ import {
OBSERVABLE_MEMORY_STORAGE,
OBSERVABLE_DISK_STORAGE,
WINDOW,
SUPPORTS_SECURE_STORAGE,
SYSTEM_THEME_OBSERVABLE,
} from "@bitwarden/angular/services/injection-tokens";
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
@ -18,6 +19,7 @@ import { PolicyService as PolicyServiceAbstraction } from "@bitwarden/common/adm
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { LoginService as LoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/login.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { LoginService } from "@bitwarden/common/auth/services/login.service";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { BroadcasterService as BroadcasterServiceAbstraction } from "@bitwarden/common/platform/abstractions/broadcaster.service";
@ -54,7 +56,10 @@ import { LoginGuard } from "../../auth/guards/login.guard";
import { Account } from "../../models/account";
import { ElectronCryptoService } from "../../platform/services/electron-crypto.service";
import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service";
import { ElectronPlatformUtilsService } from "../../platform/services/electron-platform-utils.service";
import {
ELECTRON_SUPPORTS_SECURE_STORAGE,
ElectronPlatformUtilsService,
} from "../../platform/services/electron-platform-utils.service";
import { ElectronRendererMessagingService } from "../../platform/services/electron-renderer-messaging.service";
import { ElectronRendererSecureStorageService } from "../../platform/services/electron-renderer-secure-storage.service";
import { ElectronRendererStorageService } from "../../platform/services/electron-renderer-storage.service";
@ -101,6 +106,13 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
useClass: ElectronPlatformUtilsService,
deps: [I18nServiceAbstraction, MessagingServiceAbstraction],
},
{
// We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid
// the TokenService having to inject the PlatformUtilsService which introduces a
// circular dependency on Desktop only.
provide: SUPPORTS_SECURE_STORAGE,
useValue: ELECTRON_SUPPORTS_SECURE_STORAGE,
},
{
provide: I18nServiceAbstraction,
useClass: I18nRendererService,
@ -140,6 +152,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
STATE_FACTORY,
AccountServiceAbstraction,
EnvironmentService,
TokenService,
MigrationRunner,
STATE_SERVICE_USE_CACHE,
],
@ -170,7 +183,6 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
CryptoServiceAbstraction,
CryptoFunctionServiceAbstraction,
MessagingServiceAbstraction,
I18nServiceAbstraction,
EncryptedMessageHandlerService,
DialogService,
],

View File

@ -4,6 +4,7 @@ import { FormBuilder } from "@angular/forms";
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@ -32,6 +33,7 @@ export class AddEditComponent extends BaseAddEditComponent {
sendApiService: SendApiService,
dialogService: DialogService,
formBuilder: FormBuilder,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
i18nService,
@ -46,6 +48,7 @@ export class AddEditComponent extends BaseAddEditComponent {
sendApiService,
dialogService,
formBuilder,
billingAccountProfileStateService,
);
}

View File

@ -2,7 +2,9 @@ import * as path from "path";
import { app } from "electron";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DefaultBiometricStateService } from "@bitwarden/common/platform/biometrics/biometric-state.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
@ -36,6 +38,7 @@ import { ClipboardMain } from "./platform/main/clipboard.main";
import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener";
import { MainCryptoFunctionService } from "./platform/main/main-crypto-function.service";
import { ElectronLogMainService } from "./platform/services/electron-log.main.service";
import { ELECTRON_SUPPORTS_SECURE_STORAGE } from "./platform/services/electron-platform-utils.service";
import { ElectronStateService } from "./platform/services/electron-state.service";
import { ElectronStorageService } from "./platform/services/electron-storage.service";
import { I18nMainService } from "./platform/services/i18n.main.service";
@ -53,6 +56,7 @@ export class Main {
mainCryptoFunctionService: MainCryptoFunctionService;
desktopCredentialStorageListener: DesktopCredentialStorageListener;
migrationRunner: MigrationRunner;
tokenService: TokenServiceAbstraction;
windowMain: WindowMain;
messagingMain: MessagingMain;
@ -129,8 +133,13 @@ export class Main {
stateEventRegistrarService,
);
const activeUserStateProvider = new DefaultActiveUserStateProvider(
accountService,
singleUserStateProvider,
);
const stateProvider = new DefaultStateProvider(
new DefaultActiveUserStateProvider(accountService, singleUserStateProvider),
activeUserStateProvider,
singleUserStateProvider,
globalStateProvider,
new DefaultDerivedStateProvider(this.memoryStorageForStateProviders),
@ -138,6 +147,13 @@ export class Main {
this.environmentService = new EnvironmentService(stateProvider, accountService);
this.tokenService = new TokenService(
singleUserStateProvider,
globalStateProvider,
ELECTRON_SUPPORTS_SECURE_STORAGE,
this.storageService,
);
this.migrationRunner = new MigrationRunner(
this.storageService,
this.logService,
@ -155,6 +171,7 @@ export class Main {
new StateFactory(GlobalState, Account),
accountService, // will not broadcast logouts. This is a hack until we can remove messaging dependency
this.environmentService,
this.tokenService,
this.migrationRunner,
false, // Do not use disk caching because this will get out of sync with the renderer service
);
@ -176,6 +193,7 @@ export class Main {
this.messagingService = new ElectronMainMessagingService(this.windowMain, (message) => {
this.messagingMain.onMessage(message);
});
this.powerMonitorMain = new PowerMonitorMain(this.messagingService);
this.menuMain = new MenuMain(
this.i18nService,

View File

@ -8,6 +8,7 @@ import localeBn from "@angular/common/locales/bn";
import localeBs from "@angular/common/locales/bs";
import localeCa from "@angular/common/locales/ca";
import localeCs from "@angular/common/locales/cs";
import localeCy from "@angular/common/locales/cy";
import localeDa from "@angular/common/locales/da";
import localeDe from "@angular/common/locales/de";
import localeEl from "@angular/common/locales/el";
@ -21,6 +22,7 @@ import localeFa from "@angular/common/locales/fa";
import localeFi from "@angular/common/locales/fi";
import localeFil from "@angular/common/locales/fil";
import localeFr from "@angular/common/locales/fr";
import localeGl from "@angular/common/locales/gl";
import localeHe from "@angular/common/locales/he";
import localeHi from "@angular/common/locales/hi";
import localeHr from "@angular/common/locales/hr";
@ -32,11 +34,16 @@ import localeKa from "@angular/common/locales/ka";
import localeKm from "@angular/common/locales/km";
import localeKn from "@angular/common/locales/kn";
import localeKo from "@angular/common/locales/ko";
import localeLt from "@angular/common/locales/lt";
import localeLv from "@angular/common/locales/lv";
import localeMl from "@angular/common/locales/ml";
import localeMr from "@angular/common/locales/mr";
import localeMy from "@angular/common/locales/my";
import localeNb from "@angular/common/locales/nb";
import localeNe from "@angular/common/locales/ne";
import localeNl from "@angular/common/locales/nl";
import localeNn from "@angular/common/locales/nn";
import localeOr from "@angular/common/locales/or";
import localePl from "@angular/common/locales/pl";
import localePtBr from "@angular/common/locales/pt";
import localePtPt from "@angular/common/locales/pt-PT";
@ -48,6 +55,7 @@ import localeSl from "@angular/common/locales/sl";
import localeSr from "@angular/common/locales/sr";
import localeMe from "@angular/common/locales/sr-Latn-ME";
import localeSv from "@angular/common/locales/sv";
import localeTe from "@angular/common/locales/te";
import localeTh from "@angular/common/locales/th";
import localeTr from "@angular/common/locales/tr";
import localeUk from "@angular/common/locales/uk";
@ -64,6 +72,7 @@ registerLocaleData(localeBn, "bn");
registerLocaleData(localeBs, "bs");
registerLocaleData(localeCa, "ca");
registerLocaleData(localeCs, "cs");
registerLocaleData(localeCy, "cy");
registerLocaleData(localeDa, "da");
registerLocaleData(localeDe, "de");
registerLocaleData(localeEl, "el");
@ -77,6 +86,7 @@ registerLocaleData(localeFa, "fa");
registerLocaleData(localeFi, "fi");
registerLocaleData(localeFil, "fil");
registerLocaleData(localeFr, "fr");
registerLocaleData(localeGl, "gl");
registerLocaleData(localeHe, "he");
registerLocaleData(localeHi, "hi");
registerLocaleData(localeHr, "hr");
@ -88,12 +98,17 @@ registerLocaleData(localeKa, "ka");
registerLocaleData(localeKm, "km");
registerLocaleData(localeKn, "kn");
registerLocaleData(localeKo, "ko");
registerLocaleData(localeLt, "lt");
registerLocaleData(localeLv, "lv");
registerLocaleData(localeMe, "me");
registerLocaleData(localeMl, "ml");
registerLocaleData(localeMr, "mr");
registerLocaleData(localeMy, "my");
registerLocaleData(localeNb, "nb");
registerLocaleData(localeNe, "ne");
registerLocaleData(localeNl, "nl");
registerLocaleData(localeNn, "nn");
registerLocaleData(localeOr, "or");
registerLocaleData(localePl, "pl");
registerLocaleData(localePtBr, "pt-BR");
registerLocaleData(localePtPt, "pt-PT");
@ -104,6 +119,7 @@ registerLocaleData(localeSk, "sk");
registerLocaleData(localeSl, "sl");
registerLocaleData(localeSr, "sr");
registerLocaleData(localeSv, "sv");
registerLocaleData(localeTe, "te");
registerLocaleData(localeTh, "th");
registerLocaleData(localeTr, "tr");
registerLocaleData(localeUk, "uk");

View File

@ -8,6 +8,8 @@ import {
import { ClipboardWriteMessage } from "../types/clipboard";
export const ELECTRON_SUPPORTS_SECURE_STORAGE = true;
export class ElectronPlatformUtilsService implements PlatformUtilsService {
constructor(
protected i18nService: I18nService,
@ -142,7 +144,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
}
supportsSecureStorage(): boolean {
return true;
return ELECTRON_SUPPORTS_SECURE_STORAGE;
}
getAutofillKeyboardShortcut(): Promise<string> {

View File

@ -4,7 +4,6 @@ import { firstValueFrom } from "rxjs";
import { NativeMessagingVersion } from "@bitwarden/common/enums";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncryptedString, EncString } from "@bitwarden/common/platform/models/domain/enc-string";
@ -33,7 +32,6 @@ export class NativeMessageHandlerService {
private cryptoService: CryptoService,
private cryptoFunctionService: CryptoFunctionService,
private messagingService: MessagingService,
private i18nService: I18nService,
private encryptedMessageHandlerService: EncryptedMessageHandlerService,
private dialogService: DialogService,
) {}

View File

@ -3,7 +3,6 @@ import { firstValueFrom } from "rxjs";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -35,7 +34,6 @@ export class NativeMessagingService {
private cryptoService: CryptoService,
private platformUtilService: PlatformUtilsService,
private logService: LogService,
private i18nService: I18nService,
private messagingService: MessagingService,
private stateService: StateService,
private biometricStateService: BiometricStateService,

View File

@ -7,7 +7,7 @@
{{ "premiumMembership" | i18n }}
</h1>
<div class="box-content box-content-padded">
<div *ngIf="!isPremium">
<div *ngIf="!(isPremium$ | async)">
<p class="text-center lead">{{ "premiumNotCurrentMember" | i18n }}</p>
<p>{{ "premiumSignUpAndGet" | i18n }}</p>
<ul class="bwi-ul">
@ -40,7 +40,7 @@
{{ "premiumPrice" | i18n: (price | currency: "$") }}
</p>
</div>
<div *ngIf="isPremium">
<div *ngIf="isPremium$ | async">
<p class="text-center lead">{{ "premiumCurrentMember" | i18n }}</p>
<p class="text-center">{{ "premiumCurrentMemberThanks" | i18n }}</p>
</div>
@ -48,7 +48,7 @@
</div>
</div>
<div class="modal-footer">
<button type="button" class="primary" (click)="manage()" *ngIf="isPremium">
<button type="button" class="primary" (click)="manage()" *ngIf="isPremium$ | async">
<b>{{ "premiumManage" | i18n }}</b>
</button>
<button
@ -56,13 +56,13 @@
type="button"
class="primary"
(click)="purchase()"
*ngIf="!isPremium"
*ngIf="!(isPremium$ | async)"
[disabled]="$any(purchaseBtn).loading"
>
<b>{{ "premiumPurchase" | i18n }}</b>
</button>
<button type="button" data-dismiss="modal">{{ "close" | i18n }}</button>
<div class="right" *ngIf="!isPremium">
<div class="right" *ngIf="!(isPremium$ | async)">
<button
#refreshBtn
type="button"

View File

@ -2,6 +2,7 @@ import { Component } from "@angular/core";
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@ -22,6 +23,7 @@ export class PremiumComponent extends BasePremiumComponent {
stateService: StateService,
dialogService: DialogService,
environmentService: EnvironmentService,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
i18nService,
@ -31,6 +33,7 @@ export class PremiumComponent extends BasePremiumComponent {
stateService,
dialogService,
environmentService,
billingAccountProfileStateService,
);
}
}

View File

@ -2,6 +2,7 @@ import { Component } from "@angular/core";
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -26,6 +27,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
stateService: StateService,
fileDownloadService: FileDownloadService,
dialogService: DialogService,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
cipherService,
@ -38,6 +40,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
stateService,
fileDownloadService,
dialogService,
billingAccountProfileStateService,
);
}
}

View File

@ -8,6 +8,7 @@ import {
ViewContainerRef,
} from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { Subject, takeUntil } from "rxjs";
import { first } from "rxjs/operators";
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
@ -15,6 +16,7 @@ import { ModalService } from "@bitwarden/angular/services/modal.service";
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EventType } from "@bitwarden/common/enums";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -84,6 +86,7 @@ export class VaultComponent implements OnInit, OnDestroy {
activeFilter: VaultFilter = new VaultFilter();
private modal: ModalRef = null;
private componentIsDestroyed$ = new Subject<boolean>();
constructor(
private route: ActivatedRoute,
@ -103,10 +106,16 @@ export class VaultComponent implements OnInit, OnDestroy {
private searchBarService: SearchBarService,
private apiService: ApiService,
private dialogService: DialogService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {}
async ngOnInit() {
this.userHasPremiumAccess = await this.stateService.getCanAccessPremium();
this.billingAccountProfileStateService.hasPremiumFromAnySource$
.pipe(takeUntil(this.componentIsDestroyed$))
.subscribe((canAccessPremium: boolean) => {
this.userHasPremiumAccess = canAccessPremium;
});
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
@ -229,6 +238,8 @@ export class VaultComponent implements OnInit, OnDestroy {
ngOnDestroy() {
this.searchBarService.setEnabled(false);
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
this.componentIsDestroyed$.next(true);
this.componentIsDestroyed$.complete();
}
async load() {

View File

@ -13,6 +13,7 @@ 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 { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
@ -58,6 +59,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
fileDownloadService: FileDownloadService,
dialogService: DialogService,
datePipe: DatePipe,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
cipherService,
@ -80,6 +82,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
fileDownloadService,
dialogService,
datePipe,
billingAccountProfileStateService,
);
}
ngOnInit() {

View File

@ -17,7 +17,7 @@ export class IsPaidOrgGuard implements CanActivate {
) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const org = this.organizationService.get(route.params.organizationId);
const org = await this.organizationService.get(route.params.organizationId);
if (org == null) {
return this.router.createUrlTree(["/"]);

View File

@ -66,7 +66,7 @@ describe("Organization Permissions Guard", () => {
it("permits navigation if no permissions are specified", async () => {
const org = orgFactory();
organizationService.get.calledWith(org.id).mockReturnValue(org);
organizationService.get.calledWith(org.id).mockResolvedValue(org);
const actual = await organizationPermissionsGuard.canActivate(route, state);
@ -81,7 +81,7 @@ describe("Organization Permissions Guard", () => {
};
const org = orgFactory();
organizationService.get.calledWith(org.id).mockReturnValue(org);
organizationService.get.calledWith(org.id).mockResolvedValue(org);
const actual = await organizationPermissionsGuard.canActivate(route, state);
@ -104,7 +104,7 @@ describe("Organization Permissions Guard", () => {
});
const org = orgFactory();
organizationService.get.calledWith(org.id).mockReturnValue(org);
organizationService.get.calledWith(org.id).mockResolvedValue(org);
const actual = await organizationPermissionsGuard.canActivate(route, state);
@ -124,7 +124,7 @@ describe("Organization Permissions Guard", () => {
}),
});
const org = orgFactory();
organizationService.get.calledWith(org.id).mockReturnValue(org);
organizationService.get.calledWith(org.id).mockResolvedValue(org);
const actual = await organizationPermissionsGuard.canActivate(route, state);
@ -141,7 +141,7 @@ describe("Organization Permissions Guard", () => {
type: OrganizationUserType.Admin,
enabled: false,
});
organizationService.get.calledWith(org.id).mockReturnValue(org);
organizationService.get.calledWith(org.id).mockResolvedValue(org);
const actual = await organizationPermissionsGuard.canActivate(route, state);
@ -153,7 +153,7 @@ describe("Organization Permissions Guard", () => {
type: OrganizationUserType.Owner,
enabled: false,
});
organizationService.get.calledWith(org.id).mockReturnValue(org);
organizationService.get.calledWith(org.id).mockResolvedValue(org);
const actual = await organizationPermissionsGuard.canActivate(route, state);

View File

@ -28,7 +28,7 @@ export class OrganizationPermissionsGuard implements CanActivate {
await this.syncService.fullSync(false);
}
const org = this.organizationService.get(route.params.organizationId);
const org = await this.organizationService.get(route.params.organizationId);
if (org == null) {
return this.router.createUrlTree(["/"]);
}

View File

@ -16,7 +16,7 @@ export class OrganizationRedirectGuard implements CanActivate {
) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const org = this.organizationService.get(route.params.organizationId);
const org = await this.organizationService.get(route.params.organizationId);
const customRedirect = route.data?.autoRedirectCallback;
if (customRedirect) {

View File

@ -2,6 +2,7 @@ import { Component, Input } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { BulkUserDetails } from "./bulk-status.component";
@ -14,7 +15,9 @@ export class BulkRemoveComponent {
@Input() organizationId: string;
@Input() set users(value: BulkUserDetails[]) {
this._users = value;
this.showNoMasterPasswordWarning = this._users.some((u) => u.hasMasterPassword === false);
this.showNoMasterPasswordWarning = this._users.some(
(u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false,
);
}
get users(): BulkUserDetails[] {

View File

@ -2,6 +2,7 @@ import { DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService } from "@bitwarden/components";
@ -37,7 +38,9 @@ export class BulkRestoreRevokeComponent {
this.isRevoking = data.isRevoking;
this.organizationId = data.organizationId;
this.users = data.users;
this.showNoMasterPasswordWarning = this.users.some((u) => u.hasMasterPassword === false);
this.showNoMasterPasswordWarning = this.users.some(
(u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false,
);
}
get bulkTitle() {

View File

@ -35,6 +35,7 @@ import {
PolicyType,
} from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
import { ProductType } from "@bitwarden/common/enums";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
@ -143,7 +144,7 @@ export class PeopleComponent
async ngOnInit() {
const organization$ = this.route.params.pipe(
map((params) => this.organizationService.get(params.organizationId)),
concatMap((params) => this.organizationService.get$(params.organizationId)),
shareReplay({ refCount: true, bufferSize: 1 }),
);
@ -155,7 +156,7 @@ export class PeopleComponent
switchMap((organization) => {
if (organization.isProviderUser) {
return from(this.policyApiService.getPolicies(organization.id)).pipe(
map((response) => this.policyService.mapPoliciesFromToken(response)),
map((response) => Policy.fromListResponse(response)),
);
}

View File

@ -1,9 +1,8 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, NavigationEnd, Router } from "@angular/router";
import { filter, map, Observable, startWith } from "rxjs";
import { filter, map, Observable, startWith, concatMap } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ReportVariant, reports, ReportType, ReportEntry } from "../../../tools/reports";
@ -17,7 +16,6 @@ export class ReportsHomeComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private stateService: StateService,
private organizationService: OrganizationService,
private router: Router,
) {}
@ -30,7 +28,7 @@ export class ReportsHomeComponent implements OnInit {
);
this.reports$ = this.route.params.pipe(
map((params) => this.organizationService.get(params.organizationId)),
concatMap((params) => this.organizationService.get$(params.organizationId)),
map((org) => this.buildReports(org.isFreeOrg)),
);
}

View File

@ -1,6 +1,6 @@
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { concatMap, takeUntil } from "rxjs";
import { concatMap, takeUntil, map } from "rxjs";
import { tap } from "rxjs/operators";
import { ModalService } from "@bitwarden/angular/services/modal.service";
@ -8,8 +8,8 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { TwoFactorDuoComponent } from "../../../auth/settings/two-factor-duo.component";
import { TwoFactorSetupComponent as BaseTwoFactorSetupComponent } from "../../../auth/settings/two-factor-setup.component";
@ -27,18 +27,29 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent {
messagingService: MessagingService,
policyService: PolicyService,
private route: ActivatedRoute,
stateService: StateService,
private organizationService: OrganizationService,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(apiService, modalService, messagingService, policyService, stateService);
super(
apiService,
modalService,
messagingService,
policyService,
billingAccountProfileStateService,
);
}
async ngOnInit() {
this.route.params
.pipe(
tap((params) => {
this.organizationId = params.organizationId;
this.organization = this.organizationService.get(this.organizationId);
concatMap((params) =>
this.organizationService
.get$(params.organizationId)
.pipe(map((organization) => ({ params, organization }))),
),
tap(async (mapResponse) => {
this.organizationId = mapResponse.params.organizationId;
this.organization = mapResponse.organization;
}),
concatMap(async () => await super.ngOnInit()),
takeUntil(this.destroy$),

View File

@ -9,9 +9,8 @@ import { Subject, switchMap, takeUntil, timer } from "rxjs";
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
@ -44,7 +43,6 @@ import {
SingleOrgPolicy,
TwoFactorAuthenticationPolicy,
} from "./admin-console/organizations/policies";
import { RouterService } from "./core";
const BroadcasterSubscriptionId = "AppComponent";
const IdleTimeout = 60000 * 10; // 10 minutes
@ -65,7 +63,6 @@ export class AppComponent implements OnDestroy, OnInit {
@Inject(DOCUMENT) private document: Document,
private broadcasterService: BroadcasterService,
private folderService: InternalFolderService,
private settingsService: SettingsService,
private syncService: SyncService,
private passwordGenerationService: PasswordGenerationServiceAbstraction,
private cipherService: CipherService,
@ -81,7 +78,6 @@ export class AppComponent implements OnDestroy, OnInit {
private sanitizer: DomSanitizer,
private searchService: SearchService,
private notificationsService: NotificationsService,
private routerService: RouterService,
private stateService: StateService,
private eventUploadService: EventUploadService,
private policyService: InternalPolicyService,
@ -92,7 +88,7 @@ export class AppComponent implements OnDestroy, OnInit {
private biometricStateService: BiometricStateService,
private stateEventRunnerService: StateEventRunnerService,
private paymentMethodWarningService: PaymentMethodWarningService,
private organizationService: OrganizationService,
private organizationService: InternalOrganizationServiceAbstraction,
) {}
ngOnInit() {

View File

@ -167,7 +167,7 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent {
qParams.email,
qParams.organizationUserId,
);
policyList = this.policyService.mapPoliciesFromToken(policies);
policyList = Policy.fromListResponse(policies);
} catch (e) {
this.logService.error(e);
}

View File

@ -24,7 +24,6 @@ import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/c
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
@ -60,7 +59,6 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
logService: LogService,
ngZone: NgZone,
protected stateService: StateService,
private messagingService: MessagingService,
private routerService: RouterService,
formBuilder: FormBuilder,
formValidationErrorService: FormValidationErrorsService,
@ -122,7 +120,7 @@ export class LoginComponent extends BaseLoginComponent implements OnInit {
invite.email,
invite.organizationUserId,
);
policyList = this.policyService.mapPoliciesFromToken(this.policies);
policyList = Policy.fromListResponse(this.policies);
} catch (e) {
this.logService.error(e);
}

View File

@ -7,7 +7,6 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { PasswordRequest } from "@bitwarden/common/auth/models/request/password.request";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@ -48,7 +47,6 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
private router: Router,
dialogService: DialogService,
private userVerificationService: UserVerificationService,
private configService: ConfigServiceAbstraction,
private keyRotationService: UserKeyRotationService,
) {
super(

View File

@ -2,6 +2,7 @@ import { Component } from "@angular/core";
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -30,6 +31,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen
logService: LogService,
fileDownloadService: FileDownloadService,
dialogService: DialogService,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
cipherService,
@ -42,6 +44,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen
stateService,
fileDownloadService,
dialogService,
billingAccountProfileStateService,
);
}

View File

@ -28,7 +28,7 @@
bitButton
buttonType="primary"
[bitAction]="invite"
[disabled]="!canAccessPremium"
[disabled]="!(canAccessPremium$ | async)"
>
<i aria-hidden="true" class="bwi bwi-plus bwi-fw"></i>
{{ "addEmergencyContact" | i18n }}

View File

@ -1,8 +1,9 @@
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { lastValueFrom } from "rxjs";
import { lastValueFrom, Observable, firstValueFrom } from "rxjs";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@ -44,7 +45,7 @@ export class EmergencyAccessComponent implements OnInit {
confirmModalRef: ViewContainerRef;
loaded = false;
canAccessPremium: boolean;
canAccessPremium$: Observable<boolean>;
trustedContacts: GranteeEmergencyAccess[];
grantedContacts: GrantorEmergencyAccess[];
emergencyAccessType = EmergencyAccessType;
@ -62,10 +63,12 @@ export class EmergencyAccessComponent implements OnInit {
private stateService: StateService,
private organizationService: OrganizationService,
protected dialogService: DialogService,
) {}
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
}
async ngOnInit() {
this.canAccessPremium = await this.stateService.getCanAccessPremium();
const orgs = await this.organizationService.getAll();
this.isOrganizationOwner = orgs.some((o) => o.isOwner);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
@ -80,18 +83,21 @@ export class EmergencyAccessComponent implements OnInit {
}
async premiumRequired() {
if (!this.canAccessPremium) {
const canAccessPremium = await firstValueFrom(this.canAccessPremium$);
if (!canAccessPremium) {
this.messagingService.send("premiumRequired");
return;
}
}
edit = async (details: GranteeEmergencyAccess) => {
const canAccessPremium = await firstValueFrom(this.canAccessPremium$);
const dialogRef = EmergencyAccessAddEditComponent.open(this.dialogService, {
data: {
name: this.userNamePipe.transform(details),
emergencyAccessId: details?.id,
readOnly: !this.canAccessPremium,
readOnly: !canAccessPremium,
},
});

View File

@ -5,6 +5,7 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@ -52,6 +53,7 @@ export class EmergencyAddEditCipherComponent extends BaseAddEditComponent {
dialogService: DialogService,
datePipe: DatePipe,
configService: ConfigServiceAbstraction,
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
super(
cipherService,
@ -73,6 +75,7 @@ export class EmergencyAddEditCipherComponent extends BaseAddEditComponent {
dialogService,
datePipe,
configService,
billingAccountProfileStateService,
);
}

View File

@ -70,7 +70,7 @@
type="button"
bitButton
buttonType="secondary"
[disabled]="!canAccessPremium && p.premium"
[disabled]="!(canAccessPremium$ | async) && p.premium"
(click)="manage(p.type)"
>
{{ "manage" | i18n }}

View File

@ -1,5 +1,5 @@
import { Component, OnDestroy, OnInit, Type, ViewChild, ViewContainerRef } from "@angular/core";
import { Subject, takeUntil } from "rxjs";
import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { ModalService } from "@bitwarden/angular/services/modal.service";
@ -9,9 +9,9 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { ProductType } from "@bitwarden/common/enums";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { TwoFactorAuthenticatorComponent } from "./two-factor-authenticator.component";
import { TwoFactorDuoComponent } from "./two-factor-duo.component";
@ -40,7 +40,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
organizationId: string;
organization: Organization;
providers: any[] = [];
canAccessPremium: boolean;
canAccessPremium$: Observable<boolean>;
showPolicyWarning = false;
loading = true;
modal: ModalRef;
@ -56,12 +56,12 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
protected modalService: ModalService,
protected messagingService: MessagingService,
protected policyService: PolicyService,
private stateService: StateService,
) {}
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
}
async ngOnInit() {
this.canAccessPremium = await this.stateService.getCanAccessPremium();
for (const key in TwoFactorProviders) {
// eslint-disable-next-line
if (!TwoFactorProviders.hasOwnProperty(key)) {
@ -174,7 +174,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
}
async premiumRequired() {
if (!this.canAccessPremium) {
if (!(await firstValueFrom(this.canAccessPremium$))) {
this.messagingService.send("premiumRequired");
return;
}

View File

@ -7,7 +7,6 @@ import { Subject, takeUntil } from "rxjs";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyData } from "@bitwarden/common/admin-console/models/data/policy.data";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { PlanType } from "@bitwarden/common/billing/enums";
@ -191,8 +190,7 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
invite.organizationUserId,
);
if (policies.data != null) {
const policiesData = policies.data.map((p) => new PolicyData(p));
this.policies = policiesData.map((p) => new Policy(p));
this.policies = Policy.fromListResponse(policies);
}
} catch (e) {
this.logService.error(e);

View File

@ -6,7 +6,7 @@
</div>
<bit-callout
type="info"
*ngIf="canAccessPremium"
*ngIf="canAccessPremium$ | async"
title="{{ 'youHavePremiumAccess' | i18n }}"
icon="bwi bwi-star-f"
>

View File

@ -1,14 +1,15 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { Router } from "@angular/router";
import { firstValueFrom, Observable } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { PaymentComponent, TaxInfoComponent } from "../shared";
@ -20,7 +21,7 @@ export class PremiumComponent implements OnInit {
@ViewChild(PaymentComponent) paymentComponent: PaymentComponent;
@ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent;
canAccessPremium = false;
canAccessPremium$: Observable<boolean>;
selfHosted = false;
premiumPrice = 10;
familyPlanMaxUserCount = 6;
@ -39,17 +40,16 @@ export class PremiumComponent implements OnInit {
private messagingService: MessagingService,
private syncService: SyncService,
private logService: LogService,
private stateService: StateService,
private environmentService: EnvironmentService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {
this.selfHosted = platformUtilsService.isSelfHost();
this.cloudWebVaultUrl = this.environmentService.getCloudWebVaultUrl();
this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$;
}
async ngOnInit() {
this.canAccessPremium = await this.stateService.getCanAccessPremium();
const premiumPersonally = await this.stateService.getHasPremiumPersonally();
if (premiumPersonally) {
if (await firstValueFrom(this.billingAccountProfileStateService.hasPremiumPersonally$)) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.router.navigate(["/settings/subscription/user-subscription"]);

View File

@ -1,6 +1,8 @@
<app-header>
<bit-tab-nav-bar slot="tabs" *ngIf="!selfHosted">
<bit-tab-link [route]="subscriptionRoute">{{ "subscription" | i18n }}</bit-tab-link>
<bit-tab-link [route]="(hasPremium$ | async) ? 'user-subscription' : 'premium'">{{
"subscription" | i18n
}}</bit-tab-link>
<bit-tab-link route="payment-method">{{ "paymentMethod" | i18n }}</bit-tab-link>
<bit-tab-link route="billing-history">{{ "billingHistory" | i18n }}</bit-tab-link>
</bit-tab-nav-bar>

View File

@ -1,26 +1,24 @@
import { Component } from "@angular/core";
import { Component, OnInit } from "@angular/core";
import { Observable } from "rxjs";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
@Component({
templateUrl: "subscription.component.html",
})
export class SubscriptionComponent {
hasPremium: boolean;
export class SubscriptionComponent implements OnInit {
hasPremium$: Observable<boolean>;
selfHosted: boolean;
constructor(
private stateService: StateService,
private platformUtilsService: PlatformUtilsService,
) {}
billingAccountProfileStateService: BillingAccountProfileStateService,
) {
this.hasPremium$ = billingAccountProfileStateService.hasPremiumPersonally$;
}
async ngOnInit() {
this.hasPremium = await this.stateService.getHasPremiumPersonally();
ngOnInit() {
this.selfHosted = this.platformUtilsService.isSelfHost();
}
get subscriptionRoute(): string {
return this.hasPremium ? "user-subscription" : "premium";
}
}

View File

@ -1,8 +1,9 @@
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { lastValueFrom, Observable } from "rxjs";
import { firstValueFrom, lastValueFrom, Observable } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigServiceAbstraction as ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
@ -11,7 +12,6 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil
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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { DialogService } from "@bitwarden/components";
import {
@ -37,7 +37,6 @@ export class UserSubscriptionComponent implements OnInit {
presentUserWithOffboardingSurvey$: Observable<boolean>;
constructor(
private stateService: StateService,
private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
@ -47,6 +46,7 @@ export class UserSubscriptionComponent implements OnInit {
private dialogService: DialogService,
private environmentService: EnvironmentService,
private configService: ConfigService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {
this.selfHosted = platformUtilsService.isSelfHost();
this.cloudWebVaultUrl = this.environmentService.getCloudWebVaultUrl();
@ -65,8 +65,7 @@ export class UserSubscriptionComponent implements OnInit {
return;
}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
if (this.stateService.getHasPremiumPersonally()) {
if (await firstValueFrom(this.billingAccountProfileStateService.hasPremiumPersonally$)) {
this.loading = true;
this.sub = await this.apiService.getUserSubscription();
} else {

View File

@ -150,7 +150,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
async ngOnInit() {
if (this.organizationId) {
this.organization = this.organizationService.get(this.organizationId);
this.organization = await this.organizationService.get(this.organizationId);
this.billing = await this.organizationApiService.getBilling(this.organizationId);
this.sub = await this.organizationApiService.getSubscription(this.organizationId);
}

View File

@ -94,7 +94,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
return;
}
this.loading = true;
this.userOrg = this.organizationService.get(this.organizationId);
this.userOrg = await this.organizationService.get(this.organizationId);
if (this.userOrg.canViewSubscription) {
this.sub = await this.organizationApiService.getSubscription(this.organizationId);
this.lineItems = this.sub?.subscription?.items;

View File

@ -110,7 +110,7 @@ export class OrganizationSubscriptionSelfhostComponent implements OnInit, OnDest
return;
}
this.loading = true;
this.userOrg = this.organizationService.get(this.organizationId);
this.userOrg = await this.organizationService.get(this.organizationId);
if (this.userOrg.canViewSubscription) {
const subscriptionResponse = await this.organizationApiService.getSubscription(
this.organizationId,

View File

@ -4,32 +4,39 @@ import {
RouterStateSnapshot,
Router,
CanActivateFn,
UrlTree,
} from "@angular/router";
import { Observable } from "rxjs";
import { tap } from "rxjs/operators";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
/**
* CanActivate guard that checks if the user has premium and otherwise triggers the "premiumRequired"
* message and blocks navigation.
*/
export function hasPremiumGuard(): CanActivateFn {
return async (_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot) => {
return (
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot,
): Observable<boolean | UrlTree> => {
const router = inject(Router);
const stateService = inject(StateService);
const messagingService = inject(MessagingService);
const billingAccountProfileStateService = inject(BillingAccountProfileStateService);
const userHasPremium = await stateService.getCanAccessPremium();
if (!userHasPremium) {
messagingService.send("premiumRequired");
}
// Prevent trapping the user on the login page, since that's an awful UX flow
if (!userHasPremium && router.url === "/login") {
return router.createUrlTree(["/"]);
}
return userHasPremium;
return billingAccountProfileStateService.hasPremiumFromAnySource$.pipe(
tap((userHasPremium: boolean) => {
if (!userHasPremium) {
messagingService.send("premiumRequired");
}
}),
// Prevent trapping the user on the login page, since that's an awful UX flow
tap((userHasPremium: boolean) => {
if (!userHasPremium && router.url === "/login") {
return router.createUrlTree(["/"]);
}
}),
);
};
}

View File

@ -7,6 +7,7 @@ import {
STATE_SERVICE_USE_CACHE,
} from "@bitwarden/angular/services/injection-tokens";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import {
@ -33,6 +34,7 @@ export class StateService extends BaseStateService<GlobalState, Account> {
@Inject(STATE_FACTORY) stateFactory: StateFactory<GlobalState, Account>,
accountService: AccountService,
environmentService: EnvironmentService,
tokenService: TokenService,
migrationRunner: MigrationRunner,
@Inject(STATE_SERVICE_USE_CACHE) useAccountCache = true,
) {
@ -44,6 +46,7 @@ export class StateService extends BaseStateService<GlobalState, Account> {
stateFactory,
accountService,
environmentService,
tokenService,
migrationRunner,
useAccountCache,
);

View File

@ -1,15 +1,16 @@
import { CommonModule } from "@angular/common";
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { RouterModule } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { ConfigServiceAbstraction as ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { IconModule, LayoutComponent, NavigationModule } from "@bitwarden/components";
@ -48,10 +49,10 @@ export class UserLayoutComponent implements OnInit, OnDestroy {
private ngZone: NgZone,
private platformUtilsService: PlatformUtilsService,
private organizationService: OrganizationService,
private stateService: StateService,
private apiService: ApiService,
private syncService: SyncService,
private configService: ConfigService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
) {}
async ngOnInit() {
@ -79,16 +80,21 @@ export class UserLayoutComponent implements OnInit, OnDestroy {
}
async load() {
const premium = await this.stateService.getHasPremiumPersonally();
const hasPremiumPersonally = await firstValueFrom(
this.billingAccountProfileStateService.hasPremiumPersonally$,
);
const hasPremiumFromOrg = await firstValueFrom(
this.billingAccountProfileStateService.hasPremiumFromAnyOrganization$,
);
const selfHosted = this.platformUtilsService.isSelfHost();
this.hasFamilySponsorshipAvailable = await this.organizationService.canManageSponsorships();
const hasPremiumFromOrg = await this.stateService.getHasPremiumFromOrganization();
let billing = null;
if (!selfHosted) {
// TODO: We should remove the need to call this!
billing = await this.apiService.getUserBillingHistory();
}
this.hideSubscription = !premium && hasPremiumFromOrg && (selfHosted || billing?.hasNoHistory);
this.hideSubscription =
!hasPremiumPersonally && hasPremiumFromOrg && (selfHosted || billing?.hasNoHistory);
}
}

View File

@ -2,15 +2,13 @@ import { Component, OnInit } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { concatMap, filter, firstValueFrom, map, Observable, Subject, takeUntil, tap } from "rxjs";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ThemeType } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
@ -48,13 +46,11 @@ export class PreferencesComponent implements OnInit {
constructor(
private formBuilder: FormBuilder,
private policyService: PolicyService,
private stateService: StateService,
private i18nService: I18nService,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
private platformUtilsService: PlatformUtilsService,
private messagingService: MessagingService,
private themeStateService: ThemeStateService,
private settingsService: SettingsService,
private domainSettingsService: DomainSettingsService,
private dialogService: DialogService,
) {
this.vaultTimeoutOptions = [
@ -139,7 +135,7 @@ export class PreferencesComponent implements OnInit {
vaultTimeoutAction: await firstValueFrom(
this.vaultTimeoutSettingsService.vaultTimeoutAction$(),
),
enableFavicons: !(await this.settingsService.getDisableFavicon()),
enableFavicons: await firstValueFrom(this.domainSettingsService.showFavicons$),
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
locale: (await firstValueFrom(this.i18nService.userSetLocale$)) ?? null,
};
@ -162,7 +158,7 @@ export class PreferencesComponent implements OnInit {
values.vaultTimeout,
values.vaultTimeoutAction,
);
await this.settingsService.setDisableFavicon(!values.enableFavicons);
await this.domainSettingsService.setShowFavicons(values.enableFavicons);
await this.themeStateService.setSelectedTheme(values.theme);
await this.i18nService.setLocale(values.locale);
if (values.locale !== this.startingLocale) {

View File

@ -1,13 +1,12 @@
import { Component, NgZone, OnDestroy, OnInit } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "../core";
const BroadcasterSubscriptionId = "SettingsComponent";
@Component({
@ -21,13 +20,12 @@ export class SettingsComponent implements OnInit, OnDestroy {
hideSubscription: boolean;
constructor(
private tokenService: TokenService,
private broadcasterService: BroadcasterService,
private ngZone: NgZone,
private platformUtilsService: PlatformUtilsService,
private organizationService: OrganizationService,
private stateService: StateService,
private apiService: ApiService,
private billingAccountProfileStateServiceAbstraction: BillingAccountProfileStateService,
) {}
async ngOnInit() {
@ -53,9 +51,13 @@ export class SettingsComponent implements OnInit, OnDestroy {
}
async load() {
this.premium = await this.stateService.getHasPremiumPersonally();
this.premium = await firstValueFrom(
this.billingAccountProfileStateServiceAbstraction.hasPremiumPersonally$,
);
this.hasFamilySponsorshipAvailable = await this.organizationService.canManageSponsorships();
const hasPremiumFromOrg = await this.stateService.getHasPremiumFromOrganization();
const hasPremiumFromOrg = await firstValueFrom(
this.billingAccountProfileStateServiceAbstraction.hasPremiumFromAnyOrganization$,
);
let billing = null;
if (!this.selfHosted) {
billing = await this.apiService.getUserBillingHistory();

View File

@ -8,6 +8,7 @@ import localeBn from "@angular/common/locales/bn";
import localeBs from "@angular/common/locales/bs";
import localeCa from "@angular/common/locales/ca";
import localeCs from "@angular/common/locales/cs";
import localeCy from "@angular/common/locales/cy";
import localeDa from "@angular/common/locales/da";
import localeDe from "@angular/common/locales/de";
import localeEl from "@angular/common/locales/el";
@ -17,9 +18,11 @@ import localeEo from "@angular/common/locales/eo";
import localeEs from "@angular/common/locales/es";
import localeEt from "@angular/common/locales/et";
import localeEu from "@angular/common/locales/eu";
import localeFa from "@angular/common/locales/fa";
import localeFi from "@angular/common/locales/fi";
import localeFil from "@angular/common/locales/fil";
import localeFr from "@angular/common/locales/fr";
import localeGl from "@angular/common/locales/gl";
import localeHe from "@angular/common/locales/he";
import localeHi from "@angular/common/locales/hi";
import localeHr from "@angular/common/locales/hr";
@ -33,9 +36,13 @@ import localeKn from "@angular/common/locales/kn";
import localeKo from "@angular/common/locales/ko";
import localeLv from "@angular/common/locales/lv";
import localeMl from "@angular/common/locales/ml";
import localeMr from "@angular/common/locales/mr";
import localeMy from "@angular/common/locales/my";
import localeNb from "@angular/common/locales/nb";
import localeNe from "@angular/common/locales/ne";
import localeNl from "@angular/common/locales/nl";
import localeNn from "@angular/common/locales/nn";
import localeOr from "@angular/common/locales/or";
import localePl from "@angular/common/locales/pl";
import localePtBr from "@angular/common/locales/pt";
import localePtPt from "@angular/common/locales/pt-PT";
@ -46,6 +53,8 @@ import localeSk from "@angular/common/locales/sk";
import localeSl from "@angular/common/locales/sl";
import localeSr from "@angular/common/locales/sr";
import localeSv from "@angular/common/locales/sv";
import localeTe from "@angular/common/locales/te";
import localeTh from "@angular/common/locales/th";
import localeTr from "@angular/common/locales/tr";
import localeUk from "@angular/common/locales/uk";
import localeVi from "@angular/common/locales/vi";
@ -61,6 +70,7 @@ registerLocaleData(localeBn, "bn");
registerLocaleData(localeBs, "bs");
registerLocaleData(localeCa, "ca");
registerLocaleData(localeCs, "cs");
registerLocaleData(localeCy, "cy");
registerLocaleData(localeDa, "da");
registerLocaleData(localeDe, "de");
registerLocaleData(localeEl, "el");
@ -70,9 +80,11 @@ registerLocaleData(localeEo, "eo");
registerLocaleData(localeEs, "es");
registerLocaleData(localeEt, "et");
registerLocaleData(localeEu, "eu");
registerLocaleData(localeFa, "fa");
registerLocaleData(localeFi, "fi");
registerLocaleData(localeFil, "fil");
registerLocaleData(localeFr, "fr");
registerLocaleData(localeGl, "gl");
registerLocaleData(localeHe, "he");
registerLocaleData(localeHi, "hi");
registerLocaleData(localeHr, "hr");
@ -86,9 +98,13 @@ registerLocaleData(localeKn, "kn");
registerLocaleData(localeKo, "ko");
registerLocaleData(localeLv, "lv");
registerLocaleData(localeMl, "ml");
registerLocaleData(localeMr, "mr");
registerLocaleData(localeMy, "my");
registerLocaleData(localeNb, "nb");
registerLocaleData(localeNe, "ne");
registerLocaleData(localeNl, "nl");
registerLocaleData(localeNn, "nn");
registerLocaleData(localeOr, "or");
registerLocaleData(localePl, "pl");
registerLocaleData(localePtBr, "pt-BR");
registerLocaleData(localePtPt, "pt-PT");
@ -99,6 +115,8 @@ registerLocaleData(localeSk, "sk");
registerLocaleData(localeSl, "sl");
registerLocaleData(localeSr, "sr");
registerLocaleData(localeSv, "sv");
registerLocaleData(localeTe, "te");
registerLocaleData(localeTh, "th");
registerLocaleData(localeTr, "tr");
registerLocaleData(localeUk, "uk");
registerLocaleData(localeVi, "vi");

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