mitgliederverwaltung/test/mv/authorization/permission_sets_test.exs
Moritz 9b0d022767 fix: add missing /profile page to read_only and normal_user permission sets
Both permission sets allow User:update :own, so users should be able
to access their profile page. This makes the implementation consistent
with the documentation and the logical permission model.
2026-01-06 21:55:13 +01:00

570 lines
19 KiB
Elixir

defmodule Mv.Authorization.PermissionSetsTest do
@moduledoc """
Tests for the PermissionSets module that defines hardcoded permission sets.
"""
use ExUnit.Case, async: true
alias Mv.Authorization.PermissionSets
describe "all_permission_sets/0" do
test "returns all four permission sets" do
sets = PermissionSets.all_permission_sets()
assert length(sets) == 4
assert :own_data in sets
assert :read_only in sets
assert :normal_user in sets
assert :admin in sets
end
end
describe "get_permissions/1" do
test "returns map with :resources and :pages keys for :own_data" do
permissions = PermissionSets.get_permissions(:own_data)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "returns map with :resources and :pages keys for :read_only" do
permissions = PermissionSets.get_permissions(:read_only)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "returns map with :resources and :pages keys for :normal_user" do
permissions = PermissionSets.get_permissions(:normal_user)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "returns map with :resources and :pages keys for :admin" do
permissions = PermissionSets.get_permissions(:admin)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "each resource permission has required keys" do
permissions = PermissionSets.get_permissions(:own_data)
Enum.each(permissions.resources, fn perm ->
assert Map.has_key?(perm, :resource)
assert Map.has_key?(perm, :action)
assert Map.has_key?(perm, :scope)
assert Map.has_key?(perm, :granted)
assert is_binary(perm.resource)
assert perm.action in [:read, :create, :update, :destroy]
assert perm.scope in [:own, :linked, :all]
assert is_boolean(perm.granted)
end)
end
test "pages lists are non-empty for all permission sets" do
for set <- [:own_data, :read_only, :normal_user, :admin] do
permissions = PermissionSets.get_permissions(set)
assert not Enum.empty?(permissions.pages),
"Permission set #{set} should have at least one page"
end
end
end
describe "get_permissions/1 - :own_data permission content" do
test "allows User read/update with scope :own" do
permissions = PermissionSets.get_permissions(:own_data)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
assert user_read.scope == :own
assert user_read.granted == true
assert user_update.scope == :own
assert user_update.granted == true
end
test "allows Member read/update with scope :linked" do
permissions = PermissionSets.get_permissions(:own_data)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
assert member_read.scope == :linked
assert member_read.granted == true
assert member_update.scope == :linked
assert member_update.granted == true
end
test "allows CustomFieldValue read/update with scope :linked" do
permissions = PermissionSets.get_permissions(:own_data)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
assert cfv_read.scope == :linked
assert cfv_read.granted == true
assert cfv_update.scope == :linked
assert cfv_update.granted == true
end
test "allows CustomField read with scope :all" do
permissions = PermissionSets.get_permissions(:own_data)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
assert cf_read.scope == :all
assert cf_read.granted == true
end
test "includes correct pages" do
permissions = PermissionSets.get_permissions(:own_data)
assert "/" in permissions.pages
assert "/profile" in permissions.pages
assert "/members/:id" in permissions.pages
end
end
describe "get_permissions/1 - :read_only permission content" do
test "allows User read/update with scope :own" do
permissions = PermissionSets.get_permissions(:read_only)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
assert user_read.scope == :own
assert user_read.granted == true
assert user_update.scope == :own
assert user_update.granted == true
end
test "allows Member read with scope :all" do
permissions = PermissionSets.get_permissions(:read_only)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
assert member_read.scope == :all
assert member_read.granted == true
end
test "does NOT allow Member create/update/destroy" do
permissions = PermissionSets.get_permissions(:read_only)
member_create =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
member_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "Member" && p.action == :destroy
end)
assert member_create == nil || member_create.granted == false
assert member_update == nil || member_update.granted == false
assert member_destroy == nil || member_destroy.granted == false
end
test "allows CustomFieldValue read with scope :all" do
permissions = PermissionSets.get_permissions(:read_only)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
assert cfv_read.scope == :all
assert cfv_read.granted == true
end
test "does NOT allow CustomFieldValue create/update/destroy" do
permissions = PermissionSets.get_permissions(:read_only)
cfv_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :create
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
cfv_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :destroy
end)
assert cfv_create == nil || cfv_create.granted == false
assert cfv_update == nil || cfv_update.granted == false
assert cfv_destroy == nil || cfv_destroy.granted == false
end
test "allows CustomField read with scope :all" do
permissions = PermissionSets.get_permissions(:read_only)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
assert cf_read.scope == :all
assert cf_read.granted == true
end
test "includes correct pages" do
permissions = PermissionSets.get_permissions(:read_only)
assert "/" in permissions.pages
assert "/profile" in permissions.pages
assert "/members" in permissions.pages
assert "/members/:id" in permissions.pages
assert "/custom_field_values" in permissions.pages
end
end
describe "get_permissions/1 - :normal_user permission content" do
test "allows User read/update with scope :own" do
permissions = PermissionSets.get_permissions(:normal_user)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
assert user_read.scope == :own
assert user_read.granted == true
assert user_update.scope == :own
assert user_update.granted == true
end
test "allows Member read/create/update with scope :all" do
permissions = PermissionSets.get_permissions(:normal_user)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
member_create =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
assert member_read.scope == :all
assert member_read.granted == true
assert member_create.scope == :all
assert member_create.granted == true
assert member_update.scope == :all
assert member_update.granted == true
end
test "does NOT allow Member destroy (safety)" do
permissions = PermissionSets.get_permissions(:normal_user)
member_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "Member" && p.action == :destroy
end)
assert member_destroy == nil || member_destroy.granted == false
end
test "allows CustomFieldValue full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:normal_user)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
cfv_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :create
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
cfv_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :destroy
end)
assert cfv_read.scope == :all
assert cfv_read.granted == true
assert cfv_create.scope == :all
assert cfv_create.granted == true
assert cfv_update.scope == :all
assert cfv_update.granted == true
assert cfv_destroy.scope == :all
assert cfv_destroy.granted == true
end
test "allows CustomField read with scope :all" do
permissions = PermissionSets.get_permissions(:normal_user)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
assert cf_read.scope == :all
assert cf_read.granted == true
end
test "includes correct pages" do
permissions = PermissionSets.get_permissions(:normal_user)
assert "/" in permissions.pages
assert "/profile" in permissions.pages
assert "/members" in permissions.pages
assert "/members/new" in permissions.pages
assert "/members/:id" in permissions.pages
assert "/members/:id/edit" in permissions.pages
assert "/custom_field_values" in permissions.pages
assert "/custom_field_values/new" in permissions.pages
assert "/custom_field_values/:id/edit" in permissions.pages
end
end
describe "get_permissions/1 - :admin permission content" do
test "allows User full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_create =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :create end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
user_destroy =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :destroy end)
assert user_read.scope == :all
assert user_read.granted == true
assert user_create.scope == :all
assert user_create.granted == true
assert user_update.scope == :all
assert user_update.granted == true
assert user_destroy.scope == :all
assert user_destroy.granted == true
end
test "allows Member full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
member_create =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
member_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "Member" && p.action == :destroy
end)
assert member_read.scope == :all
assert member_read.granted == true
assert member_create.scope == :all
assert member_create.granted == true
assert member_update.scope == :all
assert member_update.granted == true
assert member_destroy.scope == :all
assert member_destroy.granted == true
end
test "allows CustomFieldValue full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
cfv_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :create
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
cfv_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :destroy
end)
assert cfv_read.scope == :all
assert cfv_read.granted == true
assert cfv_create.scope == :all
assert cfv_create.granted == true
assert cfv_update.scope == :all
assert cfv_update.granted == true
assert cfv_destroy.scope == :all
assert cfv_destroy.granted == true
end
test "allows CustomField full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
cf_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :create
end)
cf_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :update
end)
cf_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :destroy
end)
assert cf_read.scope == :all
assert cf_read.granted == true
assert cf_create.scope == :all
assert cf_create.granted == true
assert cf_update.scope == :all
assert cf_update.granted == true
assert cf_destroy.scope == :all
assert cf_destroy.granted == true
end
test "allows Role full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
role_read =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :read end)
role_create =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :create end)
role_update =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :update end)
role_destroy =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :destroy end)
assert role_read.scope == :all
assert role_read.granted == true
assert role_create.scope == :all
assert role_create.granted == true
assert role_update.scope == :all
assert role_update.granted == true
assert role_destroy.scope == :all
assert role_destroy.granted == true
end
test "has wildcard page permission" do
permissions = PermissionSets.get_permissions(:admin)
assert "*" in permissions.pages
end
end
describe "valid_permission_set?/1" do
test "returns true for valid permission set string" do
assert PermissionSets.valid_permission_set?("own_data") == true
assert PermissionSets.valid_permission_set?("read_only") == true
assert PermissionSets.valid_permission_set?("normal_user") == true
assert PermissionSets.valid_permission_set?("admin") == true
end
test "returns true for valid permission set atom" do
assert PermissionSets.valid_permission_set?(:own_data) == true
assert PermissionSets.valid_permission_set?(:read_only) == true
assert PermissionSets.valid_permission_set?(:normal_user) == true
assert PermissionSets.valid_permission_set?(:admin) == true
end
test "returns false for invalid permission set string" do
assert PermissionSets.valid_permission_set?("invalid") == false
assert PermissionSets.valid_permission_set?("") == false
assert PermissionSets.valid_permission_set?("admin_user") == false
end
test "returns false for invalid permission set atom" do
assert PermissionSets.valid_permission_set?(:invalid) == false
assert PermissionSets.valid_permission_set?(:unknown) == false
end
test "returns false for nil input" do
assert PermissionSets.valid_permission_set?(nil) == false
end
end
describe "permission_set_name_to_atom/1" do
test "returns {:ok, atom} for valid permission set name" do
assert PermissionSets.permission_set_name_to_atom("own_data") == {:ok, :own_data}
assert PermissionSets.permission_set_name_to_atom("read_only") == {:ok, :read_only}
assert PermissionSets.permission_set_name_to_atom("normal_user") == {:ok, :normal_user}
assert PermissionSets.permission_set_name_to_atom("admin") == {:ok, :admin}
end
test "returns {:error, :invalid_permission_set} for invalid permission set name" do
assert PermissionSets.permission_set_name_to_atom("invalid") ==
{:error, :invalid_permission_set}
assert PermissionSets.permission_set_name_to_atom("") == {:error, :invalid_permission_set}
assert PermissionSets.permission_set_name_to_atom("admin_user") ==
{:error, :invalid_permission_set}
end
test "handles non-existent atom gracefully" do
# String.to_existing_atom will raise ArgumentError for non-existent atoms
assert PermissionSets.permission_set_name_to_atom("nonexistent_atom_12345") ==
{:error, :invalid_permission_set}
end
end
end