From 8e5dc600dfdd75ee4ba8a3322ed5c2cdcd2547d4 Mon Sep 17 00:00:00 2001 From: Rui Tome Date: Fri, 17 Nov 2023 11:22:06 +0000 Subject: [PATCH] [AC-1139] Checking if FC feature flag is enabled when using canDeleteAssignedCollections or canViewAssignedCollections --- .../organization-switcher.component.ts | 21 ++++++++++++++----- .../guards/org-permissions.guard.spec.ts | 4 +++- .../guards/org-permissions.guard.ts | 21 ++++++++++++++----- .../guards/org-redirect.guard.ts | 14 +++++++++++-- .../layouts/organization-layout.component.ts | 17 ++++++++++++--- .../organization-routing.module.ts | 7 +++++-- apps/web/src/app/layouts/navbar.component.ts | 17 +++++++++++---- .../app/tools/import/import-web.component.ts | 14 ++++++++++--- .../collection-dialog.component.html | 5 ++++- .../bulk-delete-dialog.component.ts | 16 +++++++++++--- .../vault/individual-vault/vault.component.ts | 9 +++++++- .../app/vault/org-vault/vault.component.ts | 7 ++++++- .../organization.service.abstraction.ts | 18 ++++++++++------ 13 files changed, 133 insertions(+), 37 deletions(-) diff --git a/apps/web/src/app/admin-console/components/organization-switcher.component.ts b/apps/web/src/app/admin-console/components/organization-switcher.component.ts index daca58d601a..cc7046e3b5f 100644 --- a/apps/web/src/app/admin-console/components/organization-switcher.component.ts +++ b/apps/web/src/app/admin-console/components/organization-switcher.component.ts @@ -1,11 +1,13 @@ import { Component, Input, OnInit } from "@angular/core"; -import { map, Observable } from "rxjs"; +import { combineLatest, map, Observable } from "rxjs"; import { canAccessAdmin, OrganizationService, } 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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -14,7 +16,11 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; templateUrl: "organization-switcher.component.html", }) export class OrganizationSwitcherComponent implements OnInit { - constructor(private organizationService: OrganizationService, private i18nService: I18nService) {} + constructor( + private organizationService: OrganizationService, + private i18nService: I18nService, + private configService: ConfigServiceAbstraction + ) {} @Input() activeOrganization: Organization = null; organizations$: Observable; @@ -22,9 +28,14 @@ export class OrganizationSwitcherComponent implements OnInit { loaded = false; async ngOnInit() { - this.organizations$ = this.organizationService.memberOrganizations$.pipe( - canAccessAdmin(this.i18nService), - map((orgs) => orgs.sort(Utils.getSortFunction(this.i18nService, "name"))) + this.organizations$ = combineLatest([ + this.organizationService.memberOrganizations$, + this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollections, false), + ]).pipe( + map(([orgs, flexibleCollectionsEnabled]) => { + const canAccess = canAccessAdmin(this.i18nService, flexibleCollectionsEnabled); + return canAccess ? orgs.sort(Utils.getSortFunction(this.i18nService, "name")) : []; + }) ); this.loaded = true; diff --git a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts index d599ce768df..c1707e67c1b 100644 --- a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts +++ b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.spec.ts @@ -9,6 +9,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationUserType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -52,7 +53,8 @@ describe("Organization Permissions Guard", () => { organizationService, mock(), mock(), - mock() + mock(), + mock() ); }); diff --git a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts index 595f72ba86e..8d671ec2d14 100644 --- a/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts +++ b/apps/web/src/app/admin-console/organizations/guards/org-permissions.guard.ts @@ -6,6 +6,8 @@ import { OrganizationService, } 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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -19,7 +21,8 @@ export class OrganizationPermissionsGuard implements CanActivate { private organizationService: OrganizationService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, - private syncService: SyncService + private syncService: SyncService, + private configService: ConfigServiceAbstraction ) {} async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { @@ -42,9 +45,17 @@ export class OrganizationPermissionsGuard implements CanActivate { return this.router.createUrlTree(["/"]); } - const permissionsCallback: (organization: Organization) => boolean = - route.data?.organizationPermissions; - const hasPermissions = permissionsCallback == null || permissionsCallback(org); + const flexibleCollectionsEnabled = await this.configService.getFeatureFlag( + FeatureFlag.FlexibleCollections, + false + ); + + const permissionsCallback: ( + organization: Organization, + flexibleCollectionsEnabled: boolean + ) => boolean = route.data?.organizationPermissions; + const hasPermissions = + permissionsCallback == null || permissionsCallback(org, flexibleCollectionsEnabled); if (!hasPermissions) { // Handle linkable ciphers for organizations the user only has view access to @@ -60,7 +71,7 @@ export class OrganizationPermissionsGuard implements CanActivate { } this.platformUtilsService.showToast("error", null, this.i18nService.t("accessDenied")); - return canAccessOrgAdmin(org) + return canAccessOrgAdmin(org, flexibleCollectionsEnabled) ? this.router.createUrlTree(["/organizations", org.id]) : this.router.createUrlTree(["/"]); } diff --git a/apps/web/src/app/admin-console/organizations/guards/org-redirect.guard.ts b/apps/web/src/app/admin-console/organizations/guards/org-redirect.guard.ts index bd496e79ecb..94cebd3f8b5 100644 --- a/apps/web/src/app/admin-console/organizations/guards/org-redirect.guard.ts +++ b/apps/web/src/app/admin-console/organizations/guards/org-redirect.guard.ts @@ -5,15 +5,25 @@ import { canAccessOrgAdmin, OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; @Injectable({ providedIn: "root", }) export class OrganizationRedirectGuard implements CanActivate { - constructor(private router: Router, private organizationService: OrganizationService) {} + constructor( + private router: Router, + private organizationService: OrganizationService, + private configService: ConfigServiceAbstraction + ) {} async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { const org = this.organizationService.get(route.params.organizationId); + const flexibleCollectionsEnabled = await this.configService.getFeatureFlag( + FeatureFlag.FlexibleCollections, + false + ); const customRedirect = route.data?.autoRedirectCallback; if (customRedirect) { @@ -24,7 +34,7 @@ export class OrganizationRedirectGuard implements CanActivate { return this.router.createUrlTree([state.url, ...redirectPath]); } - if (canAccessOrgAdmin(org)) { + if (canAccessOrgAdmin(org, flexibleCollectionsEnabled)) { return this.router.createUrlTree(["/organizations", org.id]); } return this.router.createUrlTree(["/"]); 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 a0a7df5a92d..47d9df042da 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 @@ -13,6 +13,8 @@ import { OrganizationService, } 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 { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; @Component({ selector: "app-organization-layout", @@ -22,12 +24,21 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { organization$: Observable; private _destroy = new Subject(); + private flexibleCollectionsEnabled: boolean; - constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {} + constructor( + private route: ActivatedRoute, + private organizationService: OrganizationService, + private configService: ConfigServiceAbstraction + ) {} - ngOnInit() { + async ngOnInit() { document.body.classList.remove("layout_frontend"); + this.flexibleCollectionsEnabled = await this.configService.getFeatureFlag( + FeatureFlag.FlexibleCollections + ); + this.organization$ = this.route.params .pipe(takeUntil(this._destroy)) .pipe(map((p) => p.organizationId)) @@ -46,7 +57,7 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy { } canShowVaultTab(organization: Organization): boolean { - return canAccessVaultTab(organization); + return canAccessVaultTab(organization, this.flexibleCollectionsEnabled); } canShowSettingsTab(organization: Organization): boolean { diff --git a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts index c4ca1dc241f..7e59cc9c7fe 100644 --- a/apps/web/src/app/admin-console/organizations/organization-routing.module.ts +++ b/apps/web/src/app/admin-console/organizations/organization-routing.module.ts @@ -78,8 +78,11 @@ const routes: Routes = [ }, ]; -function getOrganizationRoute(organization: Organization): string { - if (canAccessVaultTab(organization)) { +function getOrganizationRoute( + organization: Organization, + flexibleCollectionsEnabled: boolean +): string { + if (canAccessVaultTab(organization, flexibleCollectionsEnabled)) { return "vault"; } if (canAccessMembersTab(organization)) { diff --git a/apps/web/src/app/layouts/navbar.component.ts b/apps/web/src/app/layouts/navbar.component.ts index 8dc36e4fb37..e1aa93b501a 100644 --- a/apps/web/src/app/layouts/navbar.component.ts +++ b/apps/web/src/app/layouts/navbar.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from "@angular/core"; -import { map, Observable } from "rxjs"; +import { combineLatest, map, Observable } from "rxjs"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { @@ -9,8 +9,10 @@ import { import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { Provider } from "@bitwarden/common/models/domain/provider"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -38,7 +40,8 @@ export class NavbarComponent implements OnInit { private syncService: SyncService, private organizationService: OrganizationService, private vaultTimeoutSettingsService: VaultTimeoutSettingsService, - private i18nService: I18nService + private i18nService: I18nService, + private configService: ConfigServiceAbstraction ) { this.selfHosted = this.platformUtilsService.isSelfHost(); } @@ -57,8 +60,14 @@ export class NavbarComponent implements OnInit { } this.providers = await this.providerService.getAll(); - this.organizations$ = this.organizationService.memberOrganizations$.pipe( - canAccessAdmin(this.i18nService) + this.organizations$ = combineLatest([ + this.organizationService.memberOrganizations$, + this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollections, false), + ]).pipe( + map(([organizations, featureFlag]) => { + const canAccess = canAccessAdmin(this.i18nService, featureFlag); + return canAccess ? organizations : []; + }) ); this.canLock$ = this.vaultTimeoutSettingsService .availableVaultTimeoutActions$() diff --git a/apps/web/src/app/tools/import/import-web.component.ts b/apps/web/src/app/tools/import/import-web.component.ts index 952a717cd95..84ac3b0332b 100644 --- a/apps/web/src/app/tools/import/import-web.component.ts +++ b/apps/web/src/app/tools/import/import-web.component.ts @@ -6,6 +6,8 @@ import { OrganizationService, canAccessVaultTab, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { ImportComponent } from "@bitwarden/importer/ui"; import { SharedModule } from "../../shared"; @@ -20,14 +22,20 @@ export class ImportWebComponent implements OnInit { protected loading = false; protected disabled = false; + private flexibleCollectionsEnabled: boolean; + constructor( private route: ActivatedRoute, private organizationService: OrganizationService, - private router: Router + private router: Router, + private configService: ConfigServiceAbstraction ) {} - ngOnInit(): void { + async ngOnInit() { this.routeOrgId = this.route.snapshot.paramMap.get("organizationId"); + this.flexibleCollectionsEnabled = await this.configService.getFeatureFlag( + FeatureFlag.FlexibleCollections + ); } /** @@ -44,7 +52,7 @@ export class ImportWebComponent implements OnInit { return; } - if (canAccessVaultTab(organization)) { + if (canAccessVaultTab(organization, this.flexibleCollectionsEnabled)) { await this.router.navigate(["organizations", organizationId, "vault"]); } } diff --git a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html index 41e785a6eba..05368edd848 100644 --- a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html +++ b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.html @@ -109,7 +109,10 @@ {{ "cancel" | i18n }}