1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 00:03:56 +00:00

[PM-18566] Wire up vNextPolicyService for Clients (#13678)

* wire up vNext impl

* wire up vNextPolicyService for browser

* wire up vNextPolicyService for desktop

* wire up vNextPolicyService for cli

* fix test

* fix missed caller

* cleanup

* fix missing property assignment

* fix QA bug for PM-19205

* fix QA bug for PM-19206

* fix QA bug for pm-19228

* cleanup
This commit is contained in:
Brandon Treston
2025-03-25 11:30:47 -04:00
committed by GitHub
parent a9fd16968f
commit 0fd01ed7ee
84 changed files with 723 additions and 1246 deletions

View File

@@ -99,7 +99,7 @@ describe("AccountSecurityComponent", () => {
it("pin enabled when RemoveUnlockWithPin policy is not set", async () => {
// @ts-strict-ignore
policyService.get$.mockReturnValue(of(null));
policyService.policiesByType$.mockReturnValue(of([null]));
await component.ngOnInit();
@@ -111,7 +111,7 @@ describe("AccountSecurityComponent", () => {
policy.type = PolicyType.RemoveUnlockWithPin;
policy.enabled = false;
policyService.get$.mockReturnValue(of(policy));
policyService.policiesByType$.mockReturnValue(of([policy]));
await component.ngOnInit();
@@ -129,7 +129,7 @@ describe("AccountSecurityComponent", () => {
policy.type = PolicyType.RemoveUnlockWithPin;
policy.enabled = true;
policyService.get$.mockReturnValue(of(policy));
policyService.policiesByType$.mockReturnValue(of([policy]));
await component.ngOnInit();
@@ -143,7 +143,7 @@ describe("AccountSecurityComponent", () => {
it("pin visible when RemoveUnlockWithPin policy is not set", async () => {
// @ts-strict-ignore
policyService.get$.mockReturnValue(of(null));
policyService.policiesByType$.mockReturnValue(of([null]));
await component.ngOnInit();
fixture.detectChanges();
@@ -158,7 +158,7 @@ describe("AccountSecurityComponent", () => {
policy.type = PolicyType.RemoveUnlockWithPin;
policy.enabled = false;
policyService.get$.mockReturnValue(of(policy));
policyService.policiesByType$.mockReturnValue(of([policy]));
await component.ngOnInit();
fixture.detectChanges();
@@ -173,7 +173,7 @@ describe("AccountSecurityComponent", () => {
policy.type = PolicyType.RemoveUnlockWithPin;
policy.enabled = true;
policyService.get$.mockReturnValue(of(policy));
policyService.policiesByType$.mockReturnValue(of([policy]));
pinServiceAbstraction.isPinSet.mockResolvedValue(true);
@@ -190,7 +190,7 @@ describe("AccountSecurityComponent", () => {
policy.type = PolicyType.RemoveUnlockWithPin;
policy.enabled = true;
policyService.get$.mockReturnValue(of(policy));
policyService.policiesByType$.mockReturnValue(of([policy]));
await component.ngOnInit();
fixture.detectChanges();

View File

@@ -27,8 +27,10 @@ import { FingerprintDialogComponent, VaultTimeoutInputComponent } from "@bitward
import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { DeviceType } from "@bitwarden/common/enums";
import {
VaultTimeout,
@@ -152,8 +154,14 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
const hasMasterPassword = await this.userVerificationService.hasMasterPassword();
this.showMasterPasswordOnClientRestartOption = hasMasterPassword;
const maximumVaultTimeoutPolicy = this.policyService.get$(PolicyType.MaximumVaultTimeout);
if ((await firstValueFrom(this.policyService.get$(PolicyType.MaximumVaultTimeout))) != null) {
const maximumVaultTimeoutPolicy = this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId),
),
getFirstPolicy,
);
if ((await firstValueFrom(maximumVaultTimeoutPolicy)) != null) {
this.hasVaultTimeoutPolicy = true;
}
@@ -195,7 +203,12 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
timeout = VaultTimeoutStringType.OnRestart;
}
this.pinEnabled$ = this.policyService.get$(PolicyType.RemoveUnlockWithPin).pipe(
this.pinEnabled$ = this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policiesByType$(PolicyType.RemoveUnlockWithPin, userId),
),
getFirstPolicy,
map((policy) => {
return policy == null || !policy.enabled;
}),

View File

@@ -8,6 +8,9 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { BrowserApi } from "../../platform/browser/browser-api";
import { ScriptInjectorService } from "../../platform/services/abstractions/script-injector.service";
@@ -35,10 +38,12 @@ describe("AutoSubmitLoginBackground", () => {
let configService: MockProxy<ConfigService>;
let platformUtilsService: MockProxy<PlatformUtilsService>;
let policyDetails: MockProxy<Policy>;
let automaticAppLogInPolicy$: BehaviorSubject<Policy>;
let policyAppliesToActiveUser$: BehaviorSubject<boolean>;
let automaticAppLogInPolicy$: BehaviorSubject<Policy[]>;
let policyAppliesToUser$: BehaviorSubject<boolean>;
let policyService: MockProxy<PolicyService>;
let autoSubmitLoginBackground: AutoSubmitLoginBackground;
let accountService: FakeAccountService;
const mockUserId = Utils.newGuid() as UserId;
const validIpdUrl1 = "https://example.com";
const validIpdUrl2 = "https://subdomain.example3.com";
const validAutoSubmitHost = "some-valid-url.com";
@@ -61,12 +66,13 @@ describe("AutoSubmitLoginBackground", () => {
idpHost: `${validIpdUrl1} , https://example2.com/some/sub-route ,${validIpdUrl2}, [invalidValue] ,,`,
},
});
automaticAppLogInPolicy$ = new BehaviorSubject<Policy>(policyDetails);
policyAppliesToActiveUser$ = new BehaviorSubject<boolean>(true);
automaticAppLogInPolicy$ = new BehaviorSubject<Policy[]>([policyDetails]);
policyAppliesToUser$ = new BehaviorSubject<boolean>(true);
policyService = mock<PolicyService>({
get$: jest.fn().mockReturnValue(automaticAppLogInPolicy$),
policyAppliesToActiveUser$: jest.fn().mockReturnValue(policyAppliesToActiveUser$),
policiesByType$: jest.fn().mockReturnValue(automaticAppLogInPolicy$),
policyAppliesToUser$: jest.fn().mockReturnValue(policyAppliesToUser$),
});
accountService = mockAccountServiceWith(mockUserId);
autoSubmitLoginBackground = new AutoSubmitLoginBackground(
logService,
autofillService,
@@ -75,6 +81,7 @@ describe("AutoSubmitLoginBackground", () => {
configService,
platformUtilsService,
policyService,
accountService,
);
});
@@ -84,7 +91,7 @@ describe("AutoSubmitLoginBackground", () => {
describe("when the AutoSubmitLoginBackground feature is disabled", () => {
it("destroys all event listeners when the AutomaticAppLogIn policy is not enabled", async () => {
automaticAppLogInPolicy$.next(mock<Policy>({ ...policyDetails, enabled: false }));
automaticAppLogInPolicy$.next([mock<Policy>({ ...policyDetails, enabled: false })]);
await autoSubmitLoginBackground.init();
@@ -92,7 +99,7 @@ describe("AutoSubmitLoginBackground", () => {
});
it("destroys all event listeners when the AutomaticAppLogIn policy does not apply to the current user", async () => {
policyAppliesToActiveUser$.next(false);
policyAppliesToUser$.next(false);
await autoSubmitLoginBackground.init();
@@ -100,7 +107,7 @@ describe("AutoSubmitLoginBackground", () => {
});
it("destroys all event listeners when the idpHost is not specified in the AutomaticAppLogIn policy", async () => {
automaticAppLogInPolicy$.next(mock<Policy>({ ...policyDetails, data: { idpHost: "" } }));
automaticAppLogInPolicy$.next([mock<Policy>({ ...policyDetails, data: { idpHost: "" } })]);
await autoSubmitLoginBackground.init();
@@ -264,6 +271,7 @@ describe("AutoSubmitLoginBackground", () => {
configService,
platformUtilsService,
policyService,
accountService,
);
jest.spyOn(BrowserApi, "getTabFromCurrentWindow").mockResolvedValue(tab);
});

View File

@@ -1,12 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { firstValueFrom } from "rxjs";
import { firstValueFrom, switchMap } from "rxjs";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { getFirstPolicy } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -42,6 +45,7 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
private configService: ConfigService,
private platformUtilsService: PlatformUtilsService,
private policyService: PolicyService,
private accountService: AccountService,
) {
this.isSafariBrowser = this.platformUtilsService.isSafari();
}
@@ -56,8 +60,14 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
FeatureFlag.IdpAutoSubmitLogin,
);
if (featureFlagEnabled) {
this.policyService
.get$(PolicyType.AutomaticAppLogIn)
this.accountService.activeAccount$
.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policiesByType$(PolicyType.AutomaticAppLogIn, userId),
),
getFirstPolicy,
)
.subscribe(this.handleAutoSubmitLoginPolicySubscription.bind(this));
}
}
@@ -86,7 +96,12 @@ export class AutoSubmitLoginBackground implements AutoSubmitLoginBackgroundAbstr
*/
private applyPolicyToActiveUser = async (policy: Policy) => {
const policyAppliesToUser = await firstValueFrom(
this.policyService.policyAppliesToActiveUser$(PolicyType.AutomaticAppLogIn),
this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policyAppliesToUser$(PolicyType.AutomaticAppLogIn, userId),
),
),
);
if (!policyAppliesToUser) {

View File

@@ -2,7 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
@@ -51,7 +51,7 @@ describe("NotificationBackground", () => {
const cipherService = mock<CipherService>();
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
let authService: MockProxy<AuthService>;
const policyService = mock<PolicyService>();
const policyService = mock<DefaultPolicyService>();
const folderService = mock<FolderService>();
const userNotificationSettingsService = mock<UserNotificationSettingsService>();
const domainSettingsService = mock<DomainSettingsService>();

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { firstValueFrom } from "rxjs";
import { firstValueFrom, switchMap } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@@ -8,7 +8,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { getOptionalUserId } from "@bitwarden/common/auth/services/account.service";
import { getOptionalUserId, getUserId } from "@bitwarden/common/auth/services/account.service";
import {
ExtensionCommand,
ExtensionCommandType,
@@ -743,7 +743,12 @@ export default class NotificationBackground {
private async removeIndividualVault(): Promise<boolean> {
return await firstValueFrom(
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
),
),
);
}

View File

@@ -26,8 +26,8 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
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 { DefaultOrganizationService } from "@bitwarden/common/admin-console/services/organization/default-organization.service";
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.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";
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
@@ -685,7 +685,7 @@ export default class MainBackground {
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
this.organizationService = new DefaultOrganizationService(this.stateProvider);
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
this.policyService = new DefaultPolicyService(this.stateProvider, this.organizationService);
this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService(
this.accountService,
@@ -728,9 +728,14 @@ export default class MainBackground {
this.autofillSettingsService = new AutofillSettingsService(
this.stateProvider,
this.policyService,
this.accountService,
);
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
this.policyApiService = new PolicyApiService(
this.policyService,
this.apiService,
this.accountService,
);
this.keyConnectorService = new KeyConnectorService(
this.accountService,
this.masterPasswordService,
@@ -1202,6 +1207,7 @@ export default class MainBackground {
this.configService,
this.platformUtilsService,
this.policyService,
this.accountService,
);
const contextMenuClickedHandler = new ContextMenuClickedHandler(

View File

@@ -51,7 +51,7 @@ describe("FamiliesPolicyService", () => {
organizationService.organizations$.mockReturnValue(of(organizations));
const policies = [{ organizationId: "org1", enabled: true }] as Policy[];
policyService.getAll$.mockReturnValue(of(policies));
policyService.policiesByType$.mockReturnValue(of(policies));
const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$());
expect(result).toBe(true);
@@ -64,7 +64,7 @@ describe("FamiliesPolicyService", () => {
organizationService.organizations$.mockReturnValue(of(organizations));
const policies = [{ organizationId: "org1", enabled: false }] as Policy[];
policyService.getAll$.mockReturnValue(of(policies));
policyService.policiesByType$.mockReturnValue(of(policies));
const result = await firstValueFrom(service.isFreeFamilyPolicyEnabled$());
expect(result).toBe(false);

View File

@@ -47,7 +47,7 @@ export class FamiliesPolicyService {
map((organizations) => organizations.find((org) => org.canManageSponsorships)?.id),
switchMap((enterpriseOrgId) =>
this.policyService
.getAll$(PolicyType.FreeFamiliesSponsorshipPolicy, userId)
.policiesByType$(PolicyType.FreeFamiliesSponsorshipPolicy, userId)
.pipe(
map(
(policies) =>

View File

@@ -482,7 +482,7 @@ const safeProviders: SafeProvider[] = [
safeProvider({
provide: AutofillSettingsServiceAbstraction,
useClass: AutofillSettingsService,
deps: [StateProvider, PolicyService],
deps: [StateProvider, PolicyService, AccountService],
}),
safeProvider({
provide: UserNotificationSettingsServiceAbstraction,

View File

@@ -64,7 +64,7 @@ describe("SendV2Component", () => {
});
policyService = mock<PolicyService>();
policyService.policyAppliesToActiveUser$.mockReturnValue(of(true)); // Return `true` by default
policyService.policyAppliesToUser$.mockReturnValue(of(true)); // Return `true` by default
sendListFiltersService = new SendListFiltersService(mock(), new FormBuilder());

View File

@@ -2,11 +2,13 @@ import { CommonModule } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { RouterLink } from "@angular/router";
import { combineLatest } from "rxjs";
import { combineLatest, switchMap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { ButtonModule, CalloutModule, Icons, NoItemsModule } from "@bitwarden/components";
import {
@@ -66,6 +68,7 @@ export class SendV2Component implements OnInit, OnDestroy {
protected sendItemsService: SendItemsService,
protected sendListFiltersService: SendListFiltersService,
private policyService: PolicyService,
private accountService: AccountService,
) {
combineLatest([
this.sendItemsService.emptyList$,
@@ -93,9 +96,14 @@ export class SendV2Component implements OnInit, OnDestroy {
this.listState = null;
});
this.policyService
.policyAppliesToActiveUser$(PolicyType.DisableSend)
.pipe(takeUntilDestroyed())
this.accountService.activeAccount$
.pipe(
getUserId,
switchMap((userId) =>
this.policyService.policyAppliesToUser$(PolicyType.DisableSend, userId),
),
takeUntilDestroyed(),
)
.subscribe((sendsDisabled) => {
this.sendsDisabled = sendsDisabled;
});

View File

@@ -36,7 +36,7 @@ describe("VaultPopupListFiltersService", () => {
let folderViews$ = new BehaviorSubject([]);
const cipherViews$ = new BehaviorSubject({});
let decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]);
const policyAppliesToActiveUser$ = new BehaviorSubject<boolean>(false);
const policyAppliesToUser$ = new BehaviorSubject<boolean>(false);
let viewCacheService: {
signal: jest.Mock;
mockSignal: WritableSignal<CachedFilterState>;
@@ -65,7 +65,7 @@ describe("VaultPopupListFiltersService", () => {
} as I18nService;
const policyService = {
policyAppliesToActiveUser$: jest.fn(() => policyAppliesToActiveUser$),
policyAppliesToUser$: jest.fn(() => policyAppliesToUser$),
};
const state$ = new BehaviorSubject<boolean>(false);
@@ -75,8 +75,8 @@ describe("VaultPopupListFiltersService", () => {
_memberOrganizations$ = new BehaviorSubject<Organization[]>([]); // Fresh instance per test
folderViews$ = new BehaviorSubject([]); // Fresh instance per test
decryptedCollections$ = new BehaviorSubject<CollectionView[]>([]); // Fresh instance per test
policyAppliesToActiveUser$.next(false);
policyService.policyAppliesToActiveUser$.mockClear();
policyAppliesToUser$.next(false);
policyService.policyAppliesToUser$.mockClear();
const accountService = mockAccountServiceWith("userId" as UserId);
const mockCachedSignal = createMockSignal<CachedFilterState>({});
@@ -196,14 +196,15 @@ describe("VaultPopupListFiltersService", () => {
});
describe("PersonalOwnership policy", () => {
it('calls policyAppliesToActiveUser$ with "PersonalOwnership"', () => {
expect(policyService.policyAppliesToActiveUser$).toHaveBeenCalledWith(
it('calls policyAppliesToUser$ with "PersonalOwnership"', () => {
expect(policyService.policyAppliesToUser$).toHaveBeenCalledWith(
PolicyType.PersonalOwnership,
"userId",
);
});
it("returns an empty array when the policy applies and there is a single organization", (done) => {
policyAppliesToActiveUser$.next(true);
policyAppliesToUser$.next(true);
_memberOrganizations$.next([
{ name: "bobby's org", id: "1234-3323-23223" },
] as Organization[]);
@@ -215,7 +216,7 @@ describe("VaultPopupListFiltersService", () => {
});
it('adds "myVault" when the policy does not apply and there are multiple organizations', (done) => {
policyAppliesToActiveUser$.next(false);
policyAppliesToUser$.next(false);
const orgs = [
{ name: "bobby's org", id: "1234-3323-23223" },
{ name: "alice's org", id: "2223-4343-99888" },
@@ -234,7 +235,7 @@ describe("VaultPopupListFiltersService", () => {
});
it('does not add "myVault" the policy applies and there are multiple organizations', (done) => {
policyAppliesToActiveUser$.next(true);
policyAppliesToUser$.next(true);
const orgs = [
{ name: "bobby's org", id: "1234-3323-23223" },
{ name: "alice's org", id: "2223-3242-99888" },
@@ -679,7 +680,7 @@ function createSeededVaultPopupListFiltersService(
} as any;
const policyServiceMock = {
policyAppliesToActiveUser$: jest.fn(() => new BehaviorSubject(false)),
policyAppliesToUser$: jest.fn(() => new BehaviorSubject(false)),
} as any;
const stateProviderMock = {

View File

@@ -8,7 +8,6 @@ import {
filter,
map,
Observable,
of,
shareReplay,
startWith,
switchMap,
@@ -23,6 +22,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -288,68 +288,70 @@ export class VaultPopupListFiltersService {
/**
* Organization array structured to be directly passed to `ChipSelectComponent`
*/
organizations$: Observable<ChipSelectOption<Organization>[]> = combineLatest([
organizations$: Observable<ChipSelectOption<Organization>[]> =
this.accountService.activeAccount$.pipe(
switchMap((account) =>
account === null ? of([]) : this.organizationService.memberOrganizations$(account.id),
getUserId,
switchMap((userId) =>
combineLatest([
this.organizationService.memberOrganizations$(userId),
this.policyService.policyAppliesToUser$(PolicyType.PersonalOwnership, userId),
]),
),
),
this.policyService.policyAppliesToActiveUser$(PolicyType.PersonalOwnership),
]).pipe(
map(([orgs, personalOwnershipApplies]): [Organization[], boolean] => [
orgs.sort(Utils.getSortFunction(this.i18nService, "name")),
personalOwnershipApplies,
]),
map(([orgs, personalOwnershipApplies]) => {
// When there are no organizations return an empty array,
// resulting in the org filter being hidden
if (!orgs.length) {
return [];
}
map(([orgs, personalOwnershipApplies]): [Organization[], boolean] => [
orgs.sort(Utils.getSortFunction(this.i18nService, "name")),
personalOwnershipApplies,
]),
map(([orgs, personalOwnershipApplies]) => {
// When there are no organizations return an empty array,
// resulting in the org filter being hidden
if (!orgs.length) {
return [];
}
// When there is only one organization and personal ownership policy applies,
// return an empty array, resulting in the org filter being hidden
if (orgs.length === 1 && personalOwnershipApplies) {
return [];
}
// When there is only one organization and personal ownership policy applies,
// return an empty array, resulting in the org filter being hidden
if (orgs.length === 1 && personalOwnershipApplies) {
return [];
}
const myVaultOrg: ChipSelectOption<Organization>[] = [];
const myVaultOrg: ChipSelectOption<Organization>[] = [];
// Only add "My vault" if personal ownership policy does not apply
if (!personalOwnershipApplies) {
myVaultOrg.push({
value: { id: MY_VAULT_ID } as Organization,
label: this.i18nService.t("myVault"),
icon: "bwi-user",
});
}
// Only add "My vault" if personal ownership policy does not apply
if (!personalOwnershipApplies) {
myVaultOrg.push({
value: { id: MY_VAULT_ID } as Organization,
label: this.i18nService.t("myVault"),
icon: "bwi-user",
});
}
return [
...myVaultOrg,
...orgs.map((org) => {
let icon = "bwi-business";
return [
...myVaultOrg,
...orgs.map((org) => {
let icon = "bwi-business";
if (!org.enabled) {
// Show a warning icon if the organization is deactivated
icon = "bwi-exclamation-triangle tw-text-danger";
} else if (
org.productTierType === ProductTierType.Families ||
org.productTierType === ProductTierType.Free
) {
// Show a family icon if the organization is a family or free org
icon = "bwi-family";
}
if (!org.enabled) {
// Show a warning icon if the organization is deactivated
icon = "bwi-exclamation-triangle tw-text-danger";
} else if (
org.productTierType === ProductTierType.Families ||
org.productTierType === ProductTierType.Free
) {
// Show a family icon if the organization is a family or free org
icon = "bwi-family";
}
return {
value: org,
label: org.name,
icon,
};
}),
];
}),
shareReplay({ refCount: true, bufferSize: 1 }),
);
return {
value: org,
label: org.name,
icon,
};
}),
];
}),
shareReplay({ refCount: true, bufferSize: 1 }),
);
/**
* Folder array structured to be directly passed to `ChipSelectComponent`