This prevents security issues where :create/:read without actor would be allowed in production. Now all operations require an actor in production.
74 lines
2.3 KiB
Elixir
74 lines
2.3 KiB
Elixir
defmodule Mv.Authorization.Checks.NoActor do
|
|
@moduledoc """
|
|
Custom Ash Policy Check that allows actions when no actor is present.
|
|
|
|
**IMPORTANT:** This check ONLY works in test environment for security reasons.
|
|
In production/dev, ALL operations without an actor are denied.
|
|
|
|
## Security Note
|
|
|
|
This check uses compile-time environment detection to prevent accidental
|
|
security issues in production. In production, ALL operations (including :create
|
|
and :read) will be denied if no actor is present.
|
|
|
|
For seeds and system operations in production, use an admin actor instead:
|
|
|
|
admin_user = get_admin_user()
|
|
Ash.create!(resource, attrs, actor: admin_user)
|
|
|
|
## Usage in Policies
|
|
|
|
policies do
|
|
# Allow system operations without actor (TEST ENVIRONMENT ONLY)
|
|
# In test: All operations allowed
|
|
# In production: ALL operations denied (fail-closed)
|
|
bypass action_type([:create, :read, :update, :destroy]) do
|
|
authorize_if NoActor
|
|
end
|
|
|
|
# Check permissions when actor is present
|
|
policy action_type([:read, :create, :update, :destroy]) do
|
|
authorize_if HasPermission
|
|
end
|
|
end
|
|
|
|
## Behavior
|
|
|
|
- In test environment: Returns `true` when actor is nil (allows all operations)
|
|
- In production/dev: Returns `false` when actor is nil (denies all operations - fail-closed)
|
|
- Returns `false` when actor is present (delegates to other policies)
|
|
"""
|
|
|
|
use Ash.Policy.SimpleCheck
|
|
|
|
# Compile-time check: Only allow no-actor bypass in test environment
|
|
@allow_no_actor_bypass Mix.env() == :test
|
|
# Alternative (if you want to control via config):
|
|
# @allow_no_actor_bypass Application.compile_env(:mv, :allow_no_actor_bypass, false)
|
|
|
|
@impl true
|
|
def describe(_opts) do
|
|
if @allow_no_actor_bypass do
|
|
"allows actions when no actor is present (test environment only)"
|
|
else
|
|
"denies all actions when no actor is present (production/dev - fail-closed)"
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def match?(nil, _context, _opts) do
|
|
# Actor is nil
|
|
if @allow_no_actor_bypass do
|
|
# Test environment: Allow all operations
|
|
true
|
|
else
|
|
# Production/dev: Deny all operations (fail-closed for security)
|
|
false
|
|
end
|
|
end
|
|
|
|
def match?(_actor, _context, _opts) do
|
|
# Actor is present - don't match (let other policies decide)
|
|
false
|
|
end
|
|
end
|