Persist encrypted chat and preserve room keys when inviting viewers
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { after, test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
process.env.JWT_SECRET = "local-test-only-secret-32-characters-long";
|
||||
process.env.DATUM_ADMIN_PASSWORD = "local-test-only";
|
||||
process.env.NOSTR_OWNER_PUBKEYS = "a".repeat(64);
|
||||
const { ChatStore } = await import("../src/chat/store.js");
|
||||
const dir = mkdtempSync(join(tmpdir(), "gashboard-chat-"));
|
||||
after(() => rmSync(dir, { recursive: true, force: true }));
|
||||
const owner = "a".repeat(64);
|
||||
const viewer = "b".repeat(64);
|
||||
const newcomer = "c".repeat(64);
|
||||
|
||||
test("encrypted history, reactions and key wraps survive restart with private permissions", () => {
|
||||
const file = join(dir, "chat.json");
|
||||
const store = new ChatStore(file);
|
||||
store.shareKey(owner, [{ recipientPubkey: owner, ciphertext: "encrypted-key" }]);
|
||||
const message = store.addMessage({ pubkey: owner, content: "encrypted-message" });
|
||||
store.addReaction({ pubkey: owner, messageId: message.id, content: "encrypted-reaction" });
|
||||
assert.deepEqual(new ChatStore(file).list(), store.list());
|
||||
assert.equal(statSync(file).mode & 0o777, 0o600);
|
||||
const copy = store.list();
|
||||
copy.keyWraps[0]!.ciphertext = "changed";
|
||||
assert.equal(store.list().keyWraps[0]!.ciphertext, "encrypted-key");
|
||||
writeFileSync(file, "{}");
|
||||
assert.throws(() => new ChatStore(file));
|
||||
});
|
||||
|
||||
test("new invitations and simultaneous first visits cannot overwrite the room key", () => {
|
||||
const store = new ChatStore(join(dir, "invites.json"));
|
||||
store.shareKey(owner, [{ recipientPubkey: owner, ciphertext: "original-key" }]);
|
||||
assert.throws(() => store.shareKey(viewer, [{ recipientPubkey: viewer, ciphertext: "different-key" }]), /existing chat member/);
|
||||
store.shareKey(owner, [{ recipientPubkey: viewer, ciphertext: "same-key-for-viewer" }]);
|
||||
assert.throws(() => store.shareKey(viewer, [{ recipientPubkey: viewer, ciphertext: "replacement-key" }]), /already shared/);
|
||||
store.shareKey(viewer, [{ recipientPubkey: newcomer, ciphertext: "same-key-for-newcomer" }]);
|
||||
assert.equal(store.list().keyWraps[0]!.ciphertext, "original-key");
|
||||
assert.equal(store.list().keyWraps.length, 3);
|
||||
});
|
||||
|
||||
test("failed persistence does not report or retain an unsaved message", () => {
|
||||
const parent = join(dir, "blocked");
|
||||
const store = new ChatStore(join(parent, "chat.json"));
|
||||
writeFileSync(parent, "not a directory");
|
||||
assert.throws(() => store.addMessage({ pubkey: owner, content: "ciphertext" }));
|
||||
assert.deepEqual(store.list().messages, []);
|
||||
assert.equal(readFileSync(parent, "utf8"), "not a directory");
|
||||
});
|
||||
Reference in New Issue
Block a user