1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 21:33:27 +00:00

[PM-11360] Remove export permission for providers (#12062)

* Split organization.canAccessImportExport
* Fix import permission to include CanCreateNewCollections
* Remove provider export permission (feature flagged)
This commit is contained in:
Thomas Rittson
2024-11-22 07:58:03 +10:00
committed by GitHub
parent 9f99454b37
commit 9e9f977eb3
12 changed files with 142 additions and 97 deletions

View File

@@ -27,7 +27,7 @@ export class ImportCommand {
);
}
if (!organization.canAccessImportExport) {
if (!organization.canAccessImport) {
return Response.badRequest(
"You are not authorized to import into the provided organization.",
);

View File

@@ -84,12 +84,12 @@
<bit-nav-item
[text]="'importData' | i18n"
route="settings/tools/import"
*ngIf="organization.canAccessImportExport"
*ngIf="organization.canAccessImport"
></bit-nav-item>
<bit-nav-item
[text]="'exportVault' | i18n"
route="settings/tools/export"
*ngIf="organization.canAccessImportExport"
*ngIf="canAccessExport$ | async"
></bit-nav-item>
<bit-nav-item
[text]="'domainVerification' | i18n"

View File

@@ -1,7 +1,7 @@
import { CommonModule } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core";
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, RouterModule } from "@angular/router";
import { combineLatest, map, mergeMap, Observable, Subject, switchMap, takeUntil } from "rxjs";
import { combineLatest, filter, map, Observable, switchMap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -12,7 +12,6 @@ import {
canAccessReportingTab,
canAccessSettingsTab,
canAccessVaultTab,
getOrganizationById,
OrganizationService,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@@ -22,6 +21,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { getById } from "@bitwarden/common/platform/misc";
import { BannerModule, IconModule } from "@bitwarden/components";
import { OrgSwitcherComponent } from "../../../layouts/org-switcher/org-switcher.component";
@@ -42,19 +42,18 @@ import { AdminConsoleLogo } from "../../icons/admin-console-logo";
BannerModule,
],
})
export class OrganizationLayoutComponent implements OnInit, OnDestroy {
export class OrganizationLayoutComponent implements OnInit {
protected readonly logo = AdminConsoleLogo;
protected orgFilter = (org: Organization) => canAccessOrgAdmin(org);
organization$: Observable<Organization>;
canAccessExport$: Observable<boolean>;
showPaymentAndHistory$: Observable<boolean>;
hideNewOrgButton$: Observable<boolean>;
organizationIsUnmanaged$: Observable<boolean>;
isAccessIntelligenceFeatureEnabled = false;
private _destroy = new Subject<void>();
constructor(
private route: ActivatedRoute,
private organizationService: OrganizationService,
@@ -71,23 +70,23 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
FeatureFlag.AccessIntelligence,
);
this.organization$ = this.route.params
.pipe(takeUntil(this._destroy))
.pipe<string>(map((p) => p.organizationId))
.pipe(
mergeMap((id) => {
return this.organizationService.organizations$
.pipe(takeUntil(this._destroy))
.pipe(getOrganizationById(id));
}),
);
this.organization$ = this.route.params.pipe(
map((p) => p.organizationId),
switchMap((id) => this.organizationService.organizations$.pipe(getById(id))),
filter((org) => org != null),
);
this.canAccessExport$ = combineLatest([
this.organization$,
this.configService.getFeatureFlag$(FeatureFlag.PM11360RemoveProviderExportPermission),
]).pipe(map(([org, removeProviderExport]) => org.canAccessExport(removeProviderExport)));
this.showPaymentAndHistory$ = this.organization$.pipe(
map(
(org) =>
!this.platformUtilsService.isSelfHost() &&
org?.canViewBillingHistory &&
org?.canEditPaymentMethods,
org.canViewBillingHistory &&
org.canEditPaymentMethods,
),
);
@@ -107,11 +106,6 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
);
}
ngOnDestroy() {
this._destroy.next();
this._destroy.complete();
}
canShowVaultTab(organization: Organization): boolean {
return canAccessVaultTab(organization);
}

View File

@@ -1,8 +1,11 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { inject, NgModule } from "@angular/core";
import { CanMatchFn, RouterModule, Routes } from "@angular/router";
import { map } from "rxjs";
import { canAccessSettingsTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
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";
import { organizationPermissionsGuard } from "../../organizations/guards/org-permissions.guard";
import { organizationRedirectGuard } from "../../organizations/guards/org-redirect.guard";
@@ -11,6 +14,11 @@ import { PoliciesComponent } from "../../organizations/policies";
import { AccountComponent } from "./account.component";
import { TwoFactorSetupComponent } from "./two-factor-setup.component";
const removeProviderExportPermission$: CanMatchFn = () =>
inject(ConfigService)
.getFeatureFlag$(FeatureFlag.PM11360RemoveProviderExportPermission)
.pipe(map((removeProviderExport) => removeProviderExport === true));
const routes: Routes = [
{
path: "",
@@ -53,18 +61,32 @@ const routes: Routes = [
path: "import",
loadComponent: () =>
import("./org-import.component").then((mod) => mod.OrgImportComponent),
canActivate: [organizationPermissionsGuard((org) => org.canAccessImportExport)],
canActivate: [organizationPermissionsGuard((org) => org.canAccessImport)],
data: {
titleId: "importData",
},
},
// Export routing is temporarily duplicated to set the flag value passed into org.canAccessExport
{
path: "export",
loadComponent: () =>
import("../tools/vault-export/org-vault-export.component").then(
(mod) => mod.OrganizationVaultExportComponent,
),
canMatch: [removeProviderExportPermission$], // if this matches, the flag is ON
canActivate: [organizationPermissionsGuard((org) => org.canAccessExport(true))],
data: {
titleId: "exportVault",
},
},
{
path: "export",
loadComponent: () =>
import("../tools/vault-export/org-vault-export.component").then(
(mod) => mod.OrganizationVaultExportComponent,
),
canActivate: [organizationPermissionsGuard((org) => org.canAccessImportExport)],
canActivate: [organizationPermissionsGuard((org) => org.canAccessExport(false))],
data: {
titleId: "exportVault",
},
@@ -82,7 +104,7 @@ function getSettingsRoute(organization: Organization) {
if (organization.canManagePolicies) {
return "policies";
}
if (organization.canAccessImportExport) {
if (organization.canAccessImport) {
return ["tools", "import"];
}
if (organization.canManageSso) {

View File

@@ -152,7 +152,13 @@ export const SMAvailable: Story = {
...Template,
args: {
mockOrgs: [
{ id: "org-a", canManageUsers: false, canAccessSecretsManager: true, enabled: true },
{
id: "org-a",
canManageUsers: false,
canAccessSecretsManager: true,
enabled: true,
canAccessExport: (_) => false,
},
] as Organization[],
mockProviders: [],
},
@@ -162,7 +168,13 @@ export const SMAndACAvailable: Story = {
...Template,
args: {
mockOrgs: [
{ id: "org-a", canManageUsers: true, canAccessSecretsManager: true, enabled: true },
{
id: "org-a",
canManageUsers: true,
canAccessSecretsManager: true,
enabled: true,
canAccessExport: (_) => false,
},
] as Organization[],
mockProviders: [],
},
@@ -172,7 +184,13 @@ export const WithAllOptions: Story = {
...Template,
args: {
mockOrgs: [
{ id: "org-a", canManageUsers: true, canAccessSecretsManager: true, enabled: true },
{
id: "org-a",
canManageUsers: true,
canAccessSecretsManager: true,
enabled: true,
canAccessExport: (_) => false,
},
] as Organization[],
mockProviders: [{ id: "provider-a" }] as Provider[],
},

View File

@@ -171,7 +171,13 @@ export const WithSM: Story = {
...Template,
args: {
mockOrgs: [
{ id: "org-a", canManageUsers: false, canAccessSecretsManager: true, enabled: true },
{
id: "org-a",
canManageUsers: false,
canAccessSecretsManager: true,
enabled: true,
canAccessExport: (_) => false,
},
] as Organization[],
mockProviders: [],
},
@@ -181,7 +187,13 @@ export const WithSMAndAC: Story = {
...Template,
args: {
mockOrgs: [
{ id: "org-a", canManageUsers: true, canAccessSecretsManager: true, enabled: true },
{
id: "org-a",
canManageUsers: true,
canAccessSecretsManager: true,
enabled: true,
canAccessExport: (_) => false,
},
] as Organization[],
mockProviders: [],
},
@@ -191,7 +203,13 @@ export const WithAllOptions: Story = {
...Template,
args: {
mockOrgs: [
{ id: "org-a", canManageUsers: true, canAccessSecretsManager: true, enabled: true },
{
id: "org-a",
canManageUsers: true,
canAccessSecretsManager: true,
enabled: true,
canAccessExport: (_) => false,
},
] as Organization[],
mockProviders: [{ id: "provider-a" }] as Provider[],
},

View File

@@ -110,7 +110,12 @@ describe("ProductSwitcherService", () => {
it("is included in bento when there is an organization with SM", async () => {
organizationService.organizations$ = of([
{ id: "1234", canAccessSecretsManager: true, enabled: true },
{
id: "1234",
canAccessSecretsManager: true,
enabled: true,
canAccessExport: (_) => true,
},
] as Organization[]);
initiateService();
@@ -220,8 +225,20 @@ describe("ProductSwitcherService", () => {
router.url = "/sm/4243";
organizationService.organizations$ = of([
{ id: "23443234", canAccessSecretsManager: true, enabled: true, name: "Org 2" },
{ id: "4243", canAccessSecretsManager: true, enabled: true, name: "Org 32" },
{
id: "23443234",
canAccessSecretsManager: true,
enabled: true,
name: "Org 2",
canAccessExport: (_) => true,
},
{
id: "4243",
canAccessSecretsManager: true,
enabled: true,
name: "Org 32",
canAccessExport: (_) => true,
},
] as Organization[]);
initiateService();