Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 58 additions & 76 deletions docs/content/modeling/blocklists.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,10 @@ This is useful when:

Before you start this guide, make sure you're familiar with some <ProductConcept /> and know how to develop the things listed below.

<details>
<summary>

You will start with the _<ProductConcept section="what-is-an-authorization-model" linkName="authorization model" />_ below, it represents a `document` _<ProductConcept section="what-is-a-type" linkName="type" />_ that can have users **<ProductConcept section="what-is-a-relation" linkName="related" />** as `editor`, and `team` type that can have users related as `member`.

Let us also assume that we have a `document` called "planning", shared for editing within the product `team` (comprised of becky and carl).

</summary>

<AuthzModelSnippetViewer
configuration={{
Expand Down Expand Up @@ -85,62 +81,51 @@ Let us also assume that we have a `document` called "planning", shared for editi
}}
/>

The current state of the system is represented by the following relationship tuples being in the system already:

<RelationshipTuplesViewer
relationshipTuples={[
{
_description: 'Members of the product team can edit the planning document',
user: 'team:product#member',
relation: 'editor',
object: 'document:planning',
},
{
_description: 'Becky is a member of the product team',
user: 'user:becky',
relation: 'member',
object: 'team:product',
},
{
_description: 'Carl is a member of the product team',
user: 'user:carl',
relation: 'member',
object: 'team:product',
},
]}
/>

<hr />
Let us also assume that we have a `document` called "planning", shared for editing within the product `team` (comprised of becky and carl).

In addition, you will need to know the following:
```yaml
# Becky is a member of the product team
- user: user:becky
relation: member
object: team:product

### Modeling user groups
# Carl is a member of the product team
- user: user:carl
relation: member
object: team:product

You need to know how to add users to groups and grant groups access to resources. [Learn more →](.//user-groups.mdx)
# Members of the product team can edit the planning document
- user: team:product#member
relation: editor
object: document:planning
```

### <ProductName format={ProductNameFormat.ShortForm} /> Concepts

- A <ProductConcept section="what-is-a-type" linkName="Type" />: a class of objects that have similar characteristics
- A <ProductConcept section="what-is-a-user" linkName="User" />: an entity in the system that can be related to an object
- A <ProductConcept section="what-is-a-relation" linkName="Relation" />: is a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
- An <ProductConcept section="what-is-an-object" linkName="Object" />: represents an entity in the system. Users' relationships to it can be define through relationship tuples and the authorization model
- A <ProductConcept section="what-is-a-relationship-tuple" linkName="Relationship Tuple" />: a grouping consisting of a user, a relation and an object stored in <ProductName format={ProductNameFormat.ShortForm} />
- [Exclusion Operator](../configuration-language.mdx#the-exclusion-operator): the exclusion operator can be used to exclude certain usersets from being related to an object
<hr />

</details>

<Playground />

## Step by step

With the above authorization model and relationship tuples, <ProductName format={ProductNameFormat.LongForm} /> will correctly respond with `{"allowed":true}` when <ProductConcept section="what-is-a-check-request" linkName="check" /> is called to see if Carl and Becky can edit this `document`.

We can verify that by issuing two check requests:

<CheckRequestViewer user={'user:becky'} relation={'editor'} object={'document:planning'} allowed={true} />

<CheckRequestViewer user={'user:carl'} relation={'editor'} object={'document:planning'} allowed={true} />

We can verify that with these two tests which will pass:

```yaml
tests:
- name: "Becky can edit the 'planning' document"
check:
- user: user:becky
object: document:planning
assertions:
editor: true
- name: "Carl can edit the 'planning' document"
check:
- user: user:carl
object: document:planning
assertions:
editor: true
```
Comment on lines +114 to +128
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

OpenFGA store test YAML format documentation

💡 Result:

OpenFGA “store test YAML” files use the Store File Format (commonly named *.fga.yaml). Official docs:

Minimal .fga.yaml shape

Top-level keys include name, model/model_file, global tuples/tuple_file/tuple_files, and tests. Each test can add its own tuples and include check, list_objects, and/or list_users assertions. [1][2]

name: My Store Tests
model: |
  model
    schema 1.1
  type user
  type document
    relations
      define viewer: [user]

tuples:
  - user: user:anne
    relation: viewer
    object: document:1

tests:
  - name: can-anne-view
    check:
      - user: user:anne
        object: document:1
        assertions:
          viewer: true

[1][2]


🏁 Script executed:

# Read the actual file with surrounding context
cat -n docs/content/modeling/blocklists.mdx | sed -n '105,135p'

Repository: openfga/openfga.dev

Length of output: 1712


🏁 Script executed:

# Search for other test YAML examples in the repository
fd -e yaml -e yml | head -20

Repository: openfga/openfga.dev

Length of output: 79


🏁 Script executed:

# Search for other test YAML examples in the blocklists.mdx file
grep -n "^```yaml" docs/content/modeling/blocklists.mdx | head -10

Repository: openfga/openfga.dev

Length of output: 131


🏁 Script executed:

# Look at more of the file to see if there's a pattern with tests
rg -A 15 "tests:" docs/content/modeling/blocklists.mdx | head -50

Repository: openfga/openfga.dev

Length of output: 1028


Fix YAML test structure format in code examples.

The test YAML blocks use - tests: (as a list item) followed by nested test definitions. According to OpenFGA's official store file format documentation, tests: should be a top-level key, not a list item. Remove the leading - before tests::

Correct structure
tests:
  - name: "Becky can edit the 'planning' document"
    check:
      - user: user:becky
        object: document:planning
        assertions:
          editor: true
  - name: "Carl can edit the 'planning' document"
    check:
      - user: user:carl
        object: document:planning
        assertions:
          editor: true

This pattern appears in multiple test examples throughout the file.

🤖 Prompt for AI Agents
In `@docs/content/modeling/blocklists.mdx` around lines 114 - 128, The YAML
examples use a list item prefix before the tests key (e.g., "- tests:") which is
invalid for the OpenFGA store format; update each example where " - tests:"
appears (search for the "tests:" YAML blocks in this file) by removing the
leading dash so "tests:" is a top-level key and keep the nested test entries
(e.g., the "- name:" entries) unchanged; apply this fix to every repeated test
example in docs/content/modeling/blocklists.mdx.

We want to share a document with the product team and also have the ability to deny certain users access, even if they have the document shared with them already. We can verify this by blocking Carl (who we have seen already has edit access) from editing the document.

In order to do that, we need to:
Expand All @@ -149,11 +134,9 @@ In order to do that, we need to:
2. [Modify our model to indicate that users who are blocked can no longer edit the document](#02-modify-our-model-so-users-who-are-blocked-can-no-longer-edit-the-document)
3. [Verify that our solution works](#03-verify-our-solution-works):

a. [Indicate that Carl is blocked from the planning document](#a-indicate-that-carl-is-blocked-from-the-planning-document)

b. [Carl (now blocked) can no longer edit the document](#b-carl-now-blocked-can-no-longer-edit-the-document)

c. [Becky still has edit access](#c-becky-still-has-edit-access)
a. [Indicate that Carl is blocked from the planning document](#a-indicate-that-carl-is-blocked-from-the-planning-document)

b. [Carl (now blocked) can no longer edit the document](#b-carl-now-blocked-can-no-longer-edit-the-document), but Becky still has edit access

### 01. Modify our model so users can be blocked from accessing a document

Expand Down Expand Up @@ -266,34 +249,33 @@ To check if our new model works, we'll add a relationship tuple with Carl as `bl

With our modified authorization model, we can indicate that Carl is blocked by adding this _<ProductConcept section="what-is-a-relationship-tuple" linkName="relationship tuple" />_.

<WriteRequestViewer
relationshipTuples={[
{
_description: 'Carl is blocked from editing the planning document',
user: 'user:carl',
relation: 'blocked',
object: 'document:planning',
},
]}
/>
```yaml
# Carl is blocked from the planning document
- user: user:carl
relation: blocked
object: document:planning
```

#### b. Carl (now blocked) can no longer edit the document

We have modified the authorization model and added relationship tuples to indicate that Carl is `blocked`. Now let's make sure our solution works as expected.

To check if Carl still has access to the document, we can issue a check request with Carl as the user.

<CheckRequestViewer user={'user:carl'} relation={'editor'} object={'document:planning'} allowed={false} />

The response is `false`, so our solution is working as expected.

#### c. Becky still has edit access

To check if Becky still has access to the document, we'll issue another check request with Becky as the user.

<CheckRequestViewer user={'user:becky'} relation={'editor'} object={'document:planning'} allowed={true} />

The response is `true`, indicating our model change did not inadvertently deny access for users who have access but are not blocked.
With these tuples, Carl will not be able to edit the document, but Becky will:
```yaml
tests:
- name: "Carl can edit the 'planning' document"
check:
- user: user:carl
object: document:planning
assertions:
editor: false
- name: "Becky can edit the 'planning' document"
check:
- user: user:becky
object: document:planning
assertions:
editor: true
```

:::caution
When creating tuples for <ProductName format={ProductNameFormat.LongForm} /> make sure to use unique ids for each object and user within your application domain. We are using first names and human-readable identifiers to make this task easier to read.
Expand Down
Loading