Skip to content

Commit

Permalink
docs: add list users snippet, concept and testing (#732)
Browse files Browse the repository at this point in the history
Co-authored-by: Will Vedder <[email protected]>
Co-authored-by: Raghd Hamzeh <[email protected]>
  • Loading branch information
3 people authored May 9, 2024
1 parent 0a361a5 commit a004edd
Show file tree
Hide file tree
Showing 8 changed files with 646 additions and 11 deletions.
33 changes: 32 additions & 1 deletion docs/content/concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
RelatedSection,
RelationshipTuplesViewer,
UpdateProductNameInLinks,
ListUsersRequestViewer
} from '@components/Docs';

# Concepts
Expand Down Expand Up @@ -591,7 +592,37 @@ For example, the following returns all the objects with document type for which
expectedResults={['document:otherdoc', 'document:planning']}
/>

For more information, see the [Relationship Queries page](./interacting/relationship-queries.mdx) and the official [Check API Reference](/api/service#Relationship%20Queries/ListObjects).
For more information, see the [Relationship Queries page](./interacting/relationship-queries.mdx) and the [List Objects API Reference](/api/service#Relationship%20Queries/ListObjects).

</details>
<details>
<summary>

## What Is A List Users Request?

A **list users request** is a call to the <ProductName format={ProductNameFormat.LongForm}/> list users endpoint that returns all users of a given type that have a specified relationship with an object.

</summary>

List users requests are completed using the relevant `ListUsers` method in SDKs, the `fga query list-users` command in the CLI, or by manually calling the [ListUsers endpoint](/api/service#Relationship%20Queries/ListUsers) using curl or in your code.

The list users endpoint responds with a list of users and excluded users for a given type that have the specificed relationship with an object.

For example, the following returns all the users of type `user` that have the `viewer` relationship for `document:planning`:

<ListUsersRequestViewer
authorizationModelId="01HVMMBCMGZNT3SED4Z17ECXCA"
objectType="document"
objectId="planning"
relation="viewer"
userFilterType="user"
expectedResults={{
users: [{ object: { type: "user:anne" } }],
excluded_users: [{ object: { type: "user:beth" } }]
}}
/>

For more information, see the the [ListUsers API Reference](/api/service#Relationship%20Queries/ListUsers).

</details>
<details>
Expand Down
189 changes: 189 additions & 0 deletions docs/content/getting-started/perform-list-users.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
---
title: Perform a List Users call
sidebar_position: 4
slug: /getting-started/perform-list-users
description: List all users that have a certain relation with a particular object
---

import {
SupportedLanguage,
languageLabelMap,
ListUsersRequestViewer,
DocumentationNotice,
ProductConcept,
ProductName,
ProductNameFormat,
RelatedSection,
SdkSetupHeader,
SdkSetupPrerequisite,
} from '@components/Docs';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Perform a List Users call

:::caution Warning
ListUsers is currently in an experimental release. Read [the announcement](https://openfga.dev/blog/list-users-announcement) for more information.
:::

<DocumentationNotice />

This section will illustrate how to perform a <ProductConcept section="what-is-a-list-users-request" linkName="list users" /> request to determine all the <ProductConcept section="what-is-a-user" linkName="users" /> of a given <ProductConcept section="what-is-a-type" linkName="type" /> that have a specified <ProductConcept section="what-is-a-relationship" linkName="relationship" /> with a given <ProductConcept section="what-is-an-object" linkName="objects" />.

## Before You Start

<Tabs groupId="languages">

<TabItem value={SupportedLanguage.JS_SDK} label={languageLabelMap.get(SupportedLanguage.JS_SDK)}>

1. <SdkSetupPrerequisite />
2. You have [installed the SDK](./install-sdk.mdx).
3. You have [configured the _authorization model_](./configure-model.mdx) and [updated the _relationship tuples_](./update-tuples.mdx).
4. You have loaded `FGA_STORE_ID` and `FGA_API_URL` as environment variables.

</TabItem>

<TabItem value={SupportedLanguage.GO_SDK} label={languageLabelMap.get(SupportedLanguage.GO_SDK)}>

1. <SdkSetupPrerequisite />
2. You have [installed the SDK](./install-sdk.mdx).
3. You have [configured the _authorization model_](./configure-model.mdx) and [updated the _relationship tuples_](./update-tuples.mdx).
4. You have loaded `FGA_STORE_ID` and `FGA_API_URL` as environment variables.

</TabItem>

<TabItem value={SupportedLanguage.DOTNET_SDK} label={languageLabelMap.get(SupportedLanguage.DOTNET_SDK)}>

1. <SdkSetupPrerequisite />
2. You have [installed the SDK](./install-sdk.mdx).
3. You have [configured the _authorization model_](./configure-model.mdx) and [updated the _relationship tuples_](./update-tuples.mdx).
4. You have loaded `FGA_STORE_ID` and `FGA_API_URL` as environment variables.

</TabItem>

{/* <TabItem value={SupportedLanguage.PYTHON_SDK} label={languageLabelMap.get(SupportedLanguage.PYTHON_SDK)}>
1. <SdkSetupPrerequisite />
2. You have [installed the SDK](./install-sdk.mdx).
3. You have [configured the _authorization model_](./configure-model.mdx) and [updated the _relationship tuples_](./update-tuples.mdx).
4. You have loaded `FGA_STORE_ID` and `FGA_API_URL` as environment variables.
</TabItem> */}

<TabItem value={SupportedLanguage.JAVA_SDK} label={languageLabelMap.get(SupportedLanguage.JAVA_SDK)}>

1. <SdkSetupPrerequisite />
2. You have [installed the SDK](./install-sdk.mdx).
3. You have [configured the _authorization model_](./configure-model.mdx) and [updated the _relationship tuples_](./update-tuples.mdx).
4. You have loaded `FGA_STORE_ID` and `FGA_API_URL` as environment variables.

</TabItem>

<TabItem value={SupportedLanguage.CLI} label={languageLabelMap.get(SupportedLanguage.CLI)}>

1. <SdkSetupPrerequisite />
2. You have [configured the _authorization model_](./configure-model.mdx).
3. You have loaded `FGA_STORE_ID` and `FGA_API_URL` as environment variables.

</TabItem>

<TabItem value={SupportedLanguage.CURL} label={languageLabelMap.get(SupportedLanguage.CURL)}>

1. <SdkSetupPrerequisite />
2. You have [configured the _authorization model_](./configure-model.mdx) and [updated the _relationship tuples_](./update-tuples.mdx).
3. You have loaded `FGA_STORE_ID` and `FGA_API_URL` as environment variables.

</TabItem>
</Tabs>

## Step By Step

Assume that you want to list all users of type `user` that have a `reader` relationship with `document:planning`:

### 01. Configure the <ProductName format={ProductNameFormat.ShortForm}/> API Client

Before calling the ListUsers API, you will need to configure the API client.

<Tabs groupId="languages">
<TabItem value={SupportedLanguage.JS_SDK} label={languageLabelMap.get(SupportedLanguage.JS_SDK)}>

<SdkSetupHeader lang={SupportedLanguage.JS_SDK} />

</TabItem>
<TabItem value={SupportedLanguage.GO_SDK} label={languageLabelMap.get(SupportedLanguage.GO_SDK)}>

<SdkSetupHeader lang={SupportedLanguage.GO_SDK} />

</TabItem>
<TabItem value={SupportedLanguage.DOTNET_SDK} label={languageLabelMap.get(SupportedLanguage.DOTNET_SDK)}>

<SdkSetupHeader lang={SupportedLanguage.DOTNET_SDK} />

</TabItem>
{/* <TabItem value={SupportedLanguage.PYTHON_SDK} label={languageLabelMap.get(SupportedLanguage.PYTHON_SDK)}>
<SdkSetupHeader lang={SupportedLanguage.PYTHON_SDK} />
</TabItem> */}
<TabItem value={SupportedLanguage.JAVA_SDK} label={languageLabelMap.get(SupportedLanguage.JAVA_SDK)}>

<SdkSetupHeader lang={SupportedLanguage.JAVA_SDK} />

</TabItem>
<TabItem value={SupportedLanguage.CLI} label={languageLabelMap.get(SupportedLanguage.CLI)}>

<SdkSetupHeader lang={SupportedLanguage.CLI} />

</TabItem>
<TabItem value={SupportedLanguage.CURL} label={languageLabelMap.get(SupportedLanguage.CURL)}>

To obtain the [access token](https://auth0.com/docs/get-started/authentication-and-authorization-flow/call-your-api-using-the-client-credentials-flow):

<SdkSetupHeader lang={SupportedLanguage.CURL} />

</TabItem>
</Tabs>

### 02. Calling List Users API

To return all users of type `user` that have have the `reader` relationship with `document:planning`:

<ListUsersRequestViewer
authorizationModelId="01HVMMBCMGZNT3SED4Z17ECXCA"
objectType="document"
objectId="planning"
relation="reader"
userFilterType="user"
expectedResults={{
users: [{ object: { type: "user:anne" } }, { object: { type: "user:beth" } }],
excluded_users: []
}}
skipSetup={true}
allowedLanguages={[
SupportedLanguage.JS_SDK,
SupportedLanguage.GO_SDK,
SupportedLanguage.DOTNET_SDK,
SupportedLanguage.JAVA_SDK,
SupportedLanguage.CLI,
SupportedLanguage.CURL,
]}
/>

The result `user:anne` and `user:beth` are the `user` objects that have the `reader` relationship with `document:planning`.

:::caution Warning
The performance characteristics of the ListUsers endpoint vary drastically depending on the model complexity, number of tuples, and the relations it needs to evaluate. Relations with 'and' or 'but not' are particularly expensive to evaluate.
:::

## Related Sections

<RelatedSection
description="Take a look at the following section for more on how to perform list users in your system"
relatedLinks={[
{
title: '{ProductName} List Users API',
description: 'Read the List Users API documentation and see how it works.',
link: '/api/service#Relationship%20Queries/ListUsers',
},
]}
/>
63 changes: 63 additions & 0 deletions docs/content/modeling/testing-models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Tests have the following structure:
|`tuples` | A set of tuples that are only considered for the test |
|`check` | A set of tests for Check calls, each with a user/object and a set of assertions |
|`list_objects` | A set of tests for ListObjects calls, each one with a user/type and a set of assertions for any number of relations|
|`list_users` | A set of tests for ListUsers calls, each one with an object and user filter and a set of assertions for the users and excluded_users for any number of relations |

## Write Check tests

Expand Down Expand Up @@ -144,6 +145,68 @@ The following verifies the expected results using the `list_objects` option in <
```
The example above checks that `user:anne` has access to the `organization:acme` as a member and is not an admin of any organization. It also checks that `user:peter`, given the current time is February 1st 2024, 0:10 AM, is not related to any organization as a member, but is related to `organization:acme` as an admin.

## Write List Users tests

:::caution Warning
ListUsers is currently in an experimental release. Read [the announcement](https://openfga.dev/blog/list-users-announcement) for more information.
:::

List users tests verify the results of the [list-users API](../getting-started/perform-list-users.mdx) to validate the users who or do not have access to an object

Each list users verification has the following structure:

| Object | Description |
| -------- | -------- |
|`object` | The object to list users for |
|`user_filter` | Specifies the type or userset to filter with, this must only contain one entry |
|`user_filter.type` | The specific type of results to return with response |
|`user_filter.relation` | The specific relation of results to return with response. Specify to return usersets (optional) |
|`context` | A set of tests for contextual parameters used to evaluate [conditions](./conditions.mdx)|
|`assertions` | A list of assertions to make |
|`<relation>` | The name of the relation you want to verify |
|`<relation>.users` | The users who should have the stated relation to the object |
|`<relation>.excluded_users` | The users who should have explicitly not have access to the object due to evaluations of type bound public access and negation (e.g. "all users except anne") |

In order to simplify test writing, the following syntax is supported for the various object types included in `users` and `excluded_users` from the API response:

* `<type>:<id>` to represent a userset that is a user
* `<type>:<id>#<relation>` to represent a userset that is a relation on a type
* `<type>:*` to represent a userset that is a type bound public access for a type

The following is an example of using the `list_users` option in <ProductName format={ProductNameFormat.ShortForm}/> tests:

```yaml
list_users:
- object: organization:acme
user_filter:
- type: user
assertions:
member:
users:
- user:anne
excluded_users: []
admin:
users: []
excluded_users: []
- object: organization:acme
user_filter:
- type: employee
context:
current_time : "2024-02-01T00:10:00Z"
assertions:
member:
users: []
excluded_users: []
admin:
users:
- employee:peter
excluded_users: []
```
The example above checks that `user:anne` has access to the `organization:acme` as a member and is not an admin of any organization. It also checks that `employee:peter`, given the current time is February 1st 2024, 0:10 AM, is not related to any organization as a member, but is related to `organization:acme` as an admin.


## Running tests

Tests are run using the `model test` CLI command. For instructions on installing the OpenFGA CLI, visit the [OpenFGA CLI Github repository](https://github.com/openfga/cli).
Expand Down
5 changes: 5 additions & 0 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ const sidebars = {
label: 'Perform a List Objects Request',
id: 'content/getting-started/perform-list-objects',
},
{
type: 'doc',
label: 'Perform a List Users Request',
id: 'content/getting-started/perform-list-users',
},
{
type: 'doc',
label: 'Use the FGA CLI',
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@easyops-cn/docusaurus-search-local": "0.40.1",
"@lottiefiles/react-lottie-player": "3.5.3",
"@openfga/frontend-utils": "^0.2.0-beta.9",
"@openfga/sdk": "^0.3.5",
"@openfga/sdk": "^0.4.0",
"@openfga/syntax-transformer": "^0.2.0-beta.17",
"assert-never": "1.2.1",
"clsx": "2.1.1",
Expand Down
Loading

0 comments on commit a004edd

Please sign in to comment.