Skip to content

Commit fd32dbc

Browse files
committed
feat: typechecking in schema
1 parent 7c183c3 commit fd32dbc

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

app/best-practices/page.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ One of the big benefits to using a centralized authorization system like SpiceDB
170170
It can be tempting to define the authorization logic for an endpoint as being the `AND` or `OR` of the checks of other permissions, especially when the alternative is writing a new schema.
171171
However, this increases the likelihood of drift across your system, hides the authorization logic for a system in that system's codebase, and increases the load on SpiceDB.
172172

173+
### Use Typechecking
174+
175+
Tags: **schema**
176+
177+
Adding [type-checking](https://authzed.com/docs/spicedb/concepts/schema#typechecking) to your schema can turn some silent bugs into failures at `WriteSchema` time.
178+
173179
### Avoid Cycles in your Schema
174180

175181
Tags: **schema**

app/spicedb/concepts/schema/page.mdx

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,131 @@ definition user {
444444
But in order to express that a user should be able to view themselves, you'll need to write a corresponding relationship (e.g. `user:alice#viewer@user:alice`)
445445
for the check to evaluate to true. The `self` keyword requires less DB space and computation to achieve the same result.
446446

447+
### Typechecking
448+
449+
The `use typechecking` feature allows you to explicitly declare and validate the types that permissions can resolve to, providing type safety for your authorization schemas.
450+
451+
Consider this schema:
452+
453+
```zed
454+
definition user {}
455+
definition serviceaccount {}
456+
457+
definition document {
458+
relation viewer: user
459+
relation admin: serviceaccount
460+
permission edit = viewer & admin
461+
}
462+
```
463+
464+
This schema writes successfully, but a call to `CheckPermission` for `document#edit` will _always_ return false at runtime because no relationship can simultaneously be for a `user` subject and for a `serviceaccount` subject. This is a bug that can go unnoticed for a long period of time, because the API call doesn't return an error.
465+
466+
Adding [type-checking](https://authzed.com/docs/spicedb/concepts/schema#typechecking) will turn that silent bug into a failure:
467+
468+
```zed
469+
use typechecking
470+
471+
definition user {}
472+
definition serviceaccount {}
473+
474+
definition document {
475+
relation viewer: user
476+
relation admin: serviceaccount
477+
permission edit: user = viewer & admin
478+
}
479+
```
480+
481+
This will fail at `WriteSchema` time because the type annotation provided is incomplete: it needs to be `permission edit: user | serviceaccount = viewer & admin` for the satisfy the type checker. However, the error given by the typechecker is really pointing at the underlying issue: you should fix the permission itself.
482+
483+
- Should the `edit` permission really be an intersection?
484+
- Is `serviceaccount` the only type that can be an `admin`?
485+
- Is `user` the only type that can be a `viewer`?
486+
487+
#### Use
488+
489+
Enable type checking by adding `use typechecking` at the top of your schema, and then add the type annotations to the permissions:
490+
491+
```zed
492+
use typechecking
493+
494+
definition user {}
495+
496+
definition document {
497+
relation viewer: user
498+
permission view: user = viewer
499+
}
500+
```
501+
502+
The syntax for type annotations is:
503+
504+
```zed
505+
permission <name>: <type1> | <type2> | ... = <expression>
506+
```
507+
508+
Note that type annotations must be complete: they must include **all** types reachable through the permission expression.
509+
510+
You can also slowly transition into a type-annotated schema by mixing annotated and non-annotated permissions:
511+
512+
```zed
513+
use typechecking
514+
515+
definition user {}
516+
517+
definition document {
518+
relation viewer: user
519+
permission view: user = viewer // Validated
520+
permission edit = viewer // Not validated (no annotation)
521+
}
522+
```
523+
524+
#### Examples
525+
526+
- Single Type
527+
528+
```zed
529+
use typechecking
530+
531+
definition user {}
532+
533+
definition document {
534+
relation viewer: user
535+
permission view: user = viewer
536+
}
537+
```
538+
539+
- Multiple Types
540+
541+
```zed
542+
use typechecking
543+
544+
definition user {}
545+
definition team {}
546+
547+
definition document {
548+
relation viewer: user | team
549+
relation owner: user | team
550+
permission view: user | team = viewer + owner
551+
}
552+
```
553+
554+
- With Arrow Operations: the annotations must be for the types on the right-hand side of the arrow:
555+
556+
```zed
557+
use typechecking
558+
559+
definition user {}
560+
definition team {}
561+
562+
definition organization {
563+
relation member: user | team
564+
}
565+
566+
definition document {
567+
relation org: organization
568+
permission view: user | team = org->member
569+
}
570+
```
571+
447572
### Naming Permissions
448573

449574
Permissions define a set of objects that can perform an action or have some attribute, and thus **permissions should be named as verbs or nouns**, read as `(is/can) {permission name} (the object)`.

0 commit comments

Comments
 (0)