From d32365fbba1a9bf732846257787b44c3a9c690e8 Mon Sep 17 00:00:00 2001 From: Tom <144813356+ttalty@users.noreply.github.com> Date: Thu, 4 Dec 2025 19:04:26 -0500 Subject: [PATCH] [PM-29164] Access Intelligence display for only enterprise (#17807) * Access Intelligence display for only enterprise * modifying the access intelligence routing to properly match. Added documentation. * tasks remove useriskinsights flag * fixing tasks test cases * tasks should only check for enterprise * fixing uncommitted changes * reverting unecessary change from all activites * adding back missing test case --- .../organization-layout.component.html | 15 ++++++++----- .../layouts/organization-layout.component.ts | 5 +++++ .../organizations-routing.module.ts | 7 ++++-- .../access-intelligence-routing.module.ts | 3 ++- .../organization.service.abstraction.ts | 12 ++++++++++ .../models/domain/organization.ts | 4 ++++ .../services/default-task.service.spec.ts | 22 +++++++++---------- .../tasks/services/default-task.service.ts | 2 +- 8 files changed, 48 insertions(+), 22 deletions(-) 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(), ); });