-
+ @if (this.canEditCollection || this.canDeleteCollection) {
+
+ }
|
@@ -57,16 +58,17 @@
|
-
+ @if (canEditCollection || canDeleteCollection || canViewCollectionInfo) {
+
+ }
+
+
+
+ {{ "editInfo" | i18n }}
+
+
+
+ {{ "access" | i18n }}
+
+
+
+
+ {{ "delete" | i18n }}
+
+
+
+
+ }
;
/** Whether 'Collection' option is shown in the 'New' dropdown */
- @Input() canCreateCollections: boolean;
+ @Input() canCreateCollections: boolean = false;
/** Emits an event when the new item button is clicked in the header */
@Output() onAddCipher = new EventEmitter();
@@ -106,7 +104,7 @@ export class VaultHeaderComponent {
return this.collection.node.organizationId;
}
- if (this.filter.organizationId !== undefined) {
+ if (this.filter?.organizationId !== undefined) {
return this.filter.organizationId;
}
@@ -119,10 +117,14 @@ export class VaultHeaderComponent {
}
protected get showBreadcrumbs() {
- return this.filter.collectionId !== undefined && this.filter.collectionId !== All;
+ return this.filter?.collectionId !== undefined && this.filter.collectionId !== All;
}
protected get title() {
+ if (this.filter === undefined) {
+ return "";
+ }
+
if (this.filter.collectionId === Unassigned) {
return this.i18nService.t("unassigned");
}
@@ -144,7 +146,7 @@ export class VaultHeaderComponent {
}
protected get icon() {
- return this.filter.collectionId && this.filter.collectionId !== All
+ return this.filter?.collectionId && this.filter.collectionId !== All
? "bwi-collection-shared"
: "";
}
diff --git a/libs/admin-console/src/common/collections/models/collection-admin.view.ts b/libs/admin-console/src/common/collections/models/collection-admin.view.ts
index cfc9996cd7a..dd7a57013ca 100644
--- a/libs/admin-console/src/common/collections/models/collection-admin.view.ts
+++ b/libs/admin-console/src/common/collections/models/collection-admin.view.ts
@@ -1,5 +1,3 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { CollectionAccessSelectionView } from "./collection-access-selection.view";
@@ -16,12 +14,12 @@ export class CollectionAdminView extends CollectionView {
* Flag indicating the collection has no active user or group assigned to it with CanManage permissions
* In this case, the collection can be managed by admins/owners or custom users with appropriate permissions
*/
- unmanaged: boolean;
+ unmanaged: boolean = false;
/**
* Flag indicating the user has been explicitly assigned to this Collection
*/
- assigned: boolean;
+ assigned: boolean = false;
constructor(response?: CollectionAccessDetailsResponse) {
super(response);
@@ -45,6 +43,10 @@ export class CollectionAdminView extends CollectionView {
* Returns true if the user can edit a collection (including user and group access) from the Admin Console.
*/
override canEdit(org: Organization): boolean {
+ if (this.isDefaultCollection) {
+ return false;
+ }
+
return (
org?.canEditAnyCollection ||
(this.unmanaged && org?.canEditUnmanagedCollections) ||
@@ -56,6 +58,10 @@ export class CollectionAdminView extends CollectionView {
* Returns true if the user can delete a collection from the Admin Console.
*/
override canDelete(org: Organization): boolean {
+ if (this.isDefaultCollection) {
+ return false;
+ }
+
return org?.canDeleteAnyCollection || super.canDelete(org);
}
@@ -63,6 +69,10 @@ export class CollectionAdminView extends CollectionView {
* Whether the user can modify user access to this collection
*/
canEditUserAccess(org: Organization): boolean {
+ if (this.isDefaultCollection) {
+ return false;
+ }
+
return (
(org.permissions.manageUsers && org.allowAdminAccessToAllCollectionItems) || this.canEdit(org)
);
@@ -72,6 +82,10 @@ export class CollectionAdminView extends CollectionView {
* Whether the user can modify group access to this collection
*/
canEditGroupAccess(org: Organization): boolean {
+ if (this.isDefaultCollection) {
+ return false;
+ }
+
return (
(org.permissions.manageGroups && org.allowAdminAccessToAllCollectionItems) ||
this.canEdit(org)
@@ -82,11 +96,13 @@ export class CollectionAdminView extends CollectionView {
* Returns true if the user can view collection info and access in a read-only state from the Admin Console
*/
override canViewCollectionInfo(org: Organization | undefined): boolean {
- if (this.isUnassignedCollection) {
+ if (this.isUnassignedCollection || this.isDefaultCollection) {
return false;
}
+ const isAdmin = org?.isAdmin ?? false;
+ const permissions = org?.permissions.editAnyCollection ?? false;
- return this.manage || org?.isAdmin || org?.permissions.editAnyCollection;
+ return this.manage || isAdmin || permissions;
}
/**
diff --git a/libs/admin-console/src/common/collections/models/collection.view.ts b/libs/admin-console/src/common/collections/models/collection.view.ts
index 7baf2e2b718..bce1d558f96 100644
--- a/libs/admin-console/src/common/collections/models/collection.view.ts
+++ b/libs/admin-console/src/common/collections/models/collection.view.ts
@@ -1,27 +1,25 @@
-// FIXME: Update this file to be type safe and remove this and next line
-// @ts-strict-ignore
import { Jsonify } from "type-fest";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { View } from "@bitwarden/common/models/view/view";
import { ITreeNodeObject } from "@bitwarden/common/vault/models/domain/tree-node";
-import { Collection, CollectionType } from "./collection";
+import { Collection, CollectionType, CollectionTypes } from "./collection";
import { CollectionAccessDetailsResponse } from "./collection.response";
export const NestingDelimiter = "/";
export class CollectionView implements View, ITreeNodeObject {
- id: string = null;
- organizationId: string = null;
- name: string = null;
- externalId: string = null;
+ id: string | undefined;
+ organizationId: string | undefined;
+ name: string | undefined;
+ externalId: string | undefined;
// readOnly applies to the items within a collection
- readOnly: boolean = null;
- hidePasswords: boolean = null;
- manage: boolean = null;
- assigned: boolean = null;
- type: CollectionType = null;
+ readOnly: boolean = false;
+ hidePasswords: boolean = false;
+ manage: boolean = false;
+ assigned: boolean = false;
+ type: CollectionType = CollectionTypes.SharedCollection;
constructor(c?: Collection | CollectionAccessDetailsResponse) {
if (!c) {
@@ -57,7 +55,11 @@ export class CollectionView implements View, ITreeNodeObject {
* Returns true if the user can edit a collection (including user and group access) from the individual vault.
* Does not include admin permissions - see {@link CollectionAdminView.canEdit}.
*/
- canEdit(org: Organization): boolean {
+ canEdit(org: Organization | undefined): boolean {
+ if (this.isDefaultCollection) {
+ return false;
+ }
+
if (org != null && org.id !== this.organizationId) {
throw new Error(
"Id of the organization provided does not match the org id of the collection.",
@@ -71,7 +73,7 @@ export class CollectionView implements View, ITreeNodeObject {
* Returns true if the user can delete a collection from the individual vault.
* Does not include admin permissions - see {@link CollectionAdminView.canDelete}.
*/
- canDelete(org: Organization): boolean {
+ canDelete(org: Organization | undefined): 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.",
@@ -81,7 +83,7 @@ export class CollectionView implements View, ITreeNodeObject {
const canDeleteManagedCollections = !org?.limitCollectionDeletion || org.isAdmin;
// Only use individual permissions, not admin permissions
- return canDeleteManagedCollections && this.manage;
+ return canDeleteManagedCollections && this.manage && !this.isDefaultCollection;
}
/**
@@ -94,4 +96,8 @@ export class CollectionView implements View, ITreeNodeObject {
static fromJSON(obj: Jsonify) {
return Object.assign(new CollectionView(new Collection()), obj);
}
+
+ get isDefaultCollection() {
+ return this.type == CollectionTypes.DefaultUserCollection;
+ }
}
diff --git a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts
index 8302ff541aa..d90ae06a75f 100644
--- a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts
+++ b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts
@@ -42,7 +42,7 @@ export class EmptyVaultNudgeService extends DefaultSingleNudgeService {
const orgIds = new Set(orgs.map((org) => org.id));
const canCreateCollections = orgs.some((org) => org.canCreateNewCollections);
const hasManageCollections = collections.some(
- (c) => c.manage && orgIds.has(c.organizationId),
+ (c) => c.manage && orgIds.has(c.organizationId!),
);
// When the user has dismissed the nudge or spotlight, return the nudge status directly
diff --git a/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts
index 2d86c76dff7..df0403ba4ab 100644
--- a/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts
+++ b/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts
@@ -46,7 +46,7 @@ export class VaultSettingsImportNudgeService extends DefaultSingleNudgeService {
const orgIds = new Set(orgs.map((org) => org.id));
const canCreateCollections = orgs.some((org) => org.canCreateNewCollections);
const hasManageCollections = collections.some(
- (c) => c.manage && orgIds.has(c.organizationId),
+ (c) => c.manage && orgIds.has(c.organizationId!),
);
// When the user has dismissed the nudge or spotlight, return the nudge status directly
diff --git a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts
index fea57743055..0d633be868e 100644
--- a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts
+++ b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts
@@ -191,6 +191,9 @@ export function sortDefaultCollections(
.sort((a, b) => {
const aName = orgs.find((o) => o.id === a.organizationId)?.name ?? a.organizationId;
const bName = orgs.find((o) => o.id === b.organizationId)?.name ?? b.organizationId;
+ if (!aName || !bName) {
+ throw new Error("Collection does not have an organizationId.");
+ }
return collator.compare(aName, bName);
});
return [
diff --git a/libs/common/src/vault/models/domain/tree-node.ts b/libs/common/src/vault/models/domain/tree-node.ts
index 7af1d9e6ab4..7ba8e593908 100644
--- a/libs/common/src/vault/models/domain/tree-node.ts
+++ b/libs/common/src/vault/models/domain/tree-node.ts
@@ -16,6 +16,6 @@ export class TreeNode {
}
export interface ITreeNodeObject {
- id: string;
- name: string;
+ id: string | undefined;
+ name: string | undefined;
}
diff --git a/libs/importer/src/importers/base-importer.ts b/libs/importer/src/importers/base-importer.ts
index 9033997a475..463d61dbbdf 100644
--- a/libs/importer/src/importers/base-importer.ts
+++ b/libs/importer/src/importers/base-importer.ts
@@ -279,7 +279,7 @@ export abstract class BaseImporter {
result.collections = result.folders.map((f) => {
const collection = new CollectionView();
collection.name = f.name;
- collection.id = f.id;
+ collection.id = f.id ?? undefined; // folder id may be null, which is not suitable for collections.
return collection;
});
result.folderRelationships = [];
diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts
index dfa0f9a89ca..db8e2007c61 100644
--- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts
+++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts
@@ -31,7 +31,7 @@ const createMockCollection = (
organizationId: string,
readOnly = false,
canEdit = true,
-) => {
+): CollectionView => {
return {
id,
name,
@@ -42,6 +42,7 @@ const createMockCollection = (
manage: true,
assigned: true,
type: CollectionTypes.DefaultUserCollection,
+ isDefaultCollection: true,
canEditItems: jest.fn().mockReturnValue(canEdit),
canEdit: jest.fn(),
canDelete: jest.fn(),
|