Authorization system for Ectomancer tools.
Supports three authorization strategies:
Inline function - Simple authorization with a function
authorize fn actor, action -> actor.role == :admin or action in [:list, :get] endPolicy module - Complex authorization with a policy module
authorize with: MyApp.Policies.UserPolicyNo authorization - Public access
authorize :none
Cascade Authorization
Authorization can be defined at three levels (from broadest to specific):
- Global (in
use Ectomancer) - Schema (in
expose/2) - Action (in
expose/2with action-specific rules)
When multiple levels are defined, they cascade:
- All levels must pass for authorization to succeed
- If any level fails, the tool returns an unauthorized error
Summary
Functions
Generates an AST quote for the authorize macro call.
Checks authorization for a tool execution.
Checks if authorization is configured.
Parses authorization configuration into a standard format.
Parses an authorization handler into a canonical form.
Like parse_handler/1 but also accepts bare atoms as policy module names.
Used for global authorization configs where MyPolicyModule is a valid form.
Functions
Generates an AST quote for the authorize macro call.
@spec check(any(), atom(), keyword()) :: :ok | {:error, String.t()} | {:ok, :scoped, (term() -> term())}
Checks authorization for a tool execution.
Parameters
actor- The current actor (from frame.assigns[:ectomancer_actor])action- The action being performed (e.g., :list, :get, :create)opts- Authorization options:handler- The authorization handler (function, module, or :none):schema- The schema module (for context):parent_auth- Parent authorization config for cascading
Returns
:ok- Authorization passed{:ok, :scoped, scope_fn}- Authorization passed with row-level scope{:error, reason}- Authorization failed with reason
The scope_fn is a function that takes an Ecto query and returns a scoped query:
fn query ->
from(u in query, where: u.org_id == ^actor.org_id)
endWhen a scope is returned, it is automatically applied to all CRUD queries
(list, get, update, destroy) for that tool call.
Checks if authorization is configured.
@spec parse_authorization_config(nil | :none | :public | function() | keyword()) :: nil | %{global: term(), actions: map()}
Parses authorization configuration into a standard format.
Returns nil for no authorization, or a map with :global handler
and :actions for per-action handlers.
Forms
nil,:none,:public→nil- function or module →
%{global: handler, actions: %{}} - keyword list with per-action rules →
%{global: handler, actions: %{action: handler}}
Example
# Global admin-only auth, but list_queues is public:
expose_oban_jobs authorize: [
all: fn actor, _ -> actor.role == :admin end,
list_queues: :public
]
Parses an authorization handler into a canonical form.
Returns the handler value (function, module atom, or AST tuple), or nil for no auth.
Raises ArgumentError for unknown atoms (strict parsing, used for per-tool auth).
Like parse_handler/1 but also accepts bare atoms as policy module names.
Used for global authorization configs where MyPolicyModule is a valid form.