Blocking a user: nobody writes to someone who blocked them
A relation on the user type (the people this person blocked) can be followed from any row that names a person. recipient.has_blocked holds when the recipient blocked whoever is signed in.
authz
type user = app.users
has_blocked : user = app.blocks(blocker_id -> blocked_id)
type direct_message = app.direct_messages
sender : user = from_id
recipient : user = to_id
rules app.direct_messages
select : sender or recipient
insert : sender and not recipient.has_blockednot recipient.has_blockedrefuses a message to someone who blocked its sender.- The relation is read with the policy's rights, not the sender's. The app role isn't granted
app.blocksat all here, so nobody can list who blocked them, and the rule still works. - A
{condition}with a subquery would do the same. The relation says it in the policy's words, androwstile proveand the review can read it.
Tested
authz
test "nobody writes to someone who blocked them"
as user $ann allowed {INSERT INTO app.direct_messages (from_id, to_id, body) VALUES ($ann, $bo, 'hi')}
given {INSERT INTO app.blocks VALUES ($bo, $ann)}
as user $ann refused {INSERT INTO app.direct_messages (from_id, to_id, body) VALUES ($ann, $bo, 'hi again')}
as user $bo allowed {INSERT INTO app.direct_messages (from_id, to_id, body) VALUES ($bo, $ann, 'bye')}Try it
Open it in the playground: the tables, the policy and the test, in your browser, with nothing to install. Or take the files of docs/cookbook/blocking/ (schema.sql, policy.authz, tests.authz) and run rowstile dev. CI applies and tests them on every change (core/tests/cookbook.sh), and every line shown here is in those files.