diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a9c38c7..3c888d1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,571 +1,34 @@ - +# OpenFGA Sample Stores -# **Authoring OpenFGA Models** +This repository contains sample authorization models for [OpenFGA](https://openfga.dev). -This guide provides a comprehensive overview of authoring OpenFGA authorization models. It covers core concepts, the modeling language, relationship definitions, and testing methodologies, drawing insights from the openfga/sample-stores repository for practical examples. +## OpenFGA Modeling Guidelines -## **1. Introduction to OpenFGA and Authorization Modeling** +When authoring, reviewing, or refactoring OpenFGA models, follow the best practices +from the [OpenFGA Best Practices Skill](https://github.com/openfga/agent-skills). -OpenFGA is an open-source authorization solution that empowers developers to implement fine-grained access control within their applications through an intuitive modeling language. +The full modeling guidelines are available at: +https://raw.githubusercontent.com/openfga/agent-skills/main/skills/openfga/AGENTS.md -It functions as a flexible authorization engine, simplifying the process of defining application permissions. +Key principles: +- Define all relevant entity types, and always declare `schema 1.1`. +- Define relations on resource/object types, not on `user`. +- Keep model and tuple data separate: model defines possible relationships, tuples define actual facts. +- Use explicit direct relations for assignable roles, with clear type restrictions. +- Use concentric inheritance (`or`) so higher-privilege roles imply lower-privilege roles. +- Define permissions as `can_` relations, and check those in application code. +- Do not directly assign users to `can_*` relations; assign roles, then derive permissions from roles. +- For shared role logic across `can_*`, order from most restrictive to least restrictive (for example `can_delete` -> `can_edit` -> `can_view`). +- Use `X from Y` for parent-child inheritance to avoid duplicating tuples and role logic. +- Propagate relevant parent roles to child resources through computed relations when children need them. +- Put create permissions on parent/container types (for example `can_create_`), not on not-yet-existing child objects. +- Use usersets (`type#relation`) for group-based access. +- Use wildcards (`type:*`) only for intentional global/public or boolean-flag-like behavior, and avoid over-broad grants. +- Follow consistent naming: snake_case relations and `can_` permission prefixes. +- Test every model change by running the tests `fga model test --tests store.fga.yaml`. -Inspired by Google's Zanzibar paper, OpenFGA primarily champions Relationship-Based Access Control (ReBAC), while also effectively addressing use cases for Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC). Its "developer-first" philosophy is evident in its Domain Specific Language (DSL) and supporting tools, which lower the barrier to entry for developers. +For the complete set of rules, install the skill: -The core purpose of an authorization model is to define a system's permission structure, answering questions like, "Can user U perform action A on object O?". By externalizing authorization logic from application code, OpenFGA provides a robust mechanism for managing complex access policies, especially in large-scale systems. The modeling language is designed to be powerful for engineers yet accessible to other team stakeholders, fostering collaborative policy development. - -## **2. OpenFGA Core Concepts: The Building Blocks of Your Model** - -To effectively model authorization in OpenFGA, it is essential to understand its core building blocks: - -* **Authorization Model:** A static blueprint that combines one or more type definitions to precisely define the permission structure of a system. It represents the - *possible* relations between users and objects. Models are immutable; each modification creates a new version with a unique ID. Models aren't expected change often - only when new product features or change in functionality is introduced. They're also generally expected to be backward compatible, but can break backward compatibility once the system has completely moved off the older relations in it. -* **Type:** A string that defines a class of objects sharing similar characteristics (e.g., user, document, folder, organization, team, repo). -* **Object:** A specific instance of a defined Type (e.g., `document:roadmap`, `user:anne`, `organization:acme`). An object's relationships are defined through relationship tuples and the authorization model. -* **User:** An entity that can be related to an object. A user can be a specific individual (e.g., user:anne), a wildcard representing everyone of a certain type (e.g. `user:*` which means all users), or a userset (e.g., `team:product#member`), which denotes a group of users. -* **Relation:** A string defined within a type definition in the authorization model. It specifies the *possibility* of a relationship existing between an object of that type and a user. Relation names are arbitrary (e.g., owner, editor, viewer, member, admin), but must be defined on the object type in the model. -* **Relationship Tuple:** Dynamic data elements representing the *facts* about relationships between users and objects (e.g., {"user": "user:anne", "relation": "editor", "object": "document:new-roadmap"}).3 Without these, authorization checks will fail, as the model only defines what is *possible*, not what *currently exists*. - -The clear separation between the static authorization model (schema) and dynamic relationship tuples (data) is a fundamental design principle. This enables efficient permission evaluation and decouples core logic changes from specific user permission modifications. The immutability of models supports robust versioning, allowing for controlled rollouts and managing complex migrations. - -The following table summarizes these core concepts: - -| Concept | Description | Example | -| :---- | :---- | :---- | -| **Type** | A class of objects that share similar characteristics. | user, document, folder, organization | -| **Object** | A specific instance of a defined type, an entity in the system. | `user:anne`, `document:report_2023`, `folder:marketing_docs` | -| **User** | An entity that can be related to an object. Can be a specific object, a wildcard, or a userset. | `user:bob`, `user:*` (everyone), `team:product#member` | -| **Relation** | A string defined within a type definition that specifies the possibility of a relationship between an object of that type and a user. | owner, editor, viewer, member, admin | -| **Relationship Tuple** | A grouping of a user, a relation, and an object, representing a factual relationship in the system. | `{"user": "user:anne", "relation": "viewer", "object": "document:roadmap"}` | -| **Authorization Model** | A static definition combining type definitions to define the entire permission structure of a system. | ```type document relations define viewer: [user]``` | - -## **3. The OpenFGA Modeling Language: DSL** - -OpenFGA's Configuration Language is fundamental to constructing a system's authorization model, informing OpenFGA about object types and their relationships. It describes all possible relations for an object of a given type and the conditions under which one entity is related to that object. The language is primarily expressed in DSL (Domain Specific Language). - -### **DSL: Developer-Friendly Syntax for Readability** - -The DSL provides syntactic sugar over the underlying JSON, designed for ease of use and improved readability.10 It is the preferred syntax for developers using the Playground, CLI, and IDE extensions (like Visual Studio Code), offering features like syntax highlighting and validation.5 DSL models are compiled to JSON before being sent to OpenFGA's API.5 - -An example of an OpenFGA model in DSL: - -```dsl.openfga -model - schema 1.1 -type user -type document - relations - define viewer: [user] or editor - define editor: [user] -``` - -## **4 Defining Relationships: Crafting Your Authorization Logic** - -OpenFGA provides a rich set of constructs for defining relationships, enabling the modeling of complex authorization policies. - -### **Direct Relationships: Explicit Access Grants** - -A direct relationship is established when a specific relationship tuple (e.g., user=X, relation=R, object=Y) is explicitly stored. The authorization model must explicitly permit this through direct relationship type restrictions. These restrictions define which types of users can be directly associated with an object for a given relation, using formats like - -`[`], `[]`, or `[#]`. - -For example, - -``` -define owner: [user] -``` - -means only individual users can be directly assigned as owners. - -A tuple like: - -``` -{"user": "user:anne", "relation": "owner", "object": "document:1"} -``` - -is a direct relationship. - -### **Concentric Relationships: Inheriting Permissions** - -Concentric relationships represent nested or implied relations, where one relation automatically confers another (e.g., "all editors are viewers").This is implemented using the or keyword within a relation definition. - -For example, - -``` -define viewer: [user] or editor -``` - -means a user is a viewer if directly assigned OR if they are an editor. `user:anne` is an editor of `document:new-roadmap`, she implicitly has viewer access, reducing the number of required tuples. - -### **Indirect Relationships with 'X from Y': Scalable Hierarchical and Group-Based Access** - -The X from Y syntax is crucial for scalability, allowing a user to acquire a relation (X) to an object through an intermediary object (Y) and a defined relation on Y. This avoids individual tuple creation for every permission, enabling higher-level abstraction. It is highly effective for hierarchical and group-based access control. For example, - -``` -define admin: [user] or repo_admin from organization -``` - -on a repo type means a user is an admin if directly assigned, or if they have the repo_admin relation to an org that owns the repo. - -This simplifies management; revoking access can be done by deleting a single tuple linking the intermediary. - -### **Contextual Authorization with Conditions: Dynamic Permissions** - -Conditions introduce dynamic, contextual authorization. A condition is a function using Google's Common Expression Language (CEL), with parameters and a boolean expression. - -Example: -``` -condition less_than_hundred(x: int) { - x < 100 -} -``` - -Conditions are required to be defined at the end of the model (after the type definitions), and are instantiated using conditional relationship tuples. - -### **Leveraging Usersets for Group-Based Access Control** - -A userset represents a set or collection of users, denoted by object#relation (e.g., `company:xy#employee`). Usersets are fundamental for assigning permissions to groups, reducing tuple count and providing flexibility for bulk access management. They can be used in direct relationship type restrictions, such as: - -``` -define editor: [user, team#member] -``` - -OpenFGA computes implied relationships based on userset membership, and usersets are integral to defining complex access rules involving union, intersection, or exclusion of groups. - -Note that specifying `team#member` means "all members from a specific team". It does not mean that "you need to be a team member to be an editor". Only use it when you need to assign a relation to a set of users from specific object. - -OpenFGA's strength lies in its capacity to construct complex authorization logic from foundational elements: direct relationships, concentric relationships (or), and indirect relationships (X from Y). This compositional approach 10 enables modeling intricate real-world permission structures efficiently. - -The following table summarizes the key relationship definition patterns in OpenFGA: - -| Pattern Name | Description | DSL Syntax Example | Explanation of Effect | -| :---- | :---- | :---- | :---- | -| **Direct Relationship** | Explicitly grants a user a relation to an object via a stored tuple, subject to type restrictions. | `define owner: [user]` | Only individual users can be directly assigned as owner. | -| **Concentric Relationship** | Defines that having one relation implies having another relation to the same object (e.g., editors are viewers). | `define viewer: [user] or editor` | A user is a viewer if directly assigned as viewer OR if they are an editor. | -| **Indirect Relationship ('X from Y')** | A user gains a relation (X) to an object through another object (Y) and a specific relation on Y. | `define admin: [user] or repo_admin from owner`| A user is admin of a repo if directly assigned OR if they are repo_admin of an org that owns the repo. | -| **Conditional Relationship** | A relationship is permissible only if a specified condition, evaluated at runtime, is true. | `define admin: [user with non_expired_grant]` | A user is admin only if the non_expired_grant condition evaluates to true for their context. | -| **Usersets** | Represents a collection of users (e.g., a group or a set of users related by a specific relation). | `define editor: [user, team#member]` | An editor can be a direct user OR any member of a specified team. | - -## **5. Step-by-Step: Authoring Your First OpenFGA Model** - -The process of authoring an OpenFGA model is iterative, starting with critical features and systematically translating authorization requirements into a structured model. - -The central question guiding this process is: "Why could user U perform an action A on object O?" This encourages a relational perspective. - -The iterative nature of model design, including "Test the model" and "Iterate" steps, is fundamental. Dedicated testing tools like the CLI, Playground, and .fga.yaml configuration support this workflow, enabling rapid feedback and refinement. - -The recommended steps for defining an authorization model are: - -1. **Pick the most important feature:** Focus on a high-priority use case to establish a foundational model. -2. **List the object types:** Identify all relevant entities (e.g., user, document, folder, organization). -3. **List relations for those types:** For each type, determine relationships users or other objects can have (e.g., owner, editor, viewer, member). -4. **Define relations:** Translate these relationships into OpenFGA DSL, specifying direct, concentric, and indirect relationships as needed. -5. **Test the model:** Validate your model against expected behaviors using assertions and comprehensive test cases. -6. **Iterate:** Refine the model based on testing and evolving requirements. - -### **Illustrative Example: Building a Document Management Authorization Model** - -Consider a document management system with these requirements: - -* Documents can be created by users. -* Documents can be shared, granting editor or viewer roles. -* A document's creator inherently has delete, share, edit, and view permissions. -* Editors can edit and view. -* Viewers can only view. -* Documents can belong to folders, with permissions inherited from the parent. - -Let's walk through the modeling process: - -#### Step 1: Identify Types - -Core types: user, document, folder. An organization type can represent groups. - -#### Step 2: Define Relations for organization - -An organization can define membership: - -```dsl.openfga -type organization - relations - define member: [user] # Users can be members. -``` - -#### Step 3: Define Relations for document -The document type defines ownership, editing, viewing, sharing, and deleting: - -```dsl.openfga -type document - relations - define organization : [organization] # A document belongs to an organization. - define parent_folder: [folder] # A document can have a parent folder. - - define owner: [user] # A direct user - define editor: [user] or owner or editor from parent_folder # A direct user or editor or anyone who is an owner. - define viewer: [user] or editor or viewer from parent_folder or member from organization # A direct user, or anyone who is an editor. - define can_share: owner # Example of a permission: owner - define can_delete: owner or editor # Example of a permission: owner or editor -``` - -Step 4: Consider Folder Inheritance (Parent-Child Objects) - -Permissions can be inherited from parent folders. This requires defining relations on the folder type and using X from Y to propagate permissions: - -```dsl.openfga -type folder - relations - define parent_folder: [folder] - define owner: [user] - define editor: [user] or owner or editor from parent_folder - define viewer: [user] or editor or viewer from parent_folder -``` - -In this extension, an editor of a document could be someone directly assigned, an owner, or an editor of its parent_folder, leveraging X from Y for hierarchical permissions. - -## **6. Adding permissions** - -It's a common pratice to define specific permissions, that can't be directly assigned, using `can_` relations, for example: - -```dsl.openfga -type folder - relations - define parent_folder: [folder] - define owner: [user] - define editor: [user] or owner or editor from parent_folder - define viewer: [user] or editor or viewer from parent_folder - - define can_view: viewer - define can_edit: editor - define can_delete: editor - define can_share: owner -``` - -Always define permissions in the authorization models. - -## **7. Testing and Validating Your OpenFGA Models** - -Thorough testing and validation are indispensable. OpenFGA provides tools for rapid prototyping and automated testing to ensure your authorization model is correctly designed before deployment. - -### **OpenFGA CLI: Command-Line Model Management and Testing** - -The OpenFGA CLI serves as a cross-platform command-line tool for interacting with an OpenFGA server. It provides a robust set of commands for various tasks, including: - -* Reading, writing, validating, and transforming authorization models. -* Running tests on an authorization model, which is crucial for verifying its correctness. -* Managing OpenFGA stores, encompassing operations such as creation, listing, retrieval, deletion, import, and export. - -To execute tests defined within a .fga.yaml file, the fga model test command is utilized: `fga model test --tests .fga.yaml`. - -The CLI can be installed in different ways: - -- Using Homebrew for MacOS: `brew install openfga/tap/fga` -- Debian: `sudo apt install ./fga__linux_.deb` -- Fedora: `sudo dnf install ./fga__linux_.rpm` -- Alpine Linux: `sudo apk add --allow-untrusted ./fga__linux_.apk` -- Windows via Scoop: scoop install openfga - -It can be run using Docker: - - docker pull openfga/cli; docker run -it openfga/cli - -### **Automated Testing with .fga.yaml** - -The .fga.yaml file is central to defining and testing OpenFGA authorization models, providing a structured approach for validation. This file can include: - -* `name` (optional): A descriptive name for the test file. -* `model` or `model_file`: The authorization model can be defined inline or referenced from an external .fga, .json, or .mod file. -* `tuples` or `tuple_file` or `tuple_files` (optional): Relationship tuples can be defined inline or referenced from external JSON, JSONL, YAML, or CSV files, and are considered for all tests. - -#### Defining the Model and Tuples in .fga.yaml - -You can define your authorization model and relationship tuples directly within the .fga.yaml file. This allows for a self-contained test definition. -Example of defining a model and tuples inline: - - -```yaml -name: Model Tests # optional -model: | - model - schema 1.1 - type user - type organization - relations - define member : [user] - define admin : [user with non_expired_grant] - condition non_expired_grant(current_time: timestamp, grant_time: timestamp, grant_duration: duration) { - current_time < grant_time + grant_duration - } -tuples: # Inline tuple definitions go here - # Anne is a member of the Acme organization - - user: user:anne - relation: member - object: organization:acme - # Peter has the admin role from February 2nd 2024 0AM to 1AM - - user: user:peter - relation: admin - object: organization:acme - condition: - name: non_expired_grant - context: - grant_time : "2024-02-01T00:00:00Z" - grant_duration : 1h - -``` - -#### Writing Tests in .fga.yaml - -The tests section within the .fga.yaml file is where you define specific test cases for OpenFGA API calls, including check, list_objects, and list_users. - -* **Check Tests:** Verify whether a user U has a relation R with an object O. These tests include the user, object, context (for evaluating conditions), and assertions (pairs of relation: expected-result). - - Example of Check tests -```yaml - tests: - - name: Test - check: - - user: user:peter - object: organization:acme - context: - current_time : "2024-02-01T00:10:00Z" - assertions: - member: false - admin: true -``` - -* **List Objects Tests:** Validate the expected results when querying which objects a user has a specific relation with (e.g., user:anne is a member of organization:acme). These tests include the user, type of object, context, and assertions. Example of List Objects tests: - -```yaml - list_objects: - - user: user:anne - type: organization - assertions: - member: - - organization:acme - admin: - - - user: user:peter - type: organization - context: - current_time : "2024-02-01T00:10:00Z" - assertions: - member: - admin: - - organization:acme -``` - -* **List Users Tests:** Confirm which users possess access to a given object. These tests specify the object, a user_filter (by type or userset), context, and assertions for the users for any number of relations. The users field within assertions supports specific syntax for different userset types: `:` for a user, `:#` for a relation on a type, and `:*` for public access. - -Example of List Users tests: -```yaml - list_users: - - object: organization:acme - user_filter: - - type: user - context: - current_time : "2024-02-02T00:10:00Z" - assertions: - member: - users: - - user:anne - admin: - users: -``` - -.fga.yaml format actively promotes a test-driven development methodology for authorization logic. - -## **8. Modeling Custom Roles** - -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. - -Only consider this if you are asked to implement custom roles. - -### Simple User-Defined Roles - -With the following model, your application can support both static roles and user-defined roles, with user-defined roles at the top_level object (in this case 'organization'). - -```dsl.openfga -model - schema 1.1 - -type user - -type role - relations - define assignee: [user] - -type organization - relations - define admin: [user] # static role - - # permissions can be assigned to custom roles or static roles - define can_create_projectexport: [role#assignee] or admin - define can_edit_project: [role#assignee] or admin -``` - -#### Setting Up Custom Roles - -1. **Define role permissions** by creating tuples that grant the role-specific permissions: - -```yaml -- user: role:acme-project-admin#assignee - relation: can_create_project - object: organization:acme - -- user: role:acme-project-admin#assignee - relation: can_edit_project - object: organization:acme -``` - -2. **Assign users to the role**: - -```yaml -- user: user:anne - relation: assignee - object: role:acme-project-admin -``` - -#### Adding New Permissions - -When you add new permissions to your model, existing roles don't automatically receive them: - -```dsl.openfga -model - schema 1.1 - -type user - -type role - relations - define assignee: [user] - -type organization - relations - define admin: [user] - define can_create_project: [role#assignee] or admin - define can_edit_project: [role#assignee] or admin - define can_delete_project: [role#assignee] or admin # new permission -``` - -To grant the new permission to existing roles, create additional tuples: - -```yaml -- user: role:acme-project-admin#assignee - relation: can_delete_project - object: organization:acme -``` - -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. - -### Custom roles with role Assignments - -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. DO NOT USE this approach for defining custom roles for the top-level type (e.g. 'organization'). - -#### Example: Project-Specific Admin Roles - -Let's say you want a "Project Admin" role where each project can have different admins, but the role permissions remain consistent. - -##### Step 1: Define the Role and its Permissions - -Define a `role` type where you list all the permissions that any role can have: - -```dsl.openfga -model - schema 1.1 - -type role - relations - define can_view_project: [user:*] - define can_edit_project: [user:*] -``` - -A "Project Admin" role can have `can_view_project` and `can_edit_project`: - -```yaml -# Project Admin role has both the can_view_project and can_edit_project assigned -- user: user:* - relation: can_view_project - object: role:project-admin - -- user: user:* - relation: can_edit_project - object: role:project-admin +```sh +npx skills add openfga/agent-skills ``` - -##### Step 2: Assign Users to a Role on an Entity - -Add a `role_assignment` type to assign users to the role: - -```dsl.openfga -type role_assignment - relations - define assignee: [user] - define role: [role] - - define can_view_project: assignee and can_view_project from role - define can_edit_project: assignee and can_edit_project from role -``` - -##### Step 3: Connect to Your Objects - -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. - -```dsl.openfga -type organization - relations - define admin: [user] - -type project - relations - define organization: [organization] - define role_assignment: [role_assignment] - - # combine role assignments and static roles - define can_edit_project: can_edit_project from role_assignment or admin from organization - define can_view_project: can_view_project from role_assignment or admin from organization -``` - -#### Setting Up Role Assignments - -1. **Create the role assignment instance**: - -```yaml -- user: user:anne - relation: assignee - object: role_assignment:project-admin-openfga - -- user: role:project-admin - relation: role - object: role_assignment:project-admin-openfga -``` - -2. **Link the role assignment to the project**: - -```yaml -- user: role_assignment:project-admin-openfga - relation: role_assignment - object: project:openfga -``` -3. **Link the project to an organization**: - -```yaml -- user: organization:acme - relation: organization - object: project:openfga -``` ---- - -### **When to Use Each Custom Role Pattern** - -| Pattern | Use Case | Pros | Cons | -|---------|----------|------|------| -| **Global Custom Roles** | Organization-wide roles with consistent permissions | Simple, efficient | Less flexible for per-resource customization | -| **Role Assignments** | Resource-specific roles with different members per resource | Highly flexible | More complex, potential performance impact | - -### **Migration Strategies** -When evolving from static roles to custom roles: -1. **Additive approach**: Introduce custom roles alongside existing static roles -2. **Gradual migration**: Move permissions one at a time to custom roles -3. **Backwards compatibility**: Maintain existing static role behavior during transition - -## **9. Simplify Models** - -After generating model and tests, remove from the model all types and relations that are not referenced in the model or the tests. - diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..558fd97 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,14 @@ +# OpenFGA Sample Stores + +When working with OpenFGA models in this repository, install the OpenFGA skill: + +```sh +npx skills add openfga/agent-skills +``` + +Use `fga model test --tests store.fga.yaml` to validate models. + +Run `fga model test --tests "stores/**/store.fga.yaml"` to test all models in the repository. + + + diff --git a/README.md b/README.md index 3738476..03c6010 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,113 @@ # OpenFGA Sample Stores [![Join our community](https://img.shields.io/badge/slack-cncf_%23openfga-40abb8.svg?logo=slack)](https://openfga.dev/community) -[![Twitter](https://img.shields.io/twitter/follow/openfga?color=%23179CF0&logo=twitter&style=flat-square "@openfga on Twitter")](https://x.com/openfga) +[![Twitter](https://img.shields.io/x/follow/openfga?color=%23179CF0&logo=twitter&style=flat-square "@openfga on X")](https://x.com/openfga) -This repository contains [sample store models](#sample-stores) for [OpenFGA](https://openfga.dev). +This repository contains sample store models for [OpenFGA](https://openfga.dev). ## Table of Contents -- [Table of Contents](#table-of-contents) -- [Sample Store Models](#sample-store-models) +- [Authorization Patterns](#authorization-patterns) +- [OpenFGA Features](#openfga-features) +- [Industry Examples](#industry-examples) +- [OpenFGA Models in Open Source Projects](#openfga-models-in-open-source-projects) +- [Authoring Models using Coding Assistants](#authoring-models-using-coding-assistants) - [Creating your store and loading sample data](#creating-your-store-and-loading-sample-data) -- [OpenFGA models in open source projects](#openfga-models-in-open-source-projects) - [Modeling Resources](#modeling-resources) -## Sample Store Models - -- [Modeling ABAC with ReBAC](./stores/abac-with-rebac) -- [Advanced Entitlements](./stores/advanced-entitlements) -- [Banking](./stores/banking) -- [Condition Data Types](./stores/condition-data-types) -- [Custom Roles](./stores/custom-roles) -- [Developer Portal](./stores/developer-portal) -- [Entitlements](./stores/entitlements) -- [Expenses](./stores/expenses) -- [GitHub](./stores/github) -- [Google Drive](./stores/gdrive) -- [Group Resource Attributes](./stores/groups-resource-attributes) -- [IoT](./stores/iot) -- [IP-Based Access](./stores/ip-based-access) -- [Modeling Guide](./stores/modeling-guide) -- [Modeling Multi-Tenant Role-Based Access Control](./stores/multitenant-rbac) -- [Modular Models](./stores/modular) -- [Role Assignments](./stores/role-assignments) -- [Slack](./stores/slack) -- [Super-Admin](./stores/superadmin) -- [Temporal Access](./stores/temporal-access) +## Authorization Patterns + +Examples that demonstrate how to model authorization for well-known products and common patterns. + +| Example | Description | +|---------|-------------| +| [Modeling Guide](./stores/modeling-guide) | Step-by-step introduction to OpenFGA modeling | +| [Multi-Tenant RBAC](./stores/multitenant-rbac) | Multi-tenant role-based access control | +| [GitHub](./stores/github) | Repository, organization, and team permissions | +| [Google Drive](./stores/gdrive) | File, folder, and shared drive permissions | +| [Entitlements](./stores/entitlements) | Feature entitlements and plan-based access | +| [Slack](./stores/slack) | Workspace, channel, and messaging permissions | +| [Role Assignments](./stores/role-assignments) | Role assignment patterns for resource-specific roles | +| [Temporal Access](./stores/temporal-access) | Time-limited access grants with expiration | +| [Super-Admin](./stores/superadmin) | Super-admin override patterns | + +## OpenFGA Features + +Examples that demonstrate specific OpenFGA features and capabilities. + +| Example | Description | +|---------|-------------| +| [Modeling ABAC with ReBAC](./stores/abac-with-rebac) | Combine attribute-based and relationship-based access control | +| [Condition Data Types](./stores/condition-data-types) | CEL condition expressions with different data types | +| [Custom Roles](./stores/custom-roles) | User-defined roles with dynamic permission assignment | +| [Advanced Entitlements](./stores/advanced-entitlements) | Complex entitlement and feature-gating patterns | +| [Group Resource Attributes](./stores/groups-resource-attributes) | Group-based access with resource attributes | +| [IP-Based Access](./stores/ip-based-access) | Network-based access restrictions using conditions | +| [Modular Models](./stores/modular) | Splitting models into modules for team collaboration | + +## Industry Examples + +Full authorization models for different verticals and industries. Each includes a model, sample tuples, tests, and a README explaining the use case. + +| Example | Description | +|---------|-------------| +| [Accounting](./stores/accounting) | Charts of accounts, invoices, expenses, payments, journal entries | +| [Advertising](./stores/ads) | Campaigns, ad groups, ads, creatives, reports | +| [Applicant Tracking](./stores/applicant-tracking-system) | Jobs, candidates, applications, interviews, offers | +| [Banking](./stores/banking) | Accounts, transactions, and financial operations | +| [Calendar](./stores/calendar) | Calendars, events, scheduling links, recordings, webinars | +| [Call Center](./stores/call-center) | Calls, contacts, comments, recordings | +| [Chat & Messaging](./stores/chat) | Conversations, messages, groups, membership | +| [CRM](./stores/crm) | Accounts, contacts, leads, opportunities, pipeline | +| [Developer Portal](./stores/developer-portal) | API keys, applications, and developer access | +| [E-Commerce](./stores/ecommerce) | Stores, products, customers, orders, reviews | +| [Expenses](./stores/expenses) | Expense reports, approvals, and reimbursements | +| [File Storage](./stores/file-storage) | Drives, folders, files with hierarchical permissions | +| [Healthcare](./stores/healthcare) | Patients, encounters, diagnoses, treatments, medications | +| [Hospitality](./stores/hospitality) | Hotels, rooms, reservations, guest services | +| [Human Resources](./stores/human-resources) | Employees, teams, payroll, benefits, time-off | +| [IoT](./stores/iot) | Device management and telemetry access | +| [Issue Tracking](./stores/issue-tracking) | Collections, tickets, comments, attachments | +| [Knowledge Base](./stores/knowledge-base) | Containers, articles, attachments, public content | +| [Knowledge Management](./stores/kms) | Spaces, pages, comments with publishing workflow | +| [Learning Management](./stores/lms) | Courses, classes, content, activities, grading | +| [Manufacturing](./stores/manufacturing) | Production lines, machines, work orders, quality reports | +| [Payment](./stores/payment) | Payments, payouts, refunds, subscriptions | +| [Real Estate](./stores/real-estate) | Properties, listings, transactions, inspections | ## Authoring Models using Coding Assistants -This repository includes a [copilot-instructions](.github/copilot-instructions.md) file that can help you author OpenFGA models. It can be used in VS Code or in any AI-assisted IDE. You can also use it as a prompt, or system prompt, in any LLM. +AI coding assistants can help you author OpenFGA models. The modeling guidelines are maintained in the [OpenFGA Best Practices Skill](https://github.com/openfga/agent-skills). Some prompts you can try: - Create an OpenFGA authorization model for an insurance company. - Create an OpenFGA authorization model for B2B SaaS project management system. -- Create an OpenFGA authorization model for (e.g. create a model for Figma). +- Create an OpenFGA authorization model for \ (e.g. create a model for Figma). It will create a model in an `.fga` file, and a `.fga.yaml` with tuples/tests, and use the [CLI](https://github.com/openfga/cli) to run the tests. -To use it in Visual Studio Code: -- Setup [VS Code with Copilot](https://code.visualstudio.com/docs/copilot/setup-simplified) -- Create a new folder, with a .github folder, and copy the [copilot-instructions](.github/copilot-instructions.md) file. -- Open Copilot -- Prompt +### Claude Code -Other coding assistants look for other file names (CLAUDE.md, AGENTS.md, Cursor rules). +Install the skill, then prompt Claude Code with a modeling request: -You can also use the [DeepWiki MCP](https://docs.devin.ai/work-with-devin/deepwiki-mcp) or the [Context7 MCP](https://github.com/upstash/context7) to provide AI agents with OpenFGA context to help you implement OpenFGA using different SDKs. +```sh +npx skills add openfga/agent-skills +``` -For modeling assistance, we you use [copilot-instructions](.github/copilot-instructions.md) even if you configure an MCP. +Example prompt: `/openfga Create an OpenFGA authorization model for an insurance company.` + +### GitHub Copilot + +Guidance is auto-loaded from [`.github/copilot-instructions.md`](.github/copilot-instructions.md) when you open this repository in VS Code with [Copilot](https://code.visualstudio.com/docs/copilot/setup-simplified) enabled. + +### Other Tools (Cursor, generic LLMs) + +Reference the full guidelines from `openfga/agent-skills`: +- **AGENTS.md:** https://raw.githubusercontent.com/openfga/agent-skills/main/skills/openfga/AGENTS.md +- **Cursor rules:** Copy AGENTS.md content into `.cursorrules` + +You can also use the [DeepWiki MCP](https://docs.devin.ai/work-with-devin/deepwiki-mcp) or the [Context7 MCP](https://github.com/upstash/context7) to provide AI agents with OpenFGA context to help you implement OpenFGA using different SDKs. ## Creating your store and loading sample data @@ -77,7 +126,7 @@ To try this out, you need the following tools installed: fga model test --tests "stores/${SAMPLE_STORE}/store.fga.yaml" ``` -## OpenFGA models in open source projects +## OpenFGA Models in Open Source Projects - [Linux Foundation](https://github.com/linuxfoundation/lfx-v2-helm/blob/main/charts/lfx-platform/templates/openfga/model.yaml) - [canonical/lxd](https://github.com/canonical/lxd/blob/main/lxd/auth/drivers/openfga_model.openfga) @@ -101,8 +150,3 @@ If you are using OpenFGA in your open source project, please let us know by open ## Modeling Resources - [Modeling Guide](https://www.youtube.com/watch?v=5Lwy9aHXXHE&list=PLUR5l-oTFZqWaDdhEOVt_IfPOIbKo1Ypt) - [OpenFGA Documentation](https://openfga.dev/docs/modeling) -- [Zanzibar Academy](https://zanzibar.academy) -- [Implementing Multi-Tenancy in Chroma: Part 2 - Authorization Model with OpenFGA](https://cookbook.chromadb.dev/strategies/multi-tenancy/authorization-model-with-openfga/) -- [Introduction into OpenFGA](https://www.ericksegaar.com/2024/03/04/introduction-into-openfga/) - - diff --git a/sample-stores.code-workspace b/sample-stores.code-workspace new file mode 100644 index 0000000..ba4febf --- /dev/null +++ b/sample-stores.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../agent-skills" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/stores/accounting/README.md b/stores/accounting/README.md new file mode 100644 index 0000000..58b6db3 --- /dev/null +++ b/stores/accounting/README.md @@ -0,0 +1,25 @@ +# OpenFGA for Accounting Systems + +## Use Case + +This model represents the authorization needs of an **accounting platform**, like QuickBooks Online, Xero, or FreshBooks. These platforms manage charts of accounts, invoices, expenses, payments, journal entries, and financial statements. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own chart of accounts, contacts, and financial records. +- **Parent-scoped creation permissions**: Account and payment creation are checked on the organization (`can_create_account`, `can_create_payment`) rather than on account or payment objects that do not exist yet. +- **Separation of duties**: Three distinct roles — `admin` (full control + approvals), `accountant` (day-to-day bookkeeping), and `auditor` (read-only access to all financial data). Regular members have no access to accounting data. +- **Read-only audit access**: Auditors can view all financial data (accounts, invoices, expenses, payments, journal entries, financial statements) but cannot modify anything, supporting compliance and external audit requirements. +- **Expense workflow**: Employees submit expenses they can view and edit; accountants and admins can approve them. +- **Purchase order approval chain**: Requesters create and edit purchase orders, designated approvers authorize them, and admins have override approval authority. +- **Journal entry posting**: Accountants can create and edit journal entries, but only admins can post them to the general ledger, preventing unauthorized modifications to the books. +- **Invoice lifecycle**: Creators can manage their invoices, but voiding requires admin authority. +- **Financial statement protection**: Balance sheets, income statements, and cash flow statements are read-only resources accessible only to accountants, admins, and auditors. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `accounting` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/accounting/model.fga b/stores/accounting/model.fga new file mode 100644 index 0000000..90a1c61 --- /dev/null +++ b/stores/accounting/model.fga @@ -0,0 +1,73 @@ +model + schema 1.1 + +type user + +type organization + relations + define admin: [user] + define accountant: [user] or admin + define auditor: [user] or accountant + define member: [user] or auditor + define can_create_account: accountant + define can_create_payment: accountant + +type account + relations + define organization: [organization] + define can_edit: accountant from organization + define can_view: auditor from organization or can_edit + +type contact + relations + define organization: [organization] + define owner: [user] + define can_edit: owner or accountant from organization + define can_view: can_edit + +type invoice + relations + define organization: [organization] + define creator: [user] + define contact: [contact] + define can_void: admin from organization + define can_approve: can_void + define can_edit: creator or accountant from organization or can_approve + define can_view: owner from contact or auditor from organization or can_edit + +type expense + relations + define organization: [organization] + define submitter: [user] + define can_approve: accountant from organization + define can_edit: submitter or can_approve + define can_view: auditor from organization or can_edit + +type payment + relations + define organization: [organization] + define can_edit: accountant from organization + define can_view: auditor from organization or can_edit + +type journal_entry + relations + define organization: [organization] + define creator: [user] + define can_post: admin from organization + define can_edit: creator or accountant from organization or can_post + define can_view: auditor from organization or can_edit + +type purchase_order + relations + define organization: [organization] + define requester: [user] + define approver: [user] + define can_approve: approver or admin from organization + define can_edit: requester or can_approve or accountant from organization + define can_view: can_edit + +type financial_statement + relations + define organization: [organization] + define can_edit: admin from organization + define can_view: auditor from organization or can_edit diff --git a/stores/accounting/store.fga.yaml b/stores/accounting/store.fga.yaml new file mode 100644 index 0000000..e17c296 --- /dev/null +++ b/stores/accounting/store.fga.yaml @@ -0,0 +1,301 @@ +name: Accounting Authorization Model Tests +model_file: model.fga + +tuples: + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: accountant + object: organization:acme + - user: user:charlie + relation: auditor + object: organization:acme + - user: user:diana + relation: member + object: organization:acme + + - user: organization:acme + relation: organization + object: account:revenue + - user: organization:acme + relation: organization + object: contact:vendor-abc + - user: user:diana + relation: owner + object: contact:vendor-abc + + - user: organization:acme + relation: organization + object: invoice:inv-001 + - user: user:bob + relation: creator + object: invoice:inv-001 + + - user: organization:acme + relation: organization + object: expense:exp-001 + - user: user:diana + relation: submitter + object: expense:exp-001 + + - user: organization:acme + relation: organization + object: payment:pay-001 + - user: organization:acme + relation: organization + object: journal_entry:je-001 + - user: user:bob + relation: creator + object: journal_entry:je-001 + + - user: organization:acme + relation: organization + object: purchase_order:po-001 + - user: user:diana + relation: requester + object: purchase_order:po-001 + - user: user:bob + relation: approver + object: purchase_order:po-001 + + - user: organization:acme + relation: organization + object: financial_statement:balance-sheet-q1 + +tests: + - name: Admin has full accounting access + check: + - user: user:alice + object: organization:acme + assertions: + can_create_account: true + can_create_payment: true + - user: user:alice + object: account:revenue + assertions: + can_view: true + can_edit: true + - user: user:alice + object: invoice:inv-001 + assertions: + can_view: true + can_edit: true + can_approve: true + can_void: true + + - name: Accountant can manage books + check: + - user: user:bob + object: organization:acme + assertions: + can_create_account: true + can_create_payment: true + - user: user:bob + object: account:revenue + assertions: + can_view: true + can_edit: true + - user: user:bob + object: invoice:inv-001 + assertions: + can_view: true + can_edit: true + can_approve: false + + - name: Auditor has read-only access + check: + - user: user:charlie + object: organization:acme + assertions: + can_create_account: false + can_create_payment: false + - user: user:charlie + object: account:revenue + assertions: + can_view: true + can_edit: false + - user: user:charlie + object: invoice:inv-001 + assertions: + can_view: true + can_edit: false + - user: user:charlie + object: financial_statement:balance-sheet-q1 + assertions: + can_view: true + + - name: Auditor can view but not edit expenses payments and journal entries + check: + - user: user:charlie + object: expense:exp-001 + assertions: + can_view: true + can_edit: false + - user: user:charlie + object: payment:pay-001 + assertions: + can_view: true + can_edit: false + - user: user:charlie + object: journal_entry:je-001 + assertions: + can_view: true + can_edit: false + can_post: false + + - name: Non-admin cannot void invoice + check: + - user: user:bob + object: invoice:inv-001 + assertions: + can_void: false + - user: user:charlie + object: invoice:inv-001 + assertions: + can_void: false + + - name: Regular member cannot view financial statements + check: + - user: user:diana + object: financial_statement:balance-sheet-q1 + assertions: + can_view: false + + - name: Regular member cannot view accounting + check: + - user: user:diana + object: organization:acme + assertions: + can_create_account: false + can_create_payment: false + - user: user:diana + object: account:revenue + assertions: + can_view: false + - user: user:diana + object: payment:pay-001 + assertions: + can_view: false + + - name: Expense submitter can view own expense + check: + - user: user:diana + object: expense:exp-001 + assertions: + can_view: true + can_edit: true + can_approve: false + + - name: Purchase order flow + check: + - user: user:diana + object: purchase_order:po-001 + assertions: + can_view: true + can_edit: true + can_approve: false + - user: user:bob + object: purchase_order:po-001 + assertions: + can_view: true + can_edit: true + can_approve: true + + - name: Journal entry permissions + check: + - user: user:bob + object: journal_entry:je-001 + assertions: + can_view: true + can_edit: true + can_post: false + - user: user:alice + object: journal_entry:je-001 + assertions: + can_view: true + can_edit: true + can_post: true + + - name: List objects for admin + list_objects: + - user: user:alice + type: account + assertions: + can_view: + - account:revenue + can_edit: + - account:revenue + - user: user:alice + type: invoice + assertions: + can_view: + - invoice:inv-001 + can_edit: + - invoice:inv-001 + + - name: List objects for accountant + list_objects: + - user: user:bob + type: invoice + assertions: + can_view: + - invoice:inv-001 + can_edit: + - invoice:inv-001 + - user: user:bob + type: purchase_order + assertions: + can_edit: + - purchase_order:po-001 + can_approve: + - purchase_order:po-001 + + - name: List objects for member + list_objects: + - user: user:diana + type: contact + assertions: + can_view: + - contact:vendor-abc + can_edit: + - contact:vendor-abc + - user: user:diana + type: expense + assertions: + can_view: + - expense:exp-001 + can_edit: + - expense:exp-001 + + - name: List users who can view invoice + list_users: + - object: invoice:inv-001 + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + + - name: List users who can manage purchase order + list_users: + - object: purchase_order:po-001 + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:diana + can_approve: + users: + - user:alice + - user:bob diff --git a/stores/ads/README.md b/stores/ads/README.md new file mode 100644 index 0000000..2b6788e --- /dev/null +++ b/stores/ads/README.md @@ -0,0 +1,25 @@ +# OpenFGA for Advertising Platforms + +## Use Case + +This model represents the authorization needs of an **advertising platform**, like Google Ads, Meta Ads, TikTok Ads, or LinkedIn Ads. These platforms manage campaigns, ad groups, ads, creatives, and performance reports across advertising accounts. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own advertising campaigns, creatives, and reporting data. +- **Advertising roles**: Organization-level roles (`admin`, `campaign_manager`, `analyst`) control access to ad resources. Admins have full access, campaign managers can create and edit campaigns, and analysts have read-only access. +- **Parent-scoped report creation**: Report creation is checked on the organization (`can_create_report`) instead of on report objects that do not exist yet. +- **Hierarchical ad editing**: Ad groups inherit campaign editing rights, and ads inherit ad-group editing rights, so ad-group owners and campaign editors can manage contained ads without repeating the same role list on each child type. +- **Ad approval workflow**: Campaign managers create ads, but only admins can approve them for publishing, ensuring quality control before ads go live. +- **Campaign publishing**: Only admins can publish campaigns (go live), preventing unauthorized changes to active advertising spend. +- **Analyst read-only access**: Analysts can view all campaigns, ads, creatives, and reports but cannot modify anything, enabling safe access to performance data. +- **Creative ownership**: Creators can view and edit their own creatives. Campaign managers have broader access across the organization. +- **Report access control**: Analysts and campaign managers can view reports, campaign managers can create reports, but only admins can delete them. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `ads` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/ads/model.fga b/stores/ads/model.fga new file mode 100644 index 0000000..145d2f4 --- /dev/null +++ b/stores/ads/model.fga @@ -0,0 +1,58 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or campaign_manager or analyst + define admin: [user] + define campaign_manager: [user] + define analyst: [user] + define can_create_report: campaign_manager or admin + +type campaign + relations + define organization: [organization] + define owner: [user] + define organization_admin: admin from organization + define organization_campaign_manager: campaign_manager from organization + define organization_analyst: analyst from organization + define can_delete: organization_admin + define can_publish: can_delete + define can_edit: owner or organization_campaign_manager or can_publish + define can_view: organization_analyst or can_edit + +type ad_group + relations + define campaign: [campaign] + define owner: [user] + define organization_admin: organization_admin from campaign + define organization_campaign_manager: organization_campaign_manager from campaign + define organization_analyst: organization_analyst from campaign + define can_delete: organization_admin + define can_edit: owner or can_edit from campaign + define can_view: organization_analyst or can_edit + +type ad + relations + define ad_group: [ad_group] + define creator: [user] + define can_delete: organization_admin from ad_group + define can_approve: can_delete + define can_edit: creator or can_edit from ad_group + define can_view: organization_analyst from ad_group or can_edit + +type creative + relations + define organization: [organization] + define creator: [user] + define can_delete: admin from organization + define can_edit: creator or campaign_manager from organization or can_delete + define can_view: analyst from organization or can_edit + +type report + relations + define organization: [organization] + define can_delete: admin from organization + define can_view: analyst from organization or campaign_manager from organization or can_delete diff --git a/stores/ads/store.fga.yaml b/stores/ads/store.fga.yaml new file mode 100644 index 0000000..0252b00 --- /dev/null +++ b/stores/ads/store.fga.yaml @@ -0,0 +1,360 @@ +name: Advertising Platform Authorization Model Tests +model_file: model.fga + +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:adtech + - user: user:bob + relation: campaign_manager + object: organization:adtech + - user: user:charlie + relation: analyst + object: organization:adtech + - user: user:diana + relation: member + object: organization:adtech + + # Campaign + - user: organization:adtech + relation: organization + object: campaign:summer-sale + - user: user:bob + relation: owner + object: campaign:summer-sale + + # Ad group linked to campaign + - user: campaign:summer-sale + relation: campaign + object: ad_group:us-targeting + - user: user:bob + relation: owner + object: ad_group:us-targeting + - user: user:erin + relation: owner + object: ad_group:us-targeting + + # Ad linked to ad group + - user: ad_group:us-targeting + relation: ad_group + object: ad:banner-001 + - user: user:bob + relation: creator + object: ad:banner-001 + + # Creative + - user: organization:adtech + relation: organization + object: creative:video-spot + - user: user:bob + relation: creator + object: creative:video-spot + + # Report + - user: organization:adtech + relation: organization + object: report:q1-performance + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: organization:adtech + assertions: + can_create_report: true + - user: user:alice + object: campaign:summer-sale + assertions: + can_view: true + can_edit: true + can_delete: true + can_publish: true + - user: user:alice + object: ad_group:us-targeting + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: ad:banner-001 + assertions: + can_view: true + can_edit: true + can_delete: true + can_approve: true + - user: user:alice + object: creative:video-spot + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: report:q1-performance + assertions: + can_view: true + can_delete: true + + - name: Campaign manager can create and edit but not publish or approve + check: + - user: user:bob + object: organization:adtech + assertions: + can_create_report: true + - user: user:bob + object: campaign:summer-sale + assertions: + can_view: true + can_edit: true + can_delete: false + can_publish: false + - user: user:bob + object: ad_group:us-targeting + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:bob + object: ad:banner-001 + assertions: + can_view: true + can_edit: true + can_delete: false + can_approve: false + - user: user:bob + object: creative:video-spot + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:bob + object: report:q1-performance + assertions: + can_view: true + can_delete: false + + - name: Analyst has read-only access + check: + - user: user:charlie + object: organization:adtech + assertions: + can_create_report: false + - user: user:charlie + object: campaign:summer-sale + assertions: + can_view: true + can_edit: false + can_delete: false + can_publish: false + - user: user:charlie + object: ad_group:us-targeting + assertions: + can_view: true + can_edit: false + can_delete: false + - user: user:charlie + object: ad:banner-001 + assertions: + can_view: true + can_edit: false + can_delete: false + can_approve: false + - user: user:charlie + object: creative:video-spot + assertions: + can_view: true + can_edit: false + can_delete: false + - user: user:charlie + object: report:q1-performance + assertions: + can_view: true + can_delete: false + + - name: Member has no access to ad resources + check: + - user: user:diana + object: organization:adtech + assertions: + can_create_report: false + - user: user:diana + object: campaign:summer-sale + assertions: + can_view: false + can_edit: false + can_delete: false + can_publish: false + - user: user:diana + object: ad_group:us-targeting + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: ad:banner-001 + assertions: + can_view: false + can_edit: false + can_delete: false + can_approve: false + - user: user:diana + object: creative:video-spot + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: report:q1-performance + assertions: + can_view: false + can_delete: false + + - name: Ad approval workflow - only admin can approve + check: + - user: user:alice + object: ad:banner-001 + assertions: + can_approve: true + - user: user:bob + object: ad:banner-001 + assertions: + can_approve: false + - user: user:charlie + object: ad:banner-001 + assertions: + can_approve: false + + - name: Ad group owner can edit ads in the group + check: + - user: user:erin + object: ad_group:us-targeting + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:erin + object: ad:banner-001 + assertions: + can_view: true + can_edit: true + can_delete: false + can_approve: false + + - name: Campaign publishing - only admin can publish + check: + - user: user:alice + object: campaign:summer-sale + assertions: + can_publish: true + - user: user:bob + object: campaign:summer-sale + assertions: + can_publish: false + - user: user:charlie + object: campaign:summer-sale + assertions: + can_publish: false + + - name: Creative ownership - creator can view and edit + check: + - user: user:bob + object: creative:video-spot + assertions: + can_view: true + can_edit: true + can_delete: false + + - name: List objects for campaign manager + list_objects: + - user: user:bob + type: campaign + assertions: + can_view: + - campaign:summer-sale + can_edit: + - campaign:summer-sale + - user: user:bob + type: ad + assertions: + can_view: + - ad:banner-001 + can_edit: + - ad:banner-001 + + - name: List objects for analyst + list_objects: + - user: user:charlie + type: campaign + assertions: + can_view: + - campaign:summer-sale + - user: user:charlie + type: creative + assertions: + can_view: + - creative:video-spot + + - name: List objects for ad group owner + list_objects: + - user: user:erin + type: ad_group + assertions: + can_view: + - ad_group:us-targeting + can_edit: + - ad_group:us-targeting + - user: user:erin + type: ad + assertions: + can_view: + - ad:banner-001 + can_edit: + - ad:banner-001 + + - name: List users who can view campaign + list_users: + - object: campaign:summer-sale + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + + - name: List users who can view ad + list_users: + - object: ad:banner-001 + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:erin + can_approve: + users: + - user:alice + + - name: List users who can view creative + list_users: + - object: creative:video-spot + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob diff --git a/stores/applicant-tracking-system/README.md b/stores/applicant-tracking-system/README.md new file mode 100644 index 0000000..ca177d7 --- /dev/null +++ b/stores/applicant-tracking-system/README.md @@ -0,0 +1,24 @@ +# OpenFGA for Applicant Tracking Systems + +## Use Case + +This model represents the authorization needs of an **Applicant Tracking System (ATS)**, like Greenhouse, Lever, or Workable. These platforms manage job postings, candidate pipelines, interviews, scorecards, and hiring workflows. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own jobs, candidates, and recruiting pipelines. +- **Recruiting roles**: Organization-level roles (`admin`, `recruiter`, `hiring_manager`) control access to the hiring pipeline. Admins have full access, recruiters manage candidates and applications, and hiring managers own specific jobs. +- **Job-scoped access**: Hiring managers and recruiters are assigned per job, controlling who can view applications, change interview stages, and close positions. +- **Candidate privacy**: Candidate profiles can be viewed and edited by organization recruiters and admins. Hiring managers do not directly access candidate profiles; they only access candidates through job-linked applications. +- **Interview workflow**: Organizers manage scheduled interviews, while interviewers can only view interviews in which they participate. +- **Scorecard confidentiality**: Scorecards are visible to the interviewer, department heads in the application's hiring chain, and organization admins. Other interviewers cannot see each other's feedback. +- **Offer management**: Offer creators can view and edit offers, but only admins can approve them, ensuring proper authorization in the hiring decision chain. +- **Department & office structure**: Departments and offices are viewable by all organization members, providing organizational context for job listings. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `applicant-tracking-system` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/applicant-tracking-system/model.fga b/stores/applicant-tracking-system/model.fga new file mode 100644 index 0000000..3bb476c --- /dev/null +++ b/stores/applicant-tracking-system/model.fga @@ -0,0 +1,82 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or recruiter or hiring_manager + define admin: [user] + define recruiter: [user] + define hiring_manager: [user] + +type department + relations + define organization: [organization] + define head: [user] + define organization_admin: admin from organization + define organization_recruiter: recruiter from organization + define can_manage: head or organization_admin + define can_view: member from organization or can_manage + +type office + relations + define organization: [organization] + define can_manage: admin from organization + define can_view: member from organization or can_manage + +type job + relations + define department: [department] + define hiring_manager: [user] + define recruiter: [user] + define organization_admin: organization_admin from department + define organization_recruiter: organization_recruiter from department + define department_head: head from department + define can_close: hiring_manager or organization_admin + define can_edit: recruiter or can_close + define can_view: organization_recruiter or department_head or can_edit + +type candidate + relations + define organization: [organization] + define subject: [user] + define can_delete: admin from organization + define can_edit: recruiter from organization or can_delete + define can_view: subject or can_edit + +type application + relations + define job: [job] + define candidate: [candidate] + define organization_admin: organization_admin from job + define organization_recruiter: organization_recruiter from job + define department_head: department_head from job + define can_edit: recruiter from job or organization_admin + define can_change_stage: recruiter from job or hiring_manager from job + define can_view: can_view from job or can_view from candidate + +type scheduled_interview + relations + define application: [application] + define organizer: [user] + define interviewer: [user] + define organization_admin: organization_admin from application + define department_head: department_head from application + define can_edit: organizer or organization_admin + define can_view: department_head or interviewer or can_edit + +type scorecard + relations + define interview: [scheduled_interview] + define interviewer: [user] + define can_edit: interviewer + define can_view: department_head from interview or organization_admin from interview or can_edit + +type offer + relations + define application: [application] + define creator: [user] + define can_approve: organization_admin from application + define can_edit: creator or can_approve + define can_view: department_head from application or organization_recruiter from application or can_edit diff --git a/stores/applicant-tracking-system/store.fga.yaml b/stores/applicant-tracking-system/store.fga.yaml new file mode 100644 index 0000000..385e90e --- /dev/null +++ b/stores/applicant-tracking-system/store.fga.yaml @@ -0,0 +1,346 @@ +name: Applicant Tracking System Authorization Model Tests +model_file: model.fga + +tuples: + # Organization + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: recruiter + object: organization:acme + - user: user:diana + relation: recruiter + object: organization:acme + - user: user:charlie + relation: hiring_manager + object: organization:acme + + # Department + - user: organization:acme + relation: organization + object: department:engineering + - user: user:charlie + relation: head + object: department:engineering + + # Office + - user: organization:acme + relation: organization + object: office:san-francisco + + # Job + - user: department:engineering + relation: department + object: job:swe-senior + - user: user:charlie + relation: hiring_manager + object: job:swe-senior + - user: user:bob + relation: recruiter + object: job:swe-senior + + # Candidate + - user: organization:acme + relation: organization + object: candidate:diana + - user: organization:acme + relation: organization + object: candidate:eve + - user: user:eve + relation: subject + object: candidate:eve + + # Application + - user: job:swe-senior + relation: job + object: application:diana-swe + - user: candidate:diana + relation: candidate + object: application:diana-swe + - user: job:swe-senior + relation: job + object: application:eve-swe + - user: candidate:eve + relation: candidate + object: application:eve-swe + + # Scheduled Interview + - user: application:diana-swe + relation: application + object: scheduled_interview:diana-onsite + - user: user:bob + relation: organizer + object: scheduled_interview:diana-onsite + - user: user:charlie + relation: interviewer + object: scheduled_interview:diana-onsite + + # Scorecard + - user: scheduled_interview:diana-onsite + relation: interview + object: scorecard:charlie-feedback + - user: user:charlie + relation: interviewer + object: scorecard:charlie-feedback + + # Offer + - user: application:diana-swe + relation: application + object: offer:diana-offer + - user: user:bob + relation: creator + object: offer:diana-offer + +tests: + - name: Admin has full access to jobs + check: + - user: user:alice + object: job:swe-senior + assertions: + can_view: true + can_edit: true + can_close: true + + - name: Hiring manager can view and close jobs + check: + - user: user:charlie + object: job:swe-senior + assertions: + can_view: true + can_edit: true + can_close: true + + - name: Org recruiter can view job without direct assignment + check: + - user: user:diana + object: job:swe-senior + assertions: + can_view: true + can_edit: false + can_close: false + + - name: Recruiter can view and edit candidates + check: + - user: user:bob + object: candidate:diana + assertions: + can_view: true + can_edit: true + can_delete: false + + - name: Recruiter can manage applications + check: + - user: user:bob + object: application:diana-swe + assertions: + can_view: true + can_edit: true + can_change_stage: true + + - name: Hiring manager can view application and change stage + check: + - user: user:charlie + object: application:diana-swe + assertions: + can_view: true + can_edit: false + can_change_stage: true + + - name: Candidate can view own profile and application + check: + - user: user:eve + object: candidate:eve + assertions: + can_view: true + can_edit: false + can_delete: false + - user: user:eve + object: application:eve-swe + assertions: + can_view: true + can_edit: false + can_change_stage: false + + - name: Interview permissions + check: + - user: user:charlie + object: scheduled_interview:diana-onsite + assertions: + can_view: true + can_edit: false + - user: user:bob + object: scheduled_interview:diana-onsite + assertions: + can_view: true + can_edit: true + + - name: Scorecard permissions + check: + - user: user:charlie + object: scorecard:charlie-feedback + assertions: + can_view: true + can_edit: true + - user: user:bob + object: scorecard:charlie-feedback + assertions: + can_view: false + can_edit: false + + - name: Offer permissions + check: + - user: user:bob + object: offer:diana-offer + assertions: + can_view: true + can_edit: true + can_approve: false + - user: user:alice + object: offer:diana-offer + assertions: + can_view: true + can_edit: true + can_approve: true + + - name: Hiring manager cannot directly view candidate + check: + - user: user:charlie + object: candidate:diana + assertions: + can_view: false + can_edit: false + + - name: Department visibility for org members + check: + - user: user:bob + object: department:engineering + assertions: + can_view: true + - user: user:charlie + object: department:engineering + assertions: + can_view: true + + - name: Office visibility + check: + - user: user:bob + object: office:san-francisco + assertions: + can_view: true + + - name: List objects for admin + list_objects: + - user: user:alice + type: job + assertions: + can_view: + - job:swe-senior + can_edit: + - job:swe-senior + - user: user:alice + type: candidate + assertions: + can_view: + - candidate:eve + - candidate:diana + can_edit: + - candidate:eve + - candidate:diana + + - name: List objects for recruiter + list_objects: + - user: user:bob + type: job + assertions: + can_view: + - job:swe-senior + can_edit: + - job:swe-senior + - user: user:bob + type: application + assertions: + can_view: + - application:eve-swe + - application:diana-swe + can_edit: + - application:eve-swe + - application:diana-swe + + - name: List objects for hiring manager + list_objects: + - user: user:charlie + type: job + assertions: + can_view: + - job:swe-senior + can_edit: + - job:swe-senior + - user: user:charlie + type: scheduled_interview + assertions: + can_view: + - scheduled_interview:diana-onsite + + - name: List objects for candidate subject + list_objects: + - user: user:eve + type: candidate + assertions: + can_view: + - candidate:eve + - user: user:eve + type: application + assertions: + can_view: + - application:eve-swe + + - name: List users who can view job + list_users: + - object: job:swe-senior + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + can_edit: + users: + - user:alice + - user:bob + - user:charlie + + - name: List users who can view application + list_users: + - object: application:diana-swe + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + can_change_stage: + users: + - user:bob + - user:charlie + - object: application:eve-swe + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + - user:eve + can_change_stage: + users: + - user:bob + - user:charlie diff --git a/stores/calendar/README.md b/stores/calendar/README.md new file mode 100644 index 0000000..1813fcb --- /dev/null +++ b/stores/calendar/README.md @@ -0,0 +1,23 @@ +# OpenFGA for Calendar Platforms + +## Use Case + +This model represents the authorization needs of a **Calendar platform**, like Google Calendar, Microsoft Outlook, or Calendly. These platforms manage calendars, events, scheduling links, recordings, and webinars across organizations. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own calendars, events, and scheduling resources. +- **Calendar roles**: Organization-level roles (`admin`, `scheduler`, `viewer`) control access. Admins have full access, schedulers can create and manage events, and viewers have read-only access. +- **Calendar ownership**: Calendar owners can view, edit, and share their calendars. Only admins can delete calendars. +- **Event management**: Event organizers can edit, delete, and invite attendees. Schedulers can manage events across the organization. Attendees get view access to events they are invited to. +- **Scheduling links**: Link owners and schedulers can manage booking pages. Viewers cannot access scheduling links. +- **Recording access**: Recordings are viewable by organization viewers, schedulers, and admins. Only admins can delete recordings. +- **Webinar publishing**: Organizers and admins can publish webinars. Viewers can see webinars but cannot modify or publish them. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `calendar` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/calendar/model.fga b/stores/calendar/model.fga new file mode 100644 index 0000000..9e05244 --- /dev/null +++ b/stores/calendar/model.fga @@ -0,0 +1,60 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or scheduler or viewer + define admin: [user] + define scheduler: [user] + define viewer: [user] + +type calendar + relations + define organization: [organization] + define owner: [user] + define organization_admin: admin from organization + define organization_scheduler: scheduler from organization + define organization_viewer: viewer from organization + define can_delete: organization_admin + define can_share: owner or can_delete + define can_edit: organization_scheduler or can_share + define can_create_event: can_edit + define can_view: organization_viewer or can_edit + +type event + relations + define calendar: [calendar] + define organizer: [user] + define attendee: [user] + define organization_admin: organization_admin from calendar + define organization_scheduler: organization_scheduler from calendar + define organization_viewer: organization_viewer from calendar + define can_delete: organizer or organization_admin + define can_invite: organization_scheduler or can_delete + define can_edit: can_invite + define can_view: attendee or organization_viewer or can_edit + +type link + relations + define organization: [organization] + define owner: [user] + define can_delete: owner or admin from organization + define can_edit: scheduler from organization or can_delete + define can_view: can_edit + +type recording + relations + define event: [event] + define can_delete: organization_admin from event + define can_view: organization_viewer from event or organization_scheduler from event or can_delete + +type webinar + relations + define organization: [organization] + define organizer: [user] + define can_delete: admin from organization + define can_publish: organizer or can_delete + define can_edit: scheduler from organization or can_publish + define can_view: viewer from organization or can_edit diff --git a/stores/calendar/store.fga.yaml b/stores/calendar/store.fga.yaml new file mode 100644 index 0000000..52daa48 --- /dev/null +++ b/stores/calendar/store.fga.yaml @@ -0,0 +1,293 @@ +name: Calendar Platform Authorization Model Tests +model_file: model.fga + +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: scheduler + object: organization:acme + - user: user:charlie + relation: viewer + object: organization:acme + - user: user:diana + relation: member + object: organization:acme + + # Calendar + - user: organization:acme + relation: organization + object: calendar:team-cal + - user: user:bob + relation: owner + object: calendar:team-cal + + # Event linked to calendar + - user: calendar:team-cal + relation: calendar + object: event:standup + - user: user:bob + relation: organizer + object: event:standup + - user: user:diana + relation: attendee + object: event:standup + + # Scheduling link + - user: organization:acme + relation: organization + object: link:booking-page + - user: user:bob + relation: owner + object: link:booking-page + + # Recording linked to event + - user: event:standup + relation: event + object: recording:standup-rec + + # Webinar + - user: organization:acme + relation: organization + object: webinar:product-launch + - user: user:bob + relation: organizer + object: webinar:product-launch + +tests: + - name: Admin has full access to all calendar resources + check: + - user: user:alice + object: calendar:team-cal + assertions: + can_view: true + can_edit: true + can_delete: true + can_share: true + - user: user:alice + object: event:standup + assertions: + can_view: true + can_edit: true + can_delete: true + can_invite: true + - user: user:alice + object: link:booking-page + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: recording:standup-rec + assertions: + can_view: true + can_delete: true + - user: user:alice + object: webinar:product-launch + assertions: + can_view: true + can_edit: true + can_delete: true + can_publish: true + + - name: Scheduler can manage events and links + check: + - user: user:bob + object: calendar:team-cal + assertions: + can_view: true + can_edit: true + can_delete: false + can_share: true + - user: user:bob + object: event:standup + assertions: + can_view: true + can_edit: true + can_delete: true + can_invite: true + - user: user:bob + object: link:booking-page + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:bob + object: recording:standup-rec + assertions: + can_view: true + can_delete: false + - user: user:bob + object: webinar:product-launch + assertions: + can_view: true + can_edit: true + can_delete: false + can_publish: true + + - name: Viewer has read-only access + check: + - user: user:charlie + object: calendar:team-cal + assertions: + can_view: true + can_edit: false + can_delete: false + can_share: false + - user: user:charlie + object: event:standup + assertions: + can_view: true + can_edit: false + can_delete: false + can_invite: false + - user: user:charlie + object: link:booking-page + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:charlie + object: recording:standup-rec + assertions: + can_view: true + can_delete: false + - user: user:charlie + object: webinar:product-launch + assertions: + can_view: true + can_edit: false + can_delete: false + can_publish: false + + - name: Member has no access except as event attendee + check: + - user: user:diana + object: calendar:team-cal + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: event:standup + assertions: + can_view: true + can_edit: false + can_delete: false + - user: user:diana + object: link:booking-page + assertions: + can_view: false + can_edit: false + - user: user:diana + object: recording:standup-rec + assertions: + can_view: false + can_delete: false + - user: user:diana + object: webinar:product-launch + assertions: + can_view: false + can_edit: false + can_publish: false + + - name: Event organizer can manage their events + check: + - user: user:bob + object: event:standup + assertions: + can_view: true + can_edit: true + can_delete: true + can_invite: true + + - name: Webinar publishing requires organizer or admin + check: + - user: user:alice + object: webinar:product-launch + assertions: + can_publish: true + - user: user:bob + object: webinar:product-launch + assertions: + can_publish: true + - user: user:charlie + object: webinar:product-launch + assertions: + can_publish: false + + - name: List calendars bob can access + list_objects: + - user: user:bob + type: calendar + assertions: + owner: + - calendar:team-cal + can_edit: + - calendar:team-cal + can_view: + - calendar:team-cal + + - name: List events alice can access + list_objects: + - user: user:alice + type: event + assertions: + can_view: + - event:standup + can_edit: + - event:standup + + - name: List links bob can access + list_objects: + - user: user:bob + type: link + assertions: + owner: + - link:booking-page + can_edit: + - link:booking-page + + - name: List webinars bob can access + list_objects: + - user: user:bob + type: webinar + assertions: + can_publish: + - webinar:product-launch + can_edit: + - webinar:product-launch + + - name: Users who can view calendar:team-cal + list_users: + - object: calendar:team-cal + user_filter: + - type: user + assertions: + owner: + users: + - user:bob + can_view: + users: + - user:alice + - user:bob + - user:charlie + + - name: Users who can view event:standup + list_users: + - object: event:standup + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + can_edit: + users: + - user:alice + - user:bob diff --git a/stores/call-center/README.md b/stores/call-center/README.md new file mode 100644 index 0000000..3d81c8d --- /dev/null +++ b/stores/call-center/README.md @@ -0,0 +1,22 @@ +# OpenFGA for Unified Communications Platforms + +## Use Case + +This model represents the authorization needs of a **call center software platform**, like RingCentral, Zoom Phone, 8x8, or Vonage. These platforms manage calls, contacts, call comments, and recordings across communication teams. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own call logs, contacts, and recordings. +- **Communication roles**: Organization-level roles (`admin`, `supervisor`, `agent`) control access. Admins have full access, supervisors can monitor and manage teams, and agents handle day-to-day communications. +- **Call visibility**: Call participants and agents can view call logs. Only admins can delete call records. +- **Contact management**: Agents can create and edit contacts. Supervisors and admins can delete contacts, providing oversight over the contact directory. +- **Comment authorship**: Comment authors can edit and delete their own call notes. Supervisors can edit any comment for quality assurance. Only authors and admins can delete comments. +- **Recording privacy**: Call recordings are restricted to supervisors and admins for compliance and quality monitoring. Agents cannot access recordings. Only admins can delete recordings. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `call-center` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/call-center/model.fga b/stores/call-center/model.fga new file mode 100644 index 0000000..c5d69ed --- /dev/null +++ b/stores/call-center/model.fga @@ -0,0 +1,47 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or supervisor or agent + define admin: [user] + define supervisor: [user] + define agent: [user] + +type call + relations + define organization: [organization] + define participant: [user] + define organization_admin: admin from organization + define organization_supervisor: supervisor from organization + define organization_agent: agent from organization + define can_delete: organization_admin + define can_edit: organization_supervisor or can_delete + define can_create_comment: can_view + define can_create_recording: can_edit + define can_view: participant or organization_agent or can_edit + +type contact + relations + define organization: [organization] + define owner: [user] + define can_delete: supervisor from organization or admin from organization + define can_edit: owner or agent from organization or can_delete + define can_view: can_edit + +type comment + relations + define call: [call] + define author: [user] + define can_delete: author or organization_admin from call + define can_edit: organization_supervisor from call or can_delete + define can_view: organization_agent from call or can_edit + +type recording + relations + define call: [call] + define can_delete: organization_admin from call + define can_edit: organization_supervisor from call or can_delete + define can_view: can_edit diff --git a/stores/call-center/store.fga.yaml b/stores/call-center/store.fga.yaml new file mode 100644 index 0000000..99fed59 --- /dev/null +++ b/stores/call-center/store.fga.yaml @@ -0,0 +1,234 @@ +name: Call Center Authorization Model Tests +model_file: model.fga + +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:callcenter + - user: user:bob + relation: supervisor + object: organization:callcenter + - user: user:charlie + relation: agent + object: organization:callcenter + - user: user:diana + relation: member + object: organization:callcenter + + # Call with participant + - user: organization:callcenter + relation: organization + object: call:call-001 + - user: user:charlie + relation: participant + object: call:call-001 + + # Contact + - user: organization:callcenter + relation: organization + object: contact:john-doe + - user: user:charlie + relation: owner + object: contact:john-doe + + # Comment on a call + - user: call:call-001 + relation: call + object: comment:note-001 + - user: user:charlie + relation: author + object: comment:note-001 + + # Recording of a call + - user: call:call-001 + relation: call + object: recording:rec-001 + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: call:call-001 + assertions: + can_view: true + can_delete: true + - user: user:alice + object: contact:john-doe + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: comment:note-001 + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: recording:rec-001 + assertions: + can_view: true + can_delete: true + + - name: Supervisor can view recordings and manage comments + check: + - user: user:bob + object: call:call-001 + assertions: + can_view: true + can_delete: false + - user: user:bob + object: contact:john-doe + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:bob + object: comment:note-001 + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:bob + object: recording:rec-001 + assertions: + can_view: true + can_delete: false + + - name: Agent can view calls and manage own contacts and comments + check: + - user: user:charlie + object: call:call-001 + assertions: + can_view: true + can_delete: false + - user: user:charlie + object: contact:john-doe + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:charlie + object: comment:note-001 + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:charlie + object: recording:rec-001 + assertions: + can_view: false + can_delete: false + + - name: Member has no access to resources + check: + - user: user:diana + object: call:call-001 + assertions: + can_view: false + can_delete: false + - user: user:diana + object: contact:john-doe + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: comment:note-001 + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: recording:rec-001 + assertions: + can_view: false + can_delete: false + + - name: Recording access restricted to supervisors and admins + check: + - user: user:alice + object: recording:rec-001 + assertions: + can_view: true + - user: user:bob + object: recording:rec-001 + assertions: + can_view: true + - user: user:charlie + object: recording:rec-001 + assertions: + can_view: false + - user: user:diana + object: recording:rec-001 + assertions: + can_view: false + + - name: Comment author can edit and delete own comments + check: + - user: user:charlie + object: comment:note-001 + assertions: + can_edit: true + can_delete: true + + - name: List calls charlie can access + list_objects: + - user: user:charlie + type: call + assertions: + can_view: + - call:call-001 + + - name: List contacts bob can access + list_objects: + - user: user:bob + type: contact + assertions: + can_view: + - contact:john-doe + can_edit: + - contact:john-doe + + - name: List comments alice can access + list_objects: + - user: user:alice + type: comment + assertions: + can_view: + - comment:note-001 + can_edit: + - comment:note-001 + + - name: List recordings alice can access + list_objects: + - user: user:alice + type: recording + assertions: + can_view: + - recording:rec-001 + + - name: Users who can view call:call-001 + list_users: + - object: call:call-001 + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + + - name: Users who can view contact:john-doe + list_users: + - object: contact:john-doe + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie diff --git a/stores/chat/README.md b/stores/chat/README.md new file mode 100644 index 0000000..81f6518 --- /dev/null +++ b/stores/chat/README.md @@ -0,0 +1,25 @@ +# OpenFGA for Chat & Messaging Systems + +## Use Case + +This model represents the authorization needs of a **chat/messaging platform**, like Slack, Microsoft Teams, Discord, or Google Chat. These platforms manage conversations (channels, DMs, group chats), messages, threads, groups, and membership. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own conversations, users, and groups. +- **Conversation membership**: Only conversation members can view messages and post new ones. Non-members cannot see the conversation or its content. +- **Group-based channels**: Conversations can grant membership to entire groups using usersets (`group#member`), so adding a user to the "Engineering" group automatically gives them access to the `#engineering-general` channel. +- **Direct messages**: DMs are modeled as conversations with individual user members, ensuring only participants can see the conversation. +- **Channel ownership**: Channel owners can edit channel settings, delete the channel, and manage membership (add/remove members). Regular members can only view and post. +- **Message authorship**: Message senders can edit and delete their own messages. Other conversation members can view and reply to messages but cannot edit them. +- **Thread replies**: Reply permissions follow conversation membership — anyone who can view the conversation can reply to messages within it. +- **Admin override**: Organization admins can view, edit, and delete any conversation or message, and manage membership across all channels, supporting moderation and compliance needs. +- **Non-member isolation**: Users who are not members of a conversation cannot view it, post in it, or see any of its messages — enforcing strict channel privacy. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `chat` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/chat/model.fga b/stores/chat/model.fga new file mode 100644 index 0000000..ee76029 --- /dev/null +++ b/stores/chat/model.fga @@ -0,0 +1,38 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin + define admin: [user] + +type group + relations + define organization: [organization] + define member: [user] + define can_manage: admin from organization + define can_view: member or can_manage + +type conversation + relations + define organization: [organization] + define organization_admin: admin from organization + define owner: [user] + define member: [user, group#member] or owner + define can_delete: owner or organization_admin + define can_edit: can_delete + define can_add_member: can_delete + define can_remove_member: can_delete + define can_post: member or can_edit + define can_view: can_post + +type message + relations + define conversation: [conversation] + define sender: [user] + define can_view: sender or can_view from conversation + define can_edit: sender + define can_delete: sender or organization_admin from conversation + define can_reply: can_view from conversation diff --git a/stores/chat/store.fga.yaml b/stores/chat/store.fga.yaml new file mode 100644 index 0000000..84bcd6d --- /dev/null +++ b/stores/chat/store.fga.yaml @@ -0,0 +1,247 @@ +name: Chat Authorization Model Tests +model_file: model.fga + +tuples: + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: member + object: organization:acme + - user: user:charlie + relation: member + object: organization:acme + - user: user:diana + relation: member + object: organization:acme + + - user: organization:acme + relation: organization + object: group:engineering + - user: user:bob + relation: member + object: group:engineering + - user: user:charlie + relation: member + object: group:engineering + + # Public channel - group-based membership + - user: organization:acme + relation: organization + object: conversation:engineering-general + - user: user:bob + relation: owner + object: conversation:engineering-general + - user: group:engineering#member + relation: member + object: conversation:engineering-general + + # DM conversation + - user: organization:acme + relation: organization + object: conversation:bob-charlie-dm + - user: user:bob + relation: member + object: conversation:bob-charlie-dm + - user: user:charlie + relation: member + object: conversation:bob-charlie-dm + + # Message in engineering channel + - user: conversation:engineering-general + relation: conversation + object: message:msg-001 + - user: user:charlie + relation: sender + object: message:msg-001 + + # Message in DM + - user: conversation:bob-charlie-dm + relation: conversation + object: message:dm-001 + - user: user:bob + relation: sender + object: message:dm-001 + +tests: + - name: Channel owner has full access + check: + - user: user:bob + object: conversation:engineering-general + assertions: + can_view: true + can_post: true + can_edit: true + can_delete: true + can_add_member: true + can_remove_member: true + + - name: Group members can view and post in channel + check: + - user: user:charlie + object: conversation:engineering-general + assertions: + can_view: true + can_post: true + can_edit: false + can_add_member: false + + - name: Non-member cannot see channel + check: + - user: user:diana + object: conversation:engineering-general + assertions: + can_view: false + can_post: false + + - name: Admin can manage any conversation + check: + - user: user:alice + object: conversation:engineering-general + assertions: + can_view: true + can_edit: true + can_delete: true + can_add_member: true + + - name: DM participants can view and post + check: + - user: user:bob + object: conversation:bob-charlie-dm + assertions: + can_view: true + can_post: true + - user: user:charlie + object: conversation:bob-charlie-dm + assertions: + can_view: true + can_post: true + - user: user:diana + object: conversation:bob-charlie-dm + assertions: + can_view: false + can_post: false + + - name: Message sender can edit own messages + check: + - user: user:charlie + object: message:msg-001 + assertions: + can_view: true + can_edit: true + can_delete: true + can_reply: true + + - name: Channel member can view and reply to messages + check: + - user: user:bob + object: message:msg-001 + assertions: + can_view: true + can_edit: false + can_reply: true + + - name: Non-member cannot see messages + check: + - user: user:diana + object: message:msg-001 + assertions: + can_view: false + can_reply: false + + - name: Admin can view and delete any message + check: + - user: user:alice + object: message:msg-001 + assertions: + can_view: true + can_delete: true + - user: user:alice + object: message:dm-001 + assertions: + can_view: true + can_delete: true + + - name: DM message visibility + check: + - user: user:bob + object: message:dm-001 + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:charlie + object: message:dm-001 + assertions: + can_view: true + can_edit: false + can_reply: true + - user: user:diana + object: message:dm-001 + assertions: + can_view: false + + - name: List conversations bob can access + list_objects: + - user: user:bob + type: conversation + assertions: + owner: + - conversation:engineering-general + can_view: + - conversation:engineering-general + - conversation:bob-charlie-dm + + - name: List conversations charlie can access + list_objects: + - user: user:charlie + type: conversation + assertions: + can_view: + - conversation:engineering-general + - conversation:bob-charlie-dm + + - name: List messages alice can access + list_objects: + - user: user:alice + type: message + assertions: + can_view: + - message:msg-001 + - message:dm-001 + + - name: List messages charlie can access + list_objects: + - user: user:charlie + type: message + assertions: + can_view: + - message:msg-001 + - message:dm-001 + + - name: Users who can view conversation:engineering-general + list_users: + - object: conversation:engineering-general + user_filter: + - type: user + assertions: + owner: + users: + - user:bob + can_view: + users: + - user:alice + - user:bob + - user:charlie + + - name: Users who can view conversation:bob-charlie-dm + list_users: + - object: conversation:bob-charlie-dm + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie diff --git a/stores/crm/README.md b/stores/crm/README.md new file mode 100644 index 0000000..0e797e5 --- /dev/null +++ b/stores/crm/README.md @@ -0,0 +1,25 @@ +# OpenFGA for CRM Systems + +## Use Case + +This model represents the authorization needs of a **customer relationship management (CRM) system**, like Salesforce or HubSpot. These platforms manage accounts (companies), contacts, leads, opportunities, engagements, notes, and tasks across the sales pipeline. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own sales pipeline, accounts, and customer data. +- **Sales roles**: Organization-level roles (`admin`, `sales_manager`, `sales_rep`) control pipeline visibility. Admins have full access, sales managers can see and edit all deals, and sales reps only see records they own. +- **Account ownership**: Each account (company) has an owner who can view and edit it. Sales managers have organization-wide account visibility. +- **Contact-to-account inheritance**: Contacts inherit visibility from their parent account, so the account owner automatically sees all contacts at that company. +- **Opportunity pipeline**: Opportunities are linked to accounts. Owners can edit and close deals, while sales managers have organization-wide oversight. Only owners and managers can close deals. +- **Lead lifecycle**: Lead owners can manage and convert leads. Sales managers can also convert leads, enabling pipeline oversight. Only admins can delete leads. +- **Engagement tracking**: Call logs, meetings, and emails are visible to their owner and sales managers. Only the owner can edit their engagement records. +- **Note privacy**: Note authors can edit their own notes and delete them. Sales managers can view notes but cannot edit others' notes. +- **Task management**: Task owners can view, edit, and complete tasks. Only the owner can mark a task as complete, preventing unauthorized status changes. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `crm` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/crm/model.fga b/stores/crm/model.fga new file mode 100644 index 0000000..3583f02 --- /dev/null +++ b/stores/crm/model.fga @@ -0,0 +1,72 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or sales_rep or sales_manager + define admin: [user] + define sales_manager: [user] + define sales_rep: [user] + +type account + relations + define organization: [organization] + define owner: [user] + define organization_admin: admin from organization + define organization_sales_manager: sales_manager from organization + define can_delete: organization_admin + define can_edit: owner or organization_sales_manager or can_delete + define can_create_contact: can_edit + define can_create_opportunity: can_edit + define can_view: can_edit + +type contact + relations + define account: [account] + define owner: [user] + define can_delete: organization_admin from account + define can_edit: owner or can_delete + define can_view: can_view from account or can_edit + +type lead + relations + define organization: [organization] + define owner: [user] + define can_delete: admin from organization + define can_convert: owner or sales_manager from organization + define can_edit: can_convert or can_delete + define can_view: can_edit + +type opportunity + relations + define account: [account] + define owner: [user] + define can_delete: organization_admin from account + define can_close: owner or organization_sales_manager from account + define can_edit: can_close or can_delete + define can_view: can_view from account or can_edit + +type engagement + relations + define organization: [organization] + define owner: [user] + define can_edit: owner or admin from organization + define can_view: sales_manager from organization or can_edit + +type note + relations + define organization: [organization] + define owner: [user] + define can_delete: owner or admin from organization + define can_edit: can_delete + define can_view: sales_manager from organization or can_edit + +type task + relations + define organization: [organization] + define owner: [user] + define can_edit: owner or admin from organization + define can_complete: owner + define can_view: sales_manager from organization or can_edit diff --git a/stores/crm/store.fga.yaml b/stores/crm/store.fga.yaml new file mode 100644 index 0000000..cb37074 --- /dev/null +++ b/stores/crm/store.fga.yaml @@ -0,0 +1,250 @@ +name: CRM Authorization Model Tests +model_file: model.fga + +tuples: + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: sales_manager + object: organization:acme + - user: user:charlie + relation: sales_rep + object: organization:acme + + - user: organization:acme + relation: organization + object: account:mega-corp + - user: user:charlie + relation: owner + object: account:mega-corp + + - user: account:mega-corp + relation: account + object: contact:john-doe + - user: user:charlie + relation: owner + object: contact:john-doe + + - user: organization:acme + relation: organization + object: lead:new-prospect + - user: user:charlie + relation: owner + object: lead:new-prospect + + - user: account:mega-corp + relation: account + object: opportunity:big-deal + - user: user:charlie + relation: owner + object: opportunity:big-deal + + - user: organization:acme + relation: organization + object: engagement:call-001 + - user: user:charlie + relation: owner + object: engagement:call-001 + + - user: organization:acme + relation: organization + object: note:meeting-notes + - user: user:charlie + relation: owner + object: note:meeting-notes + + - user: organization:acme + relation: organization + object: task:follow-up + - user: user:charlie + relation: owner + object: task:follow-up + +tests: + - name: Admin has full CRM access + check: + - user: user:alice + object: account:mega-corp + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: opportunity:big-deal + assertions: + can_view: true + can_edit: true + can_delete: true + + - name: Sales manager can view and edit accounts + check: + - user: user:bob + object: account:mega-corp + assertions: + can_view: true + can_edit: true + can_delete: false + + - name: Account owner can view and edit but not delete + check: + - user: user:charlie + object: account:mega-corp + assertions: + can_view: true + can_edit: true + can_delete: false + + - name: Contact inherits from account owner + check: + - user: user:charlie + object: contact:john-doe + assertions: + can_view: true + can_edit: true + + - name: Lead owner can convert + check: + - user: user:charlie + object: lead:new-prospect + assertions: + can_view: true + can_edit: true + can_convert: true + - user: user:bob + object: lead:new-prospect + assertions: + can_view: true + can_convert: true + + - name: Opportunity follows account ownership + check: + - user: user:charlie + object: opportunity:big-deal + assertions: + can_view: true + can_edit: true + can_close: true + + - name: Sales manager can view and close opportunities + check: + - user: user:bob + object: opportunity:big-deal + assertions: + can_view: true + can_edit: true + can_close: true + can_delete: false + + - name: Engagement owner and manager visibility + check: + - user: user:charlie + object: engagement:call-001 + assertions: + can_view: true + can_edit: true + - user: user:bob + object: engagement:call-001 + assertions: + can_view: true + can_edit: false + + - name: Note owner can edit and delete their own notes + check: + - user: user:charlie + object: note:meeting-notes + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:bob + object: note:meeting-notes + assertions: + can_view: true + can_edit: false + can_delete: false + + - name: Task owner can complete + check: + - user: user:charlie + object: task:follow-up + assertions: + can_view: true + can_edit: true + can_complete: true + - user: user:bob + object: task:follow-up + assertions: + can_view: true + can_edit: false + can_complete: false + + - name: List objects charlie can access + list_objects: + - user: user:charlie + type: account + assertions: + can_view: + - account:mega-corp + can_edit: + - account:mega-corp + - user: user:charlie + type: contact + assertions: + can_view: + - contact:john-doe + can_edit: + - contact:john-doe + - user: user:charlie + type: opportunity + assertions: + can_view: + - opportunity:big-deal + can_edit: + - opportunity:big-deal + + - name: List objects bob can access + list_objects: + - user: user:bob + type: account + assertions: + can_view: + - account:mega-corp + - user: user:bob + type: lead + assertions: + can_view: + - lead:new-prospect + + - name: List users with access to account + list_users: + - object: account:mega-corp + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + - user:charlie + + - name: List users with access to note + list_users: + - object: note:meeting-notes + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:charlie diff --git a/stores/custom-roles/store.fga.yaml b/stores/custom-roles/store.fga.yaml index d1b3951..42c09e7 100644 --- a/stores/custom-roles/store.fga.yaml +++ b/stores/custom-roles/store.fga.yaml @@ -1,7 +1,7 @@ # Documentation: https://openfga.dev/docs/modeling/custom-roles # FGA Playground: https://play.fga.dev/sandbox/?store=custom-roles name: Custom Roles -model_file: ./model.fga +model_file: model.fga tuples: # Assignees of the "Content Manager" role are can create assets in the "Website Content" asset category - user: role:content-manager#assignee diff --git a/stores/ecommerce/README.md b/stores/ecommerce/README.md new file mode 100644 index 0000000..fb313fc --- /dev/null +++ b/stores/ecommerce/README.md @@ -0,0 +1,25 @@ +# OpenFGA for Ecommerce Platforms + +## Use Case + +This model represents the authorization needs of an **ecommerce platform**, like Shopify, BigCommerce, WooCommerce, Magento, or Etsy. These platforms manage stores, product catalogs, customers, orders, fulfillment, and refunds. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own stores, products, customers, and orders. +- **Store roles**: Organization-level roles (`admin`, `store_manager`, `member`) control platform-wide access. Store-level roles (`owner`, `manager`, `staff`) control per-store operations. Managers inherit from the organization's `store_manager` role. +- **Parent-scoped product creation**: Product creation is checked on the store (`can_create_product`) rather than on product objects that do not exist yet. +- **Product management**: Store managers can create, edit, and delete products. Staff members who created a product can edit it. All store staff can view products and manage inventory. +- **Customer accounts**: Customers can view and edit their own account information. Store staff can view customer records, but only managers can edit them and only admins can delete them. +- **Order lifecycle**: Customers who placed an order can view and cancel it. Store staff can view, edit, and fulfill orders. Only managers can issue refunds. Only admins can delete orders. +- **Fulfillment access**: Store staff can mark orders as fulfilled/shipped, enabling warehouse workers to process shipments without manager-level access. +- **Refund authorization**: Only store managers and organization admins can process refunds, ensuring proper financial oversight. +- **Store ownership**: Store owners can manage store settings and delete the store. Staff can view but not modify store configuration. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `ecommerce` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/ecommerce/model.fga b/stores/ecommerce/model.fga new file mode 100644 index 0000000..abcbbc3 --- /dev/null +++ b/stores/ecommerce/model.fga @@ -0,0 +1,64 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or store_manager + define admin: [user] + define store_manager: [user] + define can_create_store: admin + +type store + relations + define organization: [organization] + define owner: [user] + define manager: [user] or owner or store_manager from organization + define staff: [user] or manager + define organization_admin: admin from organization + define can_delete: owner or organization_admin + define can_create_product: manager or organization_admin + define can_create_customer: staff or can_edit + define can_create_order: staff or can_edit + define can_edit: manager or can_delete + define can_view: staff or can_edit + +type product + relations + define store: [store] + define creator: [user] + define viewer: [user] + define can_delete: manager from store or organization_admin from store + define can_manage_inventory: staff from store or organization_admin from store + define can_create_review: can_view from store + define can_edit: creator or can_manage_inventory + define can_view: viewer or can_edit + +type customer + relations + define store: [store] + define account_owner: [user] + define can_delete: organization_admin from store + define can_edit: account_owner or manager from store or can_delete + define can_view: staff from store or can_edit + +type review + relations + define product: [product] + define author: [user] + define can_delete: author or can_delete from product + define can_edit: author + define can_view: can_view from product or can_edit + +type order + relations + define store: [store] + define customer: [customer] + define placed_by: [user] + define can_delete: organization_admin from store + define can_refund: manager from store or can_delete + define can_cancel: account_owner from customer or placed_by or can_refund + define can_fulfill: staff from store or can_delete + define can_edit: can_fulfill + define can_view: account_owner from customer or placed_by or can_edit diff --git a/stores/ecommerce/store.fga.yaml b/stores/ecommerce/store.fga.yaml new file mode 100644 index 0000000..a33d677 --- /dev/null +++ b/stores/ecommerce/store.fga.yaml @@ -0,0 +1,349 @@ +name: Ecommerce Authorization Model Tests +model_file: model.fga + +tuples: + # Organization + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: store_manager + object: organization:acme + - user: user:charlie + relation: member + object: organization:acme + + # Store + - user: organization:acme + relation: organization + object: store:main-shop + - user: user:bob + relation: owner + object: store:main-shop + - user: user:charlie + relation: staff + object: store:main-shop + + # Product + - user: store:main-shop + relation: store + object: product:sneakers + - user: user:charlie + relation: creator + object: product:sneakers + + # Customer + - user: store:main-shop + relation: store + object: customer:diana + - user: user:diana + relation: account_owner + object: customer:diana + + # Review on product by diana + - user: product:sneakers + relation: product + object: review:rev-001 + - user: user:diana + relation: author + object: review:rev-001 + + # Order placed by the customer + - user: store:main-shop + relation: store + object: order:ord-001 + - user: customer:diana + relation: customer + object: order:ord-001 + - user: user:diana + relation: placed_by + object: order:ord-001 + +tests: + - name: Admin has full access to store + check: + - user: user:alice + object: organization:acme + assertions: + can_create_store: true + - user: user:alice + object: store:main-shop + assertions: + can_view: true + can_create_product: true + can_create_customer: true + can_create_order: true + can_edit: true + can_delete: true + + - name: Store owner can manage store + check: + - user: user:bob + object: organization:acme + assertions: + can_create_store: false + - user: user:bob + object: store:main-shop + assertions: + can_view: true + can_create_product: true + can_create_customer: true + can_create_order: true + can_edit: true + can_delete: true + + - name: Store staff can view but not edit store + check: + - user: user:charlie + object: organization:acme + assertions: + can_create_store: false + - user: user:charlie + object: store:main-shop + assertions: + can_view: true + can_create_product: false + can_create_customer: true + can_create_order: true + can_edit: false + can_delete: false + + - name: Store manager can manage products + check: + - user: user:bob + object: store:main-shop + assertions: + can_create_product: true + - user: user:bob + object: product:sneakers + assertions: + can_view: true + can_create_review: true + can_edit: true + can_delete: true + + - name: Product creator can edit own product + check: + - user: user:charlie + object: product:sneakers + assertions: + can_view: true + can_create_review: true + can_edit: true + can_delete: false + can_manage_inventory: true + + - name: Staff can view and manage inventory but not delete products + check: + - user: user:charlie + object: product:sneakers + assertions: + can_view: true + can_create_review: true + can_manage_inventory: true + can_delete: false + + - name: Customer can view own account + check: + - user: user:diana + object: customer:diana + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:diana + object: store:main-shop + assertions: + can_create_customer: false + can_create_order: false + + - name: Store staff can view customers + check: + - user: user:charlie + object: customer:diana + assertions: + can_view: true + can_edit: false + + - name: Store manager can edit customers + check: + - user: user:bob + object: customer:diana + assertions: + can_view: true + can_edit: true + can_delete: false + + - name: Only admin can delete customers + check: + - user: user:alice + object: customer:diana + assertions: + can_view: true + can_edit: true + can_delete: true + + - name: Customer can view own order + check: + - user: user:diana + object: order:ord-001 + assertions: + can_view: true + can_cancel: true + can_edit: false + can_refund: false + can_fulfill: false + + - name: Store staff can view and fulfill orders + check: + - user: user:charlie + object: order:ord-001 + assertions: + can_view: true + can_edit: true + can_fulfill: true + can_cancel: false + can_refund: false + + - name: Store manager can refund and cancel orders + check: + - user: user:bob + object: order:ord-001 + assertions: + can_view: true + can_edit: true + can_cancel: true + can_refund: true + can_fulfill: true + can_delete: false + + - name: Admin has full order access + check: + - user: user:alice + object: order:ord-001 + assertions: + can_view: true + can_edit: true + can_cancel: true + can_refund: true + can_fulfill: true + can_delete: true + + - name: Review author can edit and delete own review + check: + - user: user:diana + object: review:rev-001 + assertions: + can_delete: true + can_edit: true + can_view: true + + - name: Store manager can delete reviews but not edit them + check: + - user: user:bob + object: review:rev-001 + assertions: + can_delete: true + can_edit: false + can_view: true + + - name: Store staff can view reviews but not edit or delete + check: + - user: user:charlie + object: review:rev-001 + assertions: + can_delete: false + can_edit: false + can_view: true + + - name: Non-store member cannot delete customers or view products + check: + - user: user:charlie + object: customer:diana + assertions: + can_delete: false + - user: user:diana + object: product:sneakers + assertions: + can_view: false + can_create_review: false + can_edit: false + + - name: List objects charlie can access + list_objects: + - user: user:charlie + type: store + assertions: + staff: + - store:main-shop + - user: user:charlie + type: product + assertions: + can_view: + - product:sneakers + - user: user:charlie + type: customer + assertions: + can_view: + - customer:diana + + - name: List objects bob can access + list_objects: + - user: user:bob + type: store + assertions: + owner: + - store:main-shop + - user: user:bob + type: product + assertions: + can_edit: + - product:sneakers + + - name: List objects diana can access + list_objects: + - user: user:diana + type: customer + assertions: + account_owner: + - customer:diana + - user: user:diana + type: order + assertions: + placed_by: + - order:ord-001 + + - name: List users with access to product + list_users: + - object: product:sneakers + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + - user:charlie + + - name: List users with access to order + list_users: + - object: order:ord-001 + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + can_edit: + users: + - user:alice + - user:bob + - user:charlie diff --git a/stores/entitlements/store.fga.yaml b/stores/entitlements/store.fga.yaml index 5d7718c..8481842 100644 --- a/stores/entitlements/store.fga.yaml +++ b/stores/entitlements/store.fga.yaml @@ -1,7 +1,7 @@ # Documentation: https://openfga.dev/docs/modeling/advanced/entitlements # FGA Playground: https://play.fga.dev/sandbox/?store=entitlements name: Entitlements -model_file: ./model.fga +model_file: model.fga tuples: # The Enterprise plan grants access to the Draft PRs feature - user: plan:enterprise diff --git a/stores/expenses/store.fga.yaml b/stores/expenses/store.fga.yaml index 141e151..f42d6e8 100644 --- a/stores/expenses/store.fga.yaml +++ b/stores/expenses/store.fga.yaml @@ -1,6 +1,6 @@ # FGA Playground: https://play.fga.dev/sandbox/?store=expenses name: Expenses -model_file: ./model.fga +model_file: model.fga tuples: # Matt is Daniel's manager - user: employee:matt diff --git a/stores/file-storage/README.md b/stores/file-storage/README.md new file mode 100644 index 0000000..ac3e948 --- /dev/null +++ b/stores/file-storage/README.md @@ -0,0 +1,24 @@ +# OpenFGA for File Storage Systems + +## Use Case + +This model represents the authorization needs of a **file storage platform**, like Google Drive, Box, OneDrive, or SharePoint. These platforms manage drives, folders, files, groups, and sharing permissions across an organization. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own drives, folders, files, and user groups. +- **Drive-level access**: Drives have owners, writers, and readers. Organization members get default read access to shared drives. +- **Folder hierarchy with inheritance**: Folders have a single `parent` relation that accepts both drives and other folders. Permissions cascade from parent to child — granting access to a parent folder automatically grants access to everything inside it. +- **File permissions from folders**: Files inherit reader, writer, and owner permissions from their parent folder, reducing the number of permission tuples needed. +- **Group-based sharing**: Groups (e.g., "Engineering") can be granted read or write access to folders and files using usersets (`group#member`), enabling efficient team-based sharing. +- **Owner propagation**: Folder ownership cascades to child folders and files, so the owner of a top-level folder owns the entire subtree. +- **Granular file operations**: Separate permissions for viewing (`can_view`), editing (`can_edit`), downloading (`can_download`), and deleting (`can_delete`) files. +- **Admin override**: Organization admins can delete drives, folders, and files, regardless of individual ownership. The `organization_admin` role chains through the parent hierarchy automatically — no need to link every folder to the organization. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `file-storage` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/file-storage/model.fga b/stores/file-storage/model.fga new file mode 100644 index 0000000..24fa9f8 --- /dev/null +++ b/stores/file-storage/model.fga @@ -0,0 +1,48 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin + define admin: [user] + +type group + relations + define organization: [organization] + define member: [user] + +type drive + relations + define organization: [organization] + define organization_admin: admin from organization + define owner: [user] + define writer: [user, group#member] or owner + define reader: [user, group#member] or writer or member from organization + define can_delete: owner or organization_admin + define can_edit: writer or can_delete + define can_view: reader or can_edit + +type folder + relations + define parent: [drive, folder] + define organization_admin: organization_admin from parent + define owner: [user] or owner from parent + define writer: [user, group#member] or owner or writer from parent + define reader: [user, group#member] or writer or reader from parent + define can_delete: owner or organization_admin + define can_create_file: writer or can_delete + define can_edit: writer or can_delete + define can_view: reader or can_edit + +type file + relations + define folder: [folder] + define owner: [user] or owner from folder + define writer: [user, group#member] or owner or writer from folder + define reader: [user, group#member] or writer or reader from folder + define can_delete: owner or organization_admin from folder + define can_edit: writer or can_delete + define can_download: reader or can_edit + define can_view: reader or can_edit diff --git a/stores/file-storage/store.fga.yaml b/stores/file-storage/store.fga.yaml new file mode 100644 index 0000000..7b79d56 --- /dev/null +++ b/stores/file-storage/store.fga.yaml @@ -0,0 +1,197 @@ +name: File Storage Authorization Model Tests +model_file: model.fga + +tuples: + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: member + object: organization:acme + - user: user:charlie + relation: member + object: organization:acme + + - user: organization:acme + relation: organization + object: group:engineering + - user: user:charlie + relation: member + object: group:engineering + + - user: organization:acme + relation: organization + object: drive:shared-drive + - user: user:alice + relation: owner + object: drive:shared-drive + + # Bob's personal drive (alice is NOT owner) + - user: organization:acme + relation: organization + object: drive:bobs-drive + - user: user:bob + relation: owner + object: drive:bobs-drive + + - user: drive:shared-drive + relation: parent + object: folder:projects + - user: user:bob + relation: owner + object: folder:projects + + - user: folder:projects + relation: parent + object: folder:backend + - user: group:engineering#member + relation: writer + object: folder:backend + + - user: folder:backend + relation: folder + object: file:api-spec + - user: user:charlie + relation: owner + object: file:api-spec + +tests: + - name: Drive owner has full access + check: + - user: user:alice + object: drive:shared-drive + assertions: + can_view: true + can_edit: true + can_delete: true + + - name: Org members can view drive + check: + - user: user:bob + object: drive:shared-drive + assertions: + can_view: true + can_edit: false + + - name: Folder owner can manage folder + check: + - user: user:bob + object: folder:projects + assertions: + can_view: true + can_edit: true + can_create_file: true + + - name: Group members inherit folder write access + check: + - user: user:charlie + object: folder:backend + assertions: + can_view: true + can_edit: true + can_create_file: true + + - name: File inherits from folder permissions + check: + - user: user:charlie + object: file:api-spec + assertions: + can_view: true + can_edit: true + can_download: true + can_delete: true + + - name: Parent folder owner inherits to child folder + check: + - user: user:bob + object: folder:backend + assertions: + can_view: true + can_edit: true + + - name: Admin can delete drive they do not own + check: + - user: user:alice + object: drive:bobs-drive + assertions: + can_view: true + can_delete: true + + - name: Drive reader can read nested folders + check: + - user: user:bob + object: folder:projects + assertions: + can_view: true + + - name: List objects alice can access + list_objects: + - user: user:alice + type: drive + assertions: + owner: + - drive:shared-drive + - user: user:alice + type: folder + assertions: + can_view: + - folder:projects + - folder:backend + + - name: List objects charlie can access + list_objects: + - user: user:charlie + type: folder + assertions: + can_edit: + - folder:backend + - user: user:charlie + type: file + assertions: + owner: + - file:api-spec + + - name: List objects bob can access + list_objects: + - user: user:bob + type: drive + assertions: + owner: + - drive:bobs-drive + + - name: List users with access to drive + list_users: + - object: drive:shared-drive + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + + - name: List users with access to file + list_users: + - object: file:api-spec + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + - user:charlie + can_delete: + users: + - user:alice + - user:bob + - user:charlie diff --git a/stores/gdrive/store.fga.yaml b/stores/gdrive/store.fga.yaml index 0bcc771..741347d 100644 --- a/stores/gdrive/store.fga.yaml +++ b/stores/gdrive/store.fga.yaml @@ -1,7 +1,7 @@ # Documentation: https://openfga.dev/docs/modeling/advanced/gdrive # FGA Playground: https://play.fga.dev/sandbox/?store=gdrive name: Google Drive -model_file: ./model.fga +model_file: model.fga tuples: # Anne is a member of the Contoso group - user: user:anne diff --git a/stores/github/store.fga.yaml b/stores/github/store.fga.yaml index b5d1acd..a0bc178 100644 --- a/stores/github/store.fga.yaml +++ b/stores/github/store.fga.yaml @@ -1,7 +1,7 @@ # Documentation: https://openfga.dev/docs/modeling/advanced/github # FGA Playground: https://play.fga.dev/sandbox/?store=github name: GitHub -model_file: ./model.fga +model_file: model.fga tuples: # The OpenFGA organization is the owner of the openfga/openfga repository - user: organization:openfga diff --git a/stores/healthcare/README.md b/stores/healthcare/README.md new file mode 100644 index 0000000..e144c49 --- /dev/null +++ b/stores/healthcare/README.md @@ -0,0 +1,26 @@ +# OpenFGA for Healthcare Systems + +## Use Case + +This model represents the authorization needs of a **healthcare platform**, like Epic, Cerner, or Athenahealth. These platforms manage patient records, clinical encounters, diagnoses, treatments, medications, and provider networks across healthcare organizations. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own patients, providers, facilities, and clinical data. +- **Healthcare roles**: Organization-level roles (`admin`, `provider`, `nurse`, `medical_records_staff`) control access. Admins have full access, providers manage clinical care, nurses support care delivery, and medical records staff handle administrative record-keeping. +- **Patient record protection**: Patient records are only accessible to the primary provider, care team members, medical records staff, and admins. Providers not involved in a patient's care cannot view their records, enforcing minimum necessary access. +- **Sensitive data isolation**: Protected health information (allergies, blood type, date of birth) is gated behind `can_view_sensitive`, accessible to the primary provider, care team, medical records staff, and admins. +- **Care team access**: Nurses and other staff on a patient's care team can view records and sensitive data, and create encounters, but cannot edit patient records or order treatments. +- **Encounter management**: Encounters are linked to both patients and facilities. Attending providers can edit encounters and create treatment orders. Patient viewers inherit read access to all encounters. +- **Diagnosis and treatment chains**: Diagnoses inherit visibility from the patient. Treatments inherit visibility from the encounter. Diagnosing and ordering providers can edit their respective records. +- **Facility management**: Facility directors can edit facility information. All organization members can view facilities. Only admins can delete them. +- **Medication formulary**: The medication catalog is viewable by providers and nurses for clinical reference. Only admins can modify the formulary. +- **Parent-scoped creation**: Encounter creation is checked on the patient (`can_create_encounter`), and treatment creation is checked on the encounter (`can_create_treatment`), rather than on objects that do not exist yet. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `healthcare` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/healthcare/model.fga b/stores/healthcare/model.fga new file mode 100644 index 0000000..f46c98f --- /dev/null +++ b/stores/healthcare/model.fga @@ -0,0 +1,65 @@ +model + schema 1.1 + +type user + +type organization + relations + define admin: [user] + define provider: [user] + define nurse: [user] + define medical_records_staff: [user] + define member: [user] or admin or provider or nurse or medical_records_staff + +type facility + relations + define organization: [organization] + define director: [user] + define can_delete: admin from organization + define can_edit: director or can_delete + define can_view: member from organization or can_edit + +type patient + relations + define organization: [organization] + define primary_provider: [user] + define care_team: [user] + define org_admin: admin from organization + define org_medical_records: medical_records_staff from organization + define can_delete: org_admin + define can_edit: primary_provider or org_medical_records or can_delete + define can_view_sensitive: primary_provider or care_team or org_medical_records or can_delete + define can_create_encounter: can_view_sensitive + define can_create_diagnosis: can_edit + define can_view: can_view_sensitive or can_edit + +type encounter + relations + define patient: [patient] + define attending_provider: [user] + define org_admin: org_admin from patient + define can_delete: org_admin + define can_create_treatment: attending_provider or can_delete + define can_edit: attending_provider or can_delete + define can_view: can_view from patient or can_edit + +type diagnosis + relations + define patient: [patient] + define diagnosing_provider: [user] + define can_edit: diagnosing_provider or org_admin from patient + define can_view: can_view from patient or can_edit + +type treatment + relations + define encounter: [encounter] + define ordering_provider: [user] + define can_delete: can_delete from encounter + define can_edit: ordering_provider or can_delete + define can_view: can_view from encounter or can_edit + +type medication + relations + define organization: [organization] + define can_edit: admin from organization + define can_view: provider from organization or nurse from organization or can_edit diff --git a/stores/healthcare/store.fga.yaml b/stores/healthcare/store.fga.yaml new file mode 100644 index 0000000..c9073a9 --- /dev/null +++ b/stores/healthcare/store.fga.yaml @@ -0,0 +1,399 @@ +name: Healthcare +model_file: model.fga +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:mercy-hospital + - user: user:dr-smith + relation: provider + object: organization:mercy-hospital + - user: user:dr-jones + relation: provider + object: organization:mercy-hospital + - user: user:nurse-williams + relation: nurse + object: organization:mercy-hospital + - user: user:clerk-brown + relation: medical_records_staff + object: organization:mercy-hospital + - user: user:billing-davis + relation: member + object: organization:mercy-hospital + + # Facility + - user: organization:mercy-hospital + relation: organization + object: facility:main-campus + - user: user:dr-jones + relation: director + object: facility:main-campus + + # Patient doe — primary provider is dr-smith, nurse-williams on care team + - user: organization:mercy-hospital + relation: organization + object: patient:doe + - user: user:dr-smith + relation: primary_provider + object: patient:doe + - user: user:nurse-williams + relation: care_team + object: patient:doe + + # Patient roe — primary provider is dr-jones (used for isolation tests) + - user: organization:mercy-hospital + relation: organization + object: patient:roe + - user: user:dr-jones + relation: primary_provider + object: patient:roe + + # Encounter for patient doe at main-campus, attended by dr-smith + - user: patient:doe + relation: patient + object: encounter:enc-001 + - user: user:dr-smith + relation: attending_provider + object: encounter:enc-001 + + # Diagnosis for patient doe, diagnosed by dr-smith + - user: patient:doe + relation: patient + object: diagnosis:diag-001 + - user: user:dr-smith + relation: diagnosing_provider + object: diagnosis:diag-001 + + # Treatment within encounter enc-001, ordered by dr-smith + - user: encounter:enc-001 + relation: encounter + object: treatment:treat-001 + - user: user:dr-smith + relation: ordering_provider + object: treatment:treat-001 + + # Medication in the formulary + - user: organization:mercy-hospital + relation: organization + object: medication:aspirin + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: patient:doe + assertions: + can_delete: true + can_edit: true + can_view_sensitive: true + can_create_encounter: true + can_view: true + - user: user:alice + object: encounter:enc-001 + assertions: + can_delete: true + can_create_treatment: true + can_edit: true + can_view: true + - user: user:alice + object: diagnosis:diag-001 + assertions: + can_edit: true + can_view: true + - user: user:alice + object: treatment:treat-001 + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:alice + object: facility:main-campus + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:alice + object: medication:aspirin + assertions: + can_edit: true + can_view: true + + - name: Primary provider can manage patient care + check: + - user: user:dr-smith + object: patient:doe + assertions: + can_delete: false + can_edit: true + can_view_sensitive: true + can_create_encounter: true + can_view: true + - user: user:dr-smith + object: encounter:enc-001 + assertions: + can_delete: false + can_create_treatment: true + can_edit: true + can_view: true + - user: user:dr-smith + object: diagnosis:diag-001 + assertions: + can_edit: true + can_view: true + - user: user:dr-smith + object: treatment:treat-001 + assertions: + can_delete: false + can_edit: true + can_view: true + + - name: Care team nurse can view but not edit patient records + check: + - user: user:nurse-williams + object: patient:doe + assertions: + can_delete: false + can_edit: false + can_view_sensitive: true + can_create_encounter: true + can_view: true + - user: user:nurse-williams + object: encounter:enc-001 + assertions: + can_delete: false + can_create_treatment: false + can_edit: false + can_view: true + - user: user:nurse-williams + object: diagnosis:diag-001 + assertions: + can_edit: false + can_view: true + - user: user:nurse-williams + object: treatment:treat-001 + assertions: + can_delete: false + can_edit: false + can_view: true + + - name: Medical records clerk can edit demographics and view sensitive data + check: + - user: user:clerk-brown + object: patient:doe + assertions: + can_delete: false + can_edit: true + can_view_sensitive: true + can_create_encounter: true + can_create_diagnosis: true + can_view: true + - user: user:clerk-brown + object: encounter:enc-001 + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:clerk-brown + object: diagnosis:diag-001 + assertions: + can_edit: false + can_view: true + + - name: Unrelated provider cannot access other providers patients + check: + - user: user:dr-jones + object: patient:doe + assertions: + can_delete: false + can_edit: false + can_view_sensitive: false + can_create_encounter: false + can_view: false + - user: user:dr-jones + object: encounter:enc-001 + assertions: + can_delete: false + can_edit: false + can_view: false + - user: user:dr-jones + object: diagnosis:diag-001 + assertions: + can_edit: false + can_view: false + - user: user:dr-jones + object: treatment:treat-001 + assertions: + can_delete: false + can_edit: false + can_view: false + + - name: Provider can access their own patients but not others + check: + - user: user:dr-jones + object: patient:roe + assertions: + can_edit: true + can_view_sensitive: true + can_view: true + - user: user:dr-smith + object: patient:roe + assertions: + can_edit: false + can_view_sensitive: false + can_view: false + + - name: Facility access follows organization membership + check: + - user: user:dr-jones + object: facility:main-campus + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:dr-smith + object: facility:main-campus + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:billing-davis + object: facility:main-campus + assertions: + can_delete: false + can_edit: false + can_view: true + + - name: Medication formulary restricted to clinical staff + check: + - user: user:dr-smith + object: medication:aspirin + assertions: + can_edit: false + can_view: true + - user: user:nurse-williams + object: medication:aspirin + assertions: + can_edit: false + can_view: true + - user: user:clerk-brown + object: medication:aspirin + assertions: + can_edit: false + can_view: false + - user: user:billing-davis + object: medication:aspirin + assertions: + can_edit: false + can_view: false + + - name: Billing staff cannot access patient data + check: + - user: user:billing-davis + object: patient:doe + assertions: + can_delete: false + can_edit: false + can_view_sensitive: false + can_view: false + - user: user:billing-davis + object: encounter:enc-001 + assertions: + can_delete: false + can_edit: false + can_view: false + + - name: List patients for primary provider + list_objects: + - user: user:dr-smith + type: patient + assertions: + primary_provider: + - patient:doe + - user: user:dr-jones + type: patient + assertions: + primary_provider: + - patient:roe + - user: user:nurse-williams + type: patient + assertions: + care_team: + - patient:doe + + - name: List encounters for attending provider + list_objects: + - user: user:dr-smith + type: encounter + assertions: + attending_provider: + - encounter:enc-001 + + - name: List diagnoses for diagnosing provider + list_objects: + - user: user:dr-smith + type: diagnosis + assertions: + diagnosing_provider: + - diagnosis:diag-001 + + - name: List treatments for ordering provider + list_objects: + - user: user:dr-smith + type: treatment + assertions: + ordering_provider: + - treatment:treat-001 + + - name: List users with primary provider access to patient doe + list_users: + - object: patient:doe + user_filter: + - type: user + assertions: + primary_provider: + users: + - user:dr-smith + + - name: List users with care team access to patient doe + list_users: + - object: patient:doe + user_filter: + - type: user + assertions: + care_team: + users: + - user:nurse-williams + + - name: List users with patient doe permissions + list_users: + - object: patient:doe + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:clerk-brown + - user:dr-smith + - user:nurse-williams + can_edit: + users: + - user:alice + - user:clerk-brown + - user:dr-smith + can_view_sensitive: + users: + - user:alice + - user:clerk-brown + - user:dr-smith + - user:nurse-williams + + - name: List users with attending provider access to encounter enc-001 + list_users: + - object: encounter:enc-001 + user_filter: + - type: user + assertions: + attending_provider: + users: + - user:dr-smith diff --git a/stores/hospitality/README.md b/stores/hospitality/README.md new file mode 100644 index 0000000..1f5fde7 --- /dev/null +++ b/stores/hospitality/README.md @@ -0,0 +1,26 @@ +# OpenFGA for Hospitality Systems + +## Use Case + +This model represents the authorization needs of a **hotel management platform**, like Opera PMS, Mews, or Cloudbeds. These platforms manage hotels, rooms, reservations, guest profiles, and guest services across hotel chains and individual properties. + +The model captures the following requirements: + +- **Multi-tenancy**: A hotel group (organization) manages multiple hotel properties, each with their own staff, rooms, and operations. +- **Two-level role structure**: Organization-level roles (`admin`, `revenue_manager`) control chain-wide access. Hotel-level roles (`general_manager`, `front_desk`, `housekeeping`, `concierge`) control per-property operations. Hotel staff are also organization members for cross-property visibility. +- **Hotel property management**: General managers can edit hotel configuration, create rooms, reservations, and services. Only chain admins can delete hotel properties. +- **Reservation lifecycle**: Front desk staff can create, edit, and cancel reservations. Concierges can create reservations (e.g., for walk-ins) but editing and cancellation is handled by the front desk and management. Only admins can delete reservation records. +- **Room management**: Front desk and housekeeping staff can update room status (available, occupied, cleaning, maintenance). General managers inherit edit access. Only admins can delete rooms. +- **Guest profile protection**: Guest profiles are chain-wide records. All organization members can view guest profiles for cross-property service. Only admins can edit or delete guest records. +- **Service delivery**: Concierges can create and manage hotel services (spa, dining, transport). General managers have broader service management authority. All hotel staff can view available services. +- **Revenue data access**: Revenue managers and general managers can view financial data (ADR, RevPAR, occupancy). Regular staff cannot see revenue metrics. +- **Cross-property visibility**: Organization members can view resources at any hotel in the chain, but can only edit resources at hotels where they have an operational role. +- **Parent-scoped creation**: Room, reservation, and service creation are checked on the hotel (`can_create_room`, `can_create_reservation`, `can_create_service`) rather than on objects that do not exist yet. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `hospitality` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/hospitality/model.fga b/stores/hospitality/model.fga new file mode 100644 index 0000000..ed4f472 --- /dev/null +++ b/stores/hospitality/model.fga @@ -0,0 +1,60 @@ +model + schema 1.1 + +type user + +type organization + relations + define admin: [user] + define revenue_manager: [user] + define member: [user] or admin or revenue_manager + define can_create_hotel: admin + define can_create_guest: admin + +type hotel + relations + define organization: [organization] + define general_manager: [user] + define front_desk: [user] + define housekeeping: [user] + define concierge: [user] + define org_admin: admin from organization + define org_revenue_manager: revenue_manager from organization + define staff: [user] or general_manager or front_desk or housekeeping or concierge + define can_delete: org_admin + define can_edit: general_manager or can_delete + define can_create_room: can_edit + define can_create_reservation: front_desk or concierge or can_edit + define can_create_service: concierge or can_edit + define can_view_revenue: org_revenue_manager or can_edit + define can_view: staff or member from organization or can_edit + +type room + relations + define hotel: [hotel] + define can_delete: can_delete from hotel + define can_edit: front_desk from hotel or housekeeping from hotel or can_edit from hotel + define can_view: can_view from hotel or can_edit + +type reservation + relations + define hotel: [hotel] + define guest: [guest] + define can_delete: can_delete from hotel + define can_cancel: front_desk from hotel or can_edit from hotel + define can_edit: can_cancel + define can_view: can_view from hotel or can_edit + +type guest + relations + define organization: [organization] + define can_delete: admin from organization + define can_edit: can_delete + define can_view: member from organization or can_edit + +type service + relations + define hotel: [hotel] + define can_delete: can_delete from hotel + define can_edit: concierge from hotel or can_edit from hotel + define can_view: can_view from hotel or can_edit diff --git a/stores/hospitality/store.fga.yaml b/stores/hospitality/store.fga.yaml new file mode 100644 index 0000000..db82418 --- /dev/null +++ b/stores/hospitality/store.fga.yaml @@ -0,0 +1,378 @@ +name: Hospitality +model_file: model.fga +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:grand-hotel-group + - user: user:bob + relation: revenue_manager + object: organization:grand-hotel-group + + # All hotel staff are also org members + - user: user:charlie + relation: member + object: organization:grand-hotel-group + - user: user:maria + relation: member + object: organization:grand-hotel-group + - user: user:diana + relation: member + object: organization:grand-hotel-group + - user: user:eve + relation: member + object: organization:grand-hotel-group + - user: user:frank + relation: member + object: organization:grand-hotel-group + + # Hotel: grand-plaza + - user: organization:grand-hotel-group + relation: organization + object: hotel:grand-plaza + - user: user:charlie + relation: general_manager + object: hotel:grand-plaza + - user: user:maria + relation: front_desk + object: hotel:grand-plaza + - user: user:diana + relation: housekeeping + object: hotel:grand-plaza + - user: user:eve + relation: concierge + object: hotel:grand-plaza + + # Second hotel for isolation test (no staff assigned) + - user: organization:grand-hotel-group + relation: organization + object: hotel:seaside-resort + + # Room at grand-plaza + - user: hotel:grand-plaza + relation: hotel + object: room:room-101 + + # Reservation at grand-plaza linked to guest + - user: hotel:grand-plaza + relation: hotel + object: reservation:res-001 + - user: guest:guest-doe + relation: guest + object: reservation:res-001 + + # Guest profile + - user: organization:grand-hotel-group + relation: organization + object: guest:guest-doe + + # Service at grand-plaza + - user: hotel:grand-plaza + relation: hotel + object: service:spa-treatment + + # Reservation at seaside-resort (for isolation test) + - user: hotel:seaside-resort + relation: hotel + object: reservation:res-002 + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: organization:grand-hotel-group + assertions: + can_create_hotel: true + can_create_guest: true + - user: user:alice + object: hotel:grand-plaza + assertions: + can_delete: true + can_edit: true + can_create_room: true + can_create_reservation: true + can_create_service: true + can_view_revenue: true + can_view: true + - user: user:alice + object: room:room-101 + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:alice + object: reservation:res-001 + assertions: + can_delete: true + can_cancel: true + can_edit: true + can_view: true + - user: user:alice + object: guest:guest-doe + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:alice + object: service:spa-treatment + assertions: + can_delete: true + can_edit: true + can_view: true + + - name: Revenue manager views financial data but cannot edit operations + check: + - user: user:bob + object: organization:grand-hotel-group + assertions: + can_create_hotel: false + can_create_guest: false + - user: user:bob + object: hotel:grand-plaza + assertions: + can_delete: false + can_edit: false + can_view_revenue: true + can_view: true + - user: user:bob + object: room:room-101 + assertions: + can_edit: false + can_view: true + - user: user:bob + object: reservation:res-001 + assertions: + can_edit: false + can_cancel: false + can_view: true + - user: user:bob + object: guest:guest-doe + assertions: + can_edit: false + can_view: true + + - name: General manager manages all hotel operations + check: + - user: user:charlie + object: hotel:grand-plaza + assertions: + can_delete: false + can_edit: true + can_create_room: true + can_create_reservation: true + can_create_service: true + can_view_revenue: true + can_view: true + - user: user:charlie + object: room:room-101 + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:charlie + object: reservation:res-001 + assertions: + can_delete: false + can_cancel: true + can_edit: true + can_view: true + - user: user:charlie + object: service:spa-treatment + assertions: + can_delete: false + can_edit: true + can_view: true + + - name: Front desk manages reservations and room assignments + check: + - user: user:maria + object: hotel:grand-plaza + assertions: + can_delete: false + can_edit: false + can_create_reservation: true + can_view_revenue: false + can_view: true + - user: user:maria + object: room:room-101 + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:maria + object: reservation:res-001 + assertions: + can_delete: false + can_cancel: true + can_edit: true + can_view: true + - user: user:maria + object: service:spa-treatment + assertions: + can_edit: false + can_view: true + + - name: Housekeeping manages room status only + check: + - user: user:diana + object: room:room-101 + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:diana + object: reservation:res-001 + assertions: + can_edit: false + can_cancel: false + can_view: true + - user: user:diana + object: service:spa-treatment + assertions: + can_edit: false + can_view: true + - user: user:diana + object: hotel:grand-plaza + assertions: + can_edit: false + can_create_reservation: false + can_view: true + + - name: Concierge manages services and can create reservations + check: + - user: user:eve + object: hotel:grand-plaza + assertions: + can_edit: false + can_create_reservation: true + can_create_service: true + can_view_revenue: false + can_view: true + - user: user:eve + object: service:spa-treatment + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:eve + object: reservation:res-001 + assertions: + can_edit: false + can_cancel: false + can_view: true + - user: user:eve + object: room:room-101 + assertions: + can_edit: false + can_view: true + + - name: Corporate member has read-only access across all hotels + check: + - user: user:frank + object: organization:grand-hotel-group + assertions: + can_create_hotel: false + can_create_guest: false + - user: user:frank + object: hotel:grand-plaza + assertions: + can_delete: false + can_edit: false + can_create_reservation: false + can_view_revenue: false + can_view: true + - user: user:frank + object: room:room-101 + assertions: + can_edit: false + can_view: true + - user: user:frank + object: reservation:res-001 + assertions: + can_edit: false + can_view: true + - user: user:frank + object: guest:guest-doe + assertions: + can_edit: false + can_view: true + + - name: Hotel staff cannot manage other hotels resources + check: + - user: user:maria + object: reservation:res-002 + assertions: + can_edit: false + can_cancel: false + can_view: true + - user: user:maria + object: hotel:seaside-resort + assertions: + can_edit: false + can_create_reservation: false + can_view: true + + - name: List hotels for staff roles + list_objects: + - user: user:charlie + type: hotel + assertions: + general_manager: + - hotel:grand-plaza + - user: user:maria + type: hotel + assertions: + front_desk: + - hotel:grand-plaza + - user: user:diana + type: hotel + assertions: + housekeeping: + - hotel:grand-plaza + + - name: List reservations for guest + list_objects: + - user: guest:guest-doe + type: reservation + assertions: + guest: + - reservation:res-001 + + - name: List organizations for hotels + list_objects: + - user: organization:grand-hotel-group + type: hotel + assertions: + organization: + - hotel:grand-plaza + - hotel:seaside-resort + + - name: List users with general manager access to hotel grand-plaza + list_users: + - object: hotel:grand-plaza + user_filter: + - type: user + assertions: + general_manager: + users: + - user:charlie + + - name: List users with front desk access to hotel grand-plaza + list_users: + - object: hotel:grand-plaza + user_filter: + - type: user + assertions: + front_desk: + users: + - user:maria + + - name: List users with guest access to reservation res-001 + list_users: + - object: reservation:res-001 + user_filter: + - type: guest + assertions: + guest: + users: + - guest:guest-doe diff --git a/stores/human-resources/README.md b/stores/human-resources/README.md new file mode 100644 index 0000000..649eba3 --- /dev/null +++ b/stores/human-resources/README.md @@ -0,0 +1,24 @@ +# OpenFGA for HR, Payroll & Directory Systems + +## Use Case + +This model represents the authorization needs of a **human resources information system (HRIS)**, like BambooHR, Workday, or Rippling. These platforms manage employee records, payroll, benefits, time-off, and organizational structure. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own employees, teams, and HR data. +- **Role-based access**: Organization-level roles (`admin`, `hr_manager`, `member`) control who can manage employees, view sensitive data, and approve payroll. +- **Employee self-service**: Employees can view their own records and sensitive fields (SSN, salary), while managers can only see non-sensitive data for their direct reports. +- **Manager hierarchy**: Direct managers can view (but not edit) employee records, while HR managers and admins can edit them. +- **Sensitive data isolation**: Personally identifiable information (SSN, date of birth) is gated behind `can_view_sensitive`, accessible only to the employee themselves and HR administrators. +- **Payroll & benefits**: Only HR managers and admins can view payroll runs and benefits. Only admins can approve payroll. +- **Time-off workflow**: Employees submit requests, designated approvers and HR managers can approve. The requester or anyone with employee management permissions (HR managers, admins) can cancel. +- **Team hierarchy**: Teams support parent-child nesting — members of a parent team can view child teams. Group members and managers can view their groups, and only managers or admins can manage them. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `human-resources` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/human-resources/model.fga b/stores/human-resources/model.fga new file mode 100644 index 0000000..c7de5a1 --- /dev/null +++ b/stores/human-resources/model.fga @@ -0,0 +1,91 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or hr_manager + define admin: [user] + define hr_manager: [user] + define can_manage_employees: admin or hr_manager + define can_view_sensitive_data: admin or hr_manager + +type company + relations + define organization: [organization] + define admin: [user] or admin from organization + define member: [user] or member from organization + define can_manage_employees: can_manage_employees from organization + define can_view_sensitive_data: can_view_sensitive_data from organization or can_manage_employees + define can_edit: admin or can_manage_employees + define can_view: member or can_edit + +type group + relations + define organization: [organization] + define manager: [user] + define member: [user] + define can_manage: manager or admin from organization + define can_view: member or can_manage + +type team + relations + define organization: [organization] + define manager: [user] + define member: [user] + define parent_team: [team] + define can_manage: manager or admin from organization + define can_view: member or member from parent_team or can_manage + +type location + relations + define organization: [organization] + define can_edit: admin from organization + define can_view: member from organization or can_edit + +type employee + relations + define company: [company] + define assignee: [user] + define manager: [user] + define team: [team] + define company_can_manage_employees: can_manage_employees from company + define company_can_view_sensitive_data: can_view_sensitive_data from company + define hr_admin: [user] or company_can_manage_employees + define team_manager: manager from team + define can_terminate: company_can_manage_employees + define can_edit: hr_admin or can_terminate + define can_view_sensitive: assignee or company_can_view_sensitive_data or can_edit + define can_view: manager or team_manager or can_view_sensitive + +type employment + relations + define employee: [employee] + define viewer: [user] + define can_edit: can_edit from employee + define can_view_sensitive: can_view_sensitive from employee + define can_view: viewer or manager from employee or can_view_sensitive or can_edit + +type benefit + relations + define employee: [employee] + define viewer: [user] + define can_edit: can_edit from employee + define can_view_sensitive: can_view_sensitive from employee + define can_view: viewer or can_view_sensitive or can_edit + +type payroll_run + relations + define organization: [organization] + define can_approve: admin from organization + define can_view: hr_manager from organization or can_approve + +type time_off + relations + define employee: [employee] + define requester: [user] + define approver: [user] + define can_cancel: requester or company_can_manage_employees from employee + define can_approve: approver or company_can_manage_employees from employee + define can_view: manager from employee or can_approve or can_cancel diff --git a/stores/human-resources/store.fga.yaml b/stores/human-resources/store.fga.yaml new file mode 100644 index 0000000..d5ed2c0 --- /dev/null +++ b/stores/human-resources/store.fga.yaml @@ -0,0 +1,337 @@ +name: Human Resources Authorization Model Tests +model_file: model.fga + +tuples: + # Organization setup + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: hr_manager + object: organization:acme + - user: user:charlie + relation: member + object: organization:acme + - user: user:diana + relation: member + object: organization:acme + + # Company belongs to org + - user: organization:acme + relation: organization + object: company:acme-corp + + # Team setup + - user: organization:acme + relation: organization + object: team:engineering + - user: user:charlie + relation: manager + object: team:engineering + - user: user:diana + relation: member + object: team:engineering + + # Child team for hierarchy test + - user: organization:acme + relation: organization + object: team:backend + - user: team:engineering + relation: parent_team + object: team:backend + - user: user:eve + relation: member + object: team:backend + + # Group setup + - user: organization:acme + relation: organization + object: group:developers + - user: user:diana + relation: member + object: group:developers + + # Location + - user: organization:acme + relation: organization + object: location:hq-office + + # Employee setup - Diana is the employee + - user: company:acme-corp + relation: company + object: employee:diana-record + - user: user:diana + relation: assignee + object: employee:diana-record + - user: user:charlie + relation: manager + object: employee:diana-record + + # Employment for Diana (linked to employee) + - user: employee:diana-record + relation: employee + object: employment:diana-swe + + # Benefit for Diana (linked to employee) + - user: employee:diana-record + relation: employee + object: benefit:diana-health + + # Payroll run + - user: organization:acme + relation: organization + object: payroll_run:march-2026 + + # Time off request (linked to employee) + - user: employee:diana-record + relation: employee + object: time_off:diana-vacation + - user: user:diana + relation: requester + object: time_off:diana-vacation + - user: user:charlie + relation: approver + object: time_off:diana-vacation + +tests: + - name: Organization admin has full access + check: + - user: user:alice + object: organization:acme + assertions: + admin: true + can_manage_employees: true + can_view_sensitive_data: true + + - name: HR manager can manage employees + check: + - user: user:bob + object: organization:acme + assertions: + hr_manager: true + can_manage_employees: true + can_view_sensitive_data: true + + - name: Employee can view own record + check: + - user: user:diana + object: employee:diana-record + assertions: + assignee: true + can_view: true + can_view_sensitive: true + can_edit: false + + - name: Manager can view employee record but not sensitive data + check: + - user: user:charlie + object: employee:diana-record + assertions: + manager: true + can_view: true + can_view_sensitive: false + can_edit: false + + - name: HR manager can edit employee records + check: + - user: user:bob + object: employee:diana-record + assertions: + can_view: true + can_view_sensitive: true + can_edit: true + + - name: Admin can terminate employees + check: + - user: user:alice + object: employee:diana-record + assertions: + can_view: true + can_edit: true + can_terminate: true + + - name: Team manager can view team + check: + - user: user:charlie + object: team:engineering + assertions: + manager: true + can_view: true + can_manage: true + + - name: Team member can view team + check: + - user: user:diana + object: team:engineering + assertions: + member: true + can_view: true + can_manage: false + + - name: Org members can view locations + check: + - user: user:charlie + object: location:hq-office + assertions: + can_view: true + can_edit: false + - user: user:alice + object: location:hq-office + assertions: + can_view: true + can_edit: true + + - name: Only HR and admin can view payroll + check: + - user: user:bob + object: payroll_run:march-2026 + assertions: + can_view: true + can_approve: false + - user: user:alice + object: payroll_run:march-2026 + assertions: + can_view: true + can_approve: true + - user: user:diana + object: payroll_run:march-2026 + assertions: + can_view: false + can_approve: false + + - name: Time off request permissions + check: + - user: user:diana + object: time_off:diana-vacation + assertions: + can_view: true + can_approve: false + can_cancel: true + - user: user:charlie + object: time_off:diana-vacation + assertions: + can_view: true + can_approve: true + can_cancel: false + - user: user:bob + object: time_off:diana-vacation + assertions: + can_view: true + can_approve: true + + - name: Employment visibility + check: + - user: user:alice + object: employment:diana-swe + assertions: + can_view: true + can_edit: true + - user: user:bob + object: employment:diana-swe + assertions: + can_view: true + can_edit: true + + - name: Benefit visibility + check: + - user: user:bob + object: benefit:diana-health + assertions: + can_view: true + can_edit: true + - user: user:alice + object: benefit:diana-health + assertions: + can_view: true + can_edit: true + + - name: Team parent-child inheritance + check: + - user: user:diana + object: team:backend + assertions: + can_view: true + - user: user:eve + object: team:backend + assertions: + can_view: true + + - name: List teams for organization members + list_objects: + - user: user:charlie + type: team + assertions: + manager: + - team:engineering + - user: user:diana + type: team + assertions: + member: + - team:engineering + - user: user:eve + type: team + assertions: + member: + - team:backend + + - name: List employees assigned to users + list_objects: + - user: user:diana + type: employee + assertions: + assignee: + - employee:diana-record + + - name: List groups for organization members + list_objects: + - user: user:diana + type: group + assertions: + member: + - group:developers + + - name: List employment records for employees + list_objects: + - user: employee:diana-record + type: employment + assertions: + employee: + - employment:diana-swe + + - name: List time off requests for employees + list_objects: + - user: user:diana + type: time_off + assertions: + requester: + - time_off:diana-vacation + + - name: List users with manager access to employee diana-record + list_users: + - object: employee:diana-record + user_filter: + - type: user + assertions: + manager: + users: + - user:charlie + + - name: List users with approver access to time off diana-vacation + list_users: + - object: time_off:diana-vacation + user_filter: + - type: user + assertions: + approver: + users: + - user:charlie + + - name: List users who are team managers + list_users: + - object: team:engineering + user_filter: + - type: user + assertions: + manager: + users: + - user:charlie diff --git a/stores/iot/store.fga.yaml b/stores/iot/store.fga.yaml index c93ce54..32f5f27 100644 --- a/stores/iot/store.fga.yaml +++ b/stores/iot/store.fga.yaml @@ -1,7 +1,7 @@ # Documentation: https://openfga.dev/docs/modeling/advanced/iot # FGA Playground: https://play.fga.dev/sandbox/?store=iot name: IoT -model_file: ./model.fga +model_file: model.fga tuples: # Beth is an IT Admin on Device 1 - user: user:beth diff --git a/stores/issue-tracking/README.md b/stores/issue-tracking/README.md new file mode 100644 index 0000000..65eda60 --- /dev/null +++ b/stores/issue-tracking/README.md @@ -0,0 +1,24 @@ +# OpenFGA for Issue Tracking Systems + +## Use Case + +This model represents the authorization needs of an **issue tracking/project management system**, like Jira, Linear, or Asana. These platforms manage tickets, issues, tasks, collections/projects, comments, and attachments across support and engineering workflows. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own teams, collections, tickets, and contacts. +- **Agent and admin roles**: Organization-level roles (`admin`, `agent`, `member`) control access. Admins have full control, agents can manage tickets and contacts, and members have limited access. +- **Collection hierarchy**: Collections (projects/boards) support parent-child nesting with permission inheritance. Viewers of a parent collection automatically gain visibility into child collections. +- **Ticket access control**: Tickets inherit visibility from their collection. Assignees, creators, reporters, and team members can view tickets. Agents and admins get organization-wide ticket access. +- **Team-based assignment**: Tickets can be assigned to teams. All team members gain edit access, and team leads can close tickets assigned to their team. +- **Comment ownership**: Comment authors can edit and delete their own comments. Other ticket viewers can see comments but not modify them. Admins can delete any comment. +- **Attachment lifecycle**: Attachment visibility follows the parent ticket. Uploaders, team leads on the assigned team, and admins can delete attachments. +- **Contact management**: Only agents and admins can view and manage external contacts (customers, leads), keeping customer data restricted from regular members. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `issue-tracking` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/issue-tracking/model.fga b/stores/issue-tracking/model.fga new file mode 100644 index 0000000..4c33a17 --- /dev/null +++ b/stores/issue-tracking/model.fga @@ -0,0 +1,70 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or agent + define admin: [user] + define agent: [user] + +type team + relations + define organization: [organization] + define member: [user] + define lead: [user] + define can_manage: lead or admin from organization + define can_view: member or can_manage + +type collection + relations + define organization: [organization] + define parent_collection: [collection] + define owner: [user] + define organization_admin: admin from organization + define organization_agent: agent from organization + define viewer: [user, team#member] or owner or organization_admin or viewer from parent_collection + define can_delete: organization_admin + define can_edit: owner or can_delete + define can_create_ticket: owner or organization_agent or can_delete + define can_view: viewer or can_edit + +type ticket + relations + define collection: [collection] + define assignee: [user] + define assigned_team: [team] + define creator: [user] + define reporter: [user] + define organization_admin: organization_admin from collection + define organization_agent: organization_agent from collection + define viewer: [user, team#member] or assignee or creator or reporter or member from assigned_team or viewer from collection + define editor: [user] or assignee or member from assigned_team + define team_lead: lead from assigned_team + define can_delete: organization_admin + define can_assign: organization_agent or can_delete + define can_close: assignee or team_lead or can_delete + define can_edit: editor or can_assign + define can_view: viewer or can_close or can_edit + +type comment + relations + define ticket: [ticket] + define author: [user] + define can_delete: author or organization_admin from ticket + define can_edit: author + define can_view: can_edit or can_view from ticket + +type attachment + relations + define ticket: [ticket] + define uploader: [user] + define can_delete: uploader or team_lead from ticket or organization_admin from ticket + define can_view: can_view from ticket + +type contact + relations + define organization: [organization] + define can_edit: agent from organization or admin from organization + define can_view: can_edit diff --git a/stores/issue-tracking/store.fga.yaml b/stores/issue-tracking/store.fga.yaml new file mode 100644 index 0000000..dae0ed9 --- /dev/null +++ b/stores/issue-tracking/store.fga.yaml @@ -0,0 +1,264 @@ +name: Issue Tracking Model Tests +model_file: model.fga + +tuples: + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: agent + object: organization:acme + - user: user:charlie + relation: member + object: organization:acme + + - user: organization:acme + relation: organization + object: team:support + - user: user:bob + relation: member + object: team:support + - user: user:charlie + relation: lead + object: team:support + + - user: organization:acme + relation: organization + object: collection:bugs + - user: user:bob + relation: owner + object: collection:bugs + + # Child collection for hierarchy test + - user: organization:acme + relation: organization + object: collection:ui-bugs + - user: collection:bugs + relation: parent_collection + object: collection:ui-bugs + + - user: collection:bugs + relation: collection + object: ticket:bug-123 + - user: user:diana + relation: creator + object: ticket:bug-123 + - user: user:bob + relation: assignee + object: ticket:bug-123 + - user: team:support + relation: assigned_team + object: ticket:bug-123 + + - user: ticket:bug-123 + relation: ticket + object: comment:c-001 + - user: user:bob + relation: author + object: comment:c-001 + + - user: ticket:bug-123 + relation: ticket + object: attachment:screenshot + - user: user:diana + relation: uploader + object: attachment:screenshot + + - user: organization:acme + relation: organization + object: contact:customer-xyz + +tests: + - name: Admin has full ticket access + check: + - user: user:alice + object: ticket:bug-123 + assertions: + can_view: true + can_edit: true + can_close: true + can_assign: true + can_delete: true + + - name: Assignee can view edit and close ticket + check: + - user: user:bob + object: ticket:bug-123 + assertions: + can_view: true + can_edit: true + can_close: true + can_delete: false + + - name: Creator can view ticket + check: + - user: user:diana + object: ticket:bug-123 + assertions: + can_view: true + can_edit: false + can_close: false + + - name: Team lead can close tickets assigned to team + check: + - user: user:charlie + object: ticket:bug-123 + assertions: + can_view: true + can_close: true + + - name: Comment author can edit own comments + check: + - user: user:bob + object: comment:c-001 + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:diana + object: comment:c-001 + assertions: + can_view: true + can_edit: false + + - name: Attachment permissions follow ticket + check: + - user: user:diana + object: attachment:screenshot + assertions: + can_view: true + can_delete: true + - user: user:bob + object: attachment:screenshot + assertions: + can_view: true + can_delete: false + + - name: Collection owner can manage collection + check: + - user: user:bob + object: collection:bugs + assertions: + can_view: true + can_edit: true + can_delete: false + + - name: Collection hierarchy inheritance + check: + - user: user:bob + object: collection:ui-bugs + assertions: + can_view: true + + - name: Admin can delete any comment + check: + - user: user:alice + object: comment:c-001 + assertions: + can_view: true + can_delete: true + + - name: Admin can delete any attachment + check: + - user: user:alice + object: attachment:screenshot + assertions: + can_view: true + can_delete: true + + - name: Agent can manage contacts + check: + - user: user:bob + object: contact:customer-xyz + assertions: + can_view: true + can_edit: true + - user: user:charlie + object: contact:customer-xyz + assertions: + can_view: false + + - name: Admin can access multiple tickets + list_objects: + - user: user:alice + type: ticket + assertions: + can_view: + - ticket:bug-123 + - user: user:bob + type: ticket + assertions: + can_view: + - ticket:bug-123 + - user: user:diana + type: ticket + assertions: + can_view: + - ticket:bug-123 + + - name: List collections accessible by users + list_objects: + - user: user:bob + type: collection + assertions: + can_view: + - collection:bugs + - collection:ui-bugs + can_edit: + - collection:bugs + + - name: Attachment visibility by users + list_objects: + - user: user:diana + type: attachment + assertions: + can_view: + - attachment:screenshot + can_delete: + - attachment:screenshot + + - name: Comments accessible by assignee and author + list_objects: + - user: user:bob + type: comment + assertions: + can_view: + - comment:c-001 + can_edit: + - comment:c-001 + + - name: List users with access to ticket + list_users: + - object: ticket:bug-123 + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:diana + - user:charlie + + - name: List users who can close ticket + list_users: + - object: ticket:bug-123 + user_filter: + - type: user + assertions: + can_close: + users: + - user:alice + - user:bob + - user:charlie + + - name: List users with access to collection + list_users: + - object: collection:bugs + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob diff --git a/stores/kms/README.md b/stores/kms/README.md new file mode 100644 index 0000000..57d62db --- /dev/null +++ b/stores/kms/README.md @@ -0,0 +1,22 @@ +# OpenFGA for Knowledge Management Systems + +## Use Case + +This model represents the authorization needs of a **knowledge management system (KMS)**, like Confluence, Notion, or Guru. These platforms manage spaces, pages, and comments to organize team knowledge and documentation. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own knowledge bases, pages, and discussions. +- **Knowledge roles**: Organization-level roles (`admin`, `editor`, `viewer`) control access. Admins have full access, editors can create and modify content, and viewers have read-only access. +- **Space management**: Space owners can edit and manage space membership. Only admins can delete spaces, protecting organizational knowledge. +- **Page authorship**: Page authors can edit and delete their own pages. Editors can modify any page in the organization. Only admins can publish pages, enabling a review workflow. +- **Comment ownership**: Comment authors can edit and delete their own comments. Viewers can read all comments but cannot modify others' comments. +- **Publishing workflow**: Only admins can publish pages, ensuring content quality review before making knowledge articles official. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `kms` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/kms/model.fga b/stores/kms/model.fga new file mode 100644 index 0000000..6248e5d --- /dev/null +++ b/stores/kms/model.fga @@ -0,0 +1,43 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or editor or viewer + define admin: [user] + define editor: [user] + define viewer: [user] + +type space + relations + define organization: [organization] + define owner: [user] + define organization_admin: admin from organization + define organization_editor: editor from organization + define organization_viewer: viewer from organization + define can_delete: organization_admin + define can_manage_members: owner or can_delete + define can_edit: organization_editor or can_manage_members + define can_view: organization_viewer or can_edit + +type page + relations + define space: [space] + define author: [user] + define organization_admin: organization_admin from space + define organization_editor: organization_editor from space + define organization_viewer: organization_viewer from space + define can_publish: organization_admin + define can_delete: author or can_publish + define can_edit: organization_editor or can_delete + define can_view: organization_viewer or can_edit + +type comment + relations + define page: [page] + define author: [user] + define can_delete: author or organization_admin from page + define can_edit: can_delete + define can_view: can_view from page or can_edit diff --git a/stores/kms/store.fga.yaml b/stores/kms/store.fga.yaml new file mode 100644 index 0000000..1e2c824 --- /dev/null +++ b/stores/kms/store.fga.yaml @@ -0,0 +1,245 @@ +name: Knowledge Management System Authorization Model Tests +model_file: model.fga + +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:wiki-co + - user: user:bob + relation: editor + object: organization:wiki-co + - user: user:charlie + relation: viewer + object: organization:wiki-co + - user: user:diana + relation: member + object: organization:wiki-co + + # Space + - user: organization:wiki-co + relation: organization + object: space:engineering + - user: user:bob + relation: owner + object: space:engineering + + # Page in space + - user: space:engineering + relation: space + object: page:architecture-guide + - user: user:bob + relation: author + object: page:architecture-guide + + # Comment on page + - user: page:architecture-guide + relation: page + object: comment:feedback-001 + - user: user:charlie + relation: author + object: comment:feedback-001 + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: space:engineering + assertions: + can_view: true + can_edit: true + can_delete: true + can_manage_members: true + - user: user:alice + object: page:architecture-guide + assertions: + can_view: true + can_edit: true + can_delete: true + can_publish: true + - user: user:alice + object: comment:feedback-001 + assertions: + can_view: true + can_edit: true + can_delete: true + + - name: Editor can edit spaces and pages but not delete spaces + check: + - user: user:bob + object: space:engineering + assertions: + can_view: true + can_edit: true + can_delete: false + can_manage_members: true + - user: user:bob + object: page:architecture-guide + assertions: + can_view: true + can_edit: true + can_delete: true + can_publish: false + - user: user:bob + object: comment:feedback-001 + assertions: + can_view: true + can_edit: false + can_delete: false + + - name: Viewer has read-only access except on own comments + check: + - user: user:charlie + object: space:engineering + assertions: + can_view: true + can_edit: false + can_delete: false + can_manage_members: false + - user: user:charlie + object: page:architecture-guide + assertions: + can_view: true + can_edit: false + can_delete: false + can_publish: false + - user: user:charlie + object: comment:feedback-001 + assertions: + can_view: true + can_edit: true + can_delete: true + + - name: Member has no access to resources + check: + - user: user:diana + object: space:engineering + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: page:architecture-guide + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: comment:feedback-001 + assertions: + can_view: false + can_edit: false + can_delete: false + + - name: Page publishing requires admin + check: + - user: user:alice + object: page:architecture-guide + assertions: + can_publish: true + - user: user:bob + object: page:architecture-guide + assertions: + can_publish: false + - user: user:charlie + object: page:architecture-guide + assertions: + can_publish: false + + - name: Comment author can edit and delete own comments + check: + - user: user:charlie + object: comment:feedback-001 + assertions: + can_view: true + can_edit: true + can_delete: true + + - name: List spaces accessible by users + list_objects: + - user: user:alice + type: space + assertions: + can_view: + - space:engineering + can_edit: + - space:engineering + can_delete: + - space:engineering + - user: user:bob + type: space + assertions: + can_view: + - space:engineering + can_edit: + - space:engineering + - user: user:charlie + type: space + assertions: + can_view: + - space:engineering + + - name: List pages accessible by users + list_objects: + - user: user:alice + type: page + assertions: + can_view: + - page:architecture-guide + can_edit: + - page:architecture-guide + can_delete: + - page:architecture-guide + - user: user:bob + type: page + assertions: + can_view: + - page:architecture-guide + can_edit: + - page:architecture-guide + + - name: List comments accessible by users + list_objects: + - user: user:charlie + type: comment + assertions: + can_view: + - comment:feedback-001 + can_edit: + - comment:feedback-001 + can_delete: + - comment:feedback-001 + + - name: List users with access to space + list_users: + - object: space:engineering + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + + - name: List users who can edit pages + list_users: + - object: page:architecture-guide + user_filter: + - type: user + assertions: + can_edit: + users: + - user:alice + - user:bob + + - name: List users who can delete comments + list_users: + - object: comment:feedback-001 + user_filter: + - type: user + assertions: + can_delete: + users: + - user:alice + - user:charlie diff --git a/stores/knowledge-base/README.md b/stores/knowledge-base/README.md new file mode 100644 index 0000000..336e125 --- /dev/null +++ b/stores/knowledge-base/README.md @@ -0,0 +1,24 @@ +# OpenFGA for Knowledge Base Systems + +## Use Case + +This model represents the authorization needs of a **knowledge base/wiki platform**, like Confluence or Notion. These platforms manage articles, pages, containers (spaces, folders, databases), groups, and access permissions for documentation and knowledge sharing. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own knowledge base spaces, articles, and user groups. +- **Container hierarchy**: Containers (spaces, folders, sections, categories, databases) support parent-child nesting with permission inheritance. Granting access to a parent space cascades to all nested containers and articles. +- **Article visibility inheritance**: Articles inherit view and edit permissions from their parent container. Authors get additional publish, archive, and delete rights. +- **Public content**: Articles can be made public using wildcards (`user:*`), enabling unauthenticated or organization-wide read access for FAQ pages, public documentation, or help center content. +- **Group-based access**: Groups can be granted editor or viewer access to containers and articles, enabling team-based knowledge management (e.g., "docs-team" can edit the engineering space). +- **Author ownership**: Article authors can publish, archive, and delete their own articles. Organization admins can also perform these actions on any article. +- **Attachment lifecycle**: Attachments follow the visibility of their parent article. Only the uploader or an admin can delete attachments. +- **Organization-wide readability**: All organization members can view containers and articles by default, supporting a culture of knowledge sharing. Editing is restricted to designated editors, groups, and admins. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `knowledge-base` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/knowledge-base/model.fga b/stores/knowledge-base/model.fga new file mode 100644 index 0000000..d0d5d7a --- /dev/null +++ b/stores/knowledge-base/model.fga @@ -0,0 +1,48 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or editor + define admin: [user] + define editor: [user] + +type group + relations + define organization: [organization] + define member: [user] + +type container + relations + define organization: [organization] + define organization_admin: admin from organization + define parent_container: [container] + define owner: [user] + define editor: [user, group#member] or owner or organization_admin or editor from parent_container + define viewer: [user, group#member, user:*] or editor or viewer from parent_container or member from organization + define can_delete: owner or organization_admin + define can_edit: editor or can_delete + define can_create_article: editor or can_delete + define can_view: viewer or can_edit + +type article + relations + define parent_container: [container] + define organization_admin: organization_admin from parent_container + define author: [user] + define editor: [user, group#member] or author or editor from parent_container + define viewer: [user, group#member, user:*] or editor or viewer from parent_container + define can_delete: author or organization_admin + define can_archive: can_delete + define can_publish: can_delete + define can_edit: editor or can_delete + define can_view: viewer or can_edit + +type attachment + relations + define article: [article] + define uploader: [user] + define can_view: uploader or can_view from article + define can_delete: uploader or organization_admin from article diff --git a/stores/knowledge-base/store.fga.yaml b/stores/knowledge-base/store.fga.yaml new file mode 100644 index 0000000..84b9df2 --- /dev/null +++ b/stores/knowledge-base/store.fga.yaml @@ -0,0 +1,269 @@ +name: Knowledge Base Authorization Model Tests +model_file: model.fga + +tuples: + - user: user:alice + relation: admin + object: organization:acme + - user: user:bob + relation: editor + object: organization:acme + - user: user:charlie + relation: member + object: organization:acme + + - user: organization:acme + relation: organization + object: group:docs-team + - user: user:bob + relation: member + object: group:docs-team + - user: user:dave + relation: member + object: group:docs-team + + # Top-level space container + - user: organization:acme + relation: organization + object: container:engineering-space + - user: user:bob + relation: owner + object: container:engineering-space + - user: group:docs-team#member + relation: editor + object: container:engineering-space + + # Nested folder container + - user: organization:acme + relation: organization + object: container:api-docs + - user: container:engineering-space + relation: parent_container + object: container:api-docs + + # Article in the API docs container + - user: container:api-docs + relation: parent_container + object: article:getting-started + - user: user:bob + relation: author + object: article:getting-started + + # Public article (anyone can view) + - user: user:* + relation: viewer + object: article:public-faq + - user: user:bob + relation: author + object: article:public-faq + + # Attachment + - user: article:getting-started + relation: article + object: attachment:diagram-png + - user: user:bob + relation: uploader + object: attachment:diagram-png + +tests: + - name: Admin has full access to containers + check: + - user: user:alice + object: container:engineering-space + assertions: + can_view: true + can_edit: true + can_delete: true + + - name: Container owner can manage container + check: + - user: user:bob + object: container:engineering-space + assertions: + can_view: true + can_edit: true + can_create_article: true + can_delete: true + + - name: Org members can view containers + check: + - user: user:charlie + object: container:engineering-space + assertions: + can_view: true + can_edit: false + + - name: Nested container inherits from parent + check: + - user: user:bob + object: container:api-docs + assertions: + can_view: true + can_edit: true + - user: user:charlie + object: container:api-docs + assertions: + can_view: true + can_edit: false + + - name: Article author can publish and archive + check: + - user: user:bob + object: article:getting-started + assertions: + can_view: true + can_edit: true + can_publish: true + can_archive: true + can_delete: true + + - name: Article inherits container visibility + check: + - user: user:charlie + object: article:getting-started + assertions: + can_view: true + can_edit: false + can_publish: false + + - name: Public article is viewable by anyone + check: + - user: user:charlie + object: article:public-faq + assertions: + can_view: true + - user: user:bob + object: article:public-faq + assertions: + can_view: true + can_publish: true + + - name: Group-based access to containers + check: + - user: user:dave + object: container:engineering-space + assertions: + can_view: true + can_edit: true + can_create_article: true + - user: user:dave + object: article:getting-started + assertions: + can_view: true + can_edit: true + + - name: Attachment follows article visibility + check: + - user: user:bob + object: attachment:diagram-png + assertions: + can_view: true + can_delete: true + - user: user:charlie + object: attachment:diagram-png + assertions: + can_view: true + can_delete: false + + - name: List containers accessible by users + list_objects: + - user: user:alice + type: container + assertions: + can_view: + - container:engineering-space + - container:api-docs + can_edit: + - container:engineering-space + - container:api-docs + - user: user:bob + type: container + assertions: + can_view: + - container:engineering-space + - container:api-docs + can_edit: + - container:engineering-space + - container:api-docs + - user: user:dave + type: container + assertions: + can_view: + - container:engineering-space + - container:api-docs + can_edit: + - container:engineering-space + - container:api-docs + + - name: List articles accessible by users + list_objects: + - user: user:bob + type: article + assertions: + can_view: + - article:getting-started + - article:public-faq + can_edit: + - article:getting-started + - article:public-faq + can_publish: + - article:getting-started + - article:public-faq + - user: user:charlie + type: article + assertions: + can_view: + - article:getting-started + - article:public-faq + + - name: List attachments accessible by users + list_objects: + - user: user:bob + type: attachment + assertions: + can_view: + - attachment:diagram-png + can_delete: + - attachment:diagram-png + + - name: List users with access to engineering container + list_users: + - object: container:engineering-space + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:dave + can_edit: + users: + - user:alice + - user:bob + - user:dave + + - name: List users who can publish articles + list_users: + - object: article:getting-started + user_filter: + - type: user + assertions: + can_publish: + users: + - user:alice + - user:bob + + - name: List users with access to attachment + list_users: + - object: attachment:diagram-png + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:dave diff --git a/stores/lms/README.md b/stores/lms/README.md new file mode 100644 index 0000000..2f46acf --- /dev/null +++ b/stores/lms/README.md @@ -0,0 +1,24 @@ +# OpenFGA for Learning Management Systems + +## Use Case + +This model represents the authorization needs of a **learning management system (LMS)**, like Canvas, Moodle, or Blackboard. These platforms manage courses, classes, content, collections, and learning activities across educational organizations. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own courses, classes, and learning materials. +- **Educational roles**: Organization-level roles (`admin`, `instructor`, `student`) control access. Admins have full access, instructors can manage courses and grade work, and students can view enrolled content and submit assignments. +- **Course management**: Course owners can edit and publish courses. Enrolled instructors can edit course content. Students enrolled in a course can view it but cannot modify it. +- **Class enrollment**: Instructors and admins can enroll students in class sections. Students can view classes they are enrolled in but cannot modify class settings. +- **Content authorship**: Content authors can edit and delete their own materials. Instructors have broader edit access across the organization. +- **Activity grading workflow**: Instructors and activity creators can grade assignments. Only assigned students can submit work, preventing unauthorized submissions. +- **Course publishing**: Only course owners and admins can publish courses, ensuring curriculum review before content goes live. +- **Collection management**: Collection owners and instructors can organize learning materials. Only admins can delete collections. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `lms` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/lms/model.fga b/stores/lms/model.fga new file mode 100644 index 0000000..de2470d --- /dev/null +++ b/stores/lms/model.fga @@ -0,0 +1,66 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or instructor or student + define admin: [user] + define instructor: [user] + define student: [user] + +type course + relations + define organization: [organization] + define owner: [user] + define enrolled_instructor: [user] + define enrolled_student: [user] + define organization_admin: admin from organization + define organization_instructor: instructor from organization + define organization_student: student from organization + define can_delete: organization_admin + define can_publish: owner or can_delete + define can_edit: enrolled_instructor or can_publish + define can_create_class: can_edit + define can_create_content: can_edit + define can_view: enrolled_student or organization_instructor or can_edit + +type class + relations + define course: [course] + define instructor: [user] + define student: [user] + define organization_admin: organization_admin from course + define organization_instructor: organization_instructor from course + define can_delete: organization_admin + define can_enroll: instructor or can_delete + define can_edit: enrolled_instructor from course or can_enroll + define can_view: student or can_view from course or can_edit + +type content + relations + define course: [course] + define author: [user] + define can_delete: author or organization_admin from course + define can_edit: organization_instructor from course or can_delete + define can_view: organization_student from course or can_edit + +type collection + relations + define organization: [organization] + define owner: [user] + define can_delete: admin from organization + define can_edit: owner or instructor from organization or can_delete + define can_view: student from organization or can_edit + +type activity + relations + define class: [class] + define creator: [user] + define assignee: [user] + define can_delete: organization_admin from class + define can_grade: creator or instructor from class or organization_instructor from class or can_delete + define can_edit: can_grade + define can_view: assignee or can_edit + define can_submit: assignee diff --git a/stores/lms/store.fga.yaml b/stores/lms/store.fga.yaml new file mode 100644 index 0000000..da1aa88 --- /dev/null +++ b/stores/lms/store.fga.yaml @@ -0,0 +1,452 @@ +name: Learning Management System Authorization Model Tests +model_file: model.fga + +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:university + - user: user:bob + relation: instructor + object: organization:university + - user: user:charlie + relation: student + object: organization:university + - user: user:diana + relation: member + object: organization:university + + # Course + - user: organization:university + relation: organization + object: course:intro-cs + - user: user:bob + relation: owner + object: course:intro-cs + - user: user:bob + relation: enrolled_instructor + object: course:intro-cs + - user: user:charlie + relation: enrolled_student + object: course:intro-cs + + # Class linked to course + - user: course:intro-cs + relation: course + object: class:section-a + - user: user:bob + relation: instructor + object: class:section-a + - user: user:charlie + relation: student + object: class:section-a + + # Content linked to course + - user: course:intro-cs + relation: course + object: content:lecture-slides + - user: user:bob + relation: author + object: content:lecture-slides + + # Collection + - user: organization:university + relation: organization + object: collection:cs-fundamentals + - user: user:bob + relation: owner + object: collection:cs-fundamentals + + # Activity linked to class + - user: class:section-a + relation: class + object: activity:homework-1 + - user: user:bob + relation: creator + object: activity:homework-1 + - user: user:charlie + relation: assignee + object: activity:homework-1 + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: course:intro-cs + assertions: + can_view: true + can_edit: true + can_delete: true + can_publish: true + - user: user:alice + object: class:section-a + assertions: + can_view: true + can_edit: true + can_delete: true + can_enroll: true + - user: user:alice + object: content:lecture-slides + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: collection:cs-fundamentals + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: activity:homework-1 + assertions: + can_view: true + can_edit: true + can_delete: true + can_grade: true + + - name: Instructor can manage courses and grade activities + check: + - user: user:bob + object: course:intro-cs + assertions: + can_view: true + can_edit: true + can_delete: false + can_publish: true + - user: user:bob + object: class:section-a + assertions: + can_view: true + can_edit: true + can_delete: false + can_enroll: true + - user: user:bob + object: content:lecture-slides + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:bob + object: collection:cs-fundamentals + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:bob + object: activity:homework-1 + assertions: + can_view: true + can_edit: true + can_delete: false + can_grade: true + can_submit: false + + - name: Student can view enrolled courses and submit activities + check: + - user: user:charlie + object: course:intro-cs + assertions: + can_view: true + can_edit: false + can_delete: false + can_publish: false + - user: user:charlie + object: class:section-a + assertions: + can_view: true + can_edit: false + can_delete: false + can_enroll: false + - user: user:charlie + object: content:lecture-slides + assertions: + can_view: true + can_edit: false + can_delete: false + - user: user:charlie + object: collection:cs-fundamentals + assertions: + can_view: true + can_edit: false + can_delete: false + - user: user:charlie + object: activity:homework-1 + assertions: + can_view: true + can_edit: false + can_delete: false + can_grade: false + can_submit: true + + - name: Member has no access to resources + check: + - user: user:diana + object: course:intro-cs + assertions: + can_view: false + can_edit: false + can_delete: false + can_publish: false + - user: user:diana + object: class:section-a + assertions: + can_view: false + can_edit: false + can_delete: false + can_enroll: false + - user: user:diana + object: content:lecture-slides + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: collection:cs-fundamentals + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: activity:homework-1 + assertions: + can_view: false + can_edit: false + can_delete: false + can_grade: false + can_submit: false + + - name: Course publishing requires owner or admin + check: + - user: user:alice + object: course:intro-cs + assertions: + can_publish: true + - user: user:bob + object: course:intro-cs + assertions: + can_publish: true + - user: user:charlie + object: course:intro-cs + assertions: + can_publish: false + + - name: Only instructors and admins can grade activities + check: + - user: user:alice + object: activity:homework-1 + assertions: + can_grade: true + - user: user:bob + object: activity:homework-1 + assertions: + can_grade: true + - user: user:charlie + object: activity:homework-1 + assertions: + can_grade: false + + - name: Only assigned student can submit activity + check: + - user: user:charlie + object: activity:homework-1 + assertions: + can_submit: true + - user: user:bob + object: activity:homework-1 + assertions: + can_submit: false + - user: user:alice + object: activity:homework-1 + assertions: + can_submit: false + + - name: Instructor can list all their managed resources + list_objects: + - user: user:bob + type: course + assertions: + can_view: + - course:intro-cs + can_edit: + - course:intro-cs + - user: user:bob + type: class + assertions: + can_view: + - class:section-a + can_edit: + - class:section-a + - user: user:bob + type: content + assertions: + can_view: + - content:lecture-slides + can_edit: + - content:lecture-slides + - user: user:bob + type: collection + assertions: + can_view: + - collection:cs-fundamentals + can_edit: + - collection:cs-fundamentals + - user: user:bob + type: activity + assertions: + can_view: + - activity:homework-1 + can_edit: + - activity:homework-1 + can_grade: + - activity:homework-1 + + - name: Student can list resources they can access + list_objects: + - user: user:charlie + type: course + assertions: + can_view: + - course:intro-cs + - user: user:charlie + type: class + assertions: + can_view: + - class:section-a + - user: user:charlie + type: content + assertions: + can_view: + - content:lecture-slides + - user: user:charlie + type: collection + assertions: + can_view: + - collection:cs-fundamentals + - user: user:charlie + type: activity + assertions: + can_view: + - activity:homework-1 + can_submit: + - activity:homework-1 + + - name: Admin can list all organization resources + list_objects: + - user: user:alice + type: course + assertions: + can_view: + - course:intro-cs + can_edit: + - course:intro-cs + can_delete: + - course:intro-cs + - user: user:alice + type: class + assertions: + can_view: + - class:section-a + can_edit: + - class:section-a + can_delete: + - class:section-a + - user: user:alice + type: collection + assertions: + can_view: + - collection:cs-fundamentals + can_edit: + - collection:cs-fundamentals + can_delete: + - collection:cs-fundamentals + - user: user:alice + type: activity + assertions: + can_view: + - activity:homework-1 + can_edit: + - activity:homework-1 + can_delete: + - activity:homework-1 + can_grade: + - activity:homework-1 + + - name: List users with course permissions + list_users: + - object: course:intro-cs + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + can_publish: + users: + - user:alice + - user:bob + + - name: List users with activity permissions + list_users: + - object: activity:homework-1 + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + can_grade: + users: + - user:alice + - user:bob + can_submit: + users: + - user:charlie + + - name: List users with class permissions + list_users: + - object: class:section-a + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + can_enroll: + users: + - user:alice + - user:bob + + - name: List users with collection permissions + list_users: + - object: collection:cs-fundamentals + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob diff --git a/stores/manufacturing/README.md b/stores/manufacturing/README.md new file mode 100644 index 0000000..11dd083 --- /dev/null +++ b/stores/manufacturing/README.md @@ -0,0 +1,26 @@ +# OpenFGA for Manufacturing Systems + +## Use Case + +This model represents the authorization needs of a **manufacturing execution system (MES)**, like SAP Manufacturing, Oracle Manufacturing Cloud, or Siemens Opcenter. These platforms manage production lines, machines, work orders, quality reports, parts catalogs, and supplier relationships. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own production infrastructure, parts catalog, and supplier network. +- **Manufacturing roles**: Organization-level roles (`admin`, `plant_manager`, `engineer`, `quality_inspector`, `operator`, `procurement`) control access. Each role has distinct responsibilities — engineers manage equipment, operators run machines, quality inspectors manage reports, and procurement manages the supply chain. +- **Production line management**: Production lines have supervisors and inherit organization roles. Supervisors and plant managers can edit line configuration and create work orders. Only admins can delete lines. +- **Machine access control**: Machines belong to production lines. Assigned operators can edit their machine (log status, report issues). Engineers can edit and delete any machine for maintenance. Supervisors and plant managers inherit edit access from the production line. +- **Operator isolation**: Operators can only edit machines they are explicitly assigned to. They can view all machines on the production line but cannot modify unassigned equipment. +- **Work order lifecycle**: Work orders are produced on a production line. Creators and assignees can edit them. Supervisors and plant managers can approve work orders. Only admins can delete them. +- **Quality reporting**: Quality inspectors can create, edit, and approve quality reports. The inspector who authored a report can edit it. All organization members can view reports, supporting a culture of quality transparency. +- **Parts catalog**: Engineers and procurement staff can edit parts (specifications, stock levels). All organization members can view the catalog. +- **Supplier management**: Only procurement staff and admins can edit supplier records. All members can view the supplier directory. +- **Parent-scoped creation**: Work order creation is checked on the production line (`can_create_work_order`). Part, supplier, and quality report creation are checked on the organization (`can_create_part`, `can_create_supplier`, `can_create_quality_report`). + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `manufacturing` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/manufacturing/model.fga b/stores/manufacturing/model.fga new file mode 100644 index 0000000..6e4cc76 --- /dev/null +++ b/stores/manufacturing/model.fga @@ -0,0 +1,72 @@ +model + schema 1.1 + +type user + +type organization + relations + define admin: [user] + define plant_manager: [user] + define engineer: [user] + define quality_inspector: [user] + define operator: [user] + define procurement: [user] + define member: [user] or admin or plant_manager or engineer or quality_inspector or operator or procurement + define can_create_part: engineer or procurement or admin + define can_create_supplier: procurement or admin + define can_create_quality_report: quality_inspector or admin + +type production_line + relations + define organization: [organization] + define supervisor: [user] + define org_admin: admin from organization + define org_plant_manager: plant_manager from organization + define org_engineer: engineer from organization + define can_delete: org_admin + define can_edit: supervisor or org_plant_manager or can_delete + define can_create_work_order: can_edit + define can_create_machine: org_engineer or can_edit + define can_view: member from organization or can_edit + +type machine + relations + define production_line: [production_line] + define assigned_operator: [user] + define org_engineer: org_engineer from production_line + define can_delete: org_engineer or can_delete from production_line + define can_edit: assigned_operator or can_edit from production_line or can_delete + define can_view: can_view from production_line or can_edit + +type work_order + relations + define production_line: [production_line] + define creator: [user] + define assignee: [user] + define can_delete: can_delete from production_line + define can_approve: can_edit from production_line + define can_edit: creator or assignee or can_approve + define can_view: can_view from production_line or can_edit + +type quality_report + relations + define organization: [organization] + define inspector: [user] + define can_delete: admin from organization + define can_approve: quality_inspector from organization or can_delete + define can_edit: inspector or can_approve + define can_view: member from organization or can_edit + +type part + relations + define organization: [organization] + define can_delete: admin from organization + define can_edit: engineer from organization or procurement from organization or can_delete + define can_view: member from organization or can_edit + +type supplier + relations + define organization: [organization] + define can_delete: admin from organization + define can_edit: procurement from organization or can_delete + define can_view: member from organization or can_edit diff --git a/stores/manufacturing/store.fga.yaml b/stores/manufacturing/store.fga.yaml new file mode 100644 index 0000000..b5b7c49 --- /dev/null +++ b/stores/manufacturing/store.fga.yaml @@ -0,0 +1,600 @@ +name: Manufacturing +model_file: model.fga +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:acme-manufacturing + - user: user:bob + relation: plant_manager + object: organization:acme-manufacturing + - user: user:charlie + relation: engineer + object: organization:acme-manufacturing + - user: user:diana + relation: quality_inspector + object: organization:acme-manufacturing + - user: user:eve + relation: operator + object: organization:acme-manufacturing + - user: user:frank + relation: procurement + object: organization:acme-manufacturing + - user: user:grace + relation: member + object: organization:acme-manufacturing + + # Production line — bob is supervisor + - user: organization:acme-manufacturing + relation: organization + object: production_line:line-alpha + - user: user:bob + relation: supervisor + object: production_line:line-alpha + + # Machine cnc-001 on line-alpha — eve is assigned operator + - user: production_line:line-alpha + relation: production_line + object: machine:cnc-001 + - user: user:eve + relation: assigned_operator + object: machine:cnc-001 + + # Machine press-002 on line-alpha — no assigned operator + - user: production_line:line-alpha + relation: production_line + object: machine:press-002 + + # Work order on line-alpha — created by bob, assigned to eve + - user: production_line:line-alpha + relation: production_line + object: work_order:wo-001 + - user: user:bob + relation: creator + object: work_order:wo-001 + - user: user:eve + relation: assignee + object: work_order:wo-001 + + # Quality report — inspected by diana + - user: organization:acme-manufacturing + relation: organization + object: quality_report:qr-001 + - user: user:diana + relation: inspector + object: quality_report:qr-001 + + # Part in the catalog + - user: organization:acme-manufacturing + relation: organization + object: part:gear-assembly + + # Supplier + - user: organization:acme-manufacturing + relation: organization + object: supplier:steel-corp + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: production_line:line-alpha + assertions: + can_delete: true + can_create_work_order: true + can_create_machine: true + can_edit: true + can_view: true + - user: user:alice + object: machine:cnc-001 + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:alice + object: work_order:wo-001 + assertions: + can_delete: true + can_approve: true + can_edit: true + can_view: true + - user: user:alice + object: quality_report:qr-001 + assertions: + can_delete: true + can_approve: true + can_edit: true + can_view: true + - user: user:alice + object: part:gear-assembly + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:alice + object: supplier:steel-corp + assertions: + can_delete: true + can_edit: true + can_view: true + + - name: Plant manager supervises production and approves work orders + check: + - user: user:bob + object: production_line:line-alpha + assertions: + can_delete: false + can_create_work_order: true + can_create_machine: true + can_edit: true + can_view: true + - user: user:bob + object: machine:cnc-001 + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:bob + object: work_order:wo-001 + assertions: + can_delete: false + can_approve: true + can_edit: true + can_view: true + - user: user:bob + object: quality_report:qr-001 + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:bob + object: part:gear-assembly + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:bob + object: supplier:steel-corp + assertions: + can_delete: false + can_edit: false + can_view: true + + - name: Engineer manages equipment and parts but not production + check: + - user: user:charlie + object: production_line:line-alpha + assertions: + can_delete: false + can_create_work_order: false + can_create_machine: true + can_edit: false + can_view: true + - user: user:charlie + object: machine:cnc-001 + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:charlie + object: work_order:wo-001 + assertions: + can_delete: false + can_approve: false + can_edit: false + can_view: true + - user: user:charlie + object: part:gear-assembly + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:charlie + object: supplier:steel-corp + assertions: + can_delete: false + can_edit: false + can_view: true + + - name: Quality inspector manages reports but not production + check: + - user: user:diana + object: quality_report:qr-001 + assertions: + can_delete: false + can_approve: true + can_edit: true + can_view: true + - user: user:diana + object: production_line:line-alpha + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:diana + object: work_order:wo-001 + assertions: + can_delete: false + can_approve: false + can_edit: false + can_view: true + - user: user:diana + object: machine:cnc-001 + assertions: + can_delete: false + can_edit: false + can_view: true + + - name: Operator runs assigned machine and work order + check: + - user: user:eve + object: machine:cnc-001 + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:eve + object: machine:press-002 + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:eve + object: work_order:wo-001 + assertions: + can_delete: false + can_approve: false + can_edit: true + can_view: true + - user: user:eve + object: production_line:line-alpha + assertions: + can_delete: false + can_create_work_order: false + can_create_machine: false + can_edit: false + can_view: true + + - name: Procurement manages suppliers and parts catalog + check: + - user: user:frank + object: supplier:steel-corp + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:frank + object: part:gear-assembly + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:frank + object: machine:cnc-001 + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:frank + object: work_order:wo-001 + assertions: + can_delete: false + can_approve: false + can_edit: false + can_view: true + - user: user:frank + object: organization:acme-manufacturing + assertions: + can_create_part: true + can_create_supplier: true + can_create_quality_report: false + + - name: Regular member has read-only access everywhere + check: + - user: user:grace + object: production_line:line-alpha + assertions: + can_delete: false + can_create_work_order: false + can_create_machine: false + can_edit: false + can_view: true + - user: user:grace + object: machine:cnc-001 + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:grace + object: work_order:wo-001 + assertions: + can_delete: false + can_approve: false + can_edit: false + can_view: true + - user: user:grace + object: quality_report:qr-001 + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:grace + object: part:gear-assembly + assertions: + can_delete: false + can_edit: false + can_view: true + - user: user:grace + object: supplier:steel-corp + assertions: + can_delete: false + can_edit: false + can_view: true + + - name: Organization creation permissions follow role boundaries + check: + - user: user:charlie + object: organization:acme-manufacturing + assertions: + can_create_part: true + can_create_supplier: false + can_create_quality_report: false + - user: user:diana + object: organization:acme-manufacturing + assertions: + can_create_part: false + can_create_supplier: false + can_create_quality_report: true + - user: user:alice + object: organization:acme-manufacturing + assertions: + can_create_part: true + can_create_supplier: true + can_create_quality_report: true + + - name: Users can list objects through effective permissions + list_objects: + - user: user:bob + type: production_line + assertions: + can_create_work_order: + - production_line:line-alpha + can_create_machine: + - production_line:line-alpha + can_edit: + - production_line:line-alpha + can_view: + - production_line:line-alpha + - user: user:eve + type: machine + assertions: + can_edit: + - machine:cnc-001 + can_view: + - machine:cnc-001 + - machine:press-002 + - user: user:diana + type: quality_report + assertions: + can_approve: + - quality_report:qr-001 + can_edit: + - quality_report:qr-001 + can_view: + - quality_report:qr-001 + - user: user:eve + type: work_order + assertions: + can_edit: + - work_order:wo-001 + can_view: + - work_order:wo-001 + + - name: Organization members can list creation permissions + list_objects: + - user: user:alice + type: organization + assertions: + member: + - organization:acme-manufacturing + can_create_part: + - organization:acme-manufacturing + can_create_supplier: + - organization:acme-manufacturing + can_create_quality_report: + - organization:acme-manufacturing + - user: user:frank + type: organization + assertions: + member: + - organization:acme-manufacturing + can_create_part: + - organization:acme-manufacturing + can_create_supplier: + - organization:acme-manufacturing + - user: user:diana + type: organization + assertions: + member: + - organization:acme-manufacturing + can_create_quality_report: + - organization:acme-manufacturing + - user: user:grace + type: organization + assertions: + member: + - organization:acme-manufacturing + + - name: Admin and engineer can list production resources by permission + list_objects: + - user: user:alice + type: production_line + assertions: + can_delete: + - production_line:line-alpha + can_create_work_order: + - production_line:line-alpha + can_create_machine: + - production_line:line-alpha + can_edit: + - production_line:line-alpha + can_view: + - production_line:line-alpha + - user: user:alice + type: machine + assertions: + can_delete: + - machine:cnc-001 + - machine:press-002 + can_edit: + - machine:cnc-001 + - machine:press-002 + can_view: + - machine:cnc-001 + - machine:press-002 + - user: user:alice + type: work_order + assertions: + can_delete: + - work_order:wo-001 + can_approve: + - work_order:wo-001 + can_edit: + - work_order:wo-001 + can_view: + - work_order:wo-001 + - user: user:alice + type: quality_report + assertions: + can_delete: + - quality_report:qr-001 + can_approve: + - quality_report:qr-001 + can_edit: + - quality_report:qr-001 + can_view: + - quality_report:qr-001 + - user: user:charlie + type: machine + assertions: + can_delete: + - machine:cnc-001 + - machine:press-002 + can_edit: + - machine:cnc-001 + - machine:press-002 + can_view: + - machine:cnc-001 + - machine:press-002 + + - name: List users with production line permissions + list_users: + - object: production_line:line-alpha + user_filter: + - type: user + assertions: + can_delete: + users: + - user:alice + can_create_work_order: + users: + - user:alice + - user:bob + can_create_machine: + users: + - user:alice + - user:bob + - user:charlie + can_edit: + users: + - user:alice + - user:bob + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + - user:eve + - user:frank + - user:grace + + - name: List users with machine permissions + list_users: + - object: machine:cnc-001 + user_filter: + - type: user + assertions: + can_delete: + users: + - user:alice + - user:charlie + can_edit: + users: + - user:alice + - user:bob + - user:charlie + - user:eve + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + - user:eve + - user:frank + - user:grace + + - name: List users with work order permissions + list_users: + - object: work_order:wo-001 + user_filter: + - type: user + assertions: + can_approve: + users: + - user:alice + - user:bob + can_edit: + users: + - user:alice + - user:bob + - user:eve + can_view: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + - user:eve + - user:frank + - user:grace + + - name: List users with organization permissions + list_users: + - object: organization:acme-manufacturing + user_filter: + - type: user + assertions: + member: + users: + - user:alice + - user:bob + - user:charlie + - user:diana + - user:eve + - user:frank + - user:grace + can_create_part: + users: + - user:alice + - user:charlie + - user:frank + can_create_supplier: + users: + - user:alice + - user:frank + can_create_quality_report: + users: + - user:alice + - user:diana diff --git a/stores/payment/README.md b/stores/payment/README.md new file mode 100644 index 0000000..9dfb21f --- /dev/null +++ b/stores/payment/README.md @@ -0,0 +1,23 @@ +# OpenFGA for Payment Platforms + +## Use Case + +This model represents the authorization needs of a **payment platform**, like Stripe, PayPal, or Square. These platforms manage payment links, transactions, payouts, refunds, and subscriptions across merchant organizations. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple organizations, each with their own payment processing, billing, and financial data. +- **Payment roles**: Organization-level roles (`admin`, `finance_manager`, `viewer`) control access. Admins have full access, finance managers can process payments and manage subscriptions, and viewers have read-only access. +- **Payment link management**: Creators can manage their own payment links. Finance managers have broader access across the organization. +- **Refund workflow**: Finance managers can issue refunds on payments. Only admins can approve standalone refund records, ensuring financial oversight. +- **Payout approval**: Payouts are restricted to finance managers and admins for viewing. Only admins can approve payouts, preventing unauthorized disbursements. +- **Subscription management**: Finance managers can edit and cancel subscriptions. Only admins can permanently delete subscription records. +- **Payout visibility**: Viewers cannot see payout details, restricting access to sensitive disbursement information to finance managers and admins. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `payment` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/payment/model.fga b/stores/payment/model.fga new file mode 100644 index 0000000..ce1d7c2 --- /dev/null +++ b/stores/payment/model.fga @@ -0,0 +1,49 @@ +model + schema 1.1 + +type user + +type organization + relations + define member: [user] or admin or finance_manager or viewer + define admin: [user] + define finance_manager: [user] + define viewer: [user] + +type link + relations + define organization: [organization] + define creator: [user] + define can_delete: admin from organization + define can_edit: creator or finance_manager from organization or can_delete + define can_view: viewer from organization or can_edit + +type payment + relations + define organization: [organization] + define creator: [user] + define can_delete: admin from organization + define can_refund: finance_manager from organization or can_delete + define can_edit: can_refund + define can_view: creator or viewer from organization or can_edit + +type payout + relations + define organization: [organization] + define can_approve: admin from organization + define can_view: finance_manager from organization or can_approve + +type refund + relations + define organization: [organization] + define can_approve: admin from organization + define can_view: viewer from organization or finance_manager from organization or can_approve + +type subscription + relations + define organization: [organization] + define creator: [user] + define can_delete: admin from organization + define can_cancel: finance_manager from organization or can_delete + define can_edit: can_cancel + define can_view: creator or viewer from organization or can_edit diff --git a/stores/payment/store.fga.yaml b/stores/payment/store.fga.yaml new file mode 100644 index 0000000..2758118 --- /dev/null +++ b/stores/payment/store.fga.yaml @@ -0,0 +1,296 @@ +name: Payment Platform Authorization Model Tests +model_file: model.fga + +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:payments-co + - user: user:bob + relation: finance_manager + object: organization:payments-co + - user: user:charlie + relation: viewer + object: organization:payments-co + - user: user:diana + relation: member + object: organization:payments-co + + # Payment link + - user: organization:payments-co + relation: organization + object: link:checkout-page + - user: user:bob + relation: creator + object: link:checkout-page + + # Payment + - user: organization:payments-co + relation: organization + object: payment:txn-001 + - user: user:bob + relation: creator + object: payment:txn-001 + + # Payout + - user: organization:payments-co + relation: organization + object: payout:payout-001 + + # Refund + - user: organization:payments-co + relation: organization + object: refund:refund-001 + + # Subscription + - user: organization:payments-co + relation: organization + object: subscription:plan-monthly + - user: user:bob + relation: creator + object: subscription:plan-monthly + +tests: + - name: Admin has full access to all payment resources + check: + - user: user:alice + object: link:checkout-page + assertions: + can_view: true + can_edit: true + can_delete: true + - user: user:alice + object: payment:txn-001 + assertions: + can_view: true + can_edit: true + can_delete: true + can_refund: true + - user: user:alice + object: payout:payout-001 + assertions: + can_view: true + can_approve: true + - user: user:alice + object: refund:refund-001 + assertions: + can_view: true + can_approve: true + - user: user:alice + object: subscription:plan-monthly + assertions: + can_view: true + can_edit: true + can_cancel: true + can_delete: true + + - name: Finance manager can manage payments and subscriptions + check: + - user: user:bob + object: link:checkout-page + assertions: + can_view: true + can_edit: true + can_delete: false + - user: user:bob + object: payment:txn-001 + assertions: + can_view: true + can_edit: true + can_delete: false + can_refund: true + - user: user:bob + object: payout:payout-001 + assertions: + can_view: true + can_approve: false + - user: user:bob + object: refund:refund-001 + assertions: + can_view: true + can_approve: false + - user: user:bob + object: subscription:plan-monthly + assertions: + can_view: true + can_edit: true + can_cancel: true + can_delete: false + + - name: Viewer can read most resources but cannot view payouts + check: + - user: user:charlie + object: link:checkout-page + assertions: + can_view: true + can_edit: false + can_delete: false + - user: user:charlie + object: payment:txn-001 + assertions: + can_view: true + can_edit: false + can_delete: false + can_refund: false + - user: user:charlie + object: payout:payout-001 + assertions: + can_view: false + can_approve: false + - user: user:charlie + object: refund:refund-001 + assertions: + can_view: true + can_approve: false + - user: user:charlie + object: subscription:plan-monthly + assertions: + can_view: true + can_edit: false + can_cancel: false + can_delete: false + + - name: Member has no access to payment resources + check: + - user: user:diana + object: link:checkout-page + assertions: + can_view: false + can_edit: false + can_delete: false + - user: user:diana + object: payment:txn-001 + assertions: + can_view: false + can_edit: false + can_delete: false + can_refund: false + - user: user:diana + object: payout:payout-001 + assertions: + can_view: false + can_approve: false + - user: user:diana + object: refund:refund-001 + assertions: + can_view: false + can_approve: false + - user: user:diana + object: subscription:plan-monthly + assertions: + can_view: false + can_edit: false + can_cancel: false + can_delete: false + + - name: Payout approval restricted to admin + check: + - user: user:alice + object: payout:payout-001 + assertions: + can_approve: true + - user: user:bob + object: payout:payout-001 + assertions: + can_approve: false + - user: user:charlie + object: payout:payout-001 + assertions: + can_approve: false + + - name: Refund approval restricted to admin + check: + - user: user:alice + object: refund:refund-001 + assertions: + can_approve: true + - user: user:bob + object: refund:refund-001 + assertions: + can_approve: false + + - name: Finance manager can view edit and refund payments + check: + - user: user:bob + object: payment:txn-001 + assertions: + can_view: true + can_edit: true + can_refund: true + + - name: Admin can list all payment resources + list_objects: + - user: user:alice + type: link + assertions: + can_view: + - link:checkout-page + - user: user:alice + type: payment + assertions: + can_view: + - payment:txn-001 + - user: user:alice + type: subscription + assertions: + can_view: + - subscription:plan-monthly + + - name: Finance manager can list owned and organizational resources + list_objects: + - user: user:bob + type: link + assertions: + can_edit: + - link:checkout-page + - user: user:bob + type: payment + assertions: + can_refund: + - payment:txn-001 + + - name: Viewer can list viewable resources + list_objects: + - user: user:charlie + type: link + assertions: + can_view: + - link:checkout-page + - user: user:charlie + type: payment + assertions: + can_view: + - payment:txn-001 + + - name: List users with link access + list_users: + - object: link:checkout-page + user_filter: + - type: user + assertions: + can_view: + users: + - user:alice + - user:bob + - user:charlie + + - name: List users who can refund payments + list_users: + - object: payment:txn-001 + user_filter: + - type: user + assertions: + can_refund: + users: + - user:alice + - user:bob + + - name: List users who can approve refunds + list_users: + - object: refund:refund-001 + user_filter: + - type: user + assertions: + can_approve: + users: + - user:alice diff --git a/stores/real-estate/README.md b/stores/real-estate/README.md new file mode 100644 index 0000000..4204bec --- /dev/null +++ b/stores/real-estate/README.md @@ -0,0 +1,25 @@ +# OpenFGA for Real Estate Systems + +## Use Case + +This model represents the authorization needs of a **real estate platform**, like Zillow, Realtor.com, or a brokerage MLS system. These platforms manage properties, listings, transactions, inspections, and neighborhood market data across real estate brokerages. + +The model captures the following requirements: + +- **Multi-tenancy**: Multiple brokerages (organizations), each with their own agents, listings, and transaction records. +- **Real estate roles**: Organization-level roles (`admin`, `broker`, `agent`, `appraiser`) control access. Admins have full control, brokers oversee all listings and transactions, agents manage their own listings, and appraisers handle inspections. +- **Agent-scoped listings**: Listing agents can edit and close their own listings. They cannot modify listings belonging to other agents. Brokers can manage any listing across the brokerage. +- **Transaction access**: Buyer and seller agents can edit transactions they are involved in. The listing agent and broker also have edit access through the listing chain. Financial data (sale price, commission) is restricted to parties on the transaction. +- **Property management**: Properties are brokerage-wide records. Brokers and admins can edit property details. All brokerage members can view properties for market analysis. +- **Inspection reports**: Appraisers create and edit inspection reports linked to properties. All brokerage members can view inspections. Only admins can delete inspection records. +- **Neighborhood data**: Market and neighborhood data is viewable by all brokerage members for comparable analysis. Only admins can edit neighborhood records. +- **Parent-scoped creation**: Listing and inspection creation are checked on the property (`can_create_listing`, `can_create_inspection`). Transaction creation is checked on the listing (`can_create_transaction`). Property and neighborhood creation are checked on the organization (`can_create_property`, `can_create_neighborhood`). +- **Financial data isolation**: Transaction financial details (commissions, sale prices) are only visible to the agents involved in the deal and brokerage management, not to general staff. + +The model, tuples, and tests are in [model.fga](./model.fga) and [store.fga.yaml](./store.fga.yaml). + +## Try It Out + +1. Make sure you have the [FGA CLI](https://github.com/openfga/cli/?tab=readme-ov-file#installation) + +2. In the `real-estate` directory, run `fga model test --tests store.fga.yaml` diff --git a/stores/real-estate/model.fga b/stores/real-estate/model.fga new file mode 100644 index 0000000..71cb85f --- /dev/null +++ b/stores/real-estate/model.fga @@ -0,0 +1,60 @@ +model + schema 1.1 + +type user + +type organization + relations + define admin: [user] + define broker: [user] + define agent: [user] + define appraiser: [user] + define member: [user] or admin or broker or agent or appraiser + define can_create_property: admin + define can_create_neighborhood: admin + +type property + relations + define organization: [organization] + define can_delete: admin from organization + define can_edit: broker from organization or can_delete + define can_create_listing: agent from organization or broker from organization or can_delete + define can_create_inspection: appraiser from organization or broker from organization or can_delete + define can_view: member from organization or can_edit + +type listing + relations + define organization: [organization] + define property: [property] + define listing_agent: [user] + define org_admin: admin from organization + define org_broker: broker from organization + define can_delete: org_admin + define can_create_transaction: listing_agent or org_broker or can_delete + define can_close: listing_agent or org_broker or can_delete + define can_edit: can_close + define can_view: member from organization or can_edit + +type transaction + relations + define listing: [listing] + define buyer_agent: [user] + define seller_agent: [user] + define can_delete: can_delete from listing + define can_edit: buyer_agent or seller_agent or can_edit from listing + define can_view_financial: can_edit + define can_view: can_view from listing or can_edit + +type inspection + relations + define property: [property] + define inspector: [user] + define can_delete: can_delete from property + define can_edit: inspector or can_delete + define can_view: can_view from property or can_edit + +type neighborhood + relations + define organization: [organization] + define can_edit: admin from organization + define can_view: member from organization or can_edit diff --git a/stores/real-estate/store.fga.yaml b/stores/real-estate/store.fga.yaml new file mode 100644 index 0000000..bc466c1 --- /dev/null +++ b/stores/real-estate/store.fga.yaml @@ -0,0 +1,385 @@ +name: Real Estate +model_file: model.fga +tuples: + # Organization roles + - user: user:alice + relation: admin + object: organization:skyline-realty + - user: user:bob + relation: broker + object: organization:skyline-realty + - user: user:charlie + relation: agent + object: organization:skyline-realty + - user: user:diana + relation: appraiser + object: organization:skyline-realty + - user: user:eve + relation: agent + object: organization:skyline-realty + - user: user:frank + relation: member + object: organization:skyline-realty + + # Property + - user: organization:skyline-realty + relation: organization + object: property:prop-123 + + # Listing for prop-123 — charlie is the listing agent + - user: organization:skyline-realty + relation: organization + object: listing:listing-001 + - user: property:prop-123 + relation: property + object: listing:listing-001 + - user: user:charlie + relation: listing_agent + object: listing:listing-001 + + # Second listing — eve is the listing agent (for isolation test) + - user: organization:skyline-realty + relation: organization + object: listing:listing-002 + - user: user:eve + relation: listing_agent + object: listing:listing-002 + + # Transaction on listing-001 — charlie is seller agent, eve is buyer agent + - user: listing:listing-001 + relation: listing + object: transaction:txn-001 + - user: user:charlie + relation: seller_agent + object: transaction:txn-001 + - user: user:eve + relation: buyer_agent + object: transaction:txn-001 + + # Inspection on prop-123 — diana is inspector + - user: property:prop-123 + relation: property + object: inspection:insp-001 + - user: user:diana + relation: inspector + object: inspection:insp-001 + + # Neighborhood + - user: organization:skyline-realty + relation: organization + object: neighborhood:downtown + +tests: + - name: Admin has full access to all resources + check: + - user: user:alice + object: organization:skyline-realty + assertions: + can_create_property: true + can_create_neighborhood: true + - user: user:alice + object: property:prop-123 + assertions: + can_delete: true + can_edit: true + can_create_listing: true + can_create_inspection: true + can_view: true + - user: user:alice + object: listing:listing-001 + assertions: + can_delete: true + can_create_transaction: true + can_close: true + can_edit: true + can_view: true + - user: user:alice + object: transaction:txn-001 + assertions: + can_delete: true + can_edit: true + can_view_financial: true + can_view: true + - user: user:alice + object: inspection:insp-001 + assertions: + can_delete: true + can_edit: true + can_view: true + - user: user:alice + object: neighborhood:downtown + assertions: + can_edit: true + can_view: true + + - name: Broker oversees all listings and transactions + check: + - user: user:bob + object: organization:skyline-realty + assertions: + can_create_property: false + can_create_neighborhood: false + - user: user:bob + object: property:prop-123 + assertions: + can_delete: false + can_edit: true + can_create_listing: true + can_create_inspection: true + can_view: true + - user: user:bob + object: listing:listing-001 + assertions: + can_delete: false + can_create_transaction: true + can_close: true + can_edit: true + can_view: true + - user: user:bob + object: listing:listing-002 + assertions: + can_create_transaction: true + can_close: true + can_edit: true + can_view: true + - user: user:bob + object: transaction:txn-001 + assertions: + can_delete: false + can_edit: true + can_view_financial: true + can_view: true + + - name: Listing agent manages own listings and transactions + check: + - user: user:charlie + object: organization:skyline-realty + assertions: + can_create_property: false + can_create_neighborhood: false + - user: user:charlie + object: listing:listing-001 + assertions: + can_delete: false + can_create_transaction: true + can_close: true + can_edit: true + can_view: true + - user: user:charlie + object: listing:listing-002 + assertions: + can_create_transaction: false + can_close: false + can_edit: false + can_view: true + - user: user:charlie + object: transaction:txn-001 + assertions: + can_delete: false + can_edit: true + can_view_financial: true + can_view: true + - user: user:charlie + object: property:prop-123 + assertions: + can_delete: false + can_edit: false + can_create_listing: true + can_create_inspection: false + can_view: true + + - name: Appraiser manages inspections but not listings + check: + - user: user:diana + object: organization:skyline-realty + assertions: + can_create_property: false + can_create_neighborhood: false + - user: user:diana + object: inspection:insp-001 + assertions: + can_delete: false + can_edit: true + can_view: true + - user: user:diana + object: listing:listing-001 + assertions: + can_close: false + can_edit: false + can_view: true + - user: user:diana + object: transaction:txn-001 + assertions: + can_edit: false + can_view_financial: false + can_view: true + - user: user:diana + object: property:prop-123 + assertions: + can_create_listing: false + can_create_inspection: true + + - name: Buyer agent can edit transaction but not the listing + check: + - user: user:eve + object: organization:skyline-realty + assertions: + can_create_property: false + can_create_neighborhood: false + - user: user:eve + object: transaction:txn-001 + assertions: + can_delete: false + can_edit: true + can_view_financial: true + can_view: true + - user: user:eve + object: listing:listing-001 + assertions: + can_create_transaction: false + can_close: false + can_edit: false + can_view: true + - user: user:eve + object: listing:listing-002 + assertions: + can_create_transaction: true + can_close: true + can_edit: true + can_view: true + + - name: Regular member has read-only access + check: + - user: user:frank + object: organization:skyline-realty + assertions: + can_create_property: false + can_create_neighborhood: false + - user: user:frank + object: property:prop-123 + assertions: + can_delete: false + can_edit: false + can_create_listing: false + can_create_inspection: false + can_view: true + - user: user:frank + object: listing:listing-001 + assertions: + can_delete: false + can_create_transaction: false + can_close: false + can_edit: false + can_view: true + - user: user:frank + object: transaction:txn-001 + assertions: + can_delete: false + can_edit: false + can_view_financial: false + can_view: true + - user: user:frank + object: inspection:insp-001 + assertions: + can_edit: false + can_view: true + - user: user:frank + object: neighborhood:downtown + assertions: + can_edit: false + can_view: true + + - name: Agent isolation across listings + check: + - user: user:charlie + object: listing:listing-002 + assertions: + can_close: false + can_edit: false + - user: user:eve + object: listing:listing-001 + assertions: + can_close: false + can_edit: false + + - name: Admin can list all resource types + list_objects: + - user: user:alice + type: property + assertions: + can_view: + - property:prop-123 + - user: user:alice + type: listing + assertions: + can_view: + - listing:listing-001 + - listing:listing-002 + - user: user:alice + type: transaction + assertions: + can_view: + - transaction:txn-001 + + - name: Broker can list all editable resources + list_objects: + - user: user:bob + type: property + assertions: + can_edit: + - property:prop-123 + - user: user:bob + type: listing + assertions: + can_view: + - listing:listing-001 + - listing:listing-002 + + - name: Agent can list own and viewable resources + list_objects: + - user: user:charlie + type: listing + assertions: + can_edit: + - listing:listing-001 + - user: user:diana + type: inspection + assertions: + can_edit: + - inspection:insp-001 + + - name: List users with edit access to listing + list_users: + - object: listing:listing-001 + user_filter: + - type: user + assertions: + can_edit: + users: + - user:alice + - user:bob + - user:charlie + + - name: List users involved in transaction + list_users: + - object: transaction:txn-001 + user_filter: + - type: user + assertions: + can_edit: + users: + - user:alice + - user:bob + - user:charlie + - user:eve + + - name: List users who can inspect property + list_users: + - object: inspection:insp-001 + user_filter: + - type: user + assertions: + can_edit: + users: + - user:alice + - user:diana diff --git a/stores/slack/store.fga.yaml b/stores/slack/store.fga.yaml index 36ee076..49a64cc 100644 --- a/stores/slack/store.fga.yaml +++ b/stores/slack/store.fga.yaml @@ -1,7 +1,7 @@ # Documentation: https://openfga.dev/docs/modeling/advanced/slack # FGA Playground: https://play.fga.dev/sandbox/?store=slack name: Slack -model_file: ./model.fga +model_file: model.fga tuples: # The Sandcastle workspace is the parent workspace of the #general channel - user: workspace:sandcastle