From 6cb1eca55d0938912b4ca5777d1e587851b96779 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 2 Oct 2023 08:25:47 +1000 Subject: [PATCH 01/13] First pass at feature flagging --- .../settings/account.component.html | 2 +- .../settings/account.component.ts | 27 +++++++++++++++++-- .../access-selector.component.ts | 16 ++++++++--- libs/common/src/enums/feature-flag.enum.ts | 2 ++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.html b/apps/web/src/app/admin-console/organizations/settings/account.component.html index 61b4a3dc8e6..8d9195d2a12 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.html +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.html @@ -53,7 +53,7 @@

{{ "apiKey" | i18n }}

diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index b7ecae82fca..911420763de 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -1,7 +1,17 @@ import { Component, ViewChild, ViewContainerRef } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { combineLatest, lastValueFrom, Subject, switchMap, takeUntil, from, of } from "rxjs"; +import { + combineLatest, + lastValueFrom, + Subject, + switchMap, + takeUntil, + from, + of, + Observable, + firstValueFrom, +} from "rxjs"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -10,10 +20,12 @@ import { OrganizationCollectionManagementUpdateRequest } from "@bitwarden/common import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request"; import { OrganizationUpdateRequest } from "@bitwarden/common/admin-console/models/request/organization-update.request"; import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { DialogService } from "@bitwarden/components"; import { ApiKeyComponent } from "../../../settings/api-key.component"; @@ -39,6 +51,7 @@ export class AccountComponent { canUseApi = false; org: OrganizationResponse; taxFormPromise: Promise; + collectionManagementFlag$: Observable; // FormGroup validators taken from server Organization domain object protected formGroup = this.formBuilder.group({ @@ -78,7 +91,8 @@ export class AccountComponent { private organizationService: OrganizationService, private organizationApiService: OrganizationApiServiceAbstraction, private dialogService: DialogService, - private formBuilder: FormBuilder + private formBuilder: FormBuilder, + private configService: ConfigService ) {} async ngOnInit() { @@ -130,6 +144,11 @@ export class AccountComponent { limitCollectionCreationDeletion: this.org.limitCollectionCreationDeletion, }); + this.collectionManagementFlag$ = this.configService.getFeatureFlag$( + FeatureFlag.CollectionManagement, + false + ); + this.loading = false; }); } @@ -164,6 +183,10 @@ export class AccountComponent { }; submitCollectionManagement = async () => { + if (!(await firstValueFrom(this.collectionManagementFlag$))) { + return; + } + const request = new OrganizationCollectionManagementUpdateRequest(); request.limitCreateDeleteOwnerAdmin = this.collectionManagementFormGroup.value.limitCollectionCreationDeletion; diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts index 018f568a420..0d4f2ade2b1 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts @@ -10,7 +10,9 @@ import { Subject, takeUntil } from "rxjs"; import { ControlsOf } from "@bitwarden/angular/types/controls-of"; import { FormSelectionList } from "@bitwarden/angular/utils/form-selection-list"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view"; import { @@ -121,8 +123,11 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On { perm: CollectionPermission.ViewExceptPass, labelId: "canViewExceptPass" }, { perm: CollectionPermission.Edit, labelId: "canEdit" }, { perm: CollectionPermission.EditExceptPass, labelId: "canEditExceptPass" }, - { perm: CollectionPermission.Manage, labelId: "canManage" }, ]; + protected canManagePermissionListItem = { + perm: CollectionPermission.Manage, + labelId: "canManage", + }; protected initialPermission = CollectionPermission.View; disabled: boolean; @@ -195,7 +200,8 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On constructor( private readonly formBuilder: FormBuilder, - private readonly i18nService: I18nService + private readonly i18nService: I18nService, + private readonly configService: ConfigService ) {} /** Required for NG_VALUE_ACCESSOR */ @@ -255,7 +261,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On this.pauseChangeNotification = false; } - ngOnInit() { + async ngOnInit() { // Watch the internal formArray for changes and propagate them this.selectionList.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((v) => { if (!this.notifyOnChange || this.pauseChangeNotification) { @@ -269,6 +275,10 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On } this.notifyOnChange(v); }); + + if (await this.configService.getFeatureFlag(FeatureFlag.CanManageCollectionPermission)) { + this.permissionList.push(this.canManagePermissionListItem); + } } ngOnDestroy() { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index f87a8ef52c4..f8e370c2ee3 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -3,6 +3,8 @@ export enum FeatureFlag { DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning", TrustedDeviceEncryption = "trusted-device-encryption", AutofillV2 = "autofill-v2", + CollectionManagement = "collection-management", + CanManageCollectionPermission = "can-manage-collection-permission", } // Replace this with a type safe lookup of the feature flag values in PM-2282 From 1163a205b4e19f48053adb6732c672a14e8c1b52 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Tue, 3 Oct 2023 11:20:57 +1000 Subject: [PATCH 02/13] Update flag name --- .../organizations/settings/account.component.html | 2 +- .../organizations/settings/account.component.ts | 8 ++++---- .../access-selector/access-selector.component.ts | 2 +- libs/common/src/enums/feature-flag.enum.ts | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.html b/apps/web/src/app/admin-console/organizations/settings/account.component.html index 8d9195d2a12..5cfcc80bf24 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.html +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.html @@ -53,7 +53,7 @@

{{ "apiKey" | i18n }}

diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index 911420763de..bb4838918f0 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -51,7 +51,7 @@ export class AccountComponent { canUseApi = false; org: OrganizationResponse; taxFormPromise: Promise; - collectionManagementFlag$: Observable; + flexibleCollectionsFlag$: Observable; // FormGroup validators taken from server Organization domain object protected formGroup = this.formBuilder.group({ @@ -144,8 +144,8 @@ export class AccountComponent { limitCollectionCreationDeletion: this.org.limitCollectionCreationDeletion, }); - this.collectionManagementFlag$ = this.configService.getFeatureFlag$( - FeatureFlag.CollectionManagement, + this.flexibleCollectionsFlag$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollections, false ); @@ -183,7 +183,7 @@ export class AccountComponent { }; submitCollectionManagement = async () => { - if (!(await firstValueFrom(this.collectionManagementFlag$))) { + if (!(await firstValueFrom(this.flexibleCollectionsFlag$))) { return; } diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts index 0d4f2ade2b1..9bb85243604 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts @@ -276,7 +276,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On this.notifyOnChange(v); }); - if (await this.configService.getFeatureFlag(FeatureFlag.CanManageCollectionPermission)) { + if (await this.configService.getFeatureFlag(FeatureFlag.FlexibleCollections)) { this.permissionList.push(this.canManagePermissionListItem); } } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index f8e370c2ee3..e1c95388ee1 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -3,8 +3,7 @@ export enum FeatureFlag { DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning", TrustedDeviceEncryption = "trusted-device-encryption", AutofillV2 = "autofill-v2", - CollectionManagement = "collection-management", - CanManageCollectionPermission = "can-manage-collection-permission", + FlexibleCollections = "flexible-collections", } // Replace this with a type safe lookup of the feature flag values in PM-2282 From 8ab1dcb52a625e3bc57b1722ec3fb5d46716373d Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Tue, 3 Oct 2023 13:43:20 +1000 Subject: [PATCH 03/13] Add BulkEditCollectionAccess flag --- .../organizations/settings/account.component.ts | 11 ++++------- .../components/vault-items/vault-items.component.html | 2 +- .../components/vault-items/vault-items.component.ts | 1 + .../app/vault/individual-vault/vault.component.html | 1 + .../src/app/vault/individual-vault/vault.component.ts | 5 +++++ apps/web/src/app/vault/org-vault/vault.component.html | 1 + apps/web/src/app/vault/org-vault/vault.component.ts | 9 ++++++++- libs/common/src/enums/feature-flag.enum.ts | 1 + 8 files changed, 22 insertions(+), 9 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index bb4838918f0..e9cc0bd9da3 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -9,7 +9,6 @@ import { takeUntil, from, of, - Observable, firstValueFrom, } from "rxjs"; @@ -51,7 +50,10 @@ export class AccountComponent { canUseApi = false; org: OrganizationResponse; taxFormPromise: Promise; - flexibleCollectionsFlag$: Observable; + flexibleCollectionsFlag$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollections, + false + ); // FormGroup validators taken from server Organization domain object protected formGroup = this.formBuilder.group({ @@ -144,11 +146,6 @@ export class AccountComponent { limitCollectionCreationDeletion: this.org.limitCollectionCreationDeletion, }); - this.flexibleCollectionsFlag$ = this.configService.getFeatureFlag$( - FeatureFlag.FlexibleCollections, - false - ); - this.loading = false; }); } diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.html b/apps/web/src/app/vault/components/vault-items/vault-items.component.html index 44dd95f81dc..8b9f18a1eb8 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.html @@ -35,7 +35,7 @@ {{ "moveSelected" | i18n }} diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index e9cc0bd9da3..5bcc4d00db8 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -50,7 +50,7 @@ export class AccountComponent { canUseApi = false; org: OrganizationResponse; taxFormPromise: Promise; - flexibleCollectionsFlag$ = this.configService.getFeatureFlag$( + showCollectionManagementSettings$ = this.configService.getFeatureFlag$( FeatureFlag.FlexibleCollections, false ); @@ -180,7 +180,7 @@ export class AccountComponent { }; submitCollectionManagement = async () => { - if (!(await firstValueFrom(this.flexibleCollectionsFlag$))) { + if (!(await firstValueFrom(this.showCollectionManagementSettings$))) { return; } From 5e0d03c832b067d92963bdc69388b68dc77a0495 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 9 Oct 2023 10:03:24 +1000 Subject: [PATCH 05/13] Remove unnecessary early return --- .../organizations/settings/account.component.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index 5bcc4d00db8..9deb344becd 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -1,16 +1,7 @@ import { Component, ViewChild, ViewContainerRef } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; -import { - combineLatest, - lastValueFrom, - Subject, - switchMap, - takeUntil, - from, - of, - firstValueFrom, -} from "rxjs"; +import { combineLatest, lastValueFrom, Subject, switchMap, takeUntil, from, of } from "rxjs"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -180,10 +171,6 @@ export class AccountComponent { }; submitCollectionManagement = async () => { - if (!(await firstValueFrom(this.showCollectionManagementSettings$))) { - return; - } - const request = new OrganizationCollectionManagementUpdateRequest(); request.limitCreateDeleteOwnerAdmin = this.collectionManagementFormGroup.value.limitCollectionCreationDeletion; From 55ed6cb81448f2051bbdefe1b0714bd111212574 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 9 Oct 2023 10:55:56 +1000 Subject: [PATCH 06/13] Fix tests --- .../access-selector.component.spec.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts index 10bcb9e5083..2d9842f1fc0 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts @@ -1,11 +1,13 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { MockProxy, mock } from "jest-mock-extended"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationUserStatusType, OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; +import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { AvatarModule, BadgeModule, @@ -50,8 +52,11 @@ class TestableAccessSelectorComponent extends AccessSelectorComponent { describe("AccessSelectorComponent", () => { let component: TestableAccessSelectorComponent; let fixture: ComponentFixture; + let configService: MockProxy; beforeEach(() => { + configService = mock(); + TestBed.configureTestingModule({ imports: [ ButtonModule, @@ -67,7 +72,12 @@ describe("AccessSelectorComponent", () => { IconButtonModule, ], declarations: [TestableAccessSelectorComponent, UserTypePipe], - providers: [], + providers: [ + { + provide: ConfigService, + useValue: configService, + }, + ], }).compileComponents(); }); From fdc01c249c8bc6e0365c9dc692035d3d9467310f Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Mon, 9 Oct 2023 11:05:35 +1000 Subject: [PATCH 07/13] Rename flags --- .../components/access-selector/access-selector.component.ts | 2 ++ apps/web/src/app/vault/individual-vault/vault.component.html | 2 +- apps/web/src/app/vault/individual-vault/vault.component.ts | 4 ++-- apps/web/src/app/vault/org-vault/vault.component.ts | 2 +- libs/common/src/enums/feature-flag.enum.ts | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts index 9bb85243604..b1458af3f8a 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts @@ -201,6 +201,8 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On constructor( private readonly formBuilder: FormBuilder, private readonly i18nService: I18nService, + + // reminder: remove this dependency from the spec file as well when this feature flag is removed private readonly configService: ConfigService ) {} diff --git a/apps/web/src/app/vault/individual-vault/vault.component.html b/apps/web/src/app/vault/individual-vault/vault.component.html index d2aa70bc2d1..f837adf8589 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.html +++ b/apps/web/src/app/vault/individual-vault/vault.component.html @@ -47,7 +47,7 @@ [cloneableOrganizationCiphers]="false" [showAdminActions]="false" (onEvent)="onVaultItemsEvent($event)" - [showBulkEditCollectionAccess]="showBulkEditCollectionAccess | async" + [showBulkEditCollectionAccess]="showBulkCollectionAccess | async" >
| undefined; protected canCreateCollections = false; protected currentSearchText$: Observable; - protected showBulkEditCollectionAccess = this.configService.getFeatureFlag$( - FeatureFlag.BulkEditCollectionAccess, + protected showBulkCollectionAccess = this.configService.getFeatureFlag$( + FeatureFlag.BulkCollectionAccess, false ); diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts index 5fba1955eea..084ad3cfebb 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -129,7 +129,7 @@ export class VaultComponent implements OnInit, OnDestroy { protected showMissingCollectionPermissionMessage: boolean; protected currentSearchText$: Observable; protected showBulkEditCollectionAccess = this.configService.getFeatureFlag$( - FeatureFlag.BulkEditCollectionAccess, + FeatureFlag.BulkCollectionAccess, false ); diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 45bc4ffc4a2..2f9c7bf0f15 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -5,7 +5,7 @@ export enum FeatureFlag { AutofillV2 = "autofill-v2", BrowserFilelessImport = "browser-fileless-import", FlexibleCollections = "flexible-collections", - BulkEditCollectionAccess = "bulk-edit-collection-access", + BulkCollectionAccess = "bulk-collection-access", } // Replace this with a type safe lookup of the feature flag values in PM-2282 From 4cbac733a951cbc2996793f85c97c8b122e0a306 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Tue, 10 Oct 2023 11:13:24 +1000 Subject: [PATCH 08/13] Address review feedback --- .../components/access-selector/access-selector.component.ts | 2 +- apps/web/src/app/vault/individual-vault/vault.component.html | 2 +- apps/web/src/app/vault/individual-vault/vault.component.ts | 2 +- apps/web/src/app/vault/org-vault/vault.component.html | 2 +- apps/web/src/app/vault/org-vault/vault.component.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts index b1458af3f8a..4f199b42641 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts @@ -124,7 +124,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On { perm: CollectionPermission.Edit, labelId: "canEdit" }, { perm: CollectionPermission.EditExceptPass, labelId: "canEditExceptPass" }, ]; - protected canManagePermissionListItem = { + private canManagePermissionListItem = { perm: CollectionPermission.Manage, labelId: "canManage", }; diff --git a/apps/web/src/app/vault/individual-vault/vault.component.html b/apps/web/src/app/vault/individual-vault/vault.component.html index f837adf8589..4c009166f15 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.html +++ b/apps/web/src/app/vault/individual-vault/vault.component.html @@ -47,7 +47,7 @@ [cloneableOrganizationCiphers]="false" [showAdminActions]="false" (onEvent)="onVaultItemsEvent($event)" - [showBulkEditCollectionAccess]="showBulkCollectionAccess | async" + [showBulkEditCollectionAccess]="showBulkCollectionAccess$ | async" >
| undefined; protected canCreateCollections = false; protected currentSearchText$: Observable; - protected showBulkCollectionAccess = this.configService.getFeatureFlag$( + protected showBulkCollectionAccess$ = this.configService.getFeatureFlag$( FeatureFlag.BulkCollectionAccess, false ); diff --git a/apps/web/src/app/vault/org-vault/vault.component.html b/apps/web/src/app/vault/org-vault/vault.component.html index f517ac20eb3..16b37b12732 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.html +++ b/apps/web/src/app/vault/org-vault/vault.component.html @@ -54,7 +54,7 @@ [cloneableOrganizationCiphers]="true" [showAdminActions]="true" (onEvent)="onVaultItemsEvent($event)" - [showBulkEditCollectionAccess]="showBulkEditCollectionAccess | async" + [showBulkEditCollectionAccess]="showBulkEditCollectionAccess$ | async" >
; - protected showBulkEditCollectionAccess = this.configService.getFeatureFlag$( + protected showBulkEditCollectionAccess$ = this.configService.getFeatureFlag$( FeatureFlag.BulkCollectionAccess, false ); From faccce81b5918a004fe886ab930fc538df41049a Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Tue, 10 Oct 2023 13:14:33 +1000 Subject: [PATCH 09/13] Fix: inject abstraction --- .../admin-console/organizations/settings/account.component.ts | 4 ++-- .../components/access-selector/access-selector.component.ts | 4 ++-- apps/web/src/app/vault/org-vault/vault.component.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/account.component.ts b/apps/web/src/app/admin-console/organizations/settings/account.component.ts index 9deb344becd..693e718231b 100644 --- a/apps/web/src/app/admin-console/organizations/settings/account.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/account.component.ts @@ -11,11 +11,11 @@ import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/ import { OrganizationUpdateRequest } from "@bitwarden/common/admin-console/models/request/organization-update.request"; import { OrganizationResponse } from "@bitwarden/common/admin-console/models/response/organization.response"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { DialogService } from "@bitwarden/components"; import { ApiKeyComponent } from "../../../settings/api-key.component"; @@ -85,7 +85,7 @@ export class AccountComponent { private organizationApiService: OrganizationApiServiceAbstraction, private dialogService: DialogService, private formBuilder: FormBuilder, - private configService: ConfigService + private configService: ConfigServiceAbstraction ) {} async ngOnInit() { diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts index 4f199b42641..e7aecf32fc5 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts @@ -11,8 +11,8 @@ import { Subject, takeUntil } from "rxjs"; import { ControlsOf } from "@bitwarden/angular/types/controls-of"; import { FormSelectionList } from "@bitwarden/angular/utils/form-selection-list"; 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 { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view"; import { @@ -203,7 +203,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On private readonly i18nService: I18nService, // reminder: remove this dependency from the spec file as well when this feature flag is removed - private readonly configService: ConfigService + private readonly configService: ConfigServiceAbstraction ) {} /** Required for NG_VALUE_ACCESSOR */ diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts index 6dbdc911ee6..78a9d69b613 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -42,12 +42,12 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ServiceUtils } from "@bitwarden/common/misc/serviceUtils"; import { TreeNode } from "@bitwarden/common/models/domain/tree-node"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; @@ -163,7 +163,7 @@ export class VaultComponent implements OnInit, OnDestroy { private eventCollectionService: EventCollectionService, private totpService: TotpService, private apiService: ApiService, - protected configService: ConfigService + protected configService: ConfigServiceAbstraction ) {} async ngOnInit() { From 1afc94d20fbc04f2e514ad4154b9446300f78ed3 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Tue, 10 Oct 2023 13:42:30 +1000 Subject: [PATCH 10/13] Add feature flags to collection dialog changes --- .../collection-dialog.component.ts | 129 ++++++++++-------- 1 file changed, 73 insertions(+), 56 deletions(-) diff --git a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts index 8fd7b2a4f78..82077e82906 100644 --- a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts @@ -3,6 +3,7 @@ import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from "@angula import { AbstractControl, FormBuilder, Validators } from "@angular/forms"; import { combineLatest, + firstValueFrom, from, map, Observable, @@ -16,6 +17,8 @@ import { import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; 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 { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -69,6 +72,10 @@ export enum CollectionDialogAction { export class CollectionDialogComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); protected organizations$: Observable; + private flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollections, + false + ); protected tabIndex: CollectionDialogTabType; protected loading = true; @@ -82,7 +89,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy { name: ["", [Validators.required, BitValidators.forbiddenCharacters(["/"])]], externalId: "", parent: undefined as string | undefined, - access: [[] as AccessItemValue[], [validateCanManagePermission]], + access: [[] as AccessItemValue[]], selectedOrg: "", }); protected PermissionMode = PermissionMode; @@ -98,7 +105,8 @@ export class CollectionDialogComponent implements OnInit, OnDestroy { private platformUtilsService: PlatformUtilsService, private organizationUserService: OrganizationUserService, private dialogService: DialogService, - private changeDetectorRef: ChangeDetectorRef + private changeDetectorRef: ChangeDetectorRef, + private configService: ConfigServiceAbstraction ) { this.tabIndex = params.initialTab ?? CollectionDialogTabType.Info; } @@ -124,6 +132,10 @@ export class CollectionDialogComponent implements OnInit, OnDestroy { this.formGroup.patchValue({ selectedOrg: this.params.organizationId }); await this.loadOrg(this.params.organizationId, this.params.collectionIds); } + + if (await firstValueFrom(this.flexibleCollectionsEnabled$)) { + this.formGroup.controls.access.addValidators(validateCanManagePermission); + } } async loadOrg(orgId: string, collectionIds: string[]) { @@ -147,67 +159,72 @@ export class CollectionDialogComponent implements OnInit, OnDestroy { : of(null), groups: groups$, users: this.organizationUserService.getAllUsers(orgId), + flexibleCollections: this.flexibleCollectionsEnabled$, }) .pipe(takeUntil(this.formGroup.controls.selectedOrg.valueChanges), takeUntil(this.destroy$)) - .subscribe(({ organization, collections, collectionDetails, groups, users }) => { - this.organization = organization; - this.accessItems = [].concat( - groups.map(mapGroupToAccessItemView), - users.data.map(mapUserToAccessItemView) - ); - - // Force change detection to update the access selector's items - this.changeDetectorRef.detectChanges(); - - if (collectionIds) { - collections = collections.filter((c) => collectionIds.includes(c.id)); - } - - if (this.params.collectionId) { - this.collection = collections.find((c) => c.id === this.collectionId); - this.nestOptions = collections.filter((c) => c.id !== this.collectionId); - - if (!this.collection) { - throw new Error("Could not find collection to edit."); + .subscribe( + ({ organization, collections, collectionDetails, groups, users, flexibleCollections }) => { + this.organization = organization; + this.accessItems = [].concat( + groups.map(mapGroupToAccessItemView), + users.data.map(mapUserToAccessItemView) + ); + + // Force change detection to update the access selector's items + this.changeDetectorRef.detectChanges(); + + if (collectionIds) { + collections = collections.filter((c) => collectionIds.includes(c.id)); } - const { name, parent } = parseName(this.collection); - if (parent !== undefined && !this.nestOptions.find((c) => c.name === parent)) { - this.deletedParentName = parent; + if (this.params.collectionId) { + this.collection = collections.find((c) => c.id === this.collectionId); + this.nestOptions = collections.filter((c) => c.id !== this.collectionId); + + if (!this.collection) { + throw new Error("Could not find collection to edit."); + } + + const { name, parent } = parseName(this.collection); + if (parent !== undefined && !this.nestOptions.find((c) => c.name === parent)) { + this.deletedParentName = parent; + } + + const accessSelections = mapToAccessSelections(collectionDetails); + this.formGroup.patchValue({ + name, + externalId: this.collection.externalId, + parent, + access: accessSelections, + }); + } else { + this.nestOptions = collections; + const parent = collections.find((c) => c.id === this.params.parentCollectionId); + const currentOrgUserId = users.data.find( + (u) => u.userId === this.organization?.userId + )?.id; + const initialSelection: AccessItemValue[] = + currentOrgUserId !== undefined + ? [ + { + id: currentOrgUserId, + type: AccessItemType.Member, + permission: flexibleCollections + ? CollectionPermission.Manage + : CollectionPermission.Edit, + }, + ] + : []; + + this.formGroup.patchValue({ + parent: parent?.name ?? undefined, + access: initialSelection, + }); } - const accessSelections = mapToAccessSelections(collectionDetails); - this.formGroup.patchValue({ - name, - externalId: this.collection.externalId, - parent, - access: accessSelections, - }); - } else { - this.nestOptions = collections; - const parent = collections.find((c) => c.id === this.params.parentCollectionId); - const currentOrgUserId = users.data.find( - (u) => u.userId === this.organization?.userId - )?.id; - const initialSelection: AccessItemValue[] = - currentOrgUserId !== undefined - ? [ - { - id: currentOrgUserId, - type: AccessItemType.Member, - permission: CollectionPermission.Manage, - }, - ] - : []; - - this.formGroup.patchValue({ - parent: parent?.name ?? undefined, - access: initialSelection, - }); + this.loading = false; } - - this.loading = false; - }); + ); } protected get collectionId() { From 0792321975654c68332f8a93637dc2c052c0b390 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Wed, 11 Oct 2023 12:00:34 +1000 Subject: [PATCH 11/13] Update test dependencies --- .../access-selector/access-selector.component.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts index 2d9842f1fc0..bb534556af0 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts @@ -7,7 +7,7 @@ import { OrganizationUserStatusType, OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; -import { ConfigService } from "@bitwarden/common/platform/services/config/config.service"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { AvatarModule, BadgeModule, @@ -52,7 +52,7 @@ class TestableAccessSelectorComponent extends AccessSelectorComponent { describe("AccessSelectorComponent", () => { let component: TestableAccessSelectorComponent; let fixture: ComponentFixture; - let configService: MockProxy; + let configService: MockProxy; beforeEach(() => { configService = mock(); @@ -74,7 +74,7 @@ describe("AccessSelectorComponent", () => { declarations: [TestableAccessSelectorComponent, UserTypePipe], providers: [ { - provide: ConfigService, + provide: ConfigServiceAbstraction, useValue: configService, }, ], From 50c64791129e04ecbb80b457ba894c488c2d0725 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Wed, 11 Oct 2023 13:54:07 +1000 Subject: [PATCH 12/13] Remove configService from accessSelector --- .../manage/group-add-edit.component.html | 2 ++ .../manage/group-add-edit.component.ts | 10 +++++++++- .../member-dialog/member-dialog.component.html | 2 ++ .../member-dialog/member-dialog.component.ts | 10 +++++++++- .../access-selector.component.spec.ts | 12 +----------- .../access-selector/access-selector.component.ts | 14 +++++++------- .../collection-dialog.component.html | 2 ++ .../collection-dialog.component.ts | 7 ++++--- .../bulk-collections-dialog.component.html | 2 ++ .../bulk-collections-dialog.component.ts | 10 +++++++++- 10 files changed, 47 insertions(+), 24 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html index b6175d80297..66aac8dc4d4 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.html @@ -40,6 +40,7 @@ [columnHeader]="'member' | i18n" [selectorLabelText]="'selectMembers' | i18n" [emptySelectionText]="'noMembersAdded' | i18n" + [flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async" > @@ -60,6 +61,7 @@ [columnHeader]="'collection' | i18n" [selectorLabelText]="'selectCollections' | i18n" [emptySelectionText]="'noCollectionsAdded' | i18n" + [flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async" > diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index d2f4fb1d20d..9bf72d18e62 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -24,6 +24,8 @@ import { convertToSelectionView, PermissionMode, } from "../shared/components/access-selector"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; /** * Indices for the available tabs in the dialog @@ -78,6 +80,11 @@ export const openGroupAddEditDialog = ( templateUrl: "group-add-edit.component.html", }) export class GroupAddEditComponent implements OnInit, OnDestroy { + protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollections, + false + ); + protected PermissionMode = PermissionMode; protected ResultType = GroupAddEditDialogResultType; @@ -181,7 +188,8 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { private logService: LogService, private formBuilder: FormBuilder, private changeDetectorRef: ChangeDetectorRef, - private dialogService: DialogService + private dialogService: DialogService, + private configService: ConfigServiceAbstraction ) { this.tabIndex = params.initialTab ?? GroupAddEditTabType.Info; } diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html index 8c506837880..34d407e7b23 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.html @@ -289,6 +289,7 @@

[columnHeader]="'groups' | i18n" [selectorLabelText]="'selectGroups' | i18n" [emptySelectionText]="'noGroupsAdded' | i18n" + [flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async" > @@ -321,6 +322,7 @@

[columnHeader]="'collection' | i18n" [selectorLabelText]="'selectCollections' | i18n" [emptySelectionText]="'noCollectionsAdded' | i18n" + [flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async" > diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 1d1a156269b..2c195a9dc23 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -36,6 +36,8 @@ import { import { commaSeparatedEmails } from "./validators/comma-separated-emails.validator"; import { freeOrgSeatLimitReachedValidator } from "./validators/free-org-inv-limit-reached.validator"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; export enum MemberDialogTab { Role = 0, @@ -64,6 +66,11 @@ export enum MemberDialogResult { templateUrl: "member-dialog.component.html", }) export class MemberDialogComponent implements OnInit, OnDestroy { + protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollections, + false + ); + loading = true; editMode = false; isRevoked = false; @@ -134,7 +141,8 @@ export class MemberDialogComponent implements OnInit, OnDestroy { private groupService: GroupService, private userService: UserAdminService, private organizationUserService: OrganizationUserService, - private dialogService: DialogService + private dialogService: DialogService, + private configService: ConfigServiceAbstraction ) {} async ngOnInit() { diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts index bb534556af0..10bcb9e5083 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.spec.ts @@ -1,13 +1,11 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { MockProxy, mock } from "jest-mock-extended"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationUserStatusType, OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; -import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { AvatarModule, BadgeModule, @@ -52,11 +50,8 @@ class TestableAccessSelectorComponent extends AccessSelectorComponent { describe("AccessSelectorComponent", () => { let component: TestableAccessSelectorComponent; let fixture: ComponentFixture; - let configService: MockProxy; beforeEach(() => { - configService = mock(); - TestBed.configureTestingModule({ imports: [ ButtonModule, @@ -72,12 +67,7 @@ describe("AccessSelectorComponent", () => { IconButtonModule, ], declarations: [TestableAccessSelectorComponent, UserTypePipe], - providers: [ - { - provide: ConfigServiceAbstraction, - useValue: configService, - }, - ], + providers: [], }).compileComponents(); }); diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts index e7aecf32fc5..2b954063b2a 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.component.ts @@ -10,8 +10,6 @@ import { Subject, takeUntil } from "rxjs"; import { ControlsOf } from "@bitwarden/angular/types/controls-of"; import { FormSelectionList } from "@bitwarden/angular/utils/form-selection-list"; -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 { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view"; @@ -198,12 +196,14 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On */ @Input() showGroupColumn: boolean; + /** + * Enable Flexible Collections changes (feature flag) + */ + @Input() flexibleCollectionsEnabled: boolean; + constructor( private readonly formBuilder: FormBuilder, - private readonly i18nService: I18nService, - - // reminder: remove this dependency from the spec file as well when this feature flag is removed - private readonly configService: ConfigServiceAbstraction + private readonly i18nService: I18nService ) {} /** Required for NG_VALUE_ACCESSOR */ @@ -278,7 +278,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On this.notifyOnChange(v); }); - if (await this.configService.getFeatureFlag(FeatureFlag.FlexibleCollections)) { + if (this.flexibleCollectionsEnabled) { this.permissionList.push(this.canManagePermissionListItem); } } 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 4f8c04b2b04..41e785a6eba 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 @@ -79,6 +79,7 @@ [selectorLabelText]="'selectGroupsAndMembers' | i18n" [selectorHelpText]="'userPermissionOverrideHelper' | i18n" [emptySelectionText]="'noMembersOrGroupsAdded' | i18n" + [flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async" > diff --git a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts index 82077e82906..23b2ec3a817 100644 --- a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts @@ -70,13 +70,14 @@ export enum CollectionDialogAction { templateUrl: "collection-dialog.component.html", }) export class CollectionDialogComponent implements OnInit, OnDestroy { - private destroy$ = new Subject(); - protected organizations$: Observable; - private flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$( + protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$( FeatureFlag.FlexibleCollections, false ); + private destroy$ = new Subject(); + protected organizations$: Observable; + protected tabIndex: CollectionDialogTabType; protected loading = true; protected organization?: Organization; diff --git a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.html b/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.html index abee08c18b8..f35156db22b 100644 --- a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.html +++ b/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.html @@ -17,6 +17,7 @@ [selectorLabelText]="'selectGroupsAndMembers' | i18n" [selectorHelpText]="'userPermissionOverrideHelper' | i18n" [emptySelectionText]="'noMembersOrGroupsAdded' | i18n" + [flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async" >

diff --git a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts b/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts index b863559aa93..abd8ccb9cfb 100644 --- a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts +++ b/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts @@ -6,6 +6,8 @@ import { combineLatest, of, Subject, switchMap, takeUntil } from "rxjs"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; 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 { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; @@ -42,6 +44,11 @@ export enum BulkCollectionsDialogResult { standalone: true, }) export class BulkCollectionsDialogComponent implements OnDestroy { + protected flexibleCollectionsEnabled$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollections, + false + ); + protected readonly PermissionMode = PermissionMode; protected formGroup = this.formBuilder.group({ @@ -63,7 +70,8 @@ export class BulkCollectionsDialogComponent implements OnDestroy { private organizationUserService: OrganizationUserService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, - private collectionAdminService: CollectionAdminService + private collectionAdminService: CollectionAdminService, + private configService: ConfigServiceAbstraction ) { this.numCollections = this.params.collections.length; const organization$ = this.organizationService.get$(this.params.organizationId); From 4bb93b36515b5716e6a248ed1f78503ab63be12b Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Wed, 11 Oct 2023 13:58:58 +1000 Subject: [PATCH 13/13] Fix linting --- .../organizations/manage/group-add-edit.component.ts | 4 ++-- .../components/member-dialog/member-dialog.component.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index 9bf72d18e62..00e7b3872dc 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -5,7 +5,9 @@ import { catchError, combineLatest, from, map, of, Subject, switchMap, takeUntil import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; +import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -24,8 +26,6 @@ import { convertToSelectionView, PermissionMode, } from "../shared/components/access-selector"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; /** * Indices for the available tabs in the dialog diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 2c195a9dc23..69efbd3fe76 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -11,6 +11,8 @@ import { } from "@bitwarden/common/admin-console/enums"; import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api"; 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 { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; @@ -36,8 +38,6 @@ import { import { commaSeparatedEmails } from "./validators/comma-separated-emails.validator"; import { freeOrgSeatLimitReachedValidator } from "./validators/free-org-inv-limit-reached.validator"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction"; export enum MemberDialogTab { Role = 0,