mirror of
https://github.com/bitwarden/browser
synced 2026-01-05 18:13:26 +00:00
[PM-25982] Assign to Collections - My Items (#16591)
* update cipher form to exclude my items collections * handle default collections for assign to collections and bulk * account for every returning true for empty arrays
This commit is contained in:
@@ -7,7 +7,7 @@ import { BehaviorSubject, of } from "rxjs";
|
||||
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { CollectionTypes, CollectionView } from "@bitwarden/admin-console/common";
|
||||
import { CollectionType, CollectionTypes, CollectionView } from "@bitwarden/admin-console/common";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
@@ -33,6 +33,7 @@ const createMockCollection = (
|
||||
organizationId: string,
|
||||
readOnly = false,
|
||||
canEdit = true,
|
||||
type: CollectionType = CollectionTypes.DefaultUserCollection,
|
||||
): CollectionView => {
|
||||
const cv = new CollectionView({
|
||||
name,
|
||||
@@ -41,7 +42,7 @@ const createMockCollection = (
|
||||
});
|
||||
cv.readOnly = readOnly;
|
||||
cv.manage = true;
|
||||
cv.type = CollectionTypes.DefaultUserCollection;
|
||||
cv.type = type;
|
||||
cv.externalId = "";
|
||||
cv.hidePasswords = false;
|
||||
cv.assigned = true;
|
||||
@@ -519,6 +520,42 @@ describe("ItemDetailsSectionComponent", () => {
|
||||
|
||||
expect(component["collectionOptions"].map((c) => c.id)).toEqual(["col1", "col2", "col3"]);
|
||||
});
|
||||
|
||||
it("should exclude default collections when the cipher is only assigned to shared collections", async () => {
|
||||
component.config.admin = false;
|
||||
component.config.organizationDataOwnershipDisabled = true;
|
||||
component.config.organizations = [{ id: "org1" } as Organization];
|
||||
component.config.collections = new Array(4)
|
||||
.fill(null)
|
||||
.map((_, i) => i + 1)
|
||||
.map(
|
||||
(i) =>
|
||||
createMockCollection(
|
||||
`col${i}`,
|
||||
`Collection ${i}`,
|
||||
"org1",
|
||||
false,
|
||||
false,
|
||||
i < 4 ? CollectionTypes.SharedCollection : CollectionTypes.DefaultUserCollection,
|
||||
) as CollectionView,
|
||||
);
|
||||
component.originalCipherView = {
|
||||
name: "cipher1",
|
||||
organizationId: "org1",
|
||||
folderId: "folder1",
|
||||
collectionIds: ["col2", "col3"],
|
||||
favorite: true,
|
||||
} as CipherView;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
component.itemDetailsForm.controls.organizationId.setValue("org1");
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(component["collectionOptions"].map((c) => c.id)).toEqual(["col1", "col2", "col3"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("readonlyCollections", () => {
|
||||
|
||||
@@ -406,6 +406,17 @@ export class ItemDetailsSectionComponent implements OnInit {
|
||||
this.showCollectionsControl = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the the cipher is only assigned to shared collections.
|
||||
* i.e. The cipher is not assigned to a default collections.
|
||||
* Note: `.every` will return true for an empty array
|
||||
*/
|
||||
const cipherIsOnlyInOrgCollections =
|
||||
(this.originalCipherView?.collectionIds ?? []).length > 0 &&
|
||||
this.originalCipherView.collectionIds.every(
|
||||
(cId) =>
|
||||
this.collections.find((c) => c.id === cId)?.type === CollectionTypes.SharedCollection,
|
||||
);
|
||||
this.collectionOptions = this.collections
|
||||
.filter((c) => {
|
||||
// The collection belongs to the organization
|
||||
@@ -423,10 +434,17 @@ export class ItemDetailsSectionComponent implements OnInit {
|
||||
return true;
|
||||
}
|
||||
|
||||
// When the cipher is only assigned to shared collections, do not allow a user to
|
||||
// move it back to a default collection. Exclude the default collection from the list.
|
||||
if (cipherIsOnlyInOrgCollections && c.type === CollectionTypes.DefaultUserCollection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Non-admins can only select assigned collections that are not read only. (Non-AC)
|
||||
return c.assigned && !c.readOnly;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
// Show default collection first
|
||||
const aIsDefaultCollection = a.type === CollectionTypes.DefaultUserCollection ? -1 : 0;
|
||||
const bIsDefaultCollection = b.type === CollectionTypes.DefaultUserCollection ? -1 : 0;
|
||||
return aIsDefaultCollection - bIsDefaultCollection;
|
||||
|
||||
Reference in New Issue
Block a user