1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00

[AC-2086] Update CanDelete with v1 flag logic (#9100)

* feat: update org domain object deleteAnyCollection with v1 flag logic, refs AC-2086

* feat: update canDelete method to handle v1 flag logic, refs AC-2086

* feat: update canDelete references to pass v1 flag, refs AC-2086

* feat: add provider check and modify owner/admin type checks, refs AC-2086

* fix: add permission to org instantiation for vault item stories, refs AC-2086
This commit is contained in:
Vincent Salucci
2024-05-13 16:13:27 -05:00
committed by GitHub
parent 66f5d90803
commit 3900924250
11 changed files with 42 additions and 21 deletions

View File

@@ -214,7 +214,9 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
access: accessSelections, access: accessSelections,
}); });
this.collection.manage = collection?.manage ?? false; // Get manage flag from sync data collection this.collection.manage = collection?.manage ?? false; // Get manage flag from sync data collection
this.showDeleteButton = !this.dialogReadonly && this.collection.canDelete(organization); this.showDeleteButton =
!this.dialogReadonly &&
this.collection.canDelete(organization, flexibleCollectionsV1);
} else { } else {
this.nestOptions = collections; this.nestOptions = collections;
const parent = collections.find((c) => c.id === this.params.parentCollectionId); const parent = collections.find((c) => c.id === this.params.parentCollectionId);

View File

@@ -163,7 +163,7 @@ export class VaultItemsComponent {
} }
} }
return collection.canDelete(organization); return collection.canDelete(organization, this.flexibleCollectionsV1Enabled);
} }
protected canViewCollectionInfo(collection: CollectionView) { protected canViewCollectionInfo(collection: CollectionView) {

View File

@@ -4,6 +4,7 @@ import { applicationConfig, Meta, moduleMetadata, Story } from "@storybook/angul
import { BehaviorSubject, of } from "rxjs"; import { BehaviorSubject, of } from "rxjs";
import { OrganizationUserType } from "@bitwarden/common/admin-console/enums"; import { OrganizationUserType } 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 { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
@@ -318,5 +319,6 @@ function createOrganization(i: number): Organization {
organization.id = `organization-${i}`; organization.id = `organization-${i}`;
organization.name = `Organization ${i}`; organization.name = `Organization ${i}`;
organization.type = OrganizationUserType.Owner; organization.type = OrganizationUserType.Owner;
organization.permissions = new PermissionsApi();
return organization; return organization;
} }

View File

@@ -71,12 +71,6 @@ export class CollectionAdminView extends CollectionView {
(org?.canEditAssignedCollections && this.assigned); (org?.canEditAssignedCollections && this.assigned);
} }
override canDelete(org: Organization): boolean {
return org?.flexibleCollections
? org?.canDeleteAnyCollection || (!org?.limitCollectionCreationDeletion && this.manage)
: org?.canDeleteAnyCollection || (org?.canDeleteAssignedCollections && this.assigned);
}
/** /**
* Whether the user can modify user access to this collection * Whether the user can modify user access to this collection
*/ */

View File

@@ -145,9 +145,12 @@ export class BulkDeleteDialogComponent {
} }
private async deleteCollections(): Promise<any> { private async deleteCollections(): Promise<any> {
const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$);
// From org vault // From org vault
if (this.organization) { if (this.organization) {
if (this.collections.some((c) => !c.canDelete(this.organization))) { if (
this.collections.some((c) => !c.canDelete(this.organization, flexibleCollectionsV1Enabled))
) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
this.i18nService.t("errorOccurred"), this.i18nService.t("errorOccurred"),
@@ -164,7 +167,7 @@ export class BulkDeleteDialogComponent {
const deletePromises: Promise<any>[] = []; const deletePromises: Promise<any>[] = [];
for (const organization of this.organizations) { for (const organization of this.organizations) {
const orgCollections = this.collections.filter((o) => o.organizationId === organization.id); const orgCollections = this.collections.filter((o) => o.organizationId === organization.id);
if (orgCollections.some((c) => !c.canDelete(organization))) { if (orgCollections.some((c) => !c.canDelete(organization, flexibleCollectionsV1Enabled))) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
this.i18nService.t("errorOccurred"), this.i18nService.t("errorOccurred"),

View File

@@ -176,7 +176,7 @@ export class VaultHeaderComponent implements OnInit {
(o) => o.id === this.collection?.node.organizationId, (o) => o.id === this.collection?.node.organizationId,
); );
return this.collection.node.canDelete(organization); return this.collection.node.canDelete(organization, this.flexibleCollectionsV1Enabled);
} }
deleteCollection() { deleteCollection() {

View File

@@ -693,7 +693,8 @@ export class VaultComponent implements OnInit, OnDestroy {
async deleteCollection(collection: CollectionView): Promise<void> { async deleteCollection(collection: CollectionView): Promise<void> {
const organization = await this.organizationService.get(collection.organizationId); const organization = await this.organizationService.get(collection.organizationId);
if (!collection.canDelete(organization)) { const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$);
if (!collection.canDelete(organization, flexibleCollectionsV1Enabled)) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
this.i18nService.t("errorOccurred"), this.i18nService.t("errorOccurred"),

View File

@@ -207,7 +207,7 @@ export class VaultHeaderComponent implements OnInit {
} }
// Otherwise, check if we can delete the specified collection // Otherwise, check if we can delete the specified collection
return this.collection.node.canDelete(this.organization); return this.collection.node.canDelete(this.organization, this.flexibleCollectionsV1Enabled);
} }
get canViewCollectionInfo(): boolean { get canViewCollectionInfo(): boolean {

View File

@@ -1047,7 +1047,7 @@ export class VaultComponent implements OnInit, OnDestroy {
} }
async deleteCollection(collection: CollectionView): Promise<void> { async deleteCollection(collection: CollectionView): Promise<void> {
if (!collection.canDelete(this.organization)) { if (!collection.canDelete(this.organization, this.flexibleCollectionsV1Enabled)) {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
this.i18nService.t("errorOccurred"), this.i18nService.t("errorOccurred"),

View File

@@ -232,8 +232,23 @@ export class Organization {
); );
} }
get canDeleteAnyCollection() { /**
return this.isAdmin || this.permissions.deleteAnyCollection; * @param flexibleCollectionsV1Enabled - Whether or not the V1 Flexible Collection feature flag is enabled
* @returns True if the user can delete any collection
*/
canDeleteAnyCollection(flexibleCollectionsV1Enabled: boolean) {
// Providers and Users with DeleteAnyCollection permission can always delete collections
if (this.isProviderUser || this.permissions.deleteAnyCollection) {
return true;
}
// If AllowAdminAccessToAllCollectionItems is true, Owners and Admins can delete any collection, regardless of LimitCollectionCreationDeletion setting
// Using explicit type checks because provider users are handled above and this mimics the server's permission checks closely
if (!flexibleCollectionsV1Enabled || this.allowAdminAccessToAllCollectionItems) {
return this.type == OrganizationUserType.Owner || this.type == OrganizationUserType.Admin;
}
return false;
} }
/** /**
@@ -242,7 +257,9 @@ export class Organization {
*/ */
get canViewAllCollections() { get canViewAllCollections() {
// Admins can always see all collections even if collection management settings prevent them from editing them or seeing items // 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; return (
this.isAdmin || this.permissions.editAnyCollection || this.permissions.deleteAnyCollection
);
} }
/** /**

View File

@@ -75,16 +75,18 @@ export class CollectionView implements View, ITreeNodeObject {
} }
// For deleting a collection, not the items within it. // For deleting a collection, not the items within it.
canDelete(org: Organization): boolean { canDelete(org: Organization, flexibleCollectionsV1Enabled: boolean): boolean {
if (org != null && org.id !== this.organizationId) { if (org != null && org.id !== this.organizationId) {
throw new Error( throw new Error(
"Id of the organization provided does not match the org id of the collection.", "Id of the organization provided does not match the org id of the collection.",
); );
} }
return org?.flexibleCollections const canDeleteManagedCollections = !org?.limitCollectionCreationDeletion || org.isAdmin;
? org?.canDeleteAnyCollection || (!org?.limitCollectionCreationDeletion && this.manage) return (
: org?.canDeleteAnyCollection || org?.canDeleteAssignedCollections; org?.canDeleteAnyCollection(flexibleCollectionsV1Enabled) ||
(canDeleteManagedCollections && this.manage)
);
} }
/** /**