|
| 1 | +--- |
| 2 | +title: Modeling roles |
| 3 | +slug: /best-practices/modeling-roles |
| 4 | +description: Various ways of modeling static and dynamic roles in FGA - both coarse and fine-grained. |
| 5 | +sidebar_position: 1 |
| 6 | +--- |
| 7 | +import { |
| 8 | + ProductName, |
| 9 | + ProductNameFormat, |
| 10 | + RelatedSection, |
| 11 | +} from '@components/Docs'; |
| 12 | + |
| 13 | +# Modeling Roles |
| 14 | + |
| 15 | +Roles are a common way to group users and assign permissions to those groups. They can be used to simplify permission management, especially in larger systems where many users have similar access needs. |
| 16 | + |
| 17 | +In this guide, we'll explore common approaches to modeling roles with <ProductName format={ProductNameFormat.ShortForm}/>. |
| 18 | + |
| 19 | +## When to Use Each Approach |
| 20 | + |
| 21 | +Before diving into implementation details, here's a quick guide to help you choose the right approach: |
| 22 | + |
| 23 | +| Approach | Best For | Complexity | Flexibility | Example |
| 24 | +|-------------------------------|------------------------------------|------------|-------------|--------------------------------------------------------------------------------------------------------------------| |
| 25 | +| **Relations as Roles** | Static, predefined roles | Low | Low | In all instances, company admins can view project information. | |
| 26 | +| **Simple User-Defined Roles** | User-defined roles at org level | Medium | Medium | Company Acme creates an "Auditor" role that is configured to view project information for all projects. | |
| 27 | +| **Role Assignments** | Instance-specific role assignments | High | High | In Company Acme, Anne can be a custom Auditor role for Projects 1 and 5, but Beth can be an Auditor on Project 3. | |
| 28 | + |
| 29 | +## Approach 1: Relations as Roles |
| 30 | + |
| 31 | +The simplest way to implement roles is to use directly assignable relations. They work well for roles that always exist and can be defined at development-time. Adding relations is straightforward, and you do not need to add roles very frequently. If roles are static, this is always the preferred approach. |
| 32 | + |
| 33 | +### Example: Organization Admin Role |
| 34 | + |
| 35 | +In the model below, we define an `admin` role at the organization level. Admins can edit billing details and create projects. |
| 36 | + |
| 37 | +```dsl.openfga |
| 38 | +model |
| 39 | + schema 1.1 |
| 40 | +
|
| 41 | +type user |
| 42 | +
|
| 43 | +type organization |
| 44 | + relations |
| 45 | + define admin: [user] |
| 46 | + define can_create_project: admin |
| 47 | + define can_edit_billing_details: admin |
| 48 | +``` |
| 49 | + |
| 50 | +### Adding Users to Roles |
| 51 | + |
| 52 | +To add users to the admin role, create a tuple like: |
| 53 | + |
| 54 | +```yaml |
| 55 | +- user: user:anne |
| 56 | + relation: admin |
| 57 | + object: organization:acme |
| 58 | +``` |
| 59 | +
|
| 60 | +### Extending with Additional Roles |
| 61 | +
|
| 62 | +If later you need to add a `project_admin` role with permissions to view/edit projects, the model evolves to: |
| 63 | + |
| 64 | +```dsl.openfga |
| 65 | +model |
| 66 | + schema 1.1 |
| 67 | +
|
| 68 | +type user |
| 69 | +
|
| 70 | +type organization |
| 71 | + relations |
| 72 | + define admin: [user] |
| 73 | + define project_admin: [user] # new role |
| 74 | +
|
| 75 | + # existing permissions |
| 76 | + define can_edit_billing_details: admin |
| 77 | + define can_create_project: admin or project_admin |
| 78 | +
|
| 79 | + # new permissions for project admins |
| 80 | + define can_edit_project: admin or project_admin |
| 81 | + define can_view_project: admin or project_admin |
| 82 | +``` |
| 83 | + |
| 84 | +### Pros and Cons |
| 85 | + |
| 86 | +**✅ Advantages:** |
| 87 | +- Simple to implement and understand |
| 88 | +- Fast evaluation performance |
| 89 | +- Clear authorization policies |
| 90 | +- No additional tuples needed when adding permissions |
| 91 | +- Role permissions are straightforward to change, regardless of scale |
| 92 | + |
| 93 | +**❌ Disadvantages:** |
| 94 | +- Roles must be predefined in the model |
| 95 | +- Not suitable for user-defined roles |
| 96 | + |
| 97 | +--- |
| 98 | + |
| 99 | +## Approach 2: Simple User-Defined Roles |
| 100 | + |
| 101 | +Many applications require the flexibility for end-users to define their own custom roles, in addition to any pre-defined roles. This approach enables organizations to tailor permissions to their specific needs. |
| 102 | + |
| 103 | +### Example: Custom Project Admin Role |
| 104 | + |
| 105 | +With the following model, your application can support both static roles and user-defined roles: |
| 106 | + |
| 107 | +```dsl.openfga |
| 108 | +model |
| 109 | + schema 1.1 |
| 110 | +
|
| 111 | +type user |
| 112 | +
|
| 113 | +type role |
| 114 | + relations |
| 115 | + define assignee: [user] |
| 116 | +
|
| 117 | +type organization |
| 118 | + relations |
| 119 | + define admin: [user] # static role |
| 120 | +
|
| 121 | + # permissions can be assigned to custom roles or static roles |
| 122 | + define can_create_project: [role#assignee] or admin |
| 123 | + define can_edit_project: [role#assignee] or admin |
| 124 | +``` |
| 125 | + |
| 126 | +### Setting Up Custom Roles |
| 127 | + |
| 128 | +1. **Define role permissions** by creating tuples that grant the role-specific permissions: |
| 129 | + |
| 130 | +```yaml |
| 131 | +- user: role:acme-project-admin#assignee |
| 132 | + relation: can_create_project |
| 133 | + object: organization:acme |
| 134 | +
|
| 135 | +- user: role:acme-project-admin#assignee |
| 136 | + relation: can_edit_project |
| 137 | + object: organization:acme |
| 138 | +``` |
| 139 | + |
| 140 | +2. **Assign users to the role**: |
| 141 | + |
| 142 | +```yaml |
| 143 | +- user: user:anne |
| 144 | + relation: assignee |
| 145 | + object: role:acme-project-admin |
| 146 | +``` |
| 147 | + |
| 148 | +### Adding New Permissions |
| 149 | + |
| 150 | +When you add new permissions to your model, existing roles don't automatically receive them: |
| 151 | + |
| 152 | +```dsl.openfga |
| 153 | +model |
| 154 | + schema 1.1 |
| 155 | +
|
| 156 | +type user |
| 157 | +
|
| 158 | +type role |
| 159 | + relations |
| 160 | + define assignee: [user] |
| 161 | +
|
| 162 | +type organization |
| 163 | + relations |
| 164 | + define admin: [user] |
| 165 | + define can_create_project: [role#assignee] or admin |
| 166 | + define can_edit_project: [role#assignee] or admin |
| 167 | + define can_delete_project: [role#assignee] or admin # new permission |
| 168 | +``` |
| 169 | + |
| 170 | +To grant the new permission to existing roles, create additional tuples: |
| 171 | + |
| 172 | +```yaml |
| 173 | +- user: role:acme-project-admin#assignee |
| 174 | + relation: can_delete_project |
| 175 | + object: organization:acme |
| 176 | +``` |
| 177 | + |
| 178 | +You do not need to add these tuples when adding the new permission. End-users will add the new permission to their custom roles when they find it appropriate. |
| 179 | + |
| 180 | +### Pros and Cons |
| 181 | + |
| 182 | +**✅ Advantages:** |
| 183 | +- Supports user-defined roles |
| 184 | +- Flexible permission assignment |
| 185 | +- No model changes needed for new role instances |
| 186 | + |
| 187 | +**❌ Disadvantages:** |
| 188 | +- More complex than static relations |
| 189 | +- Requires additional tuples for role-permission mapping |
| 190 | + |
| 191 | +--- |
| 192 | + |
| 193 | +## Approach 3: Role Assignments |
| 194 | + |
| 195 | +The previous approach works well when custom roles are global for the organization. However, if you need roles that can be attached to different object instances with different members for each instance, you need role assignments. |
| 196 | + |
| 197 | +### Example: Project-Specific Admin Roles |
| 198 | + |
| 199 | +Let's say you want a "Project Admin" role where each project can have different admins, but the role permissions remain consistent. |
| 200 | + |
| 201 | +### Step 1: Define the Role and its Permissions |
| 202 | + |
| 203 | +Define a `role` type where you list all the permissions that any role can have: |
| 204 | + |
| 205 | +```dsl.openfga |
| 206 | +model |
| 207 | + schema 1.1 |
| 208 | +
|
| 209 | +type role |
| 210 | + relations |
| 211 | + define can_view_project: [user:*] |
| 212 | + define can_edit_project: [user:*] |
| 213 | +``` |
| 214 | + |
| 215 | +A "Project Admin" role can have `can_view_project` and `can_edit_project`: |
| 216 | + |
| 217 | +```yaml |
| 218 | +# Project Admin role has both the can_view_project and can_edit_project assigned |
| 219 | +- user: user:* |
| 220 | + relation: can_view_project |
| 221 | + object: role:project-admin |
| 222 | +
|
| 223 | +- user: user:* |
| 224 | + relation: can_edit_project |
| 225 | + object: role:project-admin |
| 226 | +``` |
| 227 | + |
| 228 | +### Step 2: Assign Users to a Role on an Entity |
| 229 | + |
| 230 | +Add a `role_assignment` type to assign users to the role: |
| 231 | + |
| 232 | +```dsl.openfga |
| 233 | +type role_assignment |
| 234 | + relations |
| 235 | + define assignee: [user] |
| 236 | + define role: [role] |
| 237 | +
|
| 238 | + define can_view_project: assignee and can_view_project from role |
| 239 | + define can_edit_project: assignee and can_edit_project from role |
| 240 | +``` |
| 241 | + |
| 242 | +### Step 3: Connect to Your Objects |
| 243 | + |
| 244 | +Define an `organization` type with an `admin` role. Then, define a `project` type that links to an `organization` and a `role_assignment`. Note that we are combining a static `admin` role with custom role assignments. We recommend to always use static roles when they are known in advance. |
| 245 | + |
| 246 | +```dsl.openfga |
| 247 | +type organization |
| 248 | + relations |
| 249 | + define admin: [user] |
| 250 | +
|
| 251 | +type project |
| 252 | + relations |
| 253 | + define organization: [organization] |
| 254 | + define role_assignment: [role_assignment] |
| 255 | + |
| 256 | + # combine role assignments and static roles |
| 257 | + define can_edit_project: can_edit_project from role_assignment or admin from organization |
| 258 | + define can_view_project: can_view_project from role_assignment or admin from organization |
| 259 | +``` |
| 260 | + |
| 261 | +### Setting Up Role Assignments |
| 262 | + |
| 263 | +1. **Create the role assignment instance**: |
| 264 | + |
| 265 | +```yaml |
| 266 | +- user: user:anne |
| 267 | + relation: assignee |
| 268 | + object: role_assignment:project-admin-openfga |
| 269 | +
|
| 270 | +- user: role:project-admin |
| 271 | + relation: role |
| 272 | + object: role_assignment:project-admin-openfga |
| 273 | +``` |
| 274 | + |
| 275 | +2. **Link the role assignment to the project**: |
| 276 | + |
| 277 | +```yaml |
| 278 | +- user: role_assignment:project-admin-openfga |
| 279 | + relation: role_assignment |
| 280 | + object: project:openfga |
| 281 | +``` |
| 282 | +3. **Link the project to an organization**: |
| 283 | + |
| 284 | +```yaml |
| 285 | +- user: organization:acme |
| 286 | + relation: organization |
| 287 | + object: project:openfga |
| 288 | +``` |
| 289 | +### Pros and Cons |
| 290 | + |
| 291 | +**✅ Advantages:** |
| 292 | +- Maximum flexibility for instance-specific roles |
| 293 | +- Reusable role definitions across different objects |
| 294 | +- Fine-grained control over role membership |
| 295 | + |
| 296 | +**❌ Disadvantages:** |
| 297 | +- Most complex approach to implement |
| 298 | +- Requires careful planning of the role hierarchy |
| 299 | +- More tuples needed for setup and maintenance |
| 300 | + |
| 301 | +--- |
| 302 | + |
| 303 | +## Choosing the Right Approach |
| 304 | + |
| 305 | +### Decision Tree |
| 306 | + |
| 307 | +1. **Do you need user-defined roles?** |
| 308 | + - No → Use **Relations as Roles** |
| 309 | + - Yes → Continue to step 2 |
| 310 | + |
| 311 | +2. **Do roles need different members per object instance?** |
| 312 | + - No → Use **Simple User-Defined Roles** |
| 313 | + - Yes → Use **Role Assignments** |
| 314 | + |
| 315 | +### Performance Considerations |
| 316 | + |
| 317 | +- **Relations as Roles**: Fastest evaluation |
| 318 | +- ***Simple User-Defined Roles**: Moderate performance impact |
| 319 | +- **Role Assignments**: Highest performance impact |
| 320 | + |
| 321 | +## Best Practices |
| 322 | + |
| 323 | +1. **Start simple**: Begin with relations as roles and evolve as needed |
| 324 | +2. **Hybrid approach**: Combine static relations for well-known roles with dynamic roles for custom ones |
| 325 | +3. **Documentation**: Clearly document your role model for your team |
| 326 | +4. **Functional Testing**: [Write tests](../modeling/testing-models.mdx) to verify your model behaves as expected |
| 327 | +5. **Performance Testing**: Test performance with realistic data volumes |
| 328 | + |
| 329 | +## Related Sections |
| 330 | + |
| 331 | +<RelatedSection |
| 332 | + description="Check out these related resources for more information about adopting OpenFGA." |
| 333 | + relatedLinks={[ |
| 334 | + { |
| 335 | + title: 'Custom Roles Step by Step', |
| 336 | + description: 'Follow a detailed walkthrough of implementing custom roles.', |
| 337 | + link: '../modeling/custom-roles', |
| 338 | + }, |
| 339 | + { |
| 340 | + title: 'Multi-tenant RBAC Example', |
| 341 | + description: 'See a complete multi-tenant role-based access control implementation.', |
| 342 | + link: 'https://github.com/openfga/sample-stores/blob/main/stores/multitenant-rbac', |
| 343 | + }, |
| 344 | + { |
| 345 | + title: 'Role Assignments Example', |
| 346 | + description: 'Explore a full role assignments implementation.', |
| 347 | + link: 'https://github.com/openfga/sample-stores/tree/main/stores/role-assignments', |
| 348 | + } |
| 349 | + ]} |
| 350 | +/> |
0 commit comments