From 010b55d39d3249cb92a2cf04f5006e5109c34c7f Mon Sep 17 00:00:00 2001
From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com>
Date: Mon, 3 Jun 2024 11:10:54 -0400
Subject: [PATCH] Prevent Provider from viewing client organization payment
method and billing history (#9442)
---
.../organization-layout.component.html | 2 +-
.../layouts/organization-layout.component.ts | 29 +++++++++++++--
.../guards/organization-is-unmanaged.guard.ts | 36 +++++++++++++++++++
.../organization-billing-routing.module.ts | 5 +--
4 files changed, 67 insertions(+), 5 deletions(-)
create mode 100644 apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts
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 d1a48a78e1..237e2c6e30 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
@@ -52,7 +52,7 @@
*ngIf="canShowBillingTab(organization)"
>
-
+
diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts
index 47ca0998bb..4383656bee 100644
--- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts
+++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.ts
@@ -1,7 +1,7 @@
import { CommonModule } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute, RouterModule } from "@angular/router";
-import { map, mergeMap, Observable, Subject, takeUntil } from "rxjs";
+import { combineLatest, map, mergeMap, Observable, Subject, switchMap, takeUntil } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -16,7 +16,8 @@ import {
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
-import { PolicyType } from "@bitwarden/common/admin-console/enums";
+import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
+import { PolicyType, ProviderStatusType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -55,9 +56,14 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
organization$: Observable;
showPaymentAndHistory$: Observable;
hideNewOrgButton$: Observable;
+ organizationIsUnmanaged$: Observable;
private _destroy = new Subject();
+ protected consolidatedBillingEnabled$ = this.configService.getFeatureFlag$(
+ FeatureFlag.EnableConsolidatedBilling,
+ );
+
protected showPaymentMethodWarningBanners$ = this.configService.getFeatureFlag$(
FeatureFlag.ShowPaymentMethodWarningBanners,
);
@@ -68,6 +74,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService,
private configService: ConfigService,
private policyService: PolicyService,
+ private providerService: ProviderService,
) {}
async ngOnInit() {
@@ -94,6 +101,24 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
);
this.hideNewOrgButton$ = this.policyService.policyAppliesToActiveUser$(PolicyType.SingleOrg);
+
+ const provider$ = this.organization$.pipe(
+ switchMap((organization) => this.providerService.get$(organization.providerId)),
+ );
+
+ this.organizationIsUnmanaged$ = combineLatest([
+ this.consolidatedBillingEnabled$,
+ this.organization$,
+ provider$,
+ ]).pipe(
+ map(
+ ([consolidatedBillingEnabled, organization, provider]) =>
+ !consolidatedBillingEnabled ||
+ !organization.hasProvider ||
+ !provider ||
+ provider.providerStatus !== ProviderStatusType.Billable,
+ ),
+ );
}
ngOnDestroy() {
diff --git a/apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts b/apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts
new file mode 100644
index 0000000000..a915d8f8a6
--- /dev/null
+++ b/apps/web/src/app/billing/guards/organization-is-unmanaged.guard.ts
@@ -0,0 +1,36 @@
+import { inject } from "@angular/core";
+import { ActivatedRouteSnapshot, CanActivateFn } from "@angular/router";
+
+import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
+import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service";
+import { ProviderStatusType } from "@bitwarden/common/admin-console/enums";
+import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
+import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
+
+export const organizationIsUnmanaged: CanActivateFn = async (route: ActivatedRouteSnapshot) => {
+ const configService = inject(ConfigService);
+ const organizationService = inject(OrganizationService);
+ const providerService = inject(ProviderService);
+
+ const consolidatedBillingEnabled = await configService.getFeatureFlag(
+ FeatureFlag.EnableConsolidatedBilling,
+ );
+
+ if (!consolidatedBillingEnabled) {
+ return true;
+ }
+
+ const organization = await organizationService.get(route.params.organizationId);
+
+ if (!organization.hasProvider) {
+ return true;
+ }
+
+ const provider = await providerService.get(organization.providerId);
+
+ if (!provider) {
+ return true;
+ }
+
+ return provider.providerStatus !== ProviderStatusType.Billable;
+};
diff --git a/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts b/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts
index 8ca7226b97..4af0662875 100644
--- a/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts
+++ b/apps/web/src/app/billing/organizations/organization-billing-routing.module.ts
@@ -5,6 +5,7 @@ import { canAccessBillingTab } from "@bitwarden/common/admin-console/abstraction
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { OrganizationPermissionsGuard } from "../../admin-console/organizations/guards/org-permissions.guard";
+import { organizationIsUnmanaged } from "../../billing/guards/organization-is-unmanaged.guard";
import { WebPlatformUtilsService } from "../../core/web-platform-utils.service";
import { PaymentMethodComponent } from "../shared";
@@ -29,7 +30,7 @@ const routes: Routes = [
{
path: "payment-method",
component: PaymentMethodComponent,
- canActivate: [OrganizationPermissionsGuard],
+ canActivate: [OrganizationPermissionsGuard, organizationIsUnmanaged],
data: {
titleId: "paymentMethod",
organizationPermissions: (org: Organization) => org.canEditPaymentMethods,
@@ -38,7 +39,7 @@ const routes: Routes = [
{
path: "history",
component: OrgBillingHistoryViewComponent,
- canActivate: [OrganizationPermissionsGuard],
+ canActivate: [OrganizationPermissionsGuard, organizationIsUnmanaged],
data: {
titleId: "billingHistory",
organizationPermissions: (org: Organization) => org.canViewBillingHistory,