embed schema and dao

This commit is contained in:
Maxime Cannoodt 2022-08-28 21:57:47 +02:00
parent a2569a2b34
commit a797aa00e9
6 changed files with 150 additions and 6 deletions

View File

@ -0,0 +1,8 @@
/*
Warnings:
- A unique constraint covering the columns `[note_id,embed_id]` on the table `EncryptedEmbed` will be added. If there are existing duplicate values, this will fail.
*/
-- CreateIndex
CREATE UNIQUE INDEX "EncryptedEmbed_note_id_embed_id_key" ON "EncryptedEmbed"("note_id", "embed_id");

View File

@ -28,6 +28,8 @@ model EncryptedEmbed {
hmac String
size_bytes Int
note EncryptedNote @relation(fields: [note_id], references: [id])
@@unique([note_id, embed_id], name: "noteId_embedId")
}
model event {

View File

@ -0,0 +1,85 @@
import { describe, it, expect } from "vitest";
import type { EncryptedEmbed, EncryptedNote } from "@prisma/client";
import { getEmbed, createEmbed } from "./embed.dao";
import { createNote } from "./note.dao";
const VALID_CIPHERTEXT = Buffer.from("sample_ciphertext").toString("base64");
describe("Reading and writing embeds", () => {
it("Should write embeds for existing note", async () => {
const note = await createNote({
ciphertext: "test",
hmac: "test",
crypto_version: "v2",
} as EncryptedNote);
const embed = {
note_id: note.id,
embed_id: "embed_id",
hmac: "hmac",
ciphertext: VALID_CIPHERTEXT,
};
const res = await createEmbed(embed);
expect(res.note_id).toEqual(embed.note_id);
expect(res.embed_id).toEqual(embed.embed_id);
expect(res.hmac).toEqual(embed.hmac);
expect(res.id).not.toBeNull();
expect(res.id.length).toBeGreaterThan(0);
expect(res.ciphertext.byteLength).toBeGreaterThan(0);
});
it("Should throw if note_id does not refer to existing note", async () => {
const embed = {
note_id: "note_id",
embed_id: "embed_id",
hmac: "hmac",
ciphertext: VALID_CIPHERTEXT,
};
await expect(createEmbed(embed)).rejects.toThrowError();
});
it("Should throw if embed_id is not unique", async () => {
const note = await createNote({
ciphertext: "test",
hmac: "test",
crypto_version: "v2",
} as EncryptedNote);
const embed = {
note_id: note.id,
embed_id: "embed_id",
hmac: "hmac",
ciphertext: VALID_CIPHERTEXT,
};
await createEmbed(embed); // embed 1
await expect(createEmbed(embed)).rejects.toThrowError(); // duplicate embed
});
it("Should read embeds for existing note", async () => {
const note = await createNote({
ciphertext: "test",
hmac: "test",
crypto_version: "v2",
} as EncryptedNote);
const embed = {
note_id: note.id,
embed_id: "embed_id",
hmac: "hmac",
ciphertext: VALID_CIPHERTEXT,
};
await createEmbed(embed);
const res = await getEmbed(note.id, embed.embed_id);
expect(res).not.toBeNull();
expect(res?.note_id).toEqual(embed.note_id);
expect(res?.embed_id).toEqual(embed.embed_id);
expect(res?.hmac).toEqual(embed.hmac);
expect(res?.ciphertext).toEqual(embed.ciphertext);
});
});

View File

@ -0,0 +1,49 @@
import { EncryptedEmbed } from "@prisma/client";
import { BufferToBase64, base64ToBuffer } from "../util";
import prisma from "./client";
export interface EncryptedEmbedDTO {
note_id: string;
embed_id: string;
ciphertext: string; // in base64
hmac: string;
}
export async function getEmbed(
noteId: string,
embedId: string
): Promise<EncryptedEmbedDTO | null> {
const embed = await prisma.encryptedEmbed.findUnique({
where: {
noteId_embedId: {
note_id: noteId,
embed_id: embedId,
},
},
});
if (!embed) return null;
console.log(embed.ciphertext.byteLength, embed.size_bytes);
return {
note_id: embed.note_id,
embed_id: embed.embed_id,
hmac: embed.hmac,
ciphertext: BufferToBase64(embed.ciphertext),
};
}
export async function createEmbed(
embed: EncryptedEmbedDTO
): Promise<EncryptedEmbed> {
const cipher_buf = base64ToBuffer(embed.ciphertext);
const data = {
note_id: embed.note_id,
embed_id: embed.embed_id,
hmac: embed.hmac,
ciphertext: cipher_buf,
size_bytes: cipher_buf.byteLength,
} as EncryptedEmbed;
return prisma.encryptedEmbed.create({ data });
}

View File

@ -13,11 +13,11 @@ export function getConnectingIp(req: Request): string {
}
// base64 to array buffer (Node JS api, so don't use atob or btoa)
export function base64ToArrayBuffer(base64: string): ArrayBuffer {
export function base64ToBuffer(base64: string): Buffer {
return Buffer.from(base64, "base64");
}
// array buffer to base64 (Node JS api, so don't use atob or btoa)
export function arrayBufferToBase64(buffer: ArrayBuffer): string {
export function BufferToBase64(buffer: Buffer): string {
return Buffer.from(buffer).toString("base64");
}

View File

@ -1,8 +1,8 @@
import { describe, it, expect } from "vitest";
import {
addDays,
arrayBufferToBase64,
base64ToArrayBuffer,
BufferToBase64,
base64ToBuffer,
getConnectingIp,
} from "./util";
@ -18,7 +18,7 @@ describe("converting to/from base64", () => {
it("Should convert a base64 string to an array buffer", () => {
const base64 = "EjRWeJA=";
const expectedBuffer = new Uint8Array([18, 52, 86, 120, 144]);
expect(new Uint8Array(base64ToArrayBuffer(base64))).toStrictEqual(
expect(new Uint8Array(base64ToBuffer(base64))).toStrictEqual(
expectedBuffer
);
});
@ -26,6 +26,6 @@ describe("converting to/from base64", () => {
it("Should convert an array buffer to a base64 string", () => {
const buffer = new Uint8Array([18, 52, 86, 120, 144]);
const expectedBase64 = "EjRWeJA=";
expect(arrayBufferToBase64(buffer)).toEqual(expectedBase64);
expect(BufferToBase64(buffer)).toEqual(expectedBase64);
});
});