From 32981ce30d984ba47e26c39d3e85d7af665f9e58 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 4 Apr 2024 13:48:41 +1000 Subject: [PATCH] [AC-2320] Update canEditAnyCollection logic for Flexible Collections v1 (#8394) * also update calling locations to use canEditAllCiphers where applicable --- .../vault-items/vault-items.component.ts | 3 +- .../vault/core/views/collection-admin.view.ts | 7 ++-- .../bulk-delete-dialog.component.ts | 19 +++++++++-- .../vault-header/vault-header.component.ts | 29 +++++++++++++--- .../individual-vault/vault.component.html | 1 + .../vault/individual-vault/vault.component.ts | 5 +++ .../vault/org-vault/attachments.component.ts | 33 +++++++++++++++---- .../vault-header/vault-header.component.ts | 17 ++++++++-- .../app/vault/org-vault/vault.component.html | 6 +++- .../app/vault/org-vault/vault.component.ts | 18 +++++----- .../vault/components/add-edit.component.ts | 6 ++-- .../models/domain/organization.ts | 32 +++++++++++++----- .../src/vault/models/view/collection.view.ts | 8 ++--- 13 files changed, 140 insertions(+), 44 deletions(-) diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts index 7a8e858ba5..8b6ead33be 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.component.ts @@ -45,6 +45,7 @@ export class VaultItemsComponent { @Input() showBulkAddToCollections = false; @Input() showPermissionsColumn = false; @Input() viewingOrgVault: boolean; + @Input({ required: true }) flexibleCollectionsV1Enabled = false; private _ciphers?: CipherView[] = []; @Input() get ciphers(): CipherView[] { @@ -101,7 +102,7 @@ export class VaultItemsComponent { } const organization = this.allOrganizations.find((o) => o.id === collection.organizationId); - return collection.canEdit(organization); + return collection.canEdit(organization, this.flexibleCollectionsV1Enabled); } protected canDeleteCollection(collection: CollectionView): boolean { diff --git a/apps/web/src/app/vault/core/views/collection-admin.view.ts b/apps/web/src/app/vault/core/views/collection-admin.view.ts index 160228576a..d942d42fb8 100644 --- a/apps/web/src/app/vault/core/views/collection-admin.view.ts +++ b/apps/web/src/app/vault/core/views/collection-admin.view.ts @@ -31,10 +31,11 @@ export class CollectionAdminView extends CollectionView { this.assigned = response.assigned; } - override canEdit(org: Organization): boolean { + override canEdit(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { return org?.flexibleCollections - ? org?.canEditAnyCollection || this.manage - : org?.canEditAnyCollection || (org?.canEditAssignedCollections && this.assigned); + ? org?.canEditAnyCollection(flexibleCollectionsV1Enabled) || this.manage + : org?.canEditAnyCollection(flexibleCollectionsV1Enabled) || + (org?.canEditAssignedCollections && this.assigned); } override canDelete(org: Organization): boolean { diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts index 70f7a555f3..4050823a6d 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts @@ -1,8 +1,11 @@ import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; +import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; 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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -49,6 +52,11 @@ export class BulkDeleteDialogComponent { organizations: Organization[]; collections: CollectionView[]; + private flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollectionsV1, + false, + ); + constructor( @Inject(DIALOG_DATA) params: BulkDeleteDialogParams, private dialogRef: DialogRef, @@ -57,6 +65,7 @@ export class BulkDeleteDialogComponent { private i18nService: I18nService, private apiService: ApiService, private collectionService: CollectionService, + private configService: ConfigService, ) { this.cipherIds = params.cipherIds ?? []; this.permanent = params.permanent; @@ -72,7 +81,12 @@ export class BulkDeleteDialogComponent { protected submit = async () => { const deletePromises: Promise[] = []; if (this.cipherIds.length) { - if (!this.organization || !this.organization.canEditAnyCollection) { + const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$); + + if ( + !this.organization || + !this.organization.canEditAllCiphers(flexibleCollectionsV1Enabled) + ) { deletePromises.push(this.deleteCiphers()); } else { deletePromises.push(this.deleteCiphersAdmin()); @@ -104,7 +118,8 @@ export class BulkDeleteDialogComponent { }; private async deleteCiphers(): Promise { - const asAdmin = this.organization?.canEditAnyCollection; + const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$); + const asAdmin = this.organization?.canEditAllCiphers(flexibleCollectionsV1Enabled); if (this.permanent) { await this.cipherService.deleteManyWithServer(this.cipherIds, asAdmin); } else { diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts index b88b3af787..08afd09982 100644 --- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.ts @@ -1,6 +1,16 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core"; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnInit, + Output, +} from "@angular/core"; +import { firstValueFrom } from "rxjs"; 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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; @@ -17,7 +27,7 @@ import { templateUrl: "./vault-header.component.html", changeDetection: ChangeDetectionStrategy.OnPush, }) -export class VaultHeaderComponent { +export class VaultHeaderComponent implements OnInit { protected Unassigned = Unassigned; protected All = All; protected CollectionDialogTabType = CollectionDialogTabType; @@ -55,7 +65,18 @@ export class VaultHeaderComponent { /** Emits an event when the delete collection button is clicked in the header */ @Output() onDeleteCollection = new EventEmitter(); - constructor(private i18nService: I18nService) {} + private flexibleCollectionsV1Enabled = false; + + constructor( + private i18nService: I18nService, + private configService: ConfigService, + ) {} + + async ngOnInit() { + this.flexibleCollectionsV1Enabled = await firstValueFrom( + this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1), + ); + } /** * The id of the organization that is currently being filtered on. @@ -137,7 +158,7 @@ export class VaultHeaderComponent { const organization = this.organizations.find( (o) => o.id === this.collection?.node.organizationId, ); - return this.collection.node.canEdit(organization); + return this.collection.node.canEdit(organization, this.flexibleCollectionsV1Enabled); } async editCollection(tab: CollectionDialogTabType): Promise { 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 5f90f8d440..003066dadd 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.html +++ b/apps/web/src/app/vault/individual-vault/vault.component.html @@ -50,6 +50,7 @@ [cloneableOrganizationCiphers]="false" [showAdminActions]="false" (onEvent)="onVaultItemsEvent($event)" + [flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled$ | async" >
| undefined; protected canCreateCollections = false; protected currentSearchText$: Observable; + protected flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$( + FeatureFlag.FlexibleCollectionsV1, + false, + ); private searchText$ = new Subject(); private refresh$ = new BehaviorSubject(null); diff --git a/apps/web/src/app/vault/org-vault/attachments.component.ts b/apps/web/src/app/vault/org-vault/attachments.component.ts index f7ef372a2e..cf1f0796ec 100644 --- a/apps/web/src/app/vault/org-vault/attachments.component.ts +++ b/apps/web/src/app/vault/org-vault/attachments.component.ts @@ -1,8 +1,11 @@ -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; +import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -21,10 +24,12 @@ import { AttachmentsComponent as BaseAttachmentsComponent } from "../individual- selector: "app-org-vault-attachments", templateUrl: "../individual-vault/attachments.component.html", }) -export class AttachmentsComponent extends BaseAttachmentsComponent { +export class AttachmentsComponent extends BaseAttachmentsComponent implements OnInit { viewOnly = false; organization: Organization; + private flexibleCollectionsV1Enabled = false; + constructor( cipherService: CipherService, i18nService: I18nService, @@ -36,6 +41,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { fileDownloadService: FileDownloadService, dialogService: DialogService, billingAccountProfileStateService: BillingAccountProfileStateService, + private configService: ConfigService, ) { super( cipherService, @@ -51,14 +57,24 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { ); } + async ngOnInit() { + await super.ngOnInit(); + this.flexibleCollectionsV1Enabled = await firstValueFrom( + this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1, false), + ); + } + protected async reupload(attachment: AttachmentView) { - if (this.organization.canEditAnyCollection && this.showFixOldAttachments(attachment)) { + if ( + this.organization.canEditAllCiphers(this.flexibleCollectionsV1Enabled) && + this.showFixOldAttachments(attachment) + ) { await super.reuploadCipherAttachment(attachment, true); } } protected async loadCipher() { - if (!this.organization.canEditAnyCollection) { + if (!this.organization.canEditAllCiphers(this.flexibleCollectionsV1Enabled)) { return await super.loadCipher(); } const response = await this.apiService.getCipherAdmin(this.cipherId); @@ -69,18 +85,21 @@ export class AttachmentsComponent extends BaseAttachmentsComponent { return this.cipherService.saveAttachmentWithServer( this.cipherDomain, file, - this.organization.canEditAnyCollection, + this.organization.canEditAllCiphers(this.flexibleCollectionsV1Enabled), ); } protected deleteCipherAttachment(attachmentId: string) { - if (!this.organization.canEditAnyCollection) { + if (!this.organization.canEditAllCiphers(this.flexibleCollectionsV1Enabled)) { return super.deleteCipherAttachment(attachmentId); } return this.apiService.deleteCipherAttachmentAdmin(this.cipherId, attachmentId); } protected showFixOldAttachments(attachment: AttachmentView) { - return attachment.key == null && this.organization.canEditAnyCollection; + return ( + attachment.key == null && + this.organization.canEditAllCiphers(this.flexibleCollectionsV1Enabled) + ); } } diff --git a/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts b/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts index 45e56d062c..c4c67759c7 100644 --- a/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts +++ b/apps/web/src/app/vault/org-vault/vault-header/vault-header.component.ts @@ -1,10 +1,12 @@ -import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { Router } from "@angular/router"; import { firstValueFrom } from "rxjs"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { ProductType } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { DialogService, SimpleDialogOptions } from "@bitwarden/components"; @@ -22,7 +24,7 @@ import { selector: "app-org-vault-header", templateUrl: "./vault-header.component.html", }) -export class VaultHeaderComponent { +export class VaultHeaderComponent implements OnInit { protected All = All; protected Unassigned = Unassigned; @@ -56,14 +58,23 @@ export class VaultHeaderComponent { protected CollectionDialogTabType = CollectionDialogTabType; protected organizations$ = this.organizationService.organizations$; + private flexibleCollectionsV1Enabled = false; + constructor( private organizationService: OrganizationService, private i18nService: I18nService, private dialogService: DialogService, private collectionAdminService: CollectionAdminService, private router: Router, + private configService: ConfigService, ) {} + async ngOnInit() { + this.flexibleCollectionsV1Enabled = await firstValueFrom( + this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1), + ); + } + get title() { const headerType = this.organization?.flexibleCollections ? this.i18nService.t("collections").toLowerCase() @@ -153,7 +164,7 @@ export class VaultHeaderComponent { } // Otherwise, check if we can edit the specified collection - return this.collection.node.canEdit(this.organization); + return this.collection.node.canEdit(this.organization, this.flexibleCollectionsV1Enabled); } addCipher() { 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 4bec92b5db..391f412f1e 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.html +++ b/apps/web/src/app/vault/org-vault/vault.component.html @@ -54,6 +54,7 @@ [showBulkEditCollectionAccess]="organization?.flexibleCollections" [showBulkAddToCollections]="organization?.flexibleCollections" [viewingOrgVault]="true" + [flexibleCollectionsV1Enabled]="flexibleCollectionsV1Enabled" > @@ -98,7 +99,10 @@ 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 a267612bd6..e4860f2dbc 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -213,7 +213,7 @@ export class VaultComponent implements OnInit, OnDestroy { switchMap(async ([organization]) => { this.organization = organization; - if (!organization.canUseAdminCollections) { + if (!organization.canUseAdminCollections(this.flexibleCollectionsV1Enabled)) { await this.syncService.fullSync(false); } @@ -322,7 +322,7 @@ export class VaultComponent implements OnInit, OnDestroy { } } else { // Pre-flexible collections logic, to be removed after flexible collections is fully released - if (organization.canEditAnyCollection) { + if (organization.canEditAllCiphers(this.flexibleCollectionsV1Enabled)) { ciphers = await this.cipherService.getAllFromApiForOrganization(organization.id); } else { ciphers = (await this.cipherService.getAllDecrypted()).filter( @@ -407,7 +407,8 @@ export class VaultComponent implements OnInit, OnDestroy { ]).pipe( map(([filter, collection, organization]) => { return ( - (filter.collectionId === Unassigned && !organization.canUseAdminCollections) || + (filter.collectionId === Unassigned && + !organization.canUseAdminCollections(this.flexibleCollectionsV1Enabled)) || (!organization.canEditAllCiphers(this.flexibleCollectionsV1Enabled) && collection != undefined && !collection.node.assigned) @@ -453,11 +454,12 @@ export class VaultComponent implements OnInit, OnDestroy { map(([filter, collection, organization]) => { return ( // Filtering by unassigned, show message if not admin - (filter.collectionId === Unassigned && !organization.canUseAdminCollections) || + (filter.collectionId === Unassigned && + !organization.canUseAdminCollections(this.flexibleCollectionsV1Enabled)) || // Filtering by a collection, so show message if user is not assigned (collection != undefined && !collection.node.assigned && - !organization.canUseAdminCollections) + !organization.canUseAdminCollections(this.flexibleCollectionsV1Enabled)) ); }), shareReplay({ refCount: true, bufferSize: 1 }), @@ -480,7 +482,7 @@ export class VaultComponent implements OnInit, OnDestroy { (await firstValueFrom(allCipherMap$))[cipherId] != undefined; } else { canEditCipher = - organization.canUseAdminCollections || + organization.canUseAdminCollections(this.flexibleCollectionsV1Enabled) || (await this.cipherService.get(cipherId)) != null; } @@ -856,7 +858,7 @@ export class VaultComponent implements OnInit, OnDestroy { } try { - const asAdmin = this.organization?.canEditAnyCollection; + const asAdmin = this.organization?.canEditAnyCollection(this.flexibleCollectionsV1Enabled); await this.cipherService.restoreWithServer(c.id, asAdmin); this.platformUtilsService.showToast("success", null, this.i18nService.t("restoredItem")); this.refresh(); @@ -1143,7 +1145,7 @@ export class VaultComponent implements OnInit, OnDestroy { } protected deleteCipherWithServer(id: string, permanent: boolean) { - const asAdmin = this.organization?.canEditAnyCollection; + const asAdmin = this.organization?.canEditAllCiphers(this.flexibleCollectionsV1Enabled); return permanent ? this.cipherService.deleteWithServer(id, asAdmin) : this.cipherService.softDeleteWithServer(id, asAdmin); diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 4c177a77f2..36182ed9cf 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -662,7 +662,7 @@ export class AddEditComponent implements OnInit, OnDestroy { // if a cipher is unassigned we want to check if they are an admin or have permission to edit any collection if (!cipher.collectionIds) { - orgAdmin = this.organization?.canEditAnyCollection; + orgAdmin = this.organization?.canEditAllCiphers(this.flexibleCollectionsV1Enabled); } return this.cipher.id == null @@ -671,14 +671,14 @@ export class AddEditComponent implements OnInit, OnDestroy { } protected deleteCipher() { - const asAdmin = this.organization?.canEditAnyCollection; + const asAdmin = this.organization?.canEditAllCiphers(this.flexibleCollectionsV1Enabled); return this.cipher.isDeleted ? this.cipherService.deleteWithServer(this.cipher.id, asAdmin) : this.cipherService.softDeleteWithServer(this.cipher.id, asAdmin); } protected restoreCipher() { - const asAdmin = this.organization?.canEditAnyCollection; + const asAdmin = this.organization?.canEditAllCiphers(this.flexibleCollectionsV1Enabled); return this.cipherService.restoreWithServer(this.cipher.id, asAdmin); } diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index 18b762207a..5850f4582e 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -188,18 +188,29 @@ export class Organization { return this.isManager || this.permissions.createNewCollections; } - get canEditAnyCollection() { - return this.isAdmin || this.permissions.editAnyCollection; + canEditAnyCollection(flexibleCollectionsV1Enabled: boolean) { + if (!this.flexibleCollections || !flexibleCollectionsV1Enabled) { + // Pre-Flexible Collections v1 logic + return this.isAdmin || this.permissions.editAnyCollection; + } + + // Post Flexible Collections V1, the allowAdminAccessToAllCollectionItems flag can restrict admins + // Providers and custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag + return ( + this.isProviderUser || + (this.type === OrganizationUserType.Custom && this.permissions.editAnyCollection) || + (this.allowAdminAccessToAllCollectionItems && this.isAdmin) + ); } - get canUseAdminCollections() { - return this.canEditAnyCollection; + canUseAdminCollections(flexibleCollectionsV1Enabled: boolean) { + return this.canEditAnyCollection(flexibleCollectionsV1Enabled); } canEditAllCiphers(flexibleCollectionsV1Enabled: boolean) { - // Before Flexible Collections, anyone with editAnyCollection permission could edit all ciphers - if (!flexibleCollectionsV1Enabled) { - return this.canEditAnyCollection; + // Before Flexible Collections, any admin or anyone with editAnyCollection permission could edit all ciphers + if (!this.flexibleCollections || !flexibleCollectionsV1Enabled) { + return this.isAdmin || this.permissions.editAnyCollection; } // Post Flexible Collections V1, the allowAdminAccessToAllCollectionItems flag can restrict admins // Providers and custom users with canEditAnyCollection are not affected by allowAdminAccessToAllCollectionItems flag @@ -214,8 +225,13 @@ export class Organization { return this.isAdmin || this.permissions.deleteAnyCollection; } + /** + * Whether the user can view all collection information, such as collection name and access. + * This does not indicate that the user can view items inside any collection - for that, see {@link canEditAllCiphers} + */ get canViewAllCollections() { - return this.canEditAnyCollection || this.canDeleteAnyCollection; + // Admins can always see all collections even if collection management settings prevent them from editing them or seeing items + return this.isAdmin || this.permissions.editAnyCollection || this.canDeleteAnyCollection; } /** diff --git a/libs/common/src/vault/models/view/collection.view.ts b/libs/common/src/vault/models/view/collection.view.ts index 74d369380b..86766bdeac 100644 --- a/libs/common/src/vault/models/view/collection.view.ts +++ b/libs/common/src/vault/models/view/collection.view.ts @@ -53,11 +53,11 @@ export class CollectionView implements View, ITreeNodeObject { ); } - return org?.canEditAnyCollection || (org?.canEditAssignedCollections && this.assigned); + return org?.canEditAnyCollection(false) || (org?.canEditAssignedCollections && this.assigned); } // For editing collection details, not the items within it. - canEdit(org: Organization): boolean { + canEdit(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean { if (org != null && org.id !== this.organizationId) { throw new Error( "Id of the organization provided does not match the org id of the collection.", @@ -65,8 +65,8 @@ export class CollectionView implements View, ITreeNodeObject { } return org?.flexibleCollections - ? org?.canEditAnyCollection || this.manage - : org?.canEditAnyCollection || org?.canEditAssignedCollections; + ? org?.canEditAnyCollection(flexibleCollectionsV1Enabled) || this.manage + : org?.canEditAnyCollection(flexibleCollectionsV1Enabled) || org?.canEditAssignedCollections; } // For deleting a collection, not the items within it.