mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
fix(attempt): revoke username
This commit is contained in:
parent
92da5e6ac3
commit
e6f4f8d23e
3 changed files with 94 additions and 4 deletions
|
|
@ -128,7 +128,7 @@ const adminAccountActionSchema = z.object({
|
||||||
});
|
});
|
||||||
|
|
||||||
const adminActionController: AppController = async (c) => {
|
const adminActionController: AppController = async (c) => {
|
||||||
const { conf, relay } = c.var;
|
const { conf, relay, signal } = c.var;
|
||||||
|
|
||||||
const body = await parseBody(c.req.raw);
|
const body = await parseBody(c.req.raw);
|
||||||
const result = adminAccountActionSchema.safeParse(body);
|
const result = adminAccountActionSchema.safeParse(body);
|
||||||
|
|
@ -161,7 +161,24 @@ const adminActionController: AppController = async (c) => {
|
||||||
if (data.type === 'revoke_name') {
|
if (data.type === 'revoke_name') {
|
||||||
n.revoke_name = true;
|
n.revoke_name = true;
|
||||||
try {
|
try {
|
||||||
await relay.remove!([{ kinds: [30360], authors: [await conf.signer.getPublicKey()], '#p': [authorId] }]);
|
const [event] = await relay.query([{
|
||||||
|
kinds: [30360],
|
||||||
|
authors: [await conf.signer.getPublicKey()],
|
||||||
|
'#p': [authorId],
|
||||||
|
}], { signal });
|
||||||
|
|
||||||
|
if (event) {
|
||||||
|
await createAdminEvent({
|
||||||
|
kind: 5,
|
||||||
|
tags: [
|
||||||
|
['e', event.id],
|
||||||
|
['k', '30360'],
|
||||||
|
['p', authorId], // NOTE: this is not in the NIP-09 spec
|
||||||
|
],
|
||||||
|
}, c);
|
||||||
|
} else {
|
||||||
|
return c.json({ error: 'Name grant not found' }, 404);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logi({ level: 'error', ns: 'ditto.api.admin.account.action', type: data.type, error: errorJson(e) });
|
logi({ level: 'error', ns: 'ditto.api.admin.account.action', type: data.type, error: errorJson(e) });
|
||||||
return c.json({ error: 'Unexpected runtime error' }, 500);
|
return c.json({ error: 'Unexpected runtime error' }, 500);
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@ import { genEvent, MockRelay } from '@nostrify/nostrify/test';
|
||||||
import { assertEquals } from '@std/assert';
|
import { assertEquals } from '@std/assert';
|
||||||
import { generateSecretKey, getPublicKey } from 'nostr-tools';
|
import { generateSecretKey, getPublicKey } from 'nostr-tools';
|
||||||
|
|
||||||
import { DittoRelayStore } from './DittoRelayStore.ts';
|
import { DittoRelayStore } from '@/storages/DittoRelayStore.ts';
|
||||||
|
|
||||||
import type { NostrMetadata } from '@nostrify/types';
|
import type { NostrMetadata } from '@nostrify/types';
|
||||||
|
import { nostrNow } from '@/utils.ts';
|
||||||
|
|
||||||
Deno.test('updateAuthorData sets nip05', async () => {
|
Deno.test('updateAuthorData sets nip05', async () => {
|
||||||
const alex = generateSecretKey();
|
const alex = generateSecretKey();
|
||||||
|
|
@ -38,6 +39,48 @@ Deno.test('updateAuthorData sets nip05', async () => {
|
||||||
assertEquals(row?.nip05_hostname, 'gleasonator.dev');
|
assertEquals(row?.nip05_hostname, 'gleasonator.dev');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.test('Admin revokes nip05 grant and nip05 column gets null', async () => {
|
||||||
|
const alex = generateSecretKey();
|
||||||
|
|
||||||
|
await using test = setupTest((req) => {
|
||||||
|
switch (req.url) {
|
||||||
|
case 'https://gleasonator.dev/.well-known/nostr.json?name=alex':
|
||||||
|
return jsonResponse({ names: { alex: getPublicKey(alex) } });
|
||||||
|
default:
|
||||||
|
return new Response('Not found', { status: 404 });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { db, store, conf } = test;
|
||||||
|
|
||||||
|
const metadata: NostrMetadata = { nip05: 'alex@gleasonator.dev' };
|
||||||
|
const event = genEvent({ kind: 0, content: JSON.stringify(metadata) }, alex);
|
||||||
|
|
||||||
|
await store.updateAuthorData(event);
|
||||||
|
|
||||||
|
const adminDeletion = await conf.signer.signEvent({
|
||||||
|
kind: 5,
|
||||||
|
created_at: nostrNow(),
|
||||||
|
tags: [
|
||||||
|
['k', '30360'],
|
||||||
|
['p', event.id], // NOTE: this is not in the NIP-09 spec
|
||||||
|
],
|
||||||
|
content: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
await store.event(adminDeletion);
|
||||||
|
|
||||||
|
const row = await db.kysely
|
||||||
|
.selectFrom('author_stats')
|
||||||
|
.selectAll()
|
||||||
|
.where('pubkey', '=', getPublicKey(alex))
|
||||||
|
.executeTakeFirst();
|
||||||
|
|
||||||
|
assertEquals(row?.nip05, null);
|
||||||
|
assertEquals(row?.nip05_domain, null);
|
||||||
|
assertEquals(row?.nip05_hostname, null);
|
||||||
|
});
|
||||||
|
|
||||||
function setupTest(cb: (req: Request) => Response | Promise<Response>) {
|
function setupTest(cb: (req: Request) => Response | Promise<Response>) {
|
||||||
const conf = new DittoConf(Deno.env);
|
const conf = new DittoConf(Deno.env);
|
||||||
const db = new DittoPolyPg(conf.databaseUrl);
|
const db = new DittoPolyPg(conf.databaseUrl);
|
||||||
|
|
@ -53,6 +96,7 @@ function setupTest(cb: (req: Request) => Response | Promise<Response>) {
|
||||||
return {
|
return {
|
||||||
db,
|
db,
|
||||||
store,
|
store,
|
||||||
|
conf,
|
||||||
[Symbol.asyncDispose]: async () => {
|
[Symbol.asyncDispose]: async () => {
|
||||||
await store[Symbol.asyncDispose]();
|
await store[Symbol.asyncDispose]();
|
||||||
await db[Symbol.asyncDispose]();
|
await db[Symbol.asyncDispose]();
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import { DittoPush } from '@/DittoPush.ts';
|
||||||
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||||
import { RelayError } from '@/RelayError.ts';
|
import { RelayError } from '@/RelayError.ts';
|
||||||
import { hydrateEvents } from '@/storages/hydrate.ts';
|
import { hydrateEvents } from '@/storages/hydrate.ts';
|
||||||
import { eventAge, nostrNow, Time } from '@/utils.ts';
|
import { eventAge, isNostrId, nostrNow, Time } from '@/utils.ts';
|
||||||
import { getAmount } from '@/utils/bolt11.ts';
|
import { getAmount } from '@/utils/bolt11.ts';
|
||||||
import { errorJson } from '@/utils/log.ts';
|
import { errorJson } from '@/utils/log.ts';
|
||||||
import { purifyEvent } from '@/utils/purify.ts';
|
import { purifyEvent } from '@/utils/purify.ts';
|
||||||
|
|
@ -189,6 +189,7 @@ export class DittoRelayStore implements NRelay {
|
||||||
Promise.allSettled([
|
Promise.allSettled([
|
||||||
this.handleZaps(event),
|
this.handleZaps(event),
|
||||||
this.updateAuthorData(event, signal),
|
this.updateAuthorData(event, signal),
|
||||||
|
this.handleRevokeNip05(event, signal),
|
||||||
this.prewarmLinkPreview(event, signal),
|
this.prewarmLinkPreview(event, signal),
|
||||||
this.generateSetEvents(event),
|
this.generateSetEvents(event),
|
||||||
])
|
])
|
||||||
|
|
@ -245,6 +246,34 @@ export class DittoRelayStore implements NRelay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets the nip05 column to null */
|
||||||
|
private async handleRevokeNip05(event: NostrEvent, signal?: AbortSignal) {
|
||||||
|
const { conf, relay } = this.opts;
|
||||||
|
|
||||||
|
if (await conf.signer.getPublicKey() !== event.pubkey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.kind !== 5) return;
|
||||||
|
|
||||||
|
const kind = event.tags.find(([name, value]) => name === 'k' && value === '30360')?.[1];
|
||||||
|
if (kind !== '30360') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authorId = event.tags.find(([name]) => name === 'p')?.[1];
|
||||||
|
if (!authorId || !isNostrId(authorId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [author] = await relay.query([{ kinds: [0], authors: [authorId] }], { signal });
|
||||||
|
if (!author) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.updateAuthorData(author);
|
||||||
|
}
|
||||||
|
|
||||||
/** Parse kind 0 metadata and track indexes in the database. */
|
/** Parse kind 0 metadata and track indexes in the database. */
|
||||||
async updateAuthorData(event: NostrEvent, signal?: AbortSignal): Promise<void> {
|
async updateAuthorData(event: NostrEvent, signal?: AbortSignal): Promise<void> {
|
||||||
if (event.kind !== 0) return;
|
if (event.kind !== 0) return;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue