AshGrant.Info (AshGrant v0.14.1)

Copy Markdown View Source

Introspection helpers for AshGrant DSL configuration.

This module provides functions to query AshGrant configuration at runtime, including resolvers, scopes, field groups, and policy settings.

Common Functions

FunctionDescription
resolver/1Get the permission resolver for a resource
default_policies/1Get the default_policies setting
default_field_policies/1Get the default_field_policies setting
resource_name/1Get the resource name for permission matching
scopes/1Get all scope definitions
get_scope/2Get a specific scope by name
resolve_scope_filter/3Resolve a scope to its read filter expression
resolve_write_scope_filter/3Resolve a scope to its write filter expression
field_groups/1Get all field group definitions
get_field_group/2Get a specific field group by name
resolve_field_group/2Resolve a field group with inheritance
owner_field/1Deprecated. Use explicit scope expressions instead

Example

iex> AshGrant.Info.default_policies(MyApp.Blog.Post)
true

iex> AshGrant.Info.resolver(MyApp.Blog.Post)
MyApp.PermissionResolver

iex> AshGrant.Info.scopes(MyApp.Blog.Post) |> Enum.map(& &1.name)
[:all, :own, :published]

Summary

Functions

ash_grant DSL entities

List of action names to generate CanPerform calculations for. Each generates a :can_<action>? boolean calculation (public by default).

List of action names to generate CanPerform calculations for. Each generates a :can_<action>? boolean calculation (public by default).

Automatically generate Ash field_policies from field_group definitions.

Automatically generate Ash field_policies from field_group definitions.

Automatically generate standard AshGrant policies.

Automatically generate standard AshGrant policies.

Field to match instance permission IDs against. Defaults to :id (primary key).

Field to match instance permission IDs against. Defaults to :id (primary key).

ash_grant DSL options

DEPRECATED: Use explicit scope :own, expr(field == ^actor(:id)) instead.

DEPRECATED: Use explicit scope :own, expr(field == ^actor(:id)) instead.

Module implementing AshGrant.PermissionResolver behaviour, or a 2-arity function (actor, context) -> permissions.

Module implementing AshGrant.PermissionResolver behaviour, or a 2-arity function (actor, context) -> permissions.

The resource name used in permission matching.

The resource name used in permission matching.

DEPRECATED: Use inline scope entities instead.

DEPRECATED: Use inline scope entities instead.

Gets the list of action names configured via can_perform_actions.

Checks if AshGrant is configured for a resource.

Gets the default_field_policies setting.

Gets the default_policies setting.

Gets all field group definitions for a resource.

Gets a specific field group by name.

Gets a specific scope by name.

Gets the field to match instance permission IDs against.

Gets the owner field for "own" scope resolution.

Gets all resolve_argument declarations for a resource.

Resolves a field group to its complete field set including inherited fields.

Resolves a scope to its read filter expression.

Resolves a scope's write filter expression.

Gets the permission resolver for a resource.

Gets the resource name for permission matching.

Gets the description for a specific scope.

Gets the scope resolver for a resource.

Gets all scope_through entities for a resource.

Gets all scope definitions for a resource.

Functions

ash_grant(dsl_or_extended)

@spec ash_grant(dsl_or_extended :: module() | map()) :: [struct()]

ash_grant DSL entities

ash_grant_can_perform_actions(dsl_or_extended)

@spec ash_grant_can_perform_actions(dsl_or_extended :: module() | map()) ::
  {:ok, [atom()]} | :error

List of action names to generate CanPerform calculations for. Each generates a :can_<action>? boolean calculation (public by default).

Example

can_perform_actions [:update, :destroy]

Generates :can_update? and :can_destroy? calculations.

ash_grant_can_perform_actions!(dsl_or_extended)

@spec ash_grant_can_perform_actions!(dsl_or_extended :: module() | map()) ::
  [atom()] | no_return()

List of action names to generate CanPerform calculations for. Each generates a :can_<action>? boolean calculation (public by default).

Example

can_perform_actions [:update, :destroy]

Generates :can_update? and :can_destroy? calculations.

ash_grant_default_field_policies(dsl_or_extended)

@spec ash_grant_default_field_policies(dsl_or_extended :: module() | map()) ::
  {:ok, boolean()} | :error

Automatically generate Ash field_policies from field_group definitions.

When true, AshGrant generates field policies that use AshGrant.FieldCheck to authorize field access based on the 5th part of permission strings.

When false (default), you can manually write field_policies using AshGrant.field_check/1 (Mode A).

ash_grant_default_field_policies!(dsl_or_extended)

@spec ash_grant_default_field_policies!(dsl_or_extended :: module() | map()) ::
  boolean() | no_return()

Automatically generate Ash field_policies from field_group definitions.

When true, AshGrant generates field policies that use AshGrant.FieldCheck to authorize field access based on the 5th part of permission strings.

When false (default), you can manually write field_policies using AshGrant.field_check/1 (Mode A).

ash_grant_default_policies(dsl_or_extended)

@spec ash_grant_default_policies(dsl_or_extended :: module() | map()) ::
  {:ok, boolean() | :all | :write | :read} | :error

Automatically generate standard AshGrant policies.

When enabled, AshGrant will automatically add policies to your resource, eliminating the need to manually define the policies block.

Options:

  • false - No policies are generated (default, explicit policies required)
  • true or :all - Generate policies for both read and write actions
  • :read - Only generate policy for read actions (filter_check)
  • :write - Only generate policy for write actions (check)

Generated policies:

policies do
  policy action_type(:read) do
    authorize_if AshGrant.filter_check()
  end

  policy action_type([:create, :update, :destroy]) do
    authorize_if AshGrant.check()
  end
end

Note: When using default_policies, you should still add authorizers: [Ash.Policy.Authorizer] to your resource options.

ash_grant_default_policies!(dsl_or_extended)

@spec ash_grant_default_policies!(dsl_or_extended :: module() | map()) ::
  (boolean() | :all | :write | :read) | no_return()

Automatically generate standard AshGrant policies.

When enabled, AshGrant will automatically add policies to your resource, eliminating the need to manually define the policies block.

Options:

  • false - No policies are generated (default, explicit policies required)
  • true or :all - Generate policies for both read and write actions
  • :read - Only generate policy for read actions (filter_check)
  • :write - Only generate policy for write actions (check)

Generated policies:

policies do
  policy action_type(:read) do
    authorize_if AshGrant.filter_check()
  end

  policy action_type([:create, :update, :destroy]) do
    authorize_if AshGrant.check()
  end
end

Note: When using default_policies, you should still add authorizers: [Ash.Policy.Authorizer] to your resource options.

ash_grant_instance_key(dsl_or_extended)

@spec ash_grant_instance_key(dsl_or_extended :: module() | map()) ::
  {:ok, atom()} | :error

Field to match instance permission IDs against. Defaults to :id (primary key).

When set, instance permissions like "feed:feed_abc:read:" will generate a filter matching the specified field instead of the primary key.

Example

ash_grant do
  instance_key :feed_id
end

With this, "feed:feed_abc:read:" generates WHERE feed_id IN ('feed_abc') instead of WHERE id IN ('feed_abc').

ash_grant_instance_key!(dsl_or_extended)

@spec ash_grant_instance_key!(dsl_or_extended :: module() | map()) ::
  atom() | no_return()

Field to match instance permission IDs against. Defaults to :id (primary key).

When set, instance permissions like "feed:feed_abc:read:" will generate a filter matching the specified field instead of the primary key.

Example

ash_grant do
  instance_key :feed_id
end

With this, "feed:feed_abc:read:" generates WHERE feed_id IN ('feed_abc') instead of WHERE id IN ('feed_abc').

ash_grant_options(dsl_or_extended)

@spec ash_grant_options(dsl_or_extended :: module() | map()) :: %{
  required(atom()) => any()
}

ash_grant DSL options

Returns a map containing the and any configured or default values.

ash_grant_owner_field(dsl_or_extended)

@spec ash_grant_owner_field(dsl_or_extended :: module() | map()) ::
  {:ok, atom()} | :error

DEPRECATED: Use explicit scope :own, expr(field == ^actor(:id)) instead.

The field that identifies the owner of a record. This option is deprecated and will be removed in v1.0.0.

ash_grant_owner_field!(dsl_or_extended)

@spec ash_grant_owner_field!(dsl_or_extended :: module() | map()) ::
  atom() | no_return()

DEPRECATED: Use explicit scope :own, expr(field == ^actor(:id)) instead.

The field that identifies the owner of a record. This option is deprecated and will be removed in v1.0.0.

ash_grant_resolver(dsl_or_extended)

@spec ash_grant_resolver(dsl_or_extended :: module() | map()) ::
  {:ok, module() | (any(), any() -> any())} | :error

Module implementing AshGrant.PermissionResolver behaviour, or a 2-arity function (actor, context) -> permissions.

This resolves permissions for the current actor. Can be inherited from the domain if the domain uses AshGrant.Domain.

ash_grant_resolver!(dsl_or_extended)

@spec ash_grant_resolver!(dsl_or_extended :: module() | map()) ::
  (module() | (any(), any() -> any())) | no_return()

Module implementing AshGrant.PermissionResolver behaviour, or a 2-arity function (actor, context) -> permissions.

This resolves permissions for the current actor. Can be inherited from the domain if the domain uses AshGrant.Domain.

ash_grant_resource_name(dsl_or_extended)

@spec ash_grant_resource_name(dsl_or_extended :: module() | map()) ::
  {:ok, String.t()} | :error

The resource name used in permission matching.

Defaults to the last part of the module name, lowercased. For example, MyApp.Blog.Post becomes "post".

ash_grant_resource_name!(dsl_or_extended)

@spec ash_grant_resource_name!(dsl_or_extended :: module() | map()) ::
  String.t() | no_return()

The resource name used in permission matching.

Defaults to the last part of the module name, lowercased. For example, MyApp.Blog.Post becomes "post".

ash_grant_scope_resolver(dsl_or_extended)

@spec ash_grant_scope_resolver(dsl_or_extended :: module() | map()) ::
  {:ok, module() | (any(), any() -> any())} | :error

DEPRECATED: Use inline scope entities instead.

Module implementing AshGrant.ScopeResolver behaviour, or a 2-arity function (scope, context) -> filter.

This resolves scope strings to Ash filter expressions. If not provided, scopes are resolved from inline scope entities.

ash_grant_scope_resolver!(dsl_or_extended)

@spec ash_grant_scope_resolver!(dsl_or_extended :: module() | map()) ::
  (module() | (any(), any() -> any())) | no_return()

DEPRECATED: Use inline scope entities instead.

Module implementing AshGrant.ScopeResolver behaviour, or a 2-arity function (scope, context) -> filter.

This resolves scope strings to Ash filter expressions. If not provided, scopes are resolved from inline scope entities.

can_perform_actions(resource)

@spec can_perform_actions(Ash.Resource.t()) :: [atom()]

Gets the list of action names configured via can_perform_actions.

Returns an empty list if not configured.

configured?(resource)

@spec configured?(Ash.Resource.t()) :: boolean()

Checks if AshGrant is configured for a resource.

default_field_policies(resource)

@spec default_field_policies(Ash.Resource.t()) :: boolean()

Gets the default_field_policies setting.

Returns true if field policies should be auto-generated from field_group definitions, or false (default) if field policies should be manually defined.

default_policies(resource)

@spec default_policies(Ash.Resource.t()) :: boolean() | :read | :write | :all

Gets the default_policies setting.

Returns false if not configured, or one of:

  • true or :all - Generate policies for both read and write
  • :read - Only generate read policy
  • :write - Only generate write policy

field_groups(resource)

@spec field_groups(Ash.Resource.t()) :: [AshGrant.Dsl.FieldGroup.t()]

Gets all field group definitions for a resource.

get_field_group(resource, name)

@spec get_field_group(Ash.Resource.t(), atom()) :: AshGrant.Dsl.FieldGroup.t() | nil

Gets a specific field group by name.

get_scope(resource, name)

@spec get_scope(Ash.Resource.t(), atom()) :: AshGrant.Dsl.Scope.t() | nil

Gets a specific scope by name.

instance_key(resource)

@spec instance_key(Ash.Resource.t()) :: atom()

Gets the field to match instance permission IDs against.

Defaults to :id (primary key) when not configured.

owner_field(resource)

This function is deprecated. Use explicit scope expressions instead of owner_field.
@spec owner_field(Ash.Resource.t()) :: atom() | nil

Gets the owner field for "own" scope resolution.

DEPRECATED: Use explicit scope :own, expr(field == ^actor(:id)) instead. This option will be removed in v1.0.0.

resolve_arguments(resource)

@spec resolve_arguments(Ash.Resource.t()) :: [AshGrant.Dsl.ResolveArgument.t()]

Gets all resolve_argument declarations for a resource.

resolve_field_group(resource, field_group_name)

@spec resolve_field_group(Ash.Resource.t(), atom()) ::
  %{fields: [atom()], masked_fields: map()} | nil

Resolves a field group to its complete field set including inherited fields.

Returns a map with:

  • :fields - Complete list of accessible field atoms (union of own + inherited)
  • :masked_fields - Map of field_name => mask_function for fields masked at THIS level only

Masking is NOT inherited. A higher-level field group sees original values unless it explicitly declares its own masking.

Returns nil if the field group does not exist.

resolve_scope_filter(resource, scope_name, context)

@spec resolve_scope_filter(Ash.Resource.t(), atom(), map()) ::
  boolean() | Ash.Expr.t()

Resolves a scope to its read filter expression.

Uses the scope's filter field (ignoring any write: option). If the scope has inheritance, the parent scopes are combined with AND. Returns false for unknown scopes.

For write action scope resolution, use resolve_write_scope_filter/3 instead.

resolve_write_scope_filter(resource, scope_name, context)

@spec resolve_write_scope_filter(Ash.Resource.t(), atom(), map()) ::
  boolean() | Ash.Expr.t()

Resolves a scope's write filter expression.

If the scope has a write field set, uses that value. Otherwise falls back to the regular filter. This ensures write actions use a direct-field expression when relationship traversal (exists/dot-paths) cannot be evaluated in-memory.

Returns false for unknown scopes, or when write: false is explicitly set.

Resolution Order

  1. If scope has write: set → use write value (false, true, or expression)
  2. If scope has no write: → fall back to scope.filter (backward compatible)
  3. Inheritance: parent write: values are resolved recursively and combined with AND
  4. If any parent returns false → short-circuit to false (deny propagation)

Examples

# Scope with write: expr(...) → returns the write expression
resolve_write_scope_filter(Resource, :same_org, context)
# => expr(org_id == ^actor(:org_id))

# Scope with write: false → returns false
resolve_write_scope_filter(Resource, :readonly, context)
# => false

# Scope without write: → falls back to filter
resolve_write_scope_filter(Resource, :own, context)
# => expr(author_id == ^actor(:id))

resolver(resource)

@spec resolver(Ash.Resource.t()) :: module() | function() | nil

Gets the permission resolver for a resource.

resource_name(resource)

@spec resource_name(Ash.Resource.t()) :: String.t()

Gets the resource name for permission matching.

Falls back to deriving from the module name if not configured.

scope_description(resource, name)

@spec scope_description(Ash.Resource.t(), atom()) :: String.t() | nil

Gets the description for a specific scope.

Returns nil if the scope doesn't exist or has no description.

Examples

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

scope_resolver(resource)

@spec scope_resolver(Ash.Resource.t()) :: module() | function() | nil

Gets the scope resolver for a resource.

DEPRECATED: Use inline scope entities instead.

scope_throughs(resource)

@spec scope_throughs(Ash.Resource.t()) :: [AshGrant.Dsl.ScopeThrough.t()]

Gets all scope_through entities for a resource.

Returns an empty list if none are configured.

scopes(resource)

@spec scopes(Ash.Resource.t()) :: [AshGrant.Dsl.Scope.t()]

Gets all scope definitions for a resource.