From 3957349911fd23024699bd94f4f0931b9dc2c229 Mon Sep 17 00:00:00 2001 From: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com> Date: Tue, 30 Apr 2024 15:58:27 -0400 Subject: [PATCH] adding tests --- .../projects/guards/project-access.guard.ts | 10 ++ .../service-account-access.guard.spec.ts | 118 ++++++++++++++++++ .../guards/service-account-access.guard.ts | 11 ++ 3 files changed, 139 insertions(+) create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/guards/project-access.guard.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/guards/project-access.guard.ts index 5ba2040c25..484d9d772b 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/guards/project-access.guard.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/guards/project-access.guard.ts @@ -1,6 +1,9 @@ import { inject } from "@angular/core"; import { ActivatedRouteSnapshot, CanActivateFn, createUrlTreeFromSnapshot } from "@angular/router"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + import { ProjectService } from "../project.service"; /** @@ -8,6 +11,8 @@ import { ProjectService } from "../project.service"; */ export const projectAccessGuard: CanActivateFn = async (route: ActivatedRouteSnapshot) => { const projectService = inject(ProjectService); + const platformUtilsService = inject(PlatformUtilsService); + const i18nService = inject(I18nService); try { const project = await projectService.getByProjectId(route.params.projectId); @@ -15,6 +20,11 @@ export const projectAccessGuard: CanActivateFn = async (route: ActivatedRouteSna return true; } } catch { + platformUtilsService.showToast( + "error", + null, + i18nService.t("notFound", i18nService.t("project")), + ); return createUrlTreeFromSnapshot(route, ["/sm", route.params.organizationId, "projects"]); } return createUrlTreeFromSnapshot(route, ["/sm", route.params.organizationId, "projects"]); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts new file mode 100644 index 0000000000..605986703f --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts @@ -0,0 +1,118 @@ +import { Component } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { provideRouter, withDebugTracing } from "@angular/router"; +import { RouterTestingHarness } from "@angular/router/testing"; +import { MockProxy, mock } from "jest-mock-extended"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions/organization-billing.service"; +import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; +import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; + +import { RouterService } from "../../core/router.service"; +import { ServiceAccountView } from "../../models/view/service-account.view"; +import { ServiceAccountService } from "../service-account.service"; + +import { serviceAccountAccessGuard } from "./service-account-access.guard"; + +@Component({ + template: "", +}) +export class GuardedRouteTestComponent {} + +@Component({ + template: "", +}) +export class RedirectTestComponent {} + +describe("Service account Redirect Guard", () => { + let syncService: MockProxy; + let organizationService: MockProxy; + let organizationBillingService: MockProxy; + let stateService: MockProxy; + let routerService: MockProxy; + let routerHarness: RouterTestingHarness; + let routerSpy: jest.SpyInstance; + let serviceAccountService: MockProxy; + + const smOrg1 = { id: "123", canAccessSecretsManager: true } as Organization; + const nonSmOrg1 = { id: "124", canAccessSecretsManager: false } as Organization; + const serviceAccountView = { + id: "123", + organizationId: "123", + name: "service-account-name", + } as ServiceAccountView; + + beforeEach(async () => { + syncService = mock(); + organizationService = mock(); + organizationBillingService = mock(); + stateService = mock(); + routerService = mock(); + + TestBed.configureTestingModule({ + providers: [ + { provide: SyncService, useValue: syncService }, + { provide: OrganizationService, useValue: organizationService }, + { provide: OrganizationBillingServiceAbstraction, useValue: organizationBillingService }, + { provide: StateService, useValue: stateService }, + { provide: RouterService, useValue: routerService }, + provideRouter( + [ + { + path: "guarded-route", + component: GuardedRouteTestComponent, + canActivate: [serviceAccountAccessGuard], + }, + { + path: "sm", + component: RedirectTestComponent, + }, + ], + withDebugTracing(), + ), + ], + }); + + routerHarness = await RouterTestingHarness.create(); + }); + + it("should navigate successfully if the org has access to sm and service account exists", async () => { + // Arrange + organizationService.getAll.mockResolvedValue([smOrg1]); + routerService.getPreviousUrl.mockReturnValue(undefined); + serviceAccountService.getByServiceAccountId.mockResolvedValue(serviceAccountView); + + // Act + await routerHarness.navigateByUrl("guarded-route"); + + // Assert + expect(routerSpy).toHaveBeenCalled(); + }); + + it("redirects to pw manager if org does not exist or doesn't have access to sm", async () => { + // Arrange + organizationService.getAll.mockResolvedValue([nonSmOrg1]); + routerService.getPreviousUrl.mockReturnValue(undefined); + + // Act + await routerHarness.navigateByUrl("guarded-route"); + + // Assert + expect(routerSpy).not.toHaveBeenCalledWith(["/sm"]); + }); + + it("redirects to sm/machine-accounts if machine account does not exist", async () => { + // Arrange + organizationService.getAll.mockResolvedValue([smOrg1]); + serviceAccountService.getByServiceAccountId.mockResolvedValue(serviceAccountView); + routerService.getPreviousUrl.mockReturnValue(undefined); + + // Act + await routerHarness.navigateByUrl("guarded-route"); + + // Assert + expect(routerSpy).toHaveBeenCalledWith(["/sm/machine-accounts/"]); + }); +}); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.ts index c474ec44d5..83cb1a4782 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.ts @@ -1,6 +1,9 @@ import { inject } from "@angular/core"; import { ActivatedRouteSnapshot, CanActivateFn, createUrlTreeFromSnapshot } from "@angular/router"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + import { ServiceAccountService } from "../service-account.service"; /** @@ -8,6 +11,8 @@ import { ServiceAccountService } from "../service-account.service"; */ export const serviceAccountAccessGuard: CanActivateFn = async (route: ActivatedRouteSnapshot) => { const serviceAccountService = inject(ServiceAccountService); + const platformUtilsService = inject(PlatformUtilsService); + const i18nService = inject(I18nService); try { const serviceAccount = await serviceAccountService.getByServiceAccountId( @@ -18,6 +23,12 @@ export const serviceAccountAccessGuard: CanActivateFn = async (route: ActivatedR return true; } } catch { + platformUtilsService.showToast( + "error", + null, + i18nService.t("notFound", i18nService.t("serviceAccount")), + ); + return createUrlTreeFromSnapshot(route, [ "/sm", route.params.organizationId,