row 206 r2 (#1704): rebased on 275 r3 957a9bfe0; share verbs need collaborator, viewer reads a share (duckworth-0169 C1, C2)
This commit is contained in:
1 parent
01ed9b839a
commit
d4fbce5014
3 files changed
+47
-18
No files matched your search
@@ -6,16 +6,25 @@ sections 4.3, 5.2-5.6 and 8. Reviewer: Duckworth. Assignment: sage-2789.
|
||||
|
||||
## Base and chain
|
||||
|
||||
- Started on aca16404c (sage-2789) on branch launchpad/row206.
|
||||
- 206 chains on 275 (193b1b, viewer verbs and board refusals), which Morgana is building. I rebase
|
||||
onto Morgana's 275 sha when it comes, and onto 253's landing under a new branch name.
|
||||
- At this base a user binds only with a collaborator grant (`#userGrants`, USER_LEVEL). Viewer
|
||||
sessions arrive with 275, so the viewer column of design 4.3 is not testable here (sage-2818
|
||||
Q4: the viewer column waits on the 275 rebase; 275 moves once more for Duckworth's B1,
|
||||
duckworth-0166). On the 275
|
||||
rebase, USER_VERBS and the viewer gate merge with the three share verbs, and the share verbs
|
||||
must refuse a viewer (design 4.3: share, unshare and shares are "refused" for viewers).
|
||||
|
||||
- Started on aca16404c (sage-2789) on branch launchpad/row206; 631a256ba pushed there (sage-2854).
|
||||
- Round 2: rebased onto Morgana's 275 r3, 957a9bfe0 (morgana-0143, which carries duckworth-0166
|
||||
B1), on the new branch launchpad/row206-r275. No force push of the old branch.
|
||||
- After 253 lands, rebase onto its tip under a new branch name.
|
||||
- 275 gives `VIEWER_VERBS`, `userLevel(verb)` and verb-aware `#granted`/`#grantedThread`, and
|
||||
`#userGrants` keeps a live grant of any level, so a viewer binds. On the rebase:
|
||||
- USER_VERBS keeps 275's block and gains the three share verbs. They are not in VIEWER_VERBS,
|
||||
so `userLevel` asks collaborator and a viewer is refused each `access-refused` (design 4.3).
|
||||
- `#threadRead`'s owner path passes `thread.messages` to `#grantedThread`, so a viewer owner
|
||||
reads its own thread at the viewer level (duckworth-0169 C1). Share and unshare pass `verb`;
|
||||
shares passes `thread.shares`.
|
||||
- Two conflicts, both in broker.mjs: the USER_VERBS block and the `thread.messages` line (275's
|
||||
`#grantedThread(s, 'thread.messages', #thread(...))` gives way to `#threadRead`, which now
|
||||
holds that check). user-principal.test.mjs merged clean.
|
||||
- C2 sweep on 957a9bfe0: 275 touches agents/morgana/work/queue-275/BUILD.md, broker.mjs,
|
||||
user-principal.test.mjs and viewer.test.mjs. Overlap with 206 is broker.mjs and
|
||||
user-principal.test.mjs, both handled above. viewer.test.mjs has no schema-version or hash
|
||||
pins, and its "every verb off the user list" test reads USER_VERBS, so the share verbs drop
|
||||
out of it on their own.
|
||||
- The live `--migrate` (3l→3m on the running bus) is not part of this build. It is Mos's go under
|
||||
Q173, with a backup first and a STATE report after (mos-1310 item 4).
|
||||
|
||||
@@ -70,17 +79,17 @@ sections 4.3, 5.2-5.6 and 8. Reviewer: Duckworth. Assignment: sage-2789.
|
||||
5. Rule 3 asks the owner's grant for any level, like rule 2 (design: "still has a live grant
|
||||
covering t"). A user owner lowered to viewer keeps the thread readable to its sharees.
|
||||
6. An unshare checks no coverage, so an owner can end a share whose target lost its grant.
|
||||
7. `thread.shares` for a user goes through `#grantedThread`, so it needs the collaborator grant
|
||||
to still cover the thread, like share and unshare. Design 4.3 says "own threads" with no grant
|
||||
note; at this base every user is a collaborator, so the two agree until 275.
|
||||
7. `thread.shares` for a user goes through `#grantedThread` at the collaborator level, like share
|
||||
and unshare, so a viewer is refused it. Design 4.3 lists shares as "refused" for viewers.
|
||||
|
||||
## Tests
|
||||
|
||||
New: tests/thread-shares.test.mjs, 15 tests: the read flow and the write refusals, `*`, rule 2,
|
||||
New: tests/thread-shares.test.mjs, 16 tests: the read flow and the write refusals, `*`, rule 2,
|
||||
rule 3 (with trail for human and reader), latest-row state, share-not-covered, target checks,
|
||||
lost owner coverage, tester and reader refusals (18 `action.refused` events, no rows), trail and
|
||||
replies, sessions, the sharee's `session.list` refusal (Q1), agents and the tester matching no
|
||||
share (Q2), the 3l→3m migration and the 3l engine's refusal of a 3m store.
|
||||
share (Q2), the viewer column (C1: a viewer reads a share, its share verbs refuse,
|
||||
and a viewer target binds and reads), the 3l→3m migration and the 3l engine's refusal of a 3m store.
|
||||
|
||||
Pins moved 3l→3m: account-, chief-of-staff, comment-, gitea-, grant-, name-, spec-, usage- and
|
||||
thread-migration, sessions, schema (23 tables, 89 triggers) and user-principal (USER_VERBS).
|
||||
|
||||
@@ -309,7 +309,8 @@ access document the broker reads then, so a removed, expired or unreadable grant
|
||||
unshare ends reads on the next request. Only `thread.messages` admits a sharee; `thread.open`,
|
||||
`thread.send` and the share verbs refuse it `thread-not-owned`. `trail` and `replies` show a
|
||||
user's thread messages to the owner and readers while the check holds. The tester can't call
|
||||
the share verbs (`tester-refused`). Tests: `tests/thread-shares.test.mjs`.
|
||||
the share verbs (`tester-refused`), and a viewer is refused them `access-refused`; a viewer
|
||||
reads a thread shared with it. Tests: `tests/thread-shares.test.mjs`.
|
||||
|
||||
`threadsState(business)` is a trusted launcher API, like `bindLaunch`, not a socket verb.
|
||||
Per thread it returns `{thread,instance,pending,unread,lastMessage,lastOpen,lastClaim,holder}`:
|
||||
|
||||
@@ -202,21 +202,40 @@ test('mutant 4: a share\'s state is its latest row, so an unshare ends it and a
|
||||
});
|
||||
|
||||
test('mutant 8: a user target needs a live grant of any level covering the thread, else share-not-covered', (t) => {
|
||||
const { access, call, code, base, A, p1, rows } = setup(t);
|
||||
const { b, caps, access, call, code, base, A, p1, rows, read } = setup(t);
|
||||
// carol's grant covers p2 only; dave has none; erin's is expired.
|
||||
access.doc.grants = [...base(), grant({ id: 'g-4', user: 'erin', projects: [p1], expires: '2000-01-01T00:00:00Z' })];
|
||||
for (const to of ['user:carol', 'user:dave', 'user:erin'])
|
||||
assert.equal(code('alice', 'thread.share', { thread: A, to }), 'share-not-covered', to);
|
||||
assert.equal(rows(), 0);
|
||||
// A viewer grant is enough to be shared with, though at this base it can't bind a session.
|
||||
// A viewer grant is enough to be shared with, and the viewer reads the share (row 275).
|
||||
access.doc.grants = [...base(), grant({ id: 'g-5', user: 'vic', projects: [p1], level: 'viewer' })];
|
||||
assert.deepEqual(call('alice', 'thread.share', { thread: A, to: 'user:vic' }), { thread: A, to: 'user:vic', op: 'share' });
|
||||
caps.vic = b.bindUser({ business: 'demo', user: 'vic' });
|
||||
assert.deepEqual(read('vic', A), ['alice: hello']);
|
||||
// An unshare checks no coverage: an uncovered target's share can still be ended.
|
||||
access.doc.grants = base();
|
||||
assert.deepEqual(call('alice', 'thread.unshare', { thread: A, to: 'user:vic' }), { thread: A, to: 'user:vic', op: 'unshare' });
|
||||
assert.equal(rows(), 2);
|
||||
});
|
||||
|
||||
test('a viewer reads a thread shared with it, and share, unshare and shares refuse a viewer access-refused (design 4.3)', (t) => {
|
||||
const { access, call, code, base, A, rows, read } = setup(t);
|
||||
call('alice', 'thread.share', { thread: A, to: 'user:bob' });
|
||||
// bob drops to viewer and still reads the share.
|
||||
access.doc.grants = base().map((g) => (g.user === 'bob' ? { ...g, level: 'viewer' } : g));
|
||||
assert.deepEqual(read('bob', A), ['alice: hello']);
|
||||
assert.deepEqual(call('bob', 'thread.list', { shared: true }).map((x) => x.id), [A]);
|
||||
// alice drops to viewer too: she reads her own thread, and each share verb on it refuses.
|
||||
access.doc.grants = base().map((g) => ({ ...g, level: 'viewer' }));
|
||||
assert.deepEqual(read('alice', A), ['alice: hello']);
|
||||
for (const [verb, args] of [['thread.share', { to: '*' }], ['thread.unshare', { to: 'user:bob' }], ['thread.shares', {}]])
|
||||
assert.equal(code('alice', verb, { thread: A, ...args }), 'access-refused', verb);
|
||||
assert.equal(rows(), 1);
|
||||
// Rule 3 asks any level, so alice's viewer grant keeps the thread readable to bob.
|
||||
assert.deepEqual(read('bob', A), ['alice: hello']);
|
||||
});
|
||||
|
||||
test('a share target is `user:<id>`, the business human or `*`, and never the sharer', (t) => {
|
||||
const { call, code, A, H, rows } = setup(t);
|
||||
for (const to of ['user:alice', 'bob', 'tester', 'user:tester', 'user:human', 'user:user', 'user:jason', 'user:', 'user:Bad Name', '', 42, null, ['*']])
|
||||
|
||||
Reference in new issue
Block a user