source: Klonkt/test/help-marker-authz.test.js@ 6d7ea52

main
Last change on this file since 6d7ea52 was 6d7ea52, checked in by Robin <roboburr@…>, 4 days ago

De noodknop is niet meer door een vreemde uit te zetten (shaer-gt70)

De markering op een hulpvraag werd van IEDEREEN aangenomen: de enige
voorwaarde was dat de actor niet lokaal is. Ondertekening zegt WIE, niet
of het MAG, en die tweede laag stond er niet. Afgehandeld kent geen
terugdraai en de vraag verdwijnt daarna uit de teller van elke guardian,
dus elke ondertekende actor die de URI kende kon de noodknop van een kind
uitzetten.

DE WARD IS DE BRON VAN WAARHEID over wie zijn guardians zijn -- onze
eigen tabel kent alleen onze relatie. existingGuardiansOf stelt die vraag
op de goede plek: lokaal als wij de ward hosten, anders shaer:guardians
van zijn actor. En WELKE ward erbij hoort komt uit onze eigen
administratie (de hulpvraag zoals wij hem opsloegen), nooit uit wat de
afzender beweert. Kennen we die hulpvraag niet, dan is er niets te
markeren.

Met een cache van vijf minuten, want het remote geval is een
netwerkaanroep in het inbox-pad -- zonder cache is dat een manier om onze
inbox te laten wachten. Een MISLUKTE ophaal wordt niet als lege lijst
onthouden: dan zou een tijdelijk onbereikbare server vijf minuten lang
elke markering weigeren.

TWEEDE VONDST, gemeten tijdens het bouwen: dezelfde regels riepen
wakeGuardian(slug) aan met een slug die in die scope niet bestaat. De
markering werd dus vastgelegd en daarna gooide de handler een
ReferenceError -- het paneel hoorde het nooit en de rest van de
verwerking van die activiteit viel weg. Nu wakeGuardian(vraag.slug): het
paneel dat de hulpvraag houdt. slugParam zou ook fout zijn, want die is
null op de gedeelde inbox.

Vijf toetsen, tegenbewijs tegen de code van hiervoor: daar vallen ze alle
vijf. Volle suite 1260 groen.

  • Property mode set to 100644
File size: 6.3 KB
Line 
1// De noodknop is niet door een vreemde uit te zetten (shaer-gt70).
2//
3// De markering op een hulpvraag ('ik kijk ernaar' / 'afgehandeld') werd van
4// IEDEREEN aangenomen: de enige voorwaarde was dat de actor niet lokaal is.
5// Ondertekening zegt WIE, niet OF HET MAG. Afgehandeld kent geen terugdraai en
6// daarna verdwijnt de vraag uit de teller van elke guardian, dus een vreemde die
7// de URI kende kon de noodknop van een kind uitzetten.
8//
9// De regel die er nu staat: de ward is de bron van waarheid over wie zijn
10// guardians zijn, en WELKE ward erbij hoort komt uit onze eigen administratie.
11import { test } from 'node:test';
12import assert from 'node:assert/strict';
13
14process.env.DATABASE_PATH = ':memory:';
15process.env.PUBLIC_BASE_URL = 'https://test.example';
16
17const dbMod = await import('../src/config/database.js');
18const db = dbMod.default;
19{ const stil = console.log; console.log = () => {}; try { dbMod.initializeDatabase(); } finally { console.log = stil; } }
20const AP = (await import('../src/services/ActivityPubService.js')).default;
21const help = (await import('../src/services/guardianship/help.js')).default;
22
23// Het kind woont hier; wij zijn zijn guardian-instantie en hebben zijn
24// hulpvraag binnengekregen. Een tweede guardian woont elders.
25db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)').run('u1', 'u1', 'u1@t', 'x', 'god');
26db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_primary) VALUES (?,?,?,?,1)').run('s1', 'kid', 'Kid', 'u1');
27db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s2', 'gua', 'Guardian', 'u1');
28
29const WARD = 'https://test.example/ap/users/kid';
30const MEDE_GUARDIAN = 'https://elders.example/u/oma';
31const VREEMDE = 'https://elders.example/u/vreemde';
32const HULPVRAAG = 'https://test.example/ap/notes/hulp-1';
33
34// De ward kent zijn guardians: dat is de lijst waar het antwoord vandaan komt.
35db.prepare("INSERT INTO ap_guardianships (slug, role, other_uri, status) VALUES ('kid','ward',?,'accepted')").run(MEDE_GUARDIAN);
36// En wij hebben de hulpvraag echt binnengekregen.
37db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, content, help_request)
38 VALUES ('gua', ?, ?, '<p>help</p>', 1)`).run(HULPVRAAG, WARD);
39
40/** Een markering zoals hij over de lijn komt, van `van`. */
41async function markeer(van, noteUri = HULPVRAAG, kind = 'handled') {
42 const note = {
43 id: `${van}/markering/${Math.random().toString(36).slice(2)}`,
44 type: 'Note', attributedTo: van, to: ['https://test.example/ap/users/gua'],
45 inReplyTo: noteUri,
46 [kind === 'handled' ? 'shaer:helpHandled' : 'shaer:helpPickup']: noteUri,
47 content: '<p>markering</p>',
48 tag: [{ type: 'Mention', href: 'https://test.example/ap/users/gua', name: '@gua@test.example' }],
49 };
50 const act = { '@context': 'https://www.w3.org/ns/activitystreams', id: `${note.id}#create`, type: 'Create', actor: van, object: note };
51 const stil = console.warn; console.warn = () => {};
52 try {
53 await AP.handleInbox(
54 { body: act, headers: {}, method: 'POST', originalUrl: '/ap/users/gua/inbox', rawBody: Buffer.from(JSON.stringify(act)) },
55 'gua', { id: van },
56 );
57 } finally { console.warn = stil; }
58}
59
60const staat = () => help.statusOf(HULPVRAAG);
61
62test('een vreemde krijgt de noodknop niet uit', async () => {
63 await markeer(VREEMDE);
64 assert.equal(staat().handled, null, 'een vreemde mag een hulpvraag niet afsluiten');
65 assert.equal(staat().open, true, 'en de vraag staat dus nog open');
66});
67
68test('een guardian van deze ward mag het wel', async () => {
69 await markeer(MEDE_GUARDIAN);
70 assert.ok(staat().handled, 'een mede-guardian sluit hem wel af');
71});
72
73test('een markering voor een hulpvraag die wij niet hebben, telt niet', async () => {
74 const ONBEKEND = 'https://elders.example/n/ergens-anders-gezien';
75 await markeer(MEDE_GUARDIAN, ONBEKEND, 'pickup');
76 assert.equal(help.statusOf(ONBEKEND).pickedUpBy.length, 0,
77 'zonder eigen rij is er niets te markeren -- de note-URI komt van de afzender');
78});
79
80test('de verwerking valt niet om op de markering zelf', async () => {
81 // Tweede vondst bij deze bead, gemeten: de markeerweg riep wakeGuardian(slug)
82 // aan met een `slug` die in die scope niet bestaat. Gevolg: de markering werd
83 // vastgelegd en daarna gooide de handler een ReferenceError -- het paneel
84 // hoorde het nooit en de rest van de verwerking van die activiteit viel weg.
85 // Deze toets faalt zodra iemand daar weer een naam neerzet die er niet is.
86 const DERDE = 'https://test.example/ap/notes/hulp-3';
87 db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, content, help_request)
88 VALUES ('gua', ?, ?, '<p>help</p>', 1)`).run(DERDE, WARD);
89 const note = {
90 id: `${MEDE_GUARDIAN}/markering/heel`, type: 'Note', attributedTo: MEDE_GUARDIAN,
91 to: ['https://test.example/ap/users/gua'], 'shaer:helpPickup': DERDE, content: '<p>ik kijk</p>',
92 tag: [{ type: 'Mention', href: 'https://test.example/ap/users/gua', name: '@gua@test.example' }],
93 };
94 const act = { id: `${note.id}#create`, type: 'Create', actor: MEDE_GUARDIAN, object: note };
95 const status = await AP.handleInbox(
96 { body: act, headers: {}, method: 'POST', originalUrl: '/ap/users/gua/inbox', rawBody: Buffer.from(JSON.stringify(act)) },
97 'gua', { id: MEDE_GUARDIAN },
98 );
99 assert.equal(status, 202, 'de inbox hoort netjes 202 te geven, niet te gooien');
100 assert.equal(help.statusOf(DERDE).pickedUpBy.length, 1, 'en de oppik is vastgelegd');
101});
102
103test('guardian van een ANDERE ward is hier geen guardian', async () => {
104 // Iemand kan een echte guardian zijn, maar van een ander kind. De vraag is
105 // niet "is dit een guardian" maar "is dit een guardian van DEZE ward".
106 db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s3', 'kid2', 'Kid2', 'u1');
107 const ANDERE = 'https://elders.example/u/opa';
108 db.prepare("INSERT INTO ap_guardianships (slug, role, other_uri, status) VALUES ('kid2','ward',?,'accepted')").run(ANDERE);
109 const TWEEDE = 'https://test.example/ap/notes/hulp-2';
110 db.prepare(`INSERT INTO ap_mentions (slug, object_uri, actor_uri, content, help_request)
111 VALUES ('gua', ?, ?, '<p>help</p>', 1)`).run(TWEEDE, WARD);
112 await markeer(ANDERE, TWEEDE);
113 assert.equal(help.statusOf(TWEEDE).handled, null, 'guardian van een ander kind blijft hier een vreemde');
114});
Note: See TracBrowser for help on using the repository browser.