mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-30 17:32:26 +00:00
Merge pull request #778 from PR0M3TH3AN/codex/update-userblocklistmanager-for-new-signer
Handle direct signer decryptors for user blocks
This commit is contained in:
2 files changed
+226
-32
No files matched your search
+62
-31
@@ -339,33 +339,53 @@ class UserBlockListManager {
|
||||
});
|
||||
};
|
||||
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
if (!permissionResult.ok) {
|
||||
userLogger.warn(
|
||||
"[UserBlockList] Unable to load block list without extension permissions.",
|
||||
permissionResult.error,
|
||||
);
|
||||
this.reset();
|
||||
this.loaded = true;
|
||||
const error =
|
||||
permissionResult.error instanceof Error
|
||||
? permissionResult.error
|
||||
: new Error("Extension permissions required to load block list.");
|
||||
emitStatus({ status: "error", error });
|
||||
emitStatus({ status: "settled" });
|
||||
return;
|
||||
const activeSigner = getActiveSigner();
|
||||
const signerDecryptor =
|
||||
activeSigner && typeof activeSigner.nip04Decrypt === "function"
|
||||
? activeSigner.nip04Decrypt
|
||||
: null;
|
||||
|
||||
let extensionDecryptor =
|
||||
typeof window?.nostr?.nip04?.decrypt === "function"
|
||||
? (pubkey, payload) => window.nostr.nip04.decrypt(pubkey, payload)
|
||||
: null;
|
||||
|
||||
if (!signerDecryptor && !extensionDecryptor) {
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
if (!permissionResult.ok) {
|
||||
userLogger.warn(
|
||||
"[UserBlockList] Unable to load block list via extension decryptor; permissions denied.",
|
||||
permissionResult.error,
|
||||
);
|
||||
this.reset();
|
||||
this.loaded = true;
|
||||
const error =
|
||||
permissionResult.error instanceof Error
|
||||
? permissionResult.error
|
||||
: new Error(
|
||||
"Extension permissions are required to use the browser decryptor.",
|
||||
);
|
||||
emitStatus({ status: "error", error, decryptor: "extension" });
|
||||
emitStatus({ status: "settled" });
|
||||
return;
|
||||
}
|
||||
|
||||
extensionDecryptor =
|
||||
typeof window?.nostr?.nip04?.decrypt === "function"
|
||||
? (pubkey, payload) => window.nostr.nip04.decrypt(pubkey, payload)
|
||||
: null;
|
||||
}
|
||||
|
||||
if (!window?.nostr?.nip04?.decrypt) {
|
||||
if (!signerDecryptor && !extensionDecryptor) {
|
||||
userLogger.warn(
|
||||
"[UserBlockList] nip04.decrypt is unavailable; treating block list as empty."
|
||||
"[UserBlockList] No nip04 decryptor available; treating block list as empty.",
|
||||
);
|
||||
this.reset();
|
||||
this.loaded = true;
|
||||
emitStatus({
|
||||
status: "error",
|
||||
error: new Error("nip04.decrypt is unavailable"),
|
||||
});
|
||||
const error = new Error(
|
||||
"No NIP-04 decryptor is available to load the block list.",
|
||||
);
|
||||
emitStatus({ status: "error", error, decryptor: "unavailable" });
|
||||
emitStatus({ status: "settled" });
|
||||
return;
|
||||
}
|
||||
@@ -549,28 +569,39 @@ class UserBlockListManager {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeSigner = getActiveSigner();
|
||||
const decryptWithActiveSigner =
|
||||
activeSigner && typeof activeSigner.nip04Decrypt === "function";
|
||||
|
||||
let decrypted = "";
|
||||
const decryptPath = signerDecryptor
|
||||
? "active-signer"
|
||||
: extensionDecryptor
|
||||
? "extension"
|
||||
: "unavailable";
|
||||
try {
|
||||
if (decryptWithActiveSigner) {
|
||||
decrypted = await activeSigner.nip04Decrypt(normalized, newest.content);
|
||||
} else if (window?.nostr?.nip04?.decrypt) {
|
||||
decrypted = await window.nostr.nip04.decrypt(normalized, newest.content);
|
||||
if (signerDecryptor) {
|
||||
decrypted = await signerDecryptor(normalized, newest.content);
|
||||
} else if (extensionDecryptor) {
|
||||
decrypted = await extensionDecryptor(normalized, newest.content);
|
||||
} else {
|
||||
throw new Error("nip04-unavailable");
|
||||
}
|
||||
} catch (err) {
|
||||
userLogger.error("[UserBlockList] Failed to decrypt block list:", err);
|
||||
userLogger.error(
|
||||
`[UserBlockList] Failed to decrypt block list via ${decryptPath}:`,
|
||||
err,
|
||||
);
|
||||
applyBlockedPubkeys([], {
|
||||
source,
|
||||
reason: "decrypt-error",
|
||||
event: newest,
|
||||
error: err,
|
||||
decryptor: decryptPath,
|
||||
});
|
||||
emitStatus({
|
||||
status: "error",
|
||||
event: newest,
|
||||
error: err,
|
||||
source,
|
||||
decryptor: decryptPath,
|
||||
});
|
||||
emitStatus({ status: "error", event: newest, error: err, source });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+164
-1
@@ -2,7 +2,12 @@ import "./test-helpers/setup-localstorage.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { userBlocks, USER_BLOCK_EVENTS } = await import("../js/userBlocks.js");
|
||||
const { nostrClient } = await import("../js/nostr.js");
|
||||
const {
|
||||
nostrClient,
|
||||
setActiveSigner,
|
||||
clearActiveSigner,
|
||||
getActiveSigner,
|
||||
} = await import("../js/nostr.js");
|
||||
|
||||
if (typeof globalThis.window === "undefined") {
|
||||
globalThis.window = {};
|
||||
@@ -436,3 +441,161 @@ await (async () => {
|
||||
localStorage.clear();
|
||||
}
|
||||
})();
|
||||
|
||||
await (async () => {
|
||||
const actor = "4".repeat(64);
|
||||
const target = "5".repeat(64);
|
||||
|
||||
const originalBlocked = new Set(userBlocks.blockedPubkeys);
|
||||
const originalBlockEventId = userBlocks.blockEventId;
|
||||
const originalBlockEventCreatedAt = userBlocks.blockEventCreatedAt;
|
||||
const originalLastPublishedCreatedAt = userBlocks.lastPublishedCreatedAt;
|
||||
const originalLoaded = userBlocks.loaded;
|
||||
const originalPool = nostrClient.pool;
|
||||
const originalRelays = Array.isArray(nostrClient.relays)
|
||||
? [...nostrClient.relays]
|
||||
: nostrClient.relays;
|
||||
const originalWriteRelays = Array.isArray(nostrClient.writeRelays)
|
||||
? [...nostrClient.writeRelays]
|
||||
: nostrClient.writeRelays;
|
||||
const originalEnsurePermissions = nostrClient.ensureExtensionPermissions;
|
||||
const originalSignerState = getActiveSigner();
|
||||
clearActiveSigner();
|
||||
const originalNostr = window.nostr;
|
||||
|
||||
let permissionRequests = 0;
|
||||
nostrClient.ensureExtensionPermissions = async () => {
|
||||
permissionRequests += 1;
|
||||
return { ok: true };
|
||||
};
|
||||
|
||||
let eventCounter = 0;
|
||||
let latestCiphertext = "cipher:[]";
|
||||
let latestEventId = "event-initial";
|
||||
let latestCreatedAt = 1_000;
|
||||
|
||||
const decryptPayloads = new Map([
|
||||
[latestCiphertext, JSON.stringify({ blockedPubkeys: [] })],
|
||||
]);
|
||||
|
||||
nostrClient.relays = ["wss://direct-signer.example"];
|
||||
nostrClient.writeRelays = nostrClient.relays;
|
||||
nostrClient.pool = {
|
||||
publish: (_targets, event) => {
|
||||
latestCiphertext = event.content;
|
||||
latestEventId = event.id || `event-${Date.now()}`;
|
||||
latestCreatedAt = event.created_at || Math.floor(Date.now() / 1000);
|
||||
return {
|
||||
on(eventName, handler) {
|
||||
if (eventName === "ok") {
|
||||
setImmediate(handler);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
},
|
||||
list: async () => [
|
||||
{
|
||||
id: latestEventId,
|
||||
created_at: latestCreatedAt,
|
||||
pubkey: actor,
|
||||
content: latestCiphertext,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
setActiveSigner({
|
||||
type: "private-key",
|
||||
pubkey: actor,
|
||||
nip04Decrypt: async (pubkey, ciphertext) => {
|
||||
assert.equal(pubkey, actor, "decrypt should target the actor pubkey");
|
||||
if (!decryptPayloads.has(ciphertext)) {
|
||||
throw new Error(`unexpected ciphertext ${ciphertext}`);
|
||||
}
|
||||
return decryptPayloads.get(ciphertext);
|
||||
},
|
||||
nip04Encrypt: async (pubkey, plaintext) => {
|
||||
assert.equal(pubkey, actor, "encrypt should target the actor pubkey");
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(plaintext);
|
||||
} catch (error) {
|
||||
throw new Error(`unexpected payload ${plaintext}`);
|
||||
}
|
||||
const blocked = Array.isArray(parsed?.blockedPubkeys)
|
||||
? [...parsed.blockedPubkeys].sort()
|
||||
: [];
|
||||
const cipher = `cipher:${JSON.stringify(blocked)}`;
|
||||
decryptPayloads.set(cipher, plaintext);
|
||||
return cipher;
|
||||
},
|
||||
signEvent: async (event) => {
|
||||
eventCounter += 1;
|
||||
return {
|
||||
...event,
|
||||
id: `event-${eventCounter}`,
|
||||
created_at: event.created_at ?? Math.floor(Date.now() / 1000),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
userBlocks.blockedPubkeys = new Set();
|
||||
userBlocks.blockEventId = null;
|
||||
userBlocks.blockEventCreatedAt = null;
|
||||
userBlocks.lastPublishedCreatedAt = null;
|
||||
userBlocks.loaded = false;
|
||||
|
||||
try {
|
||||
window.nostr = undefined;
|
||||
|
||||
await userBlocks.loadBlocks(actor);
|
||||
assert.deepEqual(
|
||||
userBlocks.getBlockedPubkeys(),
|
||||
[],
|
||||
"direct signer should load block list without extension decryptor",
|
||||
);
|
||||
assert.equal(
|
||||
permissionRequests,
|
||||
0,
|
||||
"loadBlocks should not request extension permissions when signer decryptor exists",
|
||||
);
|
||||
|
||||
const blockResult = await userBlocks.addBlock(target, actor);
|
||||
assert.equal(blockResult?.ok, true, "addBlock should succeed with direct signer");
|
||||
assert(userBlocks.blockedPubkeys.has(target), "target should be in the block list");
|
||||
|
||||
const unblockResult = await userBlocks.removeBlock(target, actor);
|
||||
assert.equal(
|
||||
unblockResult?.ok,
|
||||
true,
|
||||
"removeBlock should succeed with direct signer",
|
||||
);
|
||||
assert.equal(
|
||||
userBlocks.blockedPubkeys.has(target),
|
||||
false,
|
||||
"target should be removed from the block list",
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
permissionRequests,
|
||||
0,
|
||||
"direct signer flows should never trigger extension permission prompts",
|
||||
);
|
||||
} finally {
|
||||
userBlocks.blockedPubkeys = originalBlocked;
|
||||
userBlocks.blockEventId = originalBlockEventId;
|
||||
userBlocks.blockEventCreatedAt = originalBlockEventCreatedAt;
|
||||
userBlocks.lastPublishedCreatedAt = originalLastPublishedCreatedAt;
|
||||
userBlocks.loaded = originalLoaded;
|
||||
nostrClient.pool = originalPool;
|
||||
nostrClient.relays = originalRelays;
|
||||
nostrClient.writeRelays = originalWriteRelays;
|
||||
nostrClient.ensureExtensionPermissions = originalEnsurePermissions;
|
||||
window.nostr = originalNostr;
|
||||
clearActiveSigner();
|
||||
if (originalSignerState) {
|
||||
setActiveSigner(originalSignerState);
|
||||
}
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user