diff --git a/realm-policy/realm-policy.gts b/realm-policy/realm-policy.gts index c8ee44385..d3df35beb 100644 --- a/realm-policy/realm-policy.gts +++ b/realm-policy/realm-policy.gts @@ -28,6 +28,7 @@ import { import PolicyPredicateField from '@cardstack/catalog/fields/policy-predicate/policy-predicate'; import { subscribeToRealm } from '@cardstack/runtime-common'; import StringField from 'https://cardstack.com/base/string'; +import BooleanField from 'https://cardstack.com/base/boolean'; import { BoxelInput, Button, @@ -59,6 +60,22 @@ export class OperationGrant extends FieldDef { // A BXL boolean expression over the caller and the target. Absent means // the grant is unconditional. @field where = contains(PolicyPredicateField); + // Whether the grant also admits a caller who isn't signed in. Off, it + // admits only signed-in callers, so a grant written before anonymous + // callers could reach a policy never starts admitting them. Only a grant on + // a base operation under its own name (`read`, `update`, …) may set it, not + // one on a custom operation or a named query. The realm doesn't evaluate a + // `where` that reads `actor()` for such a caller, so a grant whose `where` + // does never admits one: an anonymous grant is scoped by what the target + // holds. How hard anonymous callers may use the realm, and which addresses + // it refuses, are the governed realm's own settings, not the policy's. + @field anonymous = contains(BooleanField); + // For an anonymous grant on a write: the key, exactly as written, in the + // governed realm's `realm.json` settings, whose value is the user the write + // is made as. The realm names the user rather than the policy, because the + // policy can live in another realm and its writers must not decide whose + // identity this realm's writes carry. + @field actingUser = contains(StringField); static embedded = class Embedded extends Component { };