Security: Fix critical deny-filter bug and improve authorization

CRITICAL FIX: Deny-filter was allowing all records instead of denying
Fix: User validation in Member now uses actor from changeset.context
This commit is contained in:
Moritz 2026-01-08 23:12:07 +01:00
parent b3eb6c9223
commit 42a463f422
Signed by: moritz
GPG key ID: 1020A035E5DD0824
4 changed files with 25 additions and 332 deletions

View file

@ -131,13 +131,12 @@ defmodule Mv.Authorization.Checks.HasPermission do
cond do
is_nil(actor) ->
# No actor - deny access (fail-closed)
# Return filter that never matches (using impossible condition)
# This ensures no records are returned when actor is missing
[id: {:not, {:in, []}}]
# Return filter that never matches (id IN [] = never matches)
deny_filter()
is_nil(action) ->
# Cannot determine action - deny access (fail-closed)
[id: {:not, {:in, []}}]
deny_filter()
true ->
auto_filter_with_permissions(actor, resource, action)
@ -169,16 +168,23 @@ defmodule Mv.Authorization.Checks.HasPermission do
false ->
# No permission - deny access (fail-closed)
# Return filter that never matches (using impossible condition)
[id: {:not, {:in, []}}]
deny_filter()
end
else
_ ->
# Error case (no role, invalid permission set, etc.) - deny access (fail-closed)
[id: {:not, {:in, []}}]
deny_filter()
end
end
# Helper function to return a filter that never matches (deny all records)
# Used when authorization should be denied (fail-closed)
# Returns [id: {:in, []}] which means "id IN []" - never matches (correct deny-all)
# NOTE: [id: {:not, {:in, []}}] would be "NOT (id IN [])" = true for all IDs (allow-all) - WRONG!
defp deny_filter do
[id: {:in, []}]
end
# Helper to extract action type from authorizer
# CRITICAL: Must use action_type, not action.name!
# Action types: :create, :read, :update, :destroy