mitgliederverwaltung/test/mv/authorization/role_test.exs
Moritz e47547e065 Add Role resource policies (defense-in-depth)
- PermissionSets: Role read :all for own_data, read_only, normal_user; admin keeps full CRUD
- Role resource: authorizers and policies with HasPermission
- Tests: role_policies_test.exs (read all, create/update/destroy admin only)
- Fix existing tests to pass actor or authorize?: false for Role operations
2026-02-04 12:37:48 +01:00

106 lines
3.2 KiB
Elixir

defmodule Mv.Authorization.RoleTest do
@moduledoc """
Unit tests for Role resource validations and constraints.
"""
use Mv.DataCase, async: true
alias Mv.Authorization
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
%{actor: system_actor}
end
describe "permission_set_name validation" do
test "accepts valid permission set names", %{actor: actor} do
attrs = %{
name: "Test Role",
permission_set_name: "own_data"
}
assert {:ok, role} = Authorization.create_role(attrs, actor: actor)
assert role.permission_set_name == "own_data"
end
test "rejects invalid permission set names", %{actor: actor} do
attrs = %{
name: "Test Role",
permission_set_name: "invalid_set"
}
assert {:error, %Ash.Error.Invalid{errors: errors}} =
Authorization.create_role(attrs, actor: actor)
assert error_message(errors, :permission_set_name) =~ "must be one of"
end
test "accepts all four valid permission sets", %{actor: actor} do
valid_sets = ["own_data", "read_only", "normal_user", "admin"]
for permission_set <- valid_sets do
attrs = %{
name: "Role #{permission_set}",
permission_set_name: permission_set
}
assert {:ok, _role} = Authorization.create_role(attrs, actor: actor)
end
end
end
describe "system role deletion protection" do
test "prevents deletion of system roles", %{actor: actor} do
# is_system_role is not settable via public API, so we use Ash.Changeset directly
changeset =
Mv.Authorization.Role
|> Ash.Changeset.for_create(:create_role, %{
name: "System Role",
permission_set_name: "own_data"
})
|> Ash.Changeset.force_change_attribute(:is_system_role, true)
{:ok, system_role} = Ash.create(changeset, actor: actor)
assert {:error, %Ash.Error.Invalid{errors: errors}} =
Authorization.destroy_role(system_role, actor: actor)
message = error_message(errors, :is_system_role)
assert message =~ "Cannot delete system role"
end
test "allows deletion of non-system roles", %{actor: actor} do
# is_system_role defaults to false, so regular create works
{:ok, regular_role} =
Authorization.create_role(
%{name: "Regular Role", permission_set_name: "read_only"},
actor: actor
)
assert :ok = Authorization.destroy_role(regular_role, actor: actor)
end
end
describe "name uniqueness" do
test "enforces unique role names", %{actor: actor} do
attrs = %{
name: "Unique Role",
permission_set_name: "own_data"
}
assert {:ok, _} = Authorization.create_role(attrs, actor: actor)
assert {:error, %Ash.Error.Invalid{errors: errors}} =
Authorization.create_role(attrs, actor: actor)
assert error_message(errors, :name) =~ "has already been taken"
end
end
# Helper function for error evaluation
defp error_message(errors, field) when is_atom(field) do
errors
|> Enum.filter(fn err -> Map.get(err, :field) == field end)
|> Enum.map(&Map.get(&1, :message, ""))
|> List.first() || ""
end
end