mitgliederverwaltung/test/mv/authorization/checks/has_permission_integration_test.exs
Moritz 42a463f422
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
2026-01-13 15:01:55 +01:00

97 lines
3.3 KiB
Elixir

defmodule Mv.Authorization.Checks.HasPermissionIntegrationTest do
@moduledoc """
Integration tests for HasPermission policy check.
These tests verify that the filter expressions generated by HasPermission
have the correct structure for relationship-based filtering.
Note: Full integration tests with real queries require resources to have
policies that use HasPermission. These tests validate filter expression
structure and ensure the relationship paths are correct.
"""
use ExUnit.Case, async: true
alias Mv.Authorization.Checks.HasPermission
# Helper to create mock actor with role
defp create_actor_with_role(permission_set_name, opts \\ []) do
actor = %{
id: "user-#{System.unique_integer([:positive])}",
role: %{permission_set_name: permission_set_name}
}
# Add member_id if provided (needed for :linked scope tests)
case Keyword.get(opts, :member_id) do
nil -> actor
member_id -> Map.put(actor, :member_id, member_id)
end
end
describe "Filter Expression Structure - :linked scope" do
test "Member filter uses actor.member_id (inverse relationship)" do
actor = create_actor_with_role("own_data", member_id: "member-123")
authorizer = create_authorizer(Mv.Membership.Member, :read)
filter = HasPermission.auto_filter(actor, authorizer, [])
# Verify filter is not nil (should return a filter for :linked scope)
assert not is_nil(filter)
# The filter should be a valid expression (keyword list or Ash.Expr)
# We verify it's not nil and can be used in queries
assert is_list(filter) or is_map(filter)
end
test "CustomFieldValue filter uses actor.member_id (via member relationship)" do
actor = create_actor_with_role("own_data", member_id: "member-123")
authorizer = create_authorizer(Mv.Membership.CustomFieldValue, :read)
filter = HasPermission.auto_filter(actor, authorizer, [])
# Verify filter is not nil
assert not is_nil(filter)
# The filter should be a valid expression
assert is_list(filter) or is_map(filter)
end
end
describe "Filter Expression Structure - :own scope" do
test "User filter uses id == actor.id" do
actor = create_actor_with_role("own_data")
authorizer = create_authorizer(Mv.Accounts.User, :read)
filter = HasPermission.auto_filter(actor, authorizer, [])
# Verify filter is not nil (should return a filter for :own scope)
assert not is_nil(filter)
# The filter should be a valid expression
assert is_list(filter) or is_map(filter)
end
end
describe "Filter Expression Structure - :all scope" do
test "Admin can read all members without filter (returns expr(true))" do
actor = create_actor_with_role("admin")
authorizer = create_authorizer(Mv.Membership.Member, :read)
filter = HasPermission.auto_filter(actor, authorizer, [])
# :all scope should return [] (empty keyword list = no filter = allow all records)
# After auto_filter fix: no longer returns nil, returns [] instead
assert filter == []
end
end
# Helper to create a mock authorizer
defp create_authorizer(resource, action) do
%Ash.Policy.Authorizer{
resource: resource,
subject: %{
action: %{type: action},
data: nil
}
}
end
end