1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 01:03:35 +00:00

[PM-12747] Move CollectionService and models to AC Team (#11278)

This commit is contained in:
Thomas Rittson
2024-10-09 00:14:39 +10:00
committed by GitHub
parent a83646be33
commit 7c72795d1c
131 changed files with 281 additions and 271 deletions

View File

@@ -1,28 +0,0 @@
import { Jsonify } from "type-fest";
import { CollectionId, OrganizationId } from "../../../types/guid";
import { CollectionDetailsResponse } from "../response/collection.response";
export class CollectionData {
id: CollectionId;
organizationId: OrganizationId;
name: string;
externalId: string;
readOnly: boolean;
manage: boolean;
hidePasswords: boolean;
constructor(response: CollectionDetailsResponse) {
this.id = response.id;
this.organizationId = response.organizationId;
this.name = response.name;
this.externalId = response.externalId;
this.readOnly = response.readOnly;
this.manage = response.manage;
this.hidePasswords = response.hidePasswords;
}
static fromJSON(obj: Jsonify<CollectionData>) {
return Object.assign(new CollectionData(new CollectionDetailsResponse({})), obj);
}
}

View File

@@ -1,77 +0,0 @@
import { makeSymmetricCryptoKey, mockEnc } from "../../../../spec";
import { CollectionId, OrganizationId } from "../../../types/guid";
import { OrgKey } from "../../../types/key";
import { CollectionData } from "../data/collection.data";
import { Collection } from "./collection";
describe("Collection", () => {
let data: CollectionData;
beforeEach(() => {
data = {
id: "id" as CollectionId,
organizationId: "orgId" as OrganizationId,
name: "encName",
externalId: "extId",
readOnly: true,
manage: true,
hidePasswords: true,
};
});
it("Convert from empty", () => {
const data = new CollectionData({} as any);
const card = new Collection(data);
expect(card).toEqual({
externalId: null,
hidePasswords: null,
id: null,
name: null,
organizationId: null,
readOnly: null,
manage: null,
});
});
it("Convert", () => {
const collection = new Collection(data);
expect(collection).toEqual({
id: "id",
organizationId: "orgId",
name: { encryptedString: "encName", encryptionType: 0 },
externalId: { encryptedString: "extId", encryptionType: 0 },
readOnly: true,
manage: true,
hidePasswords: true,
});
});
it("Decrypt", async () => {
const collection = new Collection();
collection.id = "id";
collection.organizationId = "orgId" as OrganizationId;
collection.name = mockEnc("encName");
collection.externalId = "extId";
collection.readOnly = false;
collection.hidePasswords = false;
collection.manage = true;
const key = makeSymmetricCryptoKey<OrgKey>();
const view = await collection.decrypt(key);
expect(view).toEqual({
externalId: "extId",
hidePasswords: false,
id: "id",
name: "encName",
organizationId: "orgId",
readOnly: false,
manage: true,
assigned: true,
});
});
});

View File

@@ -1,48 +0,0 @@
import Domain from "../../../platform/models/domain/domain-base";
import { EncString } from "../../../platform/models/domain/enc-string";
import { OrgKey } from "../../../types/key";
import { CollectionData } from "../data/collection.data";
import { CollectionView } from "../view/collection.view";
export class Collection extends Domain {
id: string;
organizationId: string;
name: EncString;
externalId: string;
readOnly: boolean;
hidePasswords: boolean;
manage: boolean;
constructor(obj?: CollectionData) {
super();
if (obj == null) {
return;
}
this.buildDomainModel(
this,
obj,
{
id: null,
organizationId: null,
name: null,
externalId: null,
readOnly: null,
hidePasswords: null,
manage: null,
},
["id", "organizationId", "readOnly", "hidePasswords", "manage"],
);
}
decrypt(orgKey: OrgKey): Promise<CollectionView> {
return this.decryptObj(
new CollectionView(this),
{
name: null,
},
this.organizationId,
orgKey,
);
}
}

View File

@@ -1,15 +0,0 @@
import { Collection } from "../domain/collection";
import { CollectionRequest } from "./collection.request";
export class CollectionWithIdRequest extends CollectionRequest {
id: string;
constructor(collection?: Collection) {
if (collection == null) {
return;
}
super(collection);
this.id = collection.id;
}
}

View File

@@ -1,17 +0,0 @@
import { SelectionReadOnlyRequest } from "../../../admin-console/models/request/selection-read-only.request";
import { Collection } from "../domain/collection";
export class CollectionRequest {
name: string;
externalId: string;
groups: SelectionReadOnlyRequest[] = [];
users: SelectionReadOnlyRequest[] = [];
constructor(collection?: Collection) {
if (collection == null) {
return;
}
this.name = collection.name ? collection.name.encryptedString : null;
this.externalId = collection.externalId;
}
}

View File

@@ -1,62 +0,0 @@
import { SelectionReadOnlyResponse } from "../../../admin-console/models/response/selection-read-only.response";
import { BaseResponse } from "../../../models/response/base.response";
import { CollectionId, OrganizationId } from "../../../types/guid";
export class CollectionResponse extends BaseResponse {
id: CollectionId;
organizationId: OrganizationId;
name: string;
externalId: string;
constructor(response: any) {
super(response);
this.id = this.getResponseProperty("Id");
this.organizationId = this.getResponseProperty("OrganizationId");
this.name = this.getResponseProperty("Name");
this.externalId = this.getResponseProperty("ExternalId");
}
}
export class CollectionDetailsResponse extends CollectionResponse {
readOnly: boolean;
manage: boolean;
hidePasswords: boolean;
/**
* Flag indicating the user has been explicitly assigned to this Collection
*/
assigned: boolean;
constructor(response: any) {
super(response);
this.readOnly = this.getResponseProperty("ReadOnly") || false;
this.manage = this.getResponseProperty("Manage") || false;
this.hidePasswords = this.getResponseProperty("HidePasswords") || false;
// Temporary until the API is updated to return this property in AC-2084
// For now, we can assume that if the object is 'collectionDetails' then the user is assigned
this.assigned = this.getResponseProperty("object") == "collectionDetails";
}
}
export class CollectionAccessDetailsResponse extends CollectionDetailsResponse {
groups: SelectionReadOnlyResponse[] = [];
users: SelectionReadOnlyResponse[] = [];
unmanaged: boolean;
constructor(response: any) {
super(response);
this.assigned = this.getResponseProperty("Assigned") || false;
this.unmanaged = this.getResponseProperty("Unmanaged") || false;
const groups = this.getResponseProperty("Groups");
if (groups != null) {
this.groups = groups.map((g: any) => new SelectionReadOnlyResponse(g));
}
const users = this.getResponseProperty("Users");
if (users != null) {
this.users = users.map((g: any) => new SelectionReadOnlyResponse(g));
}
}
}

View File

@@ -1,92 +0,0 @@
import { Jsonify } from "type-fest";
import { Organization } from "../../../admin-console/models/domain/organization";
import { View } from "../../../models/view/view";
import { Collection } from "../domain/collection";
import { ITreeNodeObject } from "../domain/tree-node";
import { CollectionAccessDetailsResponse } from "../response/collection.response";
export const NestingDelimiter = "/";
export class CollectionView implements View, ITreeNodeObject {
id: string = null;
organizationId: string = null;
name: string = null;
externalId: string = null;
// readOnly applies to the items within a collection
readOnly: boolean = null;
hidePasswords: boolean = null;
manage: boolean = null;
assigned: boolean = null;
constructor(c?: Collection | CollectionAccessDetailsResponse) {
if (!c) {
return;
}
this.id = c.id;
this.organizationId = c.organizationId;
this.externalId = c.externalId;
if (c instanceof Collection) {
this.readOnly = c.readOnly;
this.hidePasswords = c.hidePasswords;
this.manage = c.manage;
this.assigned = true;
}
if (c instanceof CollectionAccessDetailsResponse) {
this.assigned = c.assigned;
}
}
canEditItems(org: Organization): 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.",
);
}
return org?.canEditAllCiphers || this.manage || (this.assigned && !this.readOnly);
}
/**
* 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 {
if (org != null && org.id !== this.organizationId) {
throw new Error(
"Id of the organization provided does not match the org id of the collection.",
);
}
return this.manage;
}
/**
* 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 {
if (org != null && org.id !== this.organizationId) {
throw new Error(
"Id of the organization provided does not match the org id of the collection.",
);
}
const canDeleteManagedCollections = !org?.limitCollectionCreationDeletion || org.isAdmin;
// Only use individual permissions, not admin permissions
return canDeleteManagedCollections && this.manage;
}
/**
* Returns true if the user can view collection info and access in a read-only state from the individual vault
*/
canViewCollectionInfo(org: Organization | undefined): boolean {
return false;
}
static fromJSON(obj: Jsonify<CollectionView>) {
return Object.assign(new CollectionView(new Collection()), obj);
}
}