-
Notifications
You must be signed in to change notification settings - Fork 85
feat: adding modeling best practices #1007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
74233c5
feat: adding modeling best practices
aaguiarz e01141f
Update docs/content/best-practices/modeling.mdx
aaguiarz e32ef0a
Merge branch 'main' into feat/dynamic-static
rhamzeh f8c4b00
chore: ignore auth0.com links
rhamzeh 6657c57
chore: minor improvements
aaguiarz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
--- | ||
title: Modeling Best Practices | ||
slug: /best-practices/modeling | ||
description: Best practices when creating FGA models | ||
sidebar_position: 1 | ||
--- | ||
import { | ||
ProductName, | ||
ProductNameFormat, | ||
RelatedSection, | ||
} from '@components/Docs'; | ||
|
||
# Modeling Best Practices | ||
|
||
## Dynamic vs Static types and relations | ||
|
||
In <ProductName format={ProductNameFormat.ShortForm}/> the authorization policies are defined both in the model and tuples. You can decide how much weight you want to each one. | ||
|
||
The model below can be used to implement authorization for any organization hierarchy, any resource hierarchy and any role hierarchy. | ||
|
||
|
||
```dsl.openfga | ||
model | ||
schema 1.1 | ||
|
||
type user | ||
|
||
type role | ||
relations | ||
define assignee: [user, role#assignee] | ||
|
||
type entity | ||
relations | ||
# Organizations can be hierarchical | ||
define parent : [entity] | ||
|
||
define editor : [role#assignee] or editor from parent | ||
define viewer : [role#assignee] or editor or viewer from parent | ||
|
||
type resource | ||
relations | ||
# Resources belong to an application | ||
define entity : [entity] | ||
|
||
# Resources can have a parent | ||
define parent: [resource] | ||
|
||
define editor : [role#assignee] or editor from entity or editor from parent | ||
define viewer : [role#assignee] or editor or viewer from parent | ||
``` | ||
|
||
We do not recommend this approach. Instead, you should create models that mimic as closely as possible the business logic of the application instead of generic models. The rule of thumb is that **if the end-user of the application can define certain entities/relations, then those should be represented in tuples. If not, it should be represented in the model**. | ||
|
||
For example, when defining roles, in general you have certain roles that come built-in with the application like an “admin” or a “billing manager”. The way you would model that is very simple: | ||
|
||
```dsl.openfga | ||
|
||
type user | ||
|
||
type entity | ||
relations | ||
define admin: [user] | ||
define billing_manager: [user] | ||
|
||
define can_manage_billing: admin or billing_manager | ||
define can_manage_users: admin | ||
``` | ||
|
||
Defining the model in a way that closely resembles your application domain has several advantages: | ||
|
||
- **Enhanced clarity and maintainability**: Authorization logic is easier to understand, debug, and maintain. By just reading the model, developers or security auditors can readily grasp the meaning of each type and relationship. | ||
|
||
- **Better performance**: Models with more specific types and flatter hierarchies often lead to better performance. This is because <ProductName format={ProductNameFormat.ShortForm}/> can more efficiently process queries involving well-defined types and relationships compared to navigating complex, high-cardinality recursive relationships within a single generic type. | ||
|
||
- **Leveraging the model's flexibility**: <ProductName format={ProductNameFormat.ShortForm}/>'s modeling language is designed to be adaptable. You can define numerous distinct types and relationships without significant overhead. Model changes rarely require data migrations, allowing you to evolve your model as your application grows. <ProductName format={ProductNameFormat.ShortForm}/> is already a generic authorization engine, you don't need to build another one on top of it. | ||
|
||
- **Improved Collaboration**: The resource types that are owned by each application team can be maintained in independent [modules](./../modeling/modular-models.mdx). You can then control which application can write to specific resource types. For example, when using the API credential issued to a Document Management application, you can only write/delete tuples for documents and folders, but not for other types. This is not possible if you use a generic `resource` type, for example. | ||
|
||
## Custom Roles | ||
|
||
However, in some applications, you will want end-users to define their own roles. In that case, you do need a role type: | ||
|
||
```dsl.openfga | ||
model | ||
schema 1.1 | ||
type user | ||
|
||
type role | ||
relations | ||
define assignee: [user] | ||
|
||
type entity | ||
relations | ||
define admin: [user] | ||
define billing_manager: [user] | ||
|
||
define can_manage_billing: [role#assignee] or admin or billing_manager | ||
define can_manage_users: [role#assignee] or admin | ||
``` | ||
|
||
In this example we are combining static roles with dynamic roles. This is the recommended way of doing it. Do not always use generic roles. Define static roles for the ones you already know, and implement generic roles if your application needs them. Adding additional static roles is very easy, you just need to add a relation in the model, and it does not happen often. | ||
|
||
You can see a full example of implementing custom roles [here](https://github.com/openfga/sample-stores/tree/main/stores/custom-roles). | ||
aaguiarz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Organizational Hierarchies | ||
|
||
If you are building a B2B SaaS application, you might encounter the following requirements: | ||
|
||
- You need the employees of your organization to access customer data for help desk scenarios, or as super-admin | ||
aaguiarz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- Your customers have a hierarchical organization structure that you'll need to represent. | ||
|
||
A possible way of modeling this would be with a recursive entity type: | ||
|
||
```dsl.openfga | ||
model | ||
schema 1.1 | ||
|
||
type user | ||
type entity | ||
relations | ||
define parent: [entity] | ||
define admin: [user] or admin from parent | ||
define member: [user] or member from parent | ||
``` | ||
|
||
You can use this construct to solve both problems. You define a root entity to represent your company, and then each entity can have a set of child entities that will inherit permissions. However, it always has a recursive relation that is more expensive to evaluate, and it does not fully represent the two different hierarchies you need. | ||
|
||
If have the requirement of allowing the employees of your company to access the system, we recommend to use the model below. | ||
|
||
```dsl.openfga | ||
model | ||
schema 1.1 | ||
|
||
type user | ||
type system | ||
relations | ||
define admin: [user] | ||
|
||
type entity | ||
relations | ||
define admin: [user] or admin from system | ||
define member: [user] | ||
``` | ||
This defines a hierarchy that is not recursive, which is faster to evaluate, and is easier to understand the model intent. You can see a full example of this scenario [here](https://github.com/openfga/sample-stores/blob/main/stores/superadmin). | ||
|
||
If you also have the requirement of supporting hierarchical organizations, you can add that when you need it: | ||
|
||
```dsl.openfga | ||
model | ||
schema 1.1 | ||
|
||
type user | ||
type system | ||
relations | ||
define admin: [user] | ||
|
||
type entity | ||
rhamzeh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
relations | ||
define admin: [user] or admin from system | ||
define member: [user] | ||
``` | ||
In this case, it's clear that you have two hierarchies, one is recursive and the other is not. | ||
|
||
## Related Sections | ||
|
||
<RelatedSection | ||
description="Check out these related resources for more information about adopting OpenFGA" | ||
relatedLinks={[ | ||
{ | ||
title: 'Modular Authorization Models', | ||
description: 'Learn how to break down your authorization model into modules.', | ||
link: './../modeling/modular-models', | ||
} | ||
]} | ||
/> | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.