Update documentation: Remove NoActor bypass references
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Moritz 2026-01-23 20:18:28 +01:00
parent a6cdeaa18d
commit f5fe7b0fcd
Signed by: moritz
GPG key ID: 1020A035E5DD0824
5 changed files with 73 additions and 137 deletions

View file

@ -690,16 +690,9 @@ end
**Authorization Bootstrap Patterns:**
Three mechanisms exist for bypassing standard authorization:
Two mechanisms exist for bypassing standard authorization:
1. **NoActor** (test only) - Allows operations without actor in test environment
```elixir
# Automatically enabled in tests via config/test.exs
# Policies use: bypass action_type(...) do authorize_if NoActor end
member = create_member(%{name: "Test"}) # Works in tests
```
2. **system_actor** (systemic operations) - Admin user for operations that must always succeed
1. **system_actor** (systemic operations) - Admin user for operations that must always succeed
```elixir
# Good: Systemic operation
system_actor = SystemActor.get_system_actor()
@ -709,7 +702,7 @@ Three mechanisms exist for bypassing standard authorization:
# Never use system_actor for user-initiated actions!
```
3. **authorize?: false** (bootstrap only) - Skips policies for circular dependencies
2. **authorize?: false** (bootstrap only) - Skips policies for circular dependencies
```elixir
# Good: Bootstrap (seeds, SystemActor loading)
Accounts.create_user!(%{email: admin_email}, authorize?: false)
@ -719,10 +712,10 @@ Three mechanisms exist for bypassing standard authorization:
```
**Decision Guide:**
- Use **NoActor** for test fixtures (automatic via config)
- Use **system_actor** for email sync, cycle generation, validations
- Use **system_actor** for email sync, cycle generation, validations, and test fixtures
- Use **authorize?: false** only for bootstrap (seeds, circular dependencies)
- Always document why `authorize?: false` is necessary
- **Note:** NoActor bypass was removed to prevent masking authorization bugs in tests
**See also:** `docs/roles-and-permissions-architecture.md` (Authorization Bootstrap Patterns section)
@ -1702,65 +1695,54 @@ case Ash.read(Mv.Membership.Member, actor: actor) do
end
```
### 5.1a NoActor Pattern - Test Environment Only
### 5.1a Authorization in Tests
**IMPORTANT:** The `Mv.Authorization.Checks.NoActor` check is **ONLY for test environment**. It must NEVER be used in production.
**IMPORTANT:** All tests must explicitly provide an actor for Ash operations. The NoActor bypass has been removed to prevent masking authorization bugs.
**What NoActor Does:**
**Test Fixtures:**
- Allows CRUD operations without an actor in **test environment only**
- Denies all operations without an actor in **production/dev** (fail-closed)
- Uses compile-time config check to prevent accidental production use (release-safe)
**Security Guards:**
All test fixtures use `system_actor` for authorization:
```elixir
# config/test.exs
config :mv, :allow_no_actor_bypass, true
# lib/mv/authorization/checks/no_actor.ex
# Compile-time check from config (release-safe, no Mix.env)
@allow_no_actor_bypass Application.compile_env(:mv, :allow_no_actor_bypass, false)
# Uses compile-time flag only (no runtime Mix.env needed)
def match?(nil, _context, _opts) do
@allow_no_actor_bypass # true in test, false in prod/dev
# test/support/fixtures.ex
def member_fixture(attrs \\ %{}) do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
attrs
|> Enum.into(%{...})
|> Mv.Membership.create_member(actor: system_actor)
end
```
**Why This Pattern Exists:**
**Why Explicit Actors in Tests:**
- Test fixtures often need to create resources without an actor
- Production operations MUST always have an actor for security
- Config-based guard (not Mix.env) ensures release-safety
- Defaults to `false` (fail-closed) if config not set
- Prevents masking authorization bugs
- Makes authorization requirements explicit
- Tests fail if authorization is broken (fail-fast)
- Consistent with production code patterns
**NEVER Use NoActor in Production:**
**Using system_actor in Tests:**
```elixir
# ❌ BAD - Don't do this in production code
Ash.create!(Member, attrs) # No actor - will fail in prod
# ✅ GOOD - Use admin actor for system operations
admin_user = get_admin_user()
Ash.create!(Member, attrs, actor: admin_user)
```
**Alternative: System Actor Pattern**
For production system operations, use the System Actor Pattern (see Section 3.3) instead of NoActor:
```elixir
# System operations in production
system_actor = get_system_actor()
# ✅ GOOD - Explicit actor in tests
system_actor = Mv.Helpers.SystemActor.get_system_actor()
Ash.create!(Member, attrs, actor: system_actor)
# ❌ BAD - Missing actor (will fail)
Ash.create!(Member, attrs) # Forbidden error!
```
**Testing:**
**For Bootstrap Operations:**
- NoActor tests verify the compile-time config guard
- Production safety is guaranteed by config (only set in test.exs, defaults to false)
- See `test/mv/authorization/checks/no_actor_test.exs`
Use `authorize?: false` only for bootstrap scenarios (seeds, SystemActor initialization):
```elixir
# ✅ GOOD - Bootstrap only
Accounts.create_user!(%{email: admin_email}, authorize?: false)
# ❌ BAD - Never use in tests for normal operations
Ash.create!(Member, attrs, authorize?: false) # Never do this!
```
### 5.2 Password Security