diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index e84f78458d..59bc03babd 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -2,12 +2,15 @@ - + + @if (canShowAccessIntelligenceTab(organization)) { + + } + org.canAccessReports)], + canActivate: [organizationPermissionsGuard(canAccessAccessIntelligence)], loadChildren: () => import("../../dirt/access-intelligence/access-intelligence.module").then( (m) => m.AccessIntelligenceModule, diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence-routing.module.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence-routing.module.ts index 4bdc8e2504..85c6dbdd20 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence-routing.module.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/access-intelligence-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; +import { canAccessAccessIntelligence } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { organizationPermissionsGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/org-permissions.guard"; import { RiskInsightsComponent } from "./risk-insights.component"; @@ -8,7 +9,7 @@ import { RiskInsightsComponent } from "./risk-insights.component"; const routes: Routes = [ { path: "", - canActivate: [organizationPermissionsGuard((org) => org.canAccessReports)], + canActivate: [organizationPermissionsGuard(canAccessAccessIntelligence)], component: RiskInsightsComponent, data: { titleId: "accessIntelligence", diff --git a/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts index 363b82c507..54d2f93ac0 100644 --- a/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/organization/organization.service.abstraction.ts @@ -41,6 +41,18 @@ export function canAccessBillingTab(org: Organization): boolean { return org.isOwner; } +/** + * Access Intelligence is only available to: + * - Enterprise organizations + * - Users in those organizations with report access + * + * @param org The organization to verify access + * @returns If true can access the Access Intelligence feature + */ +export function canAccessAccessIntelligence(org: Organization): boolean { + return org.canUseAccessIntelligence && org.canAccessReports; +} + export function canAccessOrgAdmin(org: Organization): boolean { // Admin console can only be accessed by Owners for disabled organizations if (!org.enabled && !org.isOwner) { diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index b2153024ef..dba4a1fedb 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -402,4 +402,8 @@ export class Organization { this.permissions.accessEventLogs) ); } + + get canUseAccessIntelligence() { + return this.productTierType === ProductTierType.Enterprise; + } } diff --git a/libs/common/src/vault/tasks/services/default-task.service.spec.ts b/libs/common/src/vault/tasks/services/default-task.service.spec.ts index 8fc2f902ca..b57915aadb 100644 --- a/libs/common/src/vault/tasks/services/default-task.service.spec.ts +++ b/libs/common/src/vault/tasks/services/default-task.service.spec.ts @@ -51,10 +51,10 @@ describe("Default task service", () => { mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { - useAccessIntelligence: false, + canUseAccessIntelligence: false, }, { - useAccessIntelligence: true, + canUseAccessIntelligence: true, }, ] as Organization[]), ); @@ -70,10 +70,10 @@ describe("Default task service", () => { mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { - useAccessIntelligence: false, + canUseAccessIntelligence: false, }, { - useAccessIntelligence: false, + canUseAccessIntelligence: false, }, ] as Organization[]), ); @@ -91,17 +91,17 @@ describe("Default task service", () => { mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { - useAccessIntelligence: true, + canUseAccessIntelligence: true, }, ] as Organization[]), ); }); - it("should return an empty array if tasks are not enabled", async () => { + it("should return no tasks if not present and canUserAccessIntelligence is false", async () => { mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { - useAccessIntelligence: false, + canUseAccessIntelligence: false, }, ] as Organization[]), ); @@ -111,7 +111,6 @@ describe("Default task service", () => { const result = await firstValueFrom(tasks$("user-id" as UserId)); expect(result.length).toBe(0); - expect(mockApiSend).not.toHaveBeenCalled(); }); it("should fetch tasks from the API when the state is null", async () => { @@ -163,17 +162,17 @@ describe("Default task service", () => { mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { - useAccessIntelligence: true, + canUseAccessIntelligence: true, }, ] as Organization[]), ); }); - it("should return an empty array if tasks are not enabled", async () => { + it("should return no tasks if not present and canUserAccessIntelligence is false", async () => { mockGetAllOrgs$.mockReturnValue( new BehaviorSubject([ { - useAccessIntelligence: false, + canUseAccessIntelligence: false, }, ] as Organization[]), ); @@ -183,7 +182,6 @@ describe("Default task service", () => { const result = await firstValueFrom(pendingTasks$("user-id" as UserId)); expect(result.length).toBe(0); - expect(mockApiSend).not.toHaveBeenCalled(); }); it("should filter tasks to only pending tasks", async () => { diff --git a/libs/common/src/vault/tasks/services/default-task.service.ts b/libs/common/src/vault/tasks/services/default-task.service.ts index 5bd23ed860..60e05e0728 100644 --- a/libs/common/src/vault/tasks/services/default-task.service.ts +++ b/libs/common/src/vault/tasks/services/default-task.service.ts @@ -48,7 +48,7 @@ export class DefaultTaskService implements TaskService { tasksEnabled$ = perUserCache$((userId) => { return this.organizationService.organizations$(userId).pipe( - map((orgs) => orgs.some((o) => o.useAccessIntelligence)), + map((orgs) => orgs.some((o) => o.canUseAccessIntelligence)), distinctUntilChanged(), ); });