### AshGrant.PermissionInput Examples Source: https://hexdocs.pm/ash_grant/AshGrant.PermissionInput.md Examples demonstrating how to create PermissionInput structs with varying levels of detail. ```APIDOC ## Examples # Simple permission with just the string %AshGrant.PermissionInput{string: "post:*:read:always"} # Permission with metadata %AshGrant.PermissionInput{ string: "post:*:update:own", description: "Edit own posts", source: "editor_role" } # Permission with extra metadata %AshGrant.PermissionInput{ string: "post:*:delete:always", description: "Delete any post", source: "admin_role", metadata: %{granted_at: ~U[2024-01-15 10:00:00Z], granted_by: "system"} } ``` -------------------------------- ### AshGrant DSL Configuration Example Source: https://hexdocs.pm/ash_grant/AshGrant.Dsl.md This example demonstrates a typical AshGrant DSL configuration within an Ash resource. It includes setting the resolver, resource name, and defining various scopes and can_perform actions. ```elixir defmodule MyApp.Blog.Post do use Ash.Resource, extensions: [AshGrant] ash_grant do resolver MyApp.PermissionResolver resource_name "post" scope :always, true scope :own, expr(author_id == ^actor(:id)) scope :published, expr(status == :published) scope :own_draft, [:own], expr(status == :draft) # Relational scope — works for reads and writes automatically scope :team_visible, expr(exists(team.members, user_id == ^actor(:id))) # UI visibility calculations can_perform_actions [:update, :destroy] end end ``` -------------------------------- ### RBAC Permissions Examples Source: https://hexdocs.pm/ash_grant/permissions.md Provides examples of Role-Based Access Control (RBAC) permissions, demonstrating access to all resources, specific resources, and actions with different scopes. ```elixir "blog:*:read:always" # Read all blogs ``` ```elixir "blog:*:read:published" # Read only published blogs ``` ```elixir "blog:*:update:own" # Update own blogs only ``` ```elixir "blog:*:*:always" # All actions on all blogs ``` ```elixir "":*:read:always" # Read all resources ``` ```elixir "blog:*:read*:always" # All read-type actions ``` ```elixir "!blog:*:delete:always" # DENY delete on all blogs ``` -------------------------------- ### Instance Permissions Examples Source: https://hexdocs.pm/ash_grant/permissions.md Illustrates how to grant permissions for specific resource instances, including full access and denying deletion. ```elixir "blog:post_abc123xyz789ab:read:" # Read specific post ``` ```elixir "blog:post_abc123xyz789ab:* :" # Full access to specific post ``` ```elixir "!blog:post_abc123xyz789ab:delete:" # DENY delete on specific post ``` -------------------------------- ### Scope Inheritance Example Source: https://hexdocs.pm/ash_grant/scopes.md Demonstrates how scopes can inherit from parent scopes, combining filters with AND logic. ```elixir scope :base, expr(tenant_id == ^actor(:tenant_id)) scope :active, [:base], expr(status == :active) # Result: tenant_id == actor.tenant_id AND status == :active ``` -------------------------------- ### Permission String Examples with Wildcards Source: https://hexdocs.pm/ash_grant/permissions.md Demonstrates the usage of wildcard characters in permission strings for matching resources, actions, and scopes. ```elixir "*:*:read:always" # All resources, read action (exact name match) ``` ```elixir "blog:*:read*:always" # All :read-type actions on blog (type match) ``` ```elixir "blog:*:read:always" # Only the action named "read" on blog (exact match) ``` -------------------------------- ### AshGrant.Explanation Struct Example Source: https://hexdocs.pm/ash_grant/AshGrant.Explanation.md Demonstrates the structure of the AshGrant.Explanation struct returned by AshGrant.explain/4 for an allowed read action on MyApp.Post. ```elixir iex> result = AshGrant.explain(MyApp.Post, :read, actor) %AshGrant.Explanation{ resource: MyApp.Post, action: :read, decision: :allow, matching_permissions: [ %{ permission: "post:*:read:always", description: "Read all posts", source: "editor_role", scope_name: :all, scope_description: "All records without restriction", field_group: nil } ], field_groups: [], field_group_defs: [], ... } ``` -------------------------------- ### Basic Usage Examples Source: https://hexdocs.pm/ash_grant/AshGrant.FilterCheck.md Illustrates the filter expressions returned by AshGrant.filter_check/1 based on different permission scopes. ```elixir # Permission: "post:*:read:always" # Returns: true (no filter) # Permission: "post:*:read:own" # Returns: expr(author_id == ^actor(:id)) # Permission: "post:*:read:published" # Returns: expr(status == :published) ``` -------------------------------- ### Instance Permission Check Example Source: https://hexdocs.pm/ash_grant/permissions.md Demonstrates how instance permissions are checked for write actions. The provided user has a specific update permission for draft documents. ```elixir # User has: "doc:doc_123:update:draft" # This succeeds (doc is in draft) Ash.update!(draft_doc, %{title: "New"}, actor: user) # This fails (doc is published, not draft) Ash.update!(published_doc, %{title: "New"}, actor: user) ``` -------------------------------- ### Deny-Wins Pattern Example Source: https://hexdocs.pm/ash_grant/permissions.md Demonstrates the deny-wins pattern where explicit deny rules take precedence over allow rules when both match. ```elixir permissions = [ "blog:*:*:always", # Allow all blog actions "!blog:*:delete:always" # Deny delete ] # Result: # - blog:read -> allowed # - blog:update -> allowed ``` -------------------------------- ### Multi-Tenancy Usage with `^tenant()` Source: https://hexdocs.pm/ash_grant/scopes.md Provides examples of how to use multi-tenancy scopes with `^tenant()` for reading, creating, and updating resources within a specific tenant context. ```elixir # Read - only returns posts from the specified tenant posts = Post |> Ash.read!(actor: user, tenant: tenant_id) # Create - validated against tenant scope Ash.create(Post, %{title: "Hello", tenant_id: tenant_id}, actor: user, tenant: tenant_id ) # Update - must match both tenant AND ownership for own_in_tenant scope Ash.update(post, %{title: "Updated"}, actor: user, tenant: tenant_id) ``` -------------------------------- ### Get All Instance Scopes Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Gets all scopes from matching 'allow' instance permissions for a specific instance ID and action. Useful for users with multiple instance-specific permissions. ```elixir iex> permissions = ["doc:doc_123:read:draft", "doc:doc_123:read:internal"] iex> AshGrant.Evaluator.get_all_instance_scopes(permissions, "doc_123", "read") ["draft", "internal"] ``` ```elixir iex> permissions = ["doc:doc_123:*:always", "!doc:doc_123:delete:always"] iex> AshGrant.Evaluator.get_all_instance_scopes(permissions, "doc_123", "delete") [] ``` -------------------------------- ### Create AshGrant.PermissionInput Structs Source: https://hexdocs.pm/ash_grant/AshGrant.PermissionInput.md Use these examples to create PermissionInput structs with varying levels of detail, from a simple string to including descriptions, sources, and arbitrary metadata. ```elixir %AshGrant.PermissionInput{string: "post:*:read:always"} ``` ```elixir %AshGrant.PermissionInput{ string: "post:*:update:own", description: "Edit own posts", source: "editor_role" } ``` ```elixir %AshGrant.PermissionInput{ string: "post:*:delete:always", description: "Delete any post", source: "admin_role", metadata: %{granted_at: ~U[2024-01-15 10:00:00Z], granted_by: "system"} } ``` -------------------------------- ### List Available Actions (Simple) Source: https://hexdocs.pm/ash_grant/debugging-and-introspection.md Use `allowed_actions/2` to get a simple list of actions an actor is allowed to perform on a resource. ```elixir # Simple list AshGrant.Introspect.allowed_actions(Post, user) # => [:read, :create, :update] ``` -------------------------------- ### Instance Permission Read Example Source: https://hexdocs.pm/ash_grant/permissions.md Illustrates how a custom permission resolver generates instance permissions for shared documents, allowing users to read only those explicitly shared. ```elixir # Resolver returns instance permissions for shared documents defmodule MyApp.PermissionResolver do @behaviour AshGrant.PermissionResolver def resolve(%{shared_doc_ids: doc_ids}, _context) when is_list(doc_ids) do # Generate instance permission for each shared document Enum.map(doc_ids, fn doc_id -> "document:#{doc_id}:read:" end) end end # User can only read documents explicitly shared with them actor = %{id: "user-1", shared_doc_ids: ["doc_abc", "doc_xyz"]} Document |> Ash.read!(actor: actor) # => Returns only doc_abc and doc_xyz ``` -------------------------------- ### Context Injection Example Source: https://hexdocs.pm/ash_grant/AshGrant.FilterCheck.md Demonstrates how to use context injection with AshGrant scopes for deterministic testing of temporal and parameterized scopes. Context is passed via Ash.Query.set_context/2. ```elixir # Scope definition: scope :today, expr(fragment("DATE(inserted_at) = ?", ^context(:reference_date))) # Query with injected context: Post |> Ash.Query.for_read(:read) |> Ash.Query.set_context(%{reference_date: ~D[2025-01-15]}) |> Ash.read!(actor: actor) ``` -------------------------------- ### Get Configured Actions for Resource Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves the list of action names configured via `can_perform_actions`. Returns an empty list if not configured. ```elixir @spec can_perform_actions(Ash.Resource.t()) :: [atom()] ``` -------------------------------- ### Resolver Context Example Source: https://hexdocs.pm/ash_grant/getting-started.md Demonstrates how to use the `context` parameter in a permission resolver to conditionally grant permissions based on the resource, action, and actor's relationship to the resource. ```elixir defmodule MyApp.PermissionResolver do @behaviour AshGrant.PermissionResolver @impl true def resolve(actor, context) do base_permissions = get_role_permissions(actor) # Add instance permissions based on context case context do %{resource: MyApp.Document, action: %{name: :read}} -> shared_docs = get_shared_document_ids(actor) instance_perms = Enum.map(shared_docs, &"document:{&1}:read:") base_permissions ++ instance_perms _ -> base_permissions end end end ``` -------------------------------- ### Get AshGrant DSL Options Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieve a map of all configured or default AshGrant DSL options. ```elixir @spec ash_grant_options(dsl_or_extended :: module() | map()) :: %{ required(atom()) => any() } ``` -------------------------------- ### Field Group Inheritance Example Source: https://hexdocs.pm/ash_grant/field-level-permissions.md Illustrates field group inheritance, showing how a child group includes all fields from its parent groups. ```elixir # :public → [:name, :department, :position] # :sensitive → [:name, :department, :position, :phone, :address] # :confidential → [:name, :department, :position, :phone, :address, :salary, :email] ``` -------------------------------- ### Combined Authorization Patterns in AshGrant Source: https://hexdocs.pm/ash_grant/authorization-patterns.md This example demonstrates how to combine RBAC, ABAC, and ReBAC scopes, along with field-level restrictions and UI visibility controls, within a single AshGrant configuration. ```elixir ash_grant do resolver MyApp.PermissionResolver default_policies true default_field_policies true # RBAC scopes scope :always, true scope :own, expr(author_id == ^actor(:id)) # ABAC scopes scope :same_tenant, expr(tenant_id == ^actor(:tenant_id)) scope :active, expr(status == :active) scope :tenant_own, [:same_tenant], expr(author_id == ^actor(:id)) # ReBAC scope :team_member, expr(exists(team.memberships, user_id == ^actor(:id))) scope_through :feed # Field-level field_group :public, [:title, :body] field_group :internal, [:notes, :score], inherits: [:public] # UI visibility can_perform_actions [:update, :destroy] end ``` -------------------------------- ### List Available Actions (Detailed) Source: https://hexdocs.pm/ash_grant/debugging-and-introspection.md Use `allowed_actions/2` with `detailed: true` to get a list of actions with their associated scopes and field group details. ```elixir # With details AshGrant.Introspect.allowed_actions(Post, user, detailed: true) # => [ # %{action: :read, scope: "all", instance_ids: nil, field_groups: []}, # %{action: :create, scope: "all", instance_ids: nil, field_groups: []}, # %{action: :update, scope: "own", instance_ids: nil, field_groups: []} # ] ``` -------------------------------- ### RBAC Resource Setup with AshGrant Source: https://hexdocs.pm/ash_grant/authorization-patterns.md Defines an Ash resource with AshGrant extensions, including a permission resolver and default policies. Sets up scopes for 'always', 'own', and 'published' access. ```elixir defmodule MyApp.Blog.Post do use Ash.Resource, domain: MyApp.Blog, authorizers: [Ash.Policy.Authorizer], extensions: [AshGrant] ash_grant do resolver MyApp.PermissionResolver default_policies true scope :always, true scope :own, expr(author_id == ^actor(:id)) scope :published, expr(status == :published) end end ``` -------------------------------- ### Example: Resolve Write Scope Filter with `write: false` Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Demonstrates resolving a scope filter when the scope explicitly sets `write: false`. ```elixir resolve_write_scope_filter(Resource, :readonly, context) # => false ``` -------------------------------- ### Get Available Permissions for Management Source: https://hexdocs.pm/ash_grant/AshGrant.Introspect.md Returns all available permissions for a resource. Useful for permission management UIs to show what permissions can be assigned. No options are available. ```elixir iex> Introspect.available_permissions(Post) [ %{permission_string: "post:*:read:always", action: "read", scope: "always", scope_description: nil, field_group: nil}, %{permission_string: "post:*:read:own", action: "read", scope: "own", scope_description: "...", field_group: nil}, ... ] ``` -------------------------------- ### Implement PermissionResolver with PermissionInput Source: https://hexdocs.pm/ash_grant/AshGrant.PermissionInput.md Return PermissionInput structs from your resolver to enable enhanced debugging. This example shows how to map role permissions to PermissionInput structs. ```elixir defmodule MyApp.PermissionResolver do @behaviour AshGrant.PermissionResolver @impl true def resolve(actor, _context) do actor |> get_role_permissions() |> Enum.map(fn role_perm -> %AshGrant.PermissionInput{ string: role_perm.permission, description: role_perm.label, source: "role:#{role_perm.role_name}" } end) end end ``` -------------------------------- ### Relational Scope Example Source: https://hexdocs.pm/ash_grant/argument-based-scope.md This is an example of a relational scope that traverses a relationship directly. It works for read actions but can cause issues with write actions. ```elixir scope :at_own_unit, expr(order.center_id in ^actor(:own_org_unit_ids)) ``` -------------------------------- ### Scope Naming Convention Examples Source: https://hexdocs.pm/ash_grant/scope-naming-convention.md Examples illustrating good and awkward scope names based on the 'Actor can [action] [resource] [scope]' sentence test. ```elixir "Actor can update own notification" good "Actor can read member at own unit" good "Actor can cancel schedule upcoming" good "Actor can read member subtree" awkward - what's a subtree? "Actor can read post all" awkward - all what? ``` -------------------------------- ### scopes Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Gets all scope definitions for a resource. ```APIDOC ## scopes ### Description Gets all scope definitions for a resource. ### Signature `scopes(Ash.Resource.t()) :: [AshGrant.Dsl.Scope.t()]` ``` -------------------------------- ### Get Resource Name for Permissions Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Define the resource name used in permission matching. Defaults to the lowercased last part of the module name (e.g., `MyApp.Blog.Post` becomes `"post"`). ```elixir @spec ash_grant_resource_name(dsl_or_extended :: module() | map()) :: {:ok, String.t()} | :error ``` ```elixir @spec ash_grant_resource_name!(dsl_or_extended :: module() | map()) :: String.t() | no_return() ``` -------------------------------- ### Example: Resolve Write Scope Filter falling back to `filter` Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Demonstrates resolving a scope filter when the scope does not have a `write:` field, falling back to its regular `filter`. ```elixir resolve_write_scope_filter(Resource, :own, context) # => expr(author_id == ^actor(:id)) ``` -------------------------------- ### Recommended Permission Formats Source: https://hexdocs.pm/ash_grant/permissions.md Illustrates the recommended full 4-part format for RBAC and instance permissions to avoid ambiguity, contrasting with legacy formats. ```elixir # RBAC permissions "blog:*:read:always" # Explicit 4-part format (recommended) "blog:read:always" # Legacy 3-part format (works but discouraged) # Instance permissions "blog:post123:read:" # Explicit instance permission (recommended) ``` -------------------------------- ### resolver Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Gets the permission resolver for a resource. This can be a module or a function. ```APIDOC ## resolver ### Description Gets the permission resolver for a resource. ### Signature `resolver(Ash.Resource.t()) :: module() | function() | nil` ``` -------------------------------- ### Get Scopes Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves all scope definitions for a given Ash resource. ```elixir @spec scopes(Ash.Resource.t()) :: [AshGrant.Dsl.Scope.t()] ``` -------------------------------- ### Get Resolve Arguments for Resource Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves all `resolve_argument` declarations for a resource. ```elixir @spec resolve_arguments(Ash.Resource.t()) :: [AshGrant.Dsl.ResolveArgument.t()] ``` -------------------------------- ### get_all_scopes Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Gets all scopes for matching permissions for a given resource and action. ```APIDOC ## `get_all_scopes` ### Description Gets all scopes for matching permissions. Returns a list of scopes from all matching allow permissions. ### Signature ```elixir @spec get_all_scopes(permissions(), String.t(), String.t(), atom() | nil) :: [ String.t() ] ``` ### Parameters - `permissions`: A list of permission strings. - `resource`: The resource to check permissions against. - `action`: The action to check permissions for. - `field_group` (optional): The field group to filter by. ### Returns A list of scopes from matching permissions. ``` -------------------------------- ### Configure dual read/write scope with write: option Source: https://hexdocs.pm/ash_grant/changelog.md Example of using the `write:` option within a scope definition to provide a separate, explicit expression for write actions. This ensures correct authorization checks for create, update, and destroy operations, especially when relationship references are involved. ```elixir scope :active_user, read: expr(user_id == ^actor(:id)), write: expr(user_id == ^actor(:id) and is_active == true) ``` -------------------------------- ### Get Specific Scope by Name Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves a specific scope definition by its name. ```elixir @spec get_scope(Ash.Resource.t(), atom()) :: AshGrant.Dsl.Scope.t() | nil ``` -------------------------------- ### Get Field Groups for Resource Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves all field group definitions for a resource. ```elixir @spec field_groups(Ash.Resource.t()) :: [AshGrant.Dsl.FieldGroup.t()] ``` -------------------------------- ### Explain Authorization Decisions with `explain/4` Source: https://hexdocs.pm/ash_grant/debugging-and-introspection.md Use `AshGrant.explain/4` to get a detailed breakdown of why an authorization decision was made. Inspect the decision, matching permissions, and reasons for non-matches. The output can be converted to a human-readable string. ```elixir result = AshGrant.explain(MyApp.Post, :read, actor) # Check the decision result.decision # => :allow or :deny # See matching permissions with metadata result.matching_permissions # => [%{permission: "post:*:read:always", description: "Read all posts", source: "editor_role", ...}] # See why permissions didn't match result.evaluated_permissions # => [%{permission: "post:*:update:own", matched: false, reason: "Action mismatch"}, ...] # Print human-readable output result |> AshGrant.Explanation.to_string() |> IO.puts() ``` -------------------------------- ### get_instance_scope Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Gets the scope for a matching instance permission for a given instance and action. ```APIDOC ## `get_instance_scope` ### Description Gets the scope for a matching instance permission. Returns the scope from the first matching allow permission for the given instance. ### Signature ```elixir @spec get_instance_scope(permissions(), String.t(), String.t()) :: String.t() | nil ``` ### Parameters - `permissions`: A list of permission strings. - `instance_id`: The specific instance ID to check. - `action`: The action to check permissions for. ### Returns The scope from the first matching allow permission for the instance, or nil. ``` -------------------------------- ### get_all_instance_scopes Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Gets all scopes for matching instance permissions for a given instance and action. ```APIDOC ## `get_all_instance_scopes` ### Description Gets all scopes for matching instance permissions. Returns a list of scopes from all matching allow permissions for the given instance. ### Signature ```elixir @spec get_all_instance_scopes(permissions(), String.t(), String.t()) :: [String.t()] ``` ### Parameters - `permissions`: A list of permission strings. - `instance_id`: The specific instance ID to check. - `action`: The action to check permissions for. ### Returns A list of scopes from matching instance permissions. ``` -------------------------------- ### get_all_field_groups Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Gets all field groups from matching permissions for a given resource and action. ```APIDOC ## `get_all_field_groups` ### Description Gets all field groups from matching permissions. Returns a deduplicated list of field group names from all matching allow permissions. ### Signature ```elixir @spec get_all_field_groups(permissions(), String.t(), String.t(), atom() | nil) :: [ String.t() ] ``` ### Parameters - `permissions`: A list of permission strings. - `resource`: The resource to check permissions against. - `action`: The action to check permissions for. - `field_group` (optional): The field group to filter by. ### Returns A deduplicated list of field group names. ``` -------------------------------- ### scope_throughs Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Gets all scope_through entities for a resource. Returns an empty list if none are configured. ```APIDOC ## scope_throughs ### Description Gets all scope_through entities for a resource. Returns an empty list if none are configured. ### Signature `scope_throughs(Ash.Resource.t()) :: [AshGrant.Dsl.ScopeThrough.t()]` ``` -------------------------------- ### get_matching_instance_ids Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Gets all instance IDs that the user has permission to access for a given resource and action. ```APIDOC ## `get_matching_instance_ids` ### Description Gets all instance IDs that the user has permission to access. Returns a list of instance IDs from all matching instance permissions (where instance_id != "*") for the given resource and action. ### Signature ```elixir @spec get_matching_instance_ids(permissions(), String.t(), String.t(), atom() | nil) :: [String.t()] ``` ### Parameters - `permissions`: A list of permission strings. - `resource`: The resource to check permissions against. - `action`: The action to check permissions for. - `field_group` (optional): The field group to filter by. ### Returns A list of instance IDs that the user has permission to access. ``` -------------------------------- ### explain Source: https://hexdocs.pm/ash_grant/AshGrant.Explainer.md Explains an authorization decision for a resource and action, returning an AshGrant.Explanation struct with details on the decision, matching and evaluated permissions, scope information, and denial reasons. ```APIDOC ## explain ### Description Explains an authorization decision for a resource and action. Returns an `AshGrant.Explanation` struct with: - The final decision (`:allow` or `:deny`) - All matching permissions with their metadata - All evaluated permissions with match status - Scope information from both permissions and DSL - Reason for denial if applicable ### Method Signature ```elixir @spec explain(module(), atom(), term(), map()) :: AshGrant.Explanation.t() ``` ### Examples ```elixir AshGrant.Explainer.explain(MyApp.Post, :read, actor) # %AshGrant.Explanation{decision: :allow, ...} AshGrant.Explainer.explain(MyApp.Post, :read, nil) # %AshGrant.Explanation{decision: :deny, reason: :no_matching_permissions} ``` ``` -------------------------------- ### get_field_group Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Gets the field group from the first matching permission for a given resource and action. ```APIDOC ## `get_field_group` ### Description Gets the field group from the first matching permission. Returns the field_group string from the first matching allow permission. ### Signature ```elixir @spec get_field_group(permissions(), String.t(), String.t(), atom() | nil) :: String.t() | nil ``` ### Parameters - `permissions`: A list of permission strings. - `resource`: The resource to check permissions against. - `action`: The action to check permissions for. - `field_group` (optional): The field group to filter by. ### Returns The field group string from the first matching allow permission, or nil. ``` -------------------------------- ### Export Policy to Markdown Documentation Source: https://hexdocs.pm/ash_grant/Mix.Tasks.AshGrant.Export.md Create human-readable Markdown documentation from your policy configuration. This is helpful for sharing and understanding policies. ```bash mix ash_grant.export MyApp.Document --format=markdown ``` -------------------------------- ### Find Matching Permissions Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Finds all permissions that match a given resource and action, including both allow and deny rules. Useful for detailed permission analysis. ```elixir iex> permissions = ["blog:*:*:always", "!blog:*:delete:always", "blog:*:read:published"] iex> matching = AshGrant.Evaluator.find_matching(permissions, "blog", "read") iex> length(matching) 2 ``` -------------------------------- ### scope_resolver Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Gets the scope resolver for a resource. DEPRECATED: Use inline `scope` entities instead. ```APIDOC ## scope_resolver ### Description Gets the scope resolver for a resource. ### Deprecation DEPRECATED: Use inline `scope` entities instead. ### Signature `scope_resolver(Ash.Resource.t()) :: module() | function() | nil` ``` -------------------------------- ### scope_description Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Gets the description for a specific scope. Returns nil if the scope doesn't exist or has no description. ```APIDOC ## scope_description ### Description Gets the description for a specific scope. Returns `nil` if the scope doesn't exist or has no description. ### Signature `scope_description(Ash.Resource.t(), atom()) :: String.t() | nil` ### Examples ```elixir iex> AshGrant.Info.scope_description(MyApp.Blog.Post, :own) "Records owned by the current user" iex> AshGrant.Info.scope_description(MyApp.Blog.Post, :all) nil ``` ``` -------------------------------- ### Basic Access Check with `AshGrant.Evaluator` Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Demonstrates basic access checks for read and write actions on a resource. Access is denied if no explicit rule matches. ```elixir permissions = ["blog:*:read:always", "blog:*:write:own"] Evaluator.has_access?(permissions, "blog", "read") # true Evaluator.has_access?(permissions, "blog", "write") # true Evaluator.has_access?(permissions, "blog", "delete") # false ``` -------------------------------- ### Get Specific Field Group by Name Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves a specific field group definition by its name. ```elixir @spec get_field_group(Ash.Resource.t(), atom()) :: AshGrant.Dsl.FieldGroup.t() | nil ``` -------------------------------- ### Minimal AshGrant Setup with Default Policies Source: https://hexdocs.pm/ash_grant/AshGrant.md Configure AshGrant with `default_policies: true` to automatically generate read/write policies. Define custom scopes like `:always`, `:own`, and `:published` using `expr()` for conditional access. ```elixir defmodule MyApp.Blog.Post do use Ash.Resource, domain: MyApp.Blog, authorizers: [Ash.Policy.Authorizer], extensions: [AshGrant] ash_grant do resolver MyApp.PermissionResolver default_policies true # Auto-generates read/write policies! scope :always, true scope :own, expr(author_id == ^actor(:id)) scope :published, expr(status == :published) end # No policies block needed - AshGrant generates them automatically! # ... attributes, actions, etc. end ``` -------------------------------- ### Run All Policy Tests Source: https://hexdocs.pm/ash_grant/Mix.Tasks.AshGrant.Verify.md Execute all policy tests located in the default test directory. ```bash mix ash_grant.verify ``` -------------------------------- ### Wildcard Matching Rules for Permission Components Source: https://hexdocs.pm/ash_grant/permissions.md Illustrates how wildcard characters ('*') and type wildcards ('type*') apply to different components of a permission string. ```text | Component | `*` (all) | `type*` (action type) | Exact match | |-----------|-----------|----------------------|-------------| | resource | Yes | No | Yes | | instance_id | Yes | No | Yes | | action | Yes | Yes | Yes | | scope | No | No | Yes | ``` -------------------------------- ### Argument-Based Scope Example Source: https://hexdocs.pm/ash_grant/scope-naming-convention.md When the scoped attribute is not on the record itself, use `resolve_argument` to populate it and write the expression against the argument. ```elixir scope :at_own_unit, expr(^arg(:center_id) in ^actor(:own_org_unit_ids)) resolve_argument :center_id, from_path: [:order, :center_id] ``` -------------------------------- ### Convert String to PermissionInput Source: https://hexdocs.pm/ash_grant/AshGrant.Permissionable.md Example of using the `to_permission_input` function to convert a plain string into an AshGrant.PermissionInput struct. ```elixir iex> AshGrant.Permissionable.to_permission_input("post:*:read:always") %AshGrant.PermissionInput{string: "post:*:read:always"} ``` -------------------------------- ### Run Policy Tests with Verbose Output Source: https://hexdocs.pm/ash_grant/Mix.Tasks.AshGrant.Verify.md Execute policy tests and display detailed output for each test. Use the `--verbose` flag for more information. ```bash mix ash_grant.verify --verbose ``` -------------------------------- ### run_all/1 Source: https://hexdocs.pm/ash_grant/AshGrant.PolicyTest.Runner.md Executes policy tests across multiple modules and provides a summary of the test execution. ```APIDOC ## run_all/1 ### Description Runs tests across multiple modules and returns a summary. ### Method Signature `run_all(keyword()) :: summary()` ### Options - `:modules` - A list of modules from which to run tests. - `:path` - The path to a directory containing policy test files. This option enables module discovery within the specified path. ### Returns A `summary()` struct containing: - `passed`: The number of tests that passed. - `failed`: The number of tests that failed. - `results`: A list of `AshGrant.PolicyTest.Result.t()` structs detailing each test outcome. ### Examples ```elixir # Run specific modules summary = Runner.run_all(modules: [DocumentTest, PostTest]) # Discover and run all policy tests in a directory summary = Runner.run_all(path: "test/policy_tests/") ``` ``` -------------------------------- ### resource_name Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Gets the resource name for permission matching. Falls back to deriving from the module name if not configured. ```APIDOC ## resource_name ### Description Gets the resource name for permission matching. Falls back to deriving from the module name if not configured. ### Signature `resource_name(Ash.Resource.t()) :: String.t()` ``` -------------------------------- ### Define Policy Test Module Source: https://hexdocs.pm/ash_grant/AshGrant.PolicyTest.md Use `AshGrant.PolicyTest` to set up the DSL for testing policy configurations. Define resources and actors before writing tests. ```elixir defmodule MyApp.PolicyTests.DocumentPolicyTest do use AshGrant.PolicyTest resource MyApp.Document actor :reader, %{role: :reader} actor :author, %{role: :author, id: "author_001"} actor :guest, %{permissions: []} describe "read access" do test "reader can read" do assert_can :reader, :read end test "guest cannot read" do assert_cannot :guest, :read end test "reader can read published documents" do assert_can :reader, :read, %{status: :published} end end describe "update access" do test "author can update own drafts" do assert_can :author, :update, %{author_id: "author_001", status: :draft} end end end ``` -------------------------------- ### Get raw permission strings Source: https://hexdocs.pm/ash_grant/debugging-and-introspection.md Obtain a list of raw permission strings for a given resource and user. ```APIDOC ## Get Raw Permissions ### Description Retrieves a list of raw permission strings associated with a resource and user. ### Method `AshGrant.Introspect.permissions_for(resource, user)` ### Parameters - **resource**: The resource to check. - **user**: The user for whom to retrieve permissions. ### Response - `["post:*:read:always", "post:*:update:own", ...]`: A list of raw permission strings. ``` -------------------------------- ### Authorization Debugging with AshGrant.explain/4 Source: https://hexdocs.pm/ash_grant/changelog.md Utilize `AshGrant.explain/4` to obtain a detailed `AshGrant.Explanation` struct for authorization decisions. This struct includes matching permissions with metadata, evaluated permissions, and scope information, which can be converted to a human-readable string using `AshGrant.Explanation.to_string/2`. ```elixir AshGrant.explain(My.Data.User, :update, actor, context) ``` -------------------------------- ### Get Instance Key for Permissions Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves the field used to match instance permission IDs against. Defaults to `:id`. ```elixir @spec instance_key(Ash.Resource.t()) :: atom() ``` -------------------------------- ### Mix Tasks for Policy Verification Source: https://hexdocs.pm/ash_grant/policy-testing.md Execute policy tests using `mix ash_grant.verify`. This command can run tests from a specified directory or a single YAML file. Use the `--verbose` flag for detailed output. ```bash # Run DSL tests mix ash_grant.verify test/policy_tests/ # Run YAML tests mix ash_grant.verify priv/policy_tests/document.yaml # Verbose output mix ash_grant.verify test/policy_tests/ --verbose ``` -------------------------------- ### Convert AshGrant.Explanation to String Source: https://hexdocs.pm/ash_grant/AshGrant.Explanation.md Converts an AshGrant.Explanation struct to a human-readable string. Options include `:color` and `:verbose`. ```elixir iex> AshGrant.Explanation.to_string(result) """ ═══════════════════════════════════════════════════════════════════ Authorization Explanation for MyApp.Post ═══════════════════════════════════════════════════════════════════ Action: read Decision: ✓ ALLOW ... """ ``` ```elixir iex> result = AshGrant.explain(MyApp.Post, :read, actor) iex> IO.puts(AshGrant.Explanation.to_string(result)) ═══════════════════════════════════════════════════════════════════ Authorization Explanation for MyApp.Post ═══════════════════════════════════════════════════════════════════ Action: read Decision: ✓ ALLOW ... ``` -------------------------------- ### Get Scope Throughs Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves all scope_through entities configured for an Ash resource. Returns an empty list if none are configured. ```elixir @spec scope_throughs(Ash.Resource.t()) :: [AshGrant.Dsl.ScopeThrough.t()] ``` -------------------------------- ### Match Instance Permission Source: https://hexdocs.pm/ash_grant/AshGrant.Permission.md Verifies if a permission matches a specific resource instance and action. Useful for permissions with concrete instance IDs. ```elixir @spec matches_instance?(t(), String.t(), String.t()) :: boolean() # Examples perm = AshGrant.Permission.parse!("blog:post_abc123xyz789ab:read:") AshGrant.Permission.matches_instance?(perm, "post_abc123xyz789ab", "read") perm = AshGrant.Permission.parse!("blog:post_abc123xyz789ab:*:" ) AshGrant.Permission.matches_instance?(perm, "post_abc123xyz789ab", "write") ``` -------------------------------- ### Get Resource Permission Resolver Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves the permission resolver for a given Ash resource. This can be a module, function, or nil if not configured. ```elixir @spec resolver(Ash.Resource.t()) :: module() | function() | nil ``` -------------------------------- ### Create PermissionInput using `new/2` Source: https://hexdocs.pm/ash_grant/AshGrant.PermissionInput.md Use the `new/2` function to create a PermissionInput struct from a permission string, optionally providing keyword arguments for description. ```elixir iex> AshGrant.PermissionInput.new("post:*:read:always") %AshGrant.PermissionInput{string: "post:*:read:always"} ``` ```elixir iex> AshGrant.PermissionInput.new("post:*:update:own", description: "Edit own posts") %AshGrant.PermissionInput{string: "post:*:update:own", description: "Edit own posts"} ``` -------------------------------- ### Get Default Policies Setting Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves the `default_policies` setting. This controls whether policies for read and write actions are auto-generated. ```elixir @spec default_policies(Ash.Resource.t()) :: boolean() | :read | :write | :all ``` -------------------------------- ### Multiple Permissions Combination (OR) Source: https://hexdocs.pm/ash_grant/scopes.md Illustrates how multiple permissions with different scopes for the same action are combined using OR logic. ```elixir # Actor has both permissions: ["post:*:read:own", "post:*:read:published"] # Result filter: (author_id == actor.id) OR (status == :published) # Actor can see their own posts AND all published posts ``` -------------------------------- ### Run multiple policy test modules Source: https://hexdocs.pm/ash_grant/AshGrant.PolicyTest.Runner.md Use `run_all/1` to execute tests across multiple modules. You can specify modules directly or provide a path to discover test modules. ```elixir summary = Runner.run_all(modules: [DocumentTest, PostTest]) ``` ```elixir summary = Runner.run_all(path: "test/policy_tests/") ``` -------------------------------- ### Explicitly allow all writes with write: true Source: https://hexdocs.pm/ash_grant/changelog.md Shows how to use `write: true` within a scope definition to explicitly allow all write operations for resources matching this scope, bypassing any further checks. This is useful for broad administrative permissions. ```elixir scope :admin_resource, read: true, write: true ``` -------------------------------- ### Getting Scopes with `AshGrant.Evaluator` Source: https://hexdocs.pm/ash_grant/AshGrant.Evaluator.md Retrieves the first matching scope for a given resource and action, or all matching scopes if multiple exist. ```elixir permissions = [ "blog:*:read:own", "blog:*:read:published", "blog:*:update:own" ] Evaluator.get_scope(permissions, "blog", "read") # => "own" (first matching) Evaluator.get_all_scopes(permissions, "blog", "read") # => ["own", "published"] ``` -------------------------------- ### Run Policy Tests from YAML File Source: https://hexdocs.pm/ash_grant/Mix.Tasks.AshGrant.Verify.md Execute policy tests defined within a YAML file. Provide the path to the YAML file. ```bash mix ash_grant.verify priv/policy_tests/document.yaml ``` -------------------------------- ### Get Scope Description Source: https://hexdocs.pm/ash_grant/AshGrant.Info.md Retrieves the description for a specific scope within an Ash resource. Returns nil if the scope does not exist or has no description. ```elixir @spec scope_description(Ash.Resource.t(), atom()) :: String.t() | nil ``` ```elixir iex> AshGrant.Info.scope_description(MyApp.Blog.Post, :own) "Records owned by the current user" ``` ```elixir iex> AshGrant.Info.scope_description(MyApp.Blog.Post, :all) nil ```