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

[PM-15506] Implement vNextOrganizationService (#12839)

* [PM-15506] Wire up vNextOrganizationService for libs/common and libs/angular (#12683)

* Wire up vNextOrganizationService in PolicyService

* Wire vNextOrganizationService in SyncService

* wire vNextOrganizationService for EventCollectionService

* wire vNextOrganizationService for KeyConnectorService

* wire up vNextOrganizationService for CipherAuthorizationService

* Wire up vNextOrganizationService in PolicyService

* Wire vNextOrganizationService in SyncService

* wire vNextOrganizationService for EventCollectionService

* wire vNextOrganizationService for KeyConnectorService

* wire up vNextOrganizationService for CipherAuthorizationService

* wire vNextOrganizationService for share.component

* wire vNextOrganizationService for collections.component

* wire vNextOrganizationServcie for add-account-credit-dialog

* wire vNextOrganizationService for vault-filter.service

* fix browser errors for vNextOrganizationService implementation in libs

* fix desktop errors for vNextOrganizationService implementation for libs

* fix linter errors

* fix CLI errors on vNextOrganizationServcie implementations for libs

* [PM-15506] Wire up vNextOrganizationService for web client (#12810)

PR to a feature branch, no need to review until this goes to main.

* implement vNextOrganization service for browser client (#12844)

PR to feature branch, no need for review yet.

* wire vNextOrganizationService for licence and some web router guards

* wire vNextOrganizationService in tests

* remove vNext notation for OrganizationService and related

* Merge branch 'main' into ac/pm-15506-vNextOrganizationService

* fix tsstrict error

* fix test, fix ts strict error
This commit is contained in:
Brandon Treston
2025-01-22 15:20:25 -05:00
committed by GitHub
parent ba4d762dc1
commit a949f793ed
163 changed files with 1972 additions and 1246 deletions

View File

@@ -14,6 +14,8 @@ import { ProviderService } from "@bitwarden/common/admin-console/abstractions/pr
import { ProviderStatusType, ProviderUserType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { ProviderOrganizationOrganizationDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-organization.response";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { PlanType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
@@ -68,6 +70,7 @@ export class ClientsComponent {
private apiService: ApiService,
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction,
private accountService: AccountService,
private activatedRoute: ActivatedRoute,
private dialogService: DialogService,
private i18nService: I18nService,
@@ -136,13 +139,14 @@ export class ClientsComponent {
async load() {
const response = await this.apiService.getProviderClients(this.providerId);
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
const clients = response.data != null && response.data.length > 0 ? response.data : [];
this.dataSource.data = clients;
this.manageOrganizations =
(await this.providerService.get(this.providerId)).type === ProviderUserType.ProviderAdmin;
const candidateOrgs = (await this.organizationService.getAll()).filter(
(o) => o.isOwner && o.providerId == null,
);
const candidateOrgs = (
await firstValueFrom(this.organizationService.organizations$(userId))
).filter((o) => o.isOwner && o.providerId == null);
const allowedOrgsIds = await Promise.all(
candidateOrgs.map((o) => this.organizationApiService.get(o.id)),
).then((orgs) =>

View File

@@ -9,13 +9,17 @@ import {
Validators,
} from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { concatMap, Observable, Subject, takeUntil } from "rxjs";
import { concatMap, firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { ControlsOf } from "@bitwarden/angular/types/controls-of";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import {
MemberDecryptionType,
OpenIdConnectRedirectBehavior,
@@ -28,6 +32,7 @@ import { SsoConfigApi } from "@bitwarden/common/auth/models/api/sso-config.api";
import { OrganizationSsoRequest } from "@bitwarden/common/auth/models/request/organization-sso.request";
import { OrganizationSsoResponse } from "@bitwarden/common/auth/models/response/organization-sso.response";
import { SsoConfigView } from "@bitwarden/common/auth/models/view/sso-config.view";
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -195,6 +200,7 @@ export class SsoComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private organizationService: OrganizationService,
private accountService: AccountService,
private organizationApiService: OrganizationApiServiceAbstraction,
private configService: ConfigService,
private toastService: ToastService,
@@ -260,7 +266,12 @@ export class SsoComponent implements OnInit, OnDestroy {
}
async load() {
this.organization = await this.organizationService.get(this.organizationId);
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organization = await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(this.organizationId)),
);
const ssoSettings = await this.organizationApiService.getSso(this.organizationId);
this.populateForm(ssoSettings);

View File

@@ -1,7 +1,13 @@
import { inject } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivateFn, createUrlTreeFromSnapshot } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
/**
@@ -10,13 +16,17 @@ import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.serv
export const organizationEnabledGuard: CanActivateFn = async (route: ActivatedRouteSnapshot) => {
const syncService = inject(SyncService);
const orgService = inject(OrganizationService);
const accountService = inject(AccountService);
/** Workaround to avoid service initialization race condition. */
if ((await syncService.getLastSync()) == null) {
await syncService.fullSync(false);
}
const org = await orgService.get(route.params.organizationId);
const userId = await firstValueFrom(getUserId(accountService.activeAccount$));
const org = await firstValueFrom(
orgService.organizations$(userId).pipe(getOrganizationById(route.params.organizationId)),
);
if (org == null || !org.canAccessSecretsManager) {
return createUrlTreeFromSnapshot(route, ["/"]);
}

View File

@@ -5,8 +5,11 @@ import {
createUrlTreeFromSnapshot,
RouterStateSnapshot,
} from "@angular/router";
import { firstValueFrom } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
/**
@@ -18,13 +21,15 @@ export const canActivateSM: CanActivateFn = async (
) => {
const syncService = inject(SyncService);
const orgService = inject(OrganizationService);
const accountService = inject(AccountService);
/** Workaround to avoid service initialization race condition. */
if ((await syncService.getLastSync()) == null) {
await syncService.fullSync(false);
}
const orgs = await orgService.getAll();
const userId = await firstValueFrom(getUserId(accountService.activeAccount$));
const orgs = await firstValueFrom(orgService.organizations$(userId));
const smOrg = orgs.find((o) => o.canAccessSecretsManager);
if (smOrg) {
return createUrlTreeFromSnapshot(route, ["/sm", smOrg.id]);

View File

@@ -15,8 +15,13 @@ import {
takeUntil,
} from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
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 { SecretsManagerLogo } from "@bitwarden/web-vault/app/layouts/secrets-manager-logo";
import { OrganizationCounts } from "../models/view/counts.view";
@@ -41,6 +46,7 @@ export class NavigationComponent implements OnInit, OnDestroy {
constructor(
protected route: ActivatedRoute,
private organizationService: OrganizationService,
private accountService: AccountService,
private countService: CountService,
private projectService: ProjectService,
private secretService: SecretService,
@@ -50,7 +56,15 @@ export class NavigationComponent implements OnInit, OnDestroy {
ngOnInit() {
const org$ = this.route.params.pipe(
concatMap((params) => this.organizationService.get(params.organizationId)),
concatMap((params) =>
getUserId(this.accountService.activeAccount$).pipe(
switchMap((userId) =>
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
),
),
),
distinctUntilChanged(),
takeUntil(this.destroy$),
);

View File

@@ -20,8 +20,13 @@ import {
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
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 { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -112,6 +117,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
private serviceAccountService: ServiceAccountService,
private dialogService: DialogService,
private organizationService: OrganizationService,
private accountService: AccountService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private smOnboardingTasksService: SMOnboardingTasksService,
@@ -130,7 +136,15 @@ export class OverviewComponent implements OnInit, OnDestroy {
distinctUntilChanged(),
);
const org$ = orgId$.pipe(switchMap((orgId) => this.organizationService.get(orgId)));
const org$ = orgId$.pipe(
switchMap((orgId) =>
getUserId(this.accountService.activeAccount$).pipe(
switchMap((userId) =>
this.organizationService.organizations$(userId).pipe(getOrganizationById(orgId)),
),
),
),
);
org$.pipe(takeUntil(this.destroy$)).subscribe((org) => {
this.organizationId = org.id;

View File

@@ -3,10 +3,15 @@ import { TestBed } from "@angular/core/testing";
import { Router } from "@angular/router";
import { RouterTestingModule } from "@angular/router/testing";
import { MockProxy, mock } from "jest-mock-extended";
import { of } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService } from "@bitwarden/components";
import { RouterService } from "@bitwarden/web-vault/app/core";
@@ -32,6 +37,8 @@ describe("Project Redirect Guard", () => {
let i18nServiceMock: MockProxy<I18nService>;
let toastService: MockProxy<ToastService>;
let router: Router;
let accountService: FakeAccountService;
const userId = Utils.newGuid() as UserId;
const smOrg1 = { id: "123", canAccessSecretsManager: true } as Organization;
const projectView = {
@@ -50,6 +57,7 @@ describe("Project Redirect Guard", () => {
projectServiceMock = mock<ProjectService>();
i18nServiceMock = mock<I18nService>();
toastService = mock<ToastService>();
accountService = mockAccountServiceWith(userId);
TestBed.configureTestingModule({
imports: [
@@ -71,6 +79,7 @@ describe("Project Redirect Guard", () => {
],
providers: [
{ provide: OrganizationService, useValue: organizationService },
{ provide: AccountService, useValue: accountService },
{ provide: RouterService, useValue: routerService },
{ provide: ProjectService, useValue: projectServiceMock },
{ provide: I18nService, useValue: i18nServiceMock },
@@ -83,7 +92,7 @@ describe("Project Redirect Guard", () => {
it("redirects to sm/{orgId}/projects/{projectId} if project exists", async () => {
// Arrange
organizationService.getAll.mockResolvedValue([smOrg1]);
organizationService.organizations$.mockReturnValue(of([smOrg1]));
projectServiceMock.getByProjectId.mockReturnValue(Promise.resolve(projectView));
// Act
@@ -95,7 +104,7 @@ describe("Project Redirect Guard", () => {
it("redirects to sm/projects if project does not exist", async () => {
// Arrange
organizationService.getAll.mockResolvedValue([smOrg1]);
organizationService.organizations$.mockReturnValue(of([smOrg1]));
// Act
await router.navigateByUrl("sm/123/projects/124");

View File

@@ -2,9 +2,22 @@
// @ts-strict-ignore
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, combineLatestWith, filter, Observable, startWith, switchMap } from "rxjs";
import {
combineLatest,
combineLatestWith,
filter,
firstValueFrom,
Observable,
startWith,
switchMap,
} from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
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";
@@ -49,6 +62,7 @@ export class ProjectSecretsComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private organizationService: OrganizationService,
private accountService: AccountService,
private logService: LogService,
) {}
@@ -71,8 +85,13 @@ export class ProjectSecretsComponent implements OnInit {
switchMap(async ([_, params]) => {
this.organizationId = params.organizationId;
this.projectId = params.projectId;
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organizationEnabled = (
await this.organizationService.get(params.organizationId)
await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
)
)?.enabled;
return await this.getSecretsByProject();
}),

View File

@@ -14,7 +14,12 @@ import {
concatMap,
} from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { DialogService } from "@bitwarden/components";
import { ProjectCounts } from "../../models/view/counts.view";
@@ -49,6 +54,7 @@ export class ProjectComponent implements OnInit, OnDestroy {
private accessPolicyService: AccessPolicyService,
private dialogService: DialogService,
private organizationService: OrganizationService,
private accountService: AccountService,
private countService: CountService,
) {}
@@ -65,7 +71,15 @@ export class ProjectComponent implements OnInit, OnDestroy {
const projectId$ = this.route.params.pipe(map((p) => p.projectId));
const organization$ = this.route.params.pipe(
concatMap((params) => this.organizationService.get$(params.organizationId)),
concatMap((params) =>
getUserId(this.accountService.activeAccount$).pipe(
switchMap((userId) =>
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
),
),
),
);
const projectCounts$ = combineLatest([
this.route.params,

View File

@@ -2,9 +2,21 @@
// @ts-strict-ignore
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, lastValueFrom, Observable, startWith, switchMap } from "rxjs";
import {
combineLatest,
firstValueFrom,
lastValueFrom,
Observable,
startWith,
switchMap,
} from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { DialogService } from "@bitwarden/components";
import { ProjectListView } from "../../models/view/project-list.view";
@@ -41,6 +53,7 @@ export class ProjectsComponent implements OnInit {
private projectService: ProjectService,
private dialogService: DialogService,
private organizationService: OrganizationService,
private accountService: AccountService,
) {}
ngOnInit() {
@@ -50,8 +63,13 @@ export class ProjectsComponent implements OnInit {
]).pipe(
switchMap(async ([params]) => {
this.organizationId = params.organizationId;
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organizationEnabled = (
await this.organizationService.get(params.organizationId)
await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
)
)?.enabled;
return await this.getProjects();

View File

@@ -3,9 +3,14 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { lastValueFrom, Subject, takeUntil } from "rxjs";
import { firstValueFrom, lastValueFrom, Subject, takeUntil } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -97,6 +102,7 @@ export class SecretDialogComponent implements OnInit, OnDestroy {
private projectService: ProjectService,
private dialogService: DialogService,
private organizationService: OrganizationService,
private accountService: AccountService,
private accessPolicyService: AccessPolicyService,
private accessPolicySelectorService: AccessPolicySelectorService,
private toastService: ToastService,
@@ -127,7 +133,16 @@ export class SecretDialogComponent implements OnInit, OnDestroy {
await this.loadAddDialog();
}
if ((await this.organizationService.get(this.data.organizationId))?.isAdmin) {
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
if (
(
await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(this.data.organizationId)),
)
)?.isAdmin
) {
this.formGroup.get("project").removeValidators(Validators.required);
this.formGroup.get("project").updateValueAndValidity();
}

View File

@@ -2,9 +2,14 @@
// @ts-strict-ignore
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatestWith, Observable, startWith, switchMap } from "rxjs";
import { combineLatestWith, firstValueFrom, Observable, startWith, switchMap } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
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";
@@ -46,6 +51,7 @@ export class SecretsComponent implements OnInit {
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private organizationService: OrganizationService,
private accountService: AccountService,
private logService: LogService,
) {}
@@ -55,8 +61,13 @@ export class SecretsComponent implements OnInit {
combineLatestWith(this.route.params),
switchMap(async ([_, params]) => {
this.organizationId = params.organizationId;
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organizationEnabled = (
await this.organizationService.get(params.organizationId)
await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
)
)?.enabled;
return await this.getSecrets();

View File

@@ -3,10 +3,15 @@ import { TestBed } from "@angular/core/testing";
import { Router } from "@angular/router";
import { RouterTestingModule } from "@angular/router/testing";
import { MockProxy, mock } from "jest-mock-extended";
import { of } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { ToastService } from "@bitwarden/components";
import { RouterService } from "@bitwarden/web-vault/app/core";
@@ -32,6 +37,8 @@ describe("Service account Redirect Guard", () => {
let i18nServiceMock: MockProxy<I18nService>;
let toastService: MockProxy<ToastService>;
let router: Router;
let accountService: FakeAccountService;
const userId = Utils.newGuid() as UserId;
const smOrg1 = { id: "123", canAccessSecretsManager: true } as Organization;
const serviceAccountView = {
@@ -46,6 +53,7 @@ describe("Service account Redirect Guard", () => {
serviceAccountServiceMock = mock<ServiceAccountService>();
i18nServiceMock = mock<I18nService>();
toastService = mock<ToastService>();
accountService = mockAccountServiceWith(userId);
TestBed.configureTestingModule({
imports: [
@@ -67,6 +75,7 @@ describe("Service account Redirect Guard", () => {
],
providers: [
{ provide: OrganizationService, useValue: organizationService },
{ provide: AccountService, useValue: accountService },
{ provide: RouterService, useValue: routerService },
{ provide: ServiceAccountService, useValue: serviceAccountServiceMock },
{ provide: I18nService, useValue: i18nServiceMock },
@@ -79,7 +88,7 @@ describe("Service account Redirect Guard", () => {
it("redirects to sm/{orgId}/machine-accounts/{serviceAccountId} if machine account exists", async () => {
// Arrange
organizationService.getAll.mockResolvedValue([smOrg1]);
organizationService.organizations$.mockReturnValue(of([smOrg1]));
serviceAccountServiceMock.getByServiceAccountId.mockReturnValue(
Promise.resolve(serviceAccountView),
);
@@ -93,7 +102,7 @@ describe("Service account Redirect Guard", () => {
it("redirects to sm/machine-accounts if machine account does not exist", async () => {
// Arrange
organizationService.getAll.mockResolvedValue([smOrg1]);
organizationService.organizations$.mockReturnValue(of([smOrg1]));
// Act
await router.navigateByUrl("sm/123/machine-accounts/124");

View File

@@ -2,9 +2,14 @@
// @ts-strict-ignore
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, Observable, startWith, switchMap } from "rxjs";
import { combineLatest, firstValueFrom, Observable, startWith, switchMap } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { DialogService } from "@bitwarden/components";
import {
@@ -39,6 +44,7 @@ export class ServiceAccountsComponent implements OnInit {
private dialogService: DialogService,
private serviceAccountService: ServiceAccountService,
private organizationService: OrganizationService,
private accountService: AccountService,
) {}
ngOnInit() {
@@ -48,8 +54,13 @@ export class ServiceAccountsComponent implements OnInit {
]).pipe(
switchMap(async ([params]) => {
this.organizationId = params.organizationId;
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.organizationEnabled = (
await this.organizationService.get(params.organizationId)
await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
)
)?.enabled;
return await this.getServiceAccounts();

View File

@@ -5,7 +5,12 @@ import { FormControl, FormGroup, Validators } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { firstValueFrom, Subject, switchMap, takeUntil } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -40,6 +45,7 @@ export class SecretsManagerExportComponent implements OnInit, OnDestroy {
private route: ActivatedRoute,
private i18nService: I18nService,
private organizationService: OrganizationService,
private accountService: AccountService,
private platformUtilsService: PlatformUtilsService,
private smPortingService: SecretsManagerPortingService,
private fileDownloadService: FileDownloadService,
@@ -52,7 +58,14 @@ export class SecretsManagerExportComponent implements OnInit, OnDestroy {
async ngOnInit() {
this.route.params
.pipe(
switchMap(async (params) => await this.organizationService.get(params.organizationId)),
switchMap(async (params) => {
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
return await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
);
}),
takeUntil(this.destroy$),
)
.subscribe((organization) => {

View File

@@ -1,8 +1,13 @@
import { mock, MockProxy } from "jest-mock-extended";
import { of } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationUserType } 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 { Utils } from "@bitwarden/common/platform/misc/utils";
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { AccessPolicySelectorService } from "./access-policy-selector.service";
import { ApItemValueType } from "./models/ap-item-value.type";
@@ -12,13 +17,16 @@ import { ApPermissionEnum } from "./models/enums/ap-permission.enum";
describe("AccessPolicySelectorService", () => {
let organizationService: MockProxy<OrganizationService>;
let accountService: FakeAccountService;
const userId = Utils.newGuid() as UserId;
let sut: AccessPolicySelectorService;
beforeEach(() => {
organizationService = mock<OrganizationService>();
accountService = mockAccountServiceWith(userId);
sut = new AccessPolicySelectorService(organizationService);
sut = new AccessPolicySelectorService(organizationService, accountService as AccountService);
});
afterEach(() => jest.resetAllMocks());
@@ -26,7 +34,7 @@ describe("AccessPolicySelectorService", () => {
describe("showAccessRemovalWarning", () => {
it("returns false when current user is admin", async () => {
const org = orgFactory();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [];
@@ -38,7 +46,7 @@ describe("AccessPolicySelectorService", () => {
it("returns false when current user is owner", async () => {
const org = orgFactory();
org.type = OrganizationUserType.Owner;
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [];
@@ -49,7 +57,7 @@ describe("AccessPolicySelectorService", () => {
it("returns true when current user isn't owner/admin and all policies are removed", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [];
@@ -60,7 +68,7 @@ describe("AccessPolicySelectorService", () => {
it("returns true when current user isn't owner/admin and user policy is set to canRead", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [];
selectedPolicyValues.push(
@@ -79,7 +87,7 @@ describe("AccessPolicySelectorService", () => {
it("returns false when current user isn't owner/admin and user policy is set to canReadWrite", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [
createApItemValueType(
@@ -97,7 +105,7 @@ describe("AccessPolicySelectorService", () => {
it("returns true when current user isn't owner/admin and a group Read policy is submitted that the user is a member of", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [
createApItemValueType(
@@ -118,7 +126,7 @@ describe("AccessPolicySelectorService", () => {
it("returns false when current user isn't owner/admin and a group ReadWrite policy is submitted that the user is a member of", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [
createApItemValueType(
@@ -139,7 +147,7 @@ describe("AccessPolicySelectorService", () => {
it("returns true when current user isn't owner/admin and a group ReadWrite policy is submitted that the user is not a member of", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [
createApItemValueType(
@@ -160,7 +168,7 @@ describe("AccessPolicySelectorService", () => {
it("returns false when current user isn't owner/admin, user policy is set to CanRead, and user is in read write group", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [
createApItemValueType(
@@ -187,7 +195,7 @@ describe("AccessPolicySelectorService", () => {
it("returns true when current user isn't owner/admin, user policy is set to CanRead, and user is not in ReadWrite group", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [
createApItemValueType(
@@ -214,7 +222,7 @@ describe("AccessPolicySelectorService", () => {
it("returns true when current user isn't owner/admin, user policy is set to CanRead, and user is in Read group", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const selectedPolicyValues: ApItemValueType[] = [
createApItemValueType(
@@ -242,7 +250,7 @@ describe("AccessPolicySelectorService", () => {
describe("showSecretAccessRemovalWarning", () => {
it("returns false when there are no current access policies", async () => {
const org = orgFactory();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [];
const selectedPolicyValues: ApItemValueType[] = [];
@@ -257,7 +265,7 @@ describe("AccessPolicySelectorService", () => {
});
it("returns false when current user is admin", async () => {
const org = orgFactory();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(
@@ -281,7 +289,7 @@ describe("AccessPolicySelectorService", () => {
it("returns false when current user is owner", async () => {
const org = orgFactory();
org.type = OrganizationUserType.Owner;
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(
@@ -304,7 +312,7 @@ describe("AccessPolicySelectorService", () => {
});
it("returns false when current non-admin user doesn't have Read, Write access with current access policies -- user policy", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(
@@ -327,7 +335,7 @@ describe("AccessPolicySelectorService", () => {
});
it("returns false when current non-admin user doesn't have Read, Write access with current access policies -- group policy", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(
@@ -352,7 +360,7 @@ describe("AccessPolicySelectorService", () => {
});
it("returns true when current non-admin user has Read, Write access with current access policies and doesn't with selected -- user policy", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(
@@ -381,7 +389,7 @@ describe("AccessPolicySelectorService", () => {
});
it("returns true when current non-admin user has Read, Write access with current access policies and doesn't with selected -- group policy", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(
@@ -415,7 +423,7 @@ describe("AccessPolicySelectorService", () => {
});
it("returns false when current non-admin user has Read, Write access with current access policies and does with selected -- user policy", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(
@@ -446,7 +454,7 @@ describe("AccessPolicySelectorService", () => {
});
it("returns false when current non-admin user has Read, Write access with current access policies and does with selected -- group policy", async () => {
const org = setupUserOrg();
organizationService.get.calledWith(org.id).mockResolvedValue(org);
organizationService.organizations$.calledWith(userId).mockReturnValue(of([org]));
const currentAccessPolicies: ApItemViewType[] = [
createApItemViewType(

View File

@@ -1,6 +1,12 @@
import { Injectable } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { ApItemValueType } from "./models/ap-item-value.type";
import { ApItemViewType } from "./models/ap-item-view.type";
@@ -11,13 +17,22 @@ import { ApPermissionEnum } from "./models/enums/ap-permission.enum";
providedIn: "root",
})
export class AccessPolicySelectorService {
constructor(private organizationService: OrganizationService) {}
constructor(
private organizationService: OrganizationService,
private accountServcie: AccountService,
) {}
async showAccessRemovalWarning(
organizationId: string,
selectedPoliciesValues: ApItemValueType[],
): Promise<boolean> {
const organization = await this.organizationService.get(organizationId);
const userId = await firstValueFrom(getUserId(this.accountServcie.activeAccount$));
const organization = await firstValueFrom(
this.organizationService.organizations$(userId).pipe(getOrganizationById(organizationId)),
);
if (!organization) {
return false;
}
if (organization.isOwner || organization.isAdmin) {
return false;
}
@@ -38,7 +53,13 @@ export class AccessPolicySelectorService {
return false;
}
const organization = await this.organizationService.get(organizationId);
const userId = await firstValueFrom(getUserId(this.accountServcie.activeAccount$));
const organization = await firstValueFrom(
this.organizationService.organizations$(userId).pipe(getOrganizationById(organizationId)),
);
if (!organization) {
return false;
}
if (organization.isOwner || organization.isAdmin || !this.userHasReadWriteAccess(current)) {
return false;
}

View File

@@ -2,9 +2,14 @@
// @ts-strict-ignore
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Subject, takeUntil, concatMap } from "rxjs";
import { Subject, takeUntil, concatMap, firstValueFrom } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { DialogService } from "@bitwarden/components";
import {
@@ -33,12 +38,20 @@ export class NewMenuComponent implements OnInit, OnDestroy {
private route: ActivatedRoute,
private dialogService: DialogService,
private organizationService: OrganizationService,
private accountService: AccountService,
) {}
ngOnInit() {
this.route.params
.pipe(
concatMap(async (params) => await this.organizationService.get(params.organizationId)),
concatMap(async (params) => {
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
return await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
);
}),
takeUntil(this.destroy$),
)
.subscribe((org) => {

View File

@@ -1,8 +1,13 @@
import { Component } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { map, concatMap } from "rxjs";
import { map, concatMap, firstValueFrom } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { Icon, Icons } from "@bitwarden/components";
@Component({
@@ -11,12 +16,20 @@ import { Icon, Icons } from "@bitwarden/components";
export class OrgSuspendedComponent {
constructor(
private organizationService: OrganizationService,
private accountService: AccountService,
private route: ActivatedRoute,
) {}
protected NoAccess: Icon = Icons.NoAccess;
protected organizationName$ = this.route.params.pipe(
concatMap(async (params) => await this.organizationService.get(params.organizationId)),
concatMap(async (params) => {
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
return await firstValueFrom(
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(params.organizationId)),
);
}),
map((org) => org?.name),
);
}

View File

@@ -2,7 +2,7 @@ import { Component, DestroyRef, inject, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormControl } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { combineLatest, debounceTime, map, Observable, of, skipWhile } from "rxjs";
import { combineLatest, debounceTime, firstValueFrom, map, Observable, of, skipWhile } from "rxjs";
import {
CriticalAppsService,
@@ -14,8 +14,13 @@ import {
ApplicationHealthReportDetailWithCriticalFlag,
ApplicationHealthReportSummary,
} from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
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 { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -77,34 +82,42 @@ export class AllApplicationsComponent implements OnInit {
);
const organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId") ?? "";
combineLatest([
this.dataService.applications$,
this.criticalAppsService.getAppsListForOrg(organizationId),
this.organizationService.get$(organizationId),
])
.pipe(
takeUntilDestroyed(this.destroyRef),
skipWhile(([_, __, organization]) => !organization),
map(([applications, criticalApps, organization]) => {
const criticalUrls = criticalApps.map((ca) => ca.uri);
const data = applications?.map((app) => ({
...app,
isMarkedAsCritical: criticalUrls.includes(app.applicationName),
})) as ApplicationHealthReportDetailWithCriticalFlag[];
return { data, organization };
}),
)
.subscribe(({ data, organization }) => {
if (data) {
this.dataSource.data = data;
this.applicationSummary = this.reportService.generateApplicationsSummary(data);
}
if (organization) {
this.organization = organization;
}
});
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
this.isLoading$ = this.dataService.isLoading$;
if (organizationId) {
const organization$ = this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(organizationId));
combineLatest([
this.dataService.applications$,
this.criticalAppsService.getAppsListForOrg(organizationId),
organization$,
])
.pipe(
takeUntilDestroyed(this.destroyRef),
skipWhile(([_, __, organization]) => !organization),
map(([applications, criticalApps, organization]) => {
const criticalUrls = criticalApps.map((ca) => ca.uri);
const data = applications?.map((app) => ({
...app,
isMarkedAsCritical: criticalUrls.includes(app.applicationName),
})) as ApplicationHealthReportDetailWithCriticalFlag[];
return { data, organization };
}),
)
.subscribe(({ data, organization }) => {
if (data) {
this.dataSource.data = data;
this.applicationSummary = this.reportService.generateApplicationsSummary(data);
}
if (organization) {
this.organization = organization;
}
});
this.isLoading$ = this.dataService.isLoading$;
}
}
constructor(
@@ -116,6 +129,7 @@ export class AllApplicationsComponent implements OnInit {
protected dataService: RiskInsightsDataService,
protected organizationService: OrganizationService,
protected reportService: RiskInsightsReportService,
private accountService: AccountService,
protected criticalAppsService: CriticalAppsService,
protected dialogService: DialogService,
) {

View File

@@ -10,8 +10,12 @@ import {
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { mockAccountServiceWith } from "@bitwarden/common/spec";
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { TableModule } from "@bitwarden/components";
import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared";
@@ -24,6 +28,7 @@ describe("PasswordHealthMembersUriComponent", () => {
let fixture: ComponentFixture<PasswordHealthMembersURIComponent>;
let cipherServiceMock: MockProxy<CipherService>;
const passwordHealthServiceMock = mock<PasswordHealthService>();
const userId = Utils.newGuid() as UserId;
const activeRouteParams = convertToParamMap({ organizationId: "orgId" });
@@ -36,6 +41,7 @@ describe("PasswordHealthMembersUriComponent", () => {
{ provide: I18nService, useValue: mock<I18nService>() },
{ provide: AuditService, useValue: mock<AuditService>() },
{ provide: OrganizationService, useValue: mock<OrganizationService>() },
{ provide: AccountService, useValue: mockAccountServiceWith(userId) },
{
provide: PasswordStrengthServiceAbstraction,
useValue: mock<PasswordStrengthServiceAbstraction>(),