Skip to content

Commit

Permalink
fix: move attachments into credentialSubject (#290)
Browse files Browse the repository at this point in the history
  • Loading branch information
phanshiyu authored May 13, 2024
1 parent 770080a commit 78ab9df
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 219 deletions.
74 changes: 41 additions & 33 deletions src/4.0/__tests__/documentBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,17 @@ describe(`DocumentBuilder`, () => {

test("given svg rendering method, should be added into the document", async () => {
const signed = await new DocumentBuilder({
credentialSubject: { name: "John Doe" },
credentialSubject: {
name: "John Doe",
attachments: [
{
data: "data",
filename: "file",
mimeType: "application/pdf",
},
],
},
name: "Diploma",
attachments: [
{
data: "data",
fileName: "file",
mimeType: "application/pdf",
},
],
})
.svgRenderer({
type: "EMBEDDED",
Expand All @@ -288,15 +290,17 @@ describe(`DocumentBuilder`, () => {

test("given no rendering method, should reflect in the output document", async () => {
const signed = await new DocumentBuilder({
credentialSubject: { name: "John Doe" },
credentialSubject: {
name: "John Doe",
attachments: [
{
data: "data",
filename: "file",
mimeType: "application/pdf",
},
],
},
name: "Diploma",
attachments: [
{
data: "data",
fileName: "file",
mimeType: "application/pdf",
},
],
})
.noRenderer()
.noRevocation()
Expand All @@ -312,15 +316,17 @@ describe(`DocumentBuilder`, () => {

test("given attachment is added, should be added into the document", async () => {
const signed = await new DocumentBuilder({
credentialSubject: { name: "John Doe" },
credentialSubject: {
name: "John Doe",
attachments: [
{
data: "data",
filename: "file",
mimeType: "application/pdf",
},
],
},
name: "Diploma",
attachments: [
{
data: "data",
fileName: "file",
mimeType: "application/pdf",
},
],
})
.embeddedRenderer({
rendererUrl: "https://example.com",
Expand All @@ -334,11 +340,11 @@ describe(`DocumentBuilder`, () => {
})
.wrapAndSign({ signer: SAMPLE_SIGNING_KEYS });

expect(signed.attachments).toMatchInlineSnapshot(`
expect(signed.credentialSubject.attachments).toMatchInlineSnapshot(`
[
{
"data": "data",
"fileName": "file",
"filename": "file",
"mimeType": "application/pdf",
},
]
Expand Down Expand Up @@ -446,14 +452,16 @@ describe(`DocumentBuilder`, () => {
expect(() => {
try {
new DocumentBuilder({
credentialSubject: { name: "John Doe" },
credentialSubject: {
name: "John Doe",
attachments: [
{
data: "data",
fileName: "file",
} as any,
],
},
name: "Diploma",
attachments: [
{
data: "data",
fileName: "file",
} as any,
],
});
} catch (e) {
error = e;
Expand Down
17 changes: 10 additions & 7 deletions src/4.0/__tests__/guard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import { wrapDocument } from "../wrap";

const RAW_DOCUMENT = {
...RAW_DOCUMENT_DID,
attachments: [
{
mimeType: "image/png",
fileName: "aaa",
data: "abcd",
},
],
credentialSubject: {
...RAW_DOCUMENT_DID.credentialSubject,
attachments: [
{
mimeType: "image/png",
filename: "aaa",
data: "abcd",
},
],
},
} satisfies V4Document;

describe("V4.0 guard", () => {
Expand Down
83 changes: 44 additions & 39 deletions src/4.0/__tests__/obfuscate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { RAW_DOCUMENT_DID, SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED, WRAPPED_DOCUM
import { hashLeafNode } from "../digest";
import { getObfuscatedData, isObfuscated } from "../../shared/utils";

const makeV4RawDocument = <T extends Pick<V4Document, "credentialSubject" | "attachments">>(props: T) =>
const makeV4RawDocument = <T extends Pick<V4Document, "credentialSubject">>(props: T) =>
({
...RAW_DOCUMENT_DID,
...(props as T),
Expand Down Expand Up @@ -134,27 +134,27 @@ describe("privacy", () => {
});

test("given an entire array of objects to remove, should remove the array itself, then for every item, add each of its key's hash into privacy.obfuscated", async () => {
const PATH_TO_REMOVE = "attachments";
const PATH_TO_REMOVE = "credentialSubject.attachments";
const wrappedDocument = await wrapDocument(
makeV4RawDocument({
credentialSubject: {
arrayOfObject: [
{ foo: "bar", doo: "foo" },
{ foo: "baz", doo: "faz" },
],
attachments: [
{
mimeType: "image/png",
filename: "aaa",
data: "abcd",
},
{
mimeType: "image/png",
filename: "bbb",
data: "abcd",
},
],
},
attachments: [
{
mimeType: "image/png",
fileName: "aaa",
data: "abcd",
},
{
mimeType: "image/png",
fileName: "bbb",
data: "abcd",
},
],
})
);
const obfuscatedDocument = await obfuscateVerifiableCredential(wrappedDocument, PATH_TO_REMOVE);
Expand All @@ -163,12 +163,12 @@ describe("privacy", () => {
expect(verified).toBe(true);

[
"attachments[0].mimeType",
"attachments[0].fileName",
"attachments[0].data",
"attachments[1].mimeType",
"attachments[1].fileName",
"attachments[1].data",
"credentialSubject.attachments[0].mimeType",
"credentialSubject.attachments[0].filename",
"credentialSubject.attachments[0].data",
"credentialSubject.attachments[1].mimeType",
"credentialSubject.attachments[1].filename",
"credentialSubject.attachments[1].data",
].forEach((expectedRemovedField) => {
const value = get(wrappedDocument, expectedRemovedField);
const salt = findSaltByPath(wrappedDocument.proof.salts, expectedRemovedField);
Expand All @@ -180,7 +180,7 @@ describe("privacy", () => {
);
expect(findSaltByPath(obfuscatedDocument.proof.salts, expectedRemovedField)).toBeUndefined();
});
expect(obfuscatedDocument.attachments).toBeUndefined();
expect(obfuscatedDocument.credentialSubject.attachments).toBeUndefined();
expect(obfuscatedDocument.proof.privacy.obfuscated).toHaveLength(6);
});

Expand Down Expand Up @@ -213,28 +213,33 @@ describe("privacy", () => {
{ foo: "bar", doo: "foo" },
{ foo: "baz", doo: "faz" },
],
attachments: [
{
mimeType: "image/png",
filename: "aaa",
data: "abcd",
},
{
mimeType: "image/png",
filename: "bbb",
data: "abcd",
},
{
mimeType: "image/png",
filename: "ccc",
data: "abcd",
},
],
},
attachments: [
{
mimeType: "image/png",
fileName: "aaa",
data: "abcd",
},
{
mimeType: "image/png",
fileName: "bbb",
data: "abcd",
},
{
mimeType: "image/png",
fileName: "ccc",
data: "abcd",
},
],
})
);

expect(() => obfuscateVerifiableCredential(wrappedDocument, ["attachments[0]", "attachments[2]"])).toThrow();
expect(() =>
obfuscateVerifiableCredential(wrappedDocument, [
"credentialSubject.attachments[0]",
"credentialSubject.attachments[2]",
])
).toThrow();
});

test("given a path to remove all elements in an object, should throw", async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/4.0/digest.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { sortBy } from "lodash";
import { keccak256 } from "js-sha3";
import { V4Document, Salt } from "./types";
import { W3cVerifiableCredential, Salt } from "./types";
import { LeafValue, traverseAndFlatten } from "./traverseAndFlatten";
import { hashToBuffer } from "../shared/utils/hashing";

export const digestCredential = (document: V4Document, salts: Salt[], obfuscatedData: string[]) => {
export const digestCredential = (document: W3cVerifiableCredential, salts: Salt[], obfuscatedData: string[]) => {
// find all leaf nodes in the document and hash them
// proof is not part of the digest
const { proof: _, ...documentWithoutProof } = document;
Expand Down
18 changes: 4 additions & 14 deletions src/4.0/documentBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import { ZodError, z } from "zod";

const SingleDocumentProps = z.object({
name: V4Document.shape.name.unwrap(),
credentialSubject: z.record(z.unknown()),
attachments: V4Document.shape.attachments,
credentialSubject: V4Document.shape.credentialSubject,
});

const DocumentProps = z.union([SingleDocumentProps, z.array(SingleDocumentProps)]);
Expand Down Expand Up @@ -84,14 +83,7 @@ type DocumentProps = {
*
* Maps to "credentialSubject"
*/
credentialSubject: Record<string, unknown>;
/**
* Optional attachments that will be rendered out of the box with OpenAttestation's
* Decentralised Renderer Components
*
* Maps to "attachments"
*/
attachments?: V4Document["attachments"];
credentialSubject: z.infer<typeof V4Document.shape.credentialSubject>;
};

type State = {
Expand Down Expand Up @@ -137,7 +129,7 @@ export class DocumentBuilder<Props extends DocumentProps | DocumentProps[]> {
if (!issuer) throw new Error("Issuer is required");
if (Array.isArray(data)) {
const toWrap = data.map(
({ name, credentialSubject, attachments }) =>
({ name, credentialSubject }) =>
({
"@context": [
"https://www.w3.org/ns/credentials/v2",
Expand All @@ -148,7 +140,6 @@ export class DocumentBuilder<Props extends DocumentProps | DocumentProps[]> {
name,
credentialSubject,
...(renderMethod && { renderMethod }),
...(attachments && { attachments }),
...(credentialStatus && { credentialStatus }),
} satisfies V4Document)
);
Expand All @@ -159,7 +150,7 @@ export class DocumentBuilder<Props extends DocumentProps | DocumentProps[]> {
// this should never happen
if (!data) throw new Error("CredentialSubject is required");

const { name, credentialSubject, attachments } = data;
const { name, credentialSubject } = data;
return wrapDocument({
"@context": [
"https://www.w3.org/ns/credentials/v2",
Expand All @@ -170,7 +161,6 @@ export class DocumentBuilder<Props extends DocumentProps | DocumentProps[]> {
name,
credentialSubject,
...(renderMethod && { renderMethod }),
...(attachments && { attachments }),
...(credentialStatus && { credentialStatus }),
}) as unknown as WrappedReturn<Props>;
};
Expand Down
Loading

0 comments on commit 78ab9df

Please sign in to comment.