From b447fcc43906b38423aa06e96d70e394ff933ae6 Mon Sep 17 00:00:00 2001 From: Hassan Abdel-Rahman Date: Tue, 6 Oct 2026 16:25:51 -0400 Subject: [PATCH 1/5] Show in the explain panel how a realm treats callers who aren't signed in, who each grant opened to them writes as, and each grant's own issues Co-Authored-By: Claude Opus 5.5 --- realm-policy/realm-policy.gts | 126 ++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/realm-policy/realm-policy.gts b/realm-policy/realm-policy.gts index 7c7a6d45b..b90bcebcf 100644 --- a/realm-policy/realm-policy.gts +++ b/realm-policy/realm-policy.gts @@ -543,6 +543,39 @@ interface ExplanationViewSignature { Args: { explanation: PolicyExplanation }; } +// What an explanation reports about callers who aren't signed in: how the +// realm limits and blocks them, and for a grant that opts in to them, the +// user its writes are made as. Read where present, so the panel answers the +// same on a platform that doesn't report them. +interface AnonymousAccessDetail { + limit: { requests: number; windowSeconds: number }; + limitFrom: 'realm' | 'platform'; + invalidBlocklistEntries: string[]; +} +interface GrantAnonymousDetail { + actingUserKey?: string; + actingUser?: string; + actingUserFailure?: 'key-missing' | 'not-a-matrix-id' | 'no-write'; +} +type ExplainedGrantDetail = PolicyExplanation['rules'][number]['grants'][number] & { + anonymous?: GrantAnonymousDetail; + issues?: DraftIssue[]; +}; + +// Why an acting-user key names no one a write may be made as, in the words a +// policy author reads it in. +const ACTING_USER_FAILURES: Record< + NonNullable, + (key: string) => string +> = { + 'key-missing': (key) => + `this realm's settings have no "${key}", so it admits none of them.`, + 'not-a-matrix-id': (key) => + `this realm's "${key}" setting isn't a user ID, so it admits none of them.`, + 'no-write': (key) => + `the user this realm's "${key}" setting names can't write to the realm, so it admits none of them.`, +}; + class ExplanationView extends GlimmerComponent { get explanation() { return this.args.explanation; @@ -553,6 +586,49 @@ class ExplanationView extends GlimmerComponent { return (search ? SEARCH_REASONS[reason] : undefined) ?? REASONS[reason]; } + get anonymousAccess(): AnonymousAccessDetail | undefined { + return ( + this.explanation as PolicyExplanation & { + anonymous?: AnonymousAccessDetail; + } + ).anonymous; + } + + get anonymousLimit(): string { + let access = this.anonymousAccess; + if (!access) { + return ''; + } + let { requests, windowSeconds } = access.limit; + return `${requests} requests per ${windowSeconds} seconds from one address, ${ + access.limitFrom === 'realm' ? 'set by this realm' : 'the platform default' + }`; + } + + get invalidBlocklist(): string { + return (this.anonymousAccess?.invalidBlocklistEntries ?? []) + .map((entry) => `"${entry}"`) + .join(', '); + } + + grantDetail = ( + grant: PolicyExplanation['rules'][number]['grants'][number], + ): ExplainedGrantDetail => grant as ExplainedGrantDetail; + + // Who a grant opened to callers who aren't signed in makes their writes + // as, or why it admits none of them. + actingUserLine = (anonymous: GrantAnonymousDetail): string => { + let opened = "Open to people who aren't signed in"; + let key = anonymous.actingUserKey; + if (!key) { + return `${opened}.`; + } + if (anonymous.actingUserFailure) { + return `${opened}, but ${ACTING_USER_FAILURES[anonymous.actingUserFailure](key)}`; + } + return `${opened}, writing as ${anonymous.actingUser} (the realm's "${key}" setting).`; + }; + // What the realm's own permissions let the actor do. Write without read // is a shape the realm accepts, so it is named rather than read as both. get aclStanding(): string { @@ -665,6 +741,18 @@ class ExplanationView extends GlimmerComponent { {{this.explanation.refusal.code}} {{/if}} + {{#if this.anonymousAccess}} +
Limit
+
{{this.anonymousLimit}}
+ {{#if this.anonymousAccess.invalidBlocklistEntries.length}} +
Blocklist
+
+ Some entries aren't an address or a range ({{this.invalidBlocklist}}), + so this realm turns away everyone who isn't signed in until + they're fixed. +
+ {{/if}} + {{/if}} {{#if this.explanation.search}}