Expand description
What one role can see.
Hasura builds a separate GraphQL schema for every role, and the difference
between them is not cosmetic. A role with no select permission on a table
does not get a table it is refused access to – it gets a schema with no
such field to read it by, and naming one is a validation failure rather
than a denial. The same goes one level down: a column outside the
permission is absent from the type, not merely null.
That shape is what the corpus tests. artist_select_query_Track_fail.yaml
expects field "Track" not found in type: 'query_root', which no amount of
filtering at execution time produces.
§Why this is a filtered cache rather than a flag threaded through the
builders
A select permission is a view of a table, and a set of them is a view of the database. Reducing the schema cache to what a role may see and then building the schema from that means the builders need to know nothing about permissions: a table that was dropped generates no root field, no relationship pointing at it and no boolean expression naming it, because the code that would have generated them cannot see it either.
The alternative – asking “may this role?” at each of the several dozen places a name is emitted – has to be right every time, and is wrong the moment someone adds a place. This has to be right once.
§The one thing a cache cannot hold: two column sets
Hasura grants reading and writing separately, so a role may be allowed to set a column it is not allowed to see – ten permissions in the corpus do – and may be allowed to write a table it cannot read at all, which eight more do. A schema cache has one column list per table and no place to say which half of it is which.
The union goes in the cache, and the split is made once, where the object
type’s fields are built: crate::schema::object::TableObjectType::fields
is what may be read and writable_fields is what may be written. That
keeps the property above – everything a read is made of (the type, its
boolean expression, its ordering, its column enum, its aggregates) is
derived from fields and narrows with it, and the write inputs are built
from the other list and narrowed again by the permission that names them.
A table with no readable field is the limit of that: it has no GraphQL type
at all, because a type with no fields is not a legal one. Its bulk writes
remain and answer with affected_rows alone – there is no returning to
hang rows on – and everything that would return a row of it (the query
roots, insert_one, the _by_pk writes, a relationship pointing at it, a
function returning it) is left out with the type.
What is left over goes on SchemaConfig: allow_aggregations, which is
a fact about a permission rather than about a table.
Structs§
- Caller
- Who is asking, for the places that have to narrow rows to them.
Enums§
- Fault
- Why a read could not be narrowed to the caller.
- Verb
- Which of the four grants is being asked about.
Constants§
- CHECK_
FAILED - Hasura’s wording for a written row that a permission refuses.
Functions§
- allows_
aggregations - Whether a role may ask for aggregates over a table.
- cache_
for_ role - Reduce a schema cache to what one role may see.
- exists_
target - The table
_existslooks in, as(schema, table). - has_
backend_ only - Whether any of this role’s permissions is reachable only by a backend.
- permission_
where - Turn a permission’s row filter into the boolean expression a
whereargument would have been. - presets
- The columns the server fills in itself, with their values.
- read_
limit - How many rows a role’s permission lets one read return.
- read_
predicate - The predicate a role’s select permission adds to a read of one table.
- reads_
anything - Whether this role can read anything of this table at all.
- row_
filter - Which rows a role’s permission lets one operation reach.
- write_
check - What a written row has to satisfy for the write to stand.