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:
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user