Add actor parameter to all tests requiring authorization
This commit adds actor: system_actor to all Ash operations in tests that require authorization.
This commit is contained in:
parent
686f69c9e9
commit
0f48a9b15a
75 changed files with 4686 additions and 2859 deletions
|
|
@ -101,7 +101,8 @@ defmodule Mv.Membership.Import.MemberCSVTest do
|
|||
assert chunk_result.errors == []
|
||||
|
||||
# Verify member was created
|
||||
members = Mv.Membership.list_members!()
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
members = Mv.Membership.list_members!(actor: system_actor)
|
||||
assert Enum.any?(members, &(&1.email == "john@example.com"))
|
||||
end
|
||||
|
||||
|
|
@ -174,8 +175,12 @@ defmodule Mv.Membership.Import.MemberCSVTest do
|
|||
|
||||
test "returns error for duplicate email" do
|
||||
# Create existing member first
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
{:ok, _existing} =
|
||||
Mv.Membership.create_member(%{email: "duplicate@example.com", first_name: "Existing"})
|
||||
Mv.Membership.create_member(%{email: "duplicate@example.com", first_name: "Existing"},
|
||||
actor: system_actor
|
||||
)
|
||||
|
||||
chunk_rows_with_lines = [
|
||||
{2, %{member: %{email: "duplicate@example.com", first_name: "New"}, custom: %{}}}
|
||||
|
|
@ -199,6 +204,8 @@ defmodule Mv.Membership.Import.MemberCSVTest do
|
|||
end
|
||||
|
||||
test "creates member with custom field values" do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
|
||||
# Create custom field first
|
||||
{:ok, custom_field} =
|
||||
Mv.Membership.CustomField
|
||||
|
|
@ -206,7 +213,7 @@ defmodule Mv.Membership.Import.MemberCSVTest do
|
|||
name: "Phone",
|
||||
value_type: :string
|
||||
})
|
||||
|> Ash.create()
|
||||
|> Ash.create(actor: system_actor)
|
||||
|
||||
chunk_rows_with_lines = [
|
||||
{2,
|
||||
|
|
@ -232,7 +239,8 @@ defmodule Mv.Membership.Import.MemberCSVTest do
|
|||
assert chunk_result.failed == 0
|
||||
|
||||
# Verify member and custom field value were created
|
||||
members = Mv.Membership.list_members!()
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
members = Mv.Membership.list_members!(actor: system_actor)
|
||||
member = Enum.find(members, &(&1.email == "withcustom@example.com"))
|
||||
assert member != nil
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,13 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
|
||||
require Ash.Query
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
%{actor: system_actor}
|
||||
end
|
||||
|
||||
# Helper to create a role with a specific permission set
|
||||
defp create_role_with_permission_set(permission_set_name) do
|
||||
defp create_role_with_permission_set(permission_set_name, _actor) do
|
||||
role_name = "Test Role #{permission_set_name} #{System.unique_integer([:positive])}"
|
||||
|
||||
case Authorization.create_role(%{
|
||||
|
|
@ -32,9 +37,9 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
|
||||
# Helper to create a user with a specific permission set
|
||||
# Returns user with role preloaded (required for authorization)
|
||||
defp create_user_with_permission_set(permission_set_name) do
|
||||
defp create_user_with_permission_set(permission_set_name, actor) do
|
||||
# Create role with permission set
|
||||
role = create_role_with_permission_set(permission_set_name)
|
||||
role = create_role_with_permission_set(permission_set_name, actor)
|
||||
|
||||
# Create user
|
||||
{:ok, user} =
|
||||
|
|
@ -43,28 +48,28 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
email: "user#{System.unique_integer([:positive])}@example.com",
|
||||
password: "testpassword123"
|
||||
})
|
||||
|> Ash.create()
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
# Assign role to user
|
||||
{:ok, user} =
|
||||
user
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.Changeset.manage_relationship(:role, role, type: :append_and_remove)
|
||||
|> Ash.update()
|
||||
|> Ash.update(actor: actor)
|
||||
|
||||
# Reload user with role preloaded (critical for authorization!)
|
||||
{:ok, user_with_role} = Ash.load(user, :role, domain: Mv.Accounts)
|
||||
{:ok, user_with_role} = Ash.load(user, :role, domain: Mv.Accounts, actor: actor)
|
||||
user_with_role
|
||||
end
|
||||
|
||||
# Helper to create an admin user (for creating test fixtures)
|
||||
defp create_admin_user do
|
||||
create_user_with_permission_set("admin")
|
||||
defp create_admin_user(actor) do
|
||||
create_user_with_permission_set("admin", actor)
|
||||
end
|
||||
|
||||
# Helper to create a member linked to a user
|
||||
defp create_linked_member_for_user(user) do
|
||||
admin = create_admin_user()
|
||||
defp create_linked_member_for_user(user, actor) do
|
||||
admin = create_admin_user(actor)
|
||||
|
||||
# Create member
|
||||
# NOTE: We need to ensure the member is actually persisted to the database
|
||||
|
|
@ -96,8 +101,8 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
|
||||
# Helper to create an unlinked member (no user relationship)
|
||||
defp create_unlinked_member do
|
||||
admin = create_admin_user()
|
||||
defp create_unlinked_member(actor) do
|
||||
admin = create_admin_user(actor)
|
||||
|
||||
{:ok, member} =
|
||||
Membership.Member
|
||||
|
|
@ -112,14 +117,16 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
|
||||
describe "own_data permission set (Mitglied)" do
|
||||
setup do
|
||||
user = create_user_with_permission_set("own_data")
|
||||
linked_member = create_linked_member_for_user(user)
|
||||
unlinked_member = create_unlinked_member()
|
||||
setup %{actor: actor} do
|
||||
user = create_user_with_permission_set("own_data", actor)
|
||||
linked_member = create_linked_member_for_user(user, actor)
|
||||
unlinked_member = create_unlinked_member(actor)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user} = Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role])
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts)
|
||||
{:ok, user} =
|
||||
Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role], actor: actor)
|
||||
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts, actor: actor)
|
||||
|
||||
%{user: user, linked_member: linked_member, unlinked_member: unlinked_member}
|
||||
end
|
||||
|
|
@ -165,7 +172,10 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "list members returns only linked member", %{user: user, linked_member: linked_member} do
|
||||
test "list members returns only linked member", %{
|
||||
user: user,
|
||||
linked_member: linked_member
|
||||
} do
|
||||
{:ok, members} = Ash.read(Membership.Member, actor: user, domain: Mv.Membership)
|
||||
|
||||
# Should only return the linked member (scope :linked filters)
|
||||
|
|
@ -185,7 +195,10 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "cannot destroy member (returns forbidden)", %{user: user, linked_member: linked_member} do
|
||||
test "cannot destroy member (returns forbidden)", %{
|
||||
user: user,
|
||||
linked_member: linked_member
|
||||
} do
|
||||
assert_raise Ash.Error.Forbidden, fn ->
|
||||
Ash.destroy!(linked_member, actor: user)
|
||||
end
|
||||
|
|
@ -193,13 +206,14 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
|
||||
describe "read_only permission set (Vorstand/Buchhaltung)" do
|
||||
setup do
|
||||
user = create_user_with_permission_set("read_only")
|
||||
linked_member = create_linked_member_for_user(user)
|
||||
unlinked_member = create_unlinked_member()
|
||||
setup %{actor: actor} do
|
||||
user = create_user_with_permission_set("read_only", actor)
|
||||
linked_member = create_linked_member_for_user(user, actor)
|
||||
unlinked_member = create_unlinked_member(actor)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user} = Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role])
|
||||
{:ok, user} =
|
||||
Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role], actor: actor)
|
||||
|
||||
%{user: user, linked_member: linked_member, unlinked_member: unlinked_member}
|
||||
end
|
||||
|
|
@ -217,7 +231,10 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
assert unlinked_member.id in member_ids
|
||||
end
|
||||
|
||||
test "can read individual member", %{user: user, unlinked_member: unlinked_member} do
|
||||
test "can read individual member", %{
|
||||
user: user,
|
||||
unlinked_member: unlinked_member
|
||||
} do
|
||||
{:ok, member} =
|
||||
Ash.get(Membership.Member, unlinked_member.id, actor: user, domain: Mv.Membership)
|
||||
|
||||
|
|
@ -258,13 +275,14 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
|
||||
describe "normal_user permission set (Kassenwart)" do
|
||||
setup do
|
||||
user = create_user_with_permission_set("normal_user")
|
||||
linked_member = create_linked_member_for_user(user)
|
||||
unlinked_member = create_unlinked_member()
|
||||
setup %{actor: actor} do
|
||||
user = create_user_with_permission_set("normal_user", actor)
|
||||
linked_member = create_linked_member_for_user(user, actor)
|
||||
unlinked_member = create_unlinked_member(actor)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user} = Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role])
|
||||
{:ok, user} =
|
||||
Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role], actor: actor)
|
||||
|
||||
%{user: user, linked_member: linked_member, unlinked_member: unlinked_member}
|
||||
end
|
||||
|
|
@ -315,13 +333,14 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
|
||||
describe "admin permission set" do
|
||||
setup do
|
||||
user = create_user_with_permission_set("admin")
|
||||
linked_member = create_linked_member_for_user(user)
|
||||
unlinked_member = create_unlinked_member()
|
||||
setup %{actor: actor} do
|
||||
user = create_user_with_permission_set("admin", actor)
|
||||
linked_member = create_linked_member_for_user(user, actor)
|
||||
unlinked_member = create_unlinked_member(actor)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user} = Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role])
|
||||
{:ok, user} =
|
||||
Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role], actor: actor)
|
||||
|
||||
%{user: user, linked_member: linked_member, unlinked_member: unlinked_member}
|
||||
end
|
||||
|
|
@ -361,7 +380,10 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
assert updated_member.first_name == "Updated"
|
||||
end
|
||||
|
||||
test "can destroy any member", %{user: user, unlinked_member: unlinked_member} do
|
||||
test "can destroy any member", %{
|
||||
user: user,
|
||||
unlinked_member: unlinked_member
|
||||
} do
|
||||
:ok = Ash.destroy(unlinked_member, actor: user)
|
||||
|
||||
# Verify member is deleted
|
||||
|
|
@ -370,19 +392,24 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
end
|
||||
|
||||
describe "special case: user can always READ linked member" do
|
||||
# Note: The special case policy only applies to :read actions.
|
||||
# Updates are handled by HasPermission with :linked scope (if permission exists).
|
||||
setup %{actor: _actor} do
|
||||
# Note: The special case policy only applies to :read actions.
|
||||
# Updates are handled by HasPermission with :linked scope (if permission exists).
|
||||
:ok
|
||||
end
|
||||
|
||||
test "read_only user can read linked member (via special case bypass)" do
|
||||
test "read_only user can read linked member (via special case bypass)", %{actor: actor} do
|
||||
# read_only has Member.read scope :all, but the special case ensures
|
||||
# users can ALWAYS read their linked member, even if they had no read permission.
|
||||
# This test verifies the special case works independently of permission sets.
|
||||
user = create_user_with_permission_set("read_only")
|
||||
linked_member = create_linked_member_for_user(user)
|
||||
user = create_user_with_permission_set("read_only", actor)
|
||||
linked_member = create_linked_member_for_user(user, actor)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user} = Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role])
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts)
|
||||
{:ok, user} =
|
||||
Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role], actor: actor)
|
||||
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts, actor: actor)
|
||||
|
||||
# Should succeed (special case bypass policy for :read takes precedence)
|
||||
{:ok, member} =
|
||||
|
|
@ -391,15 +418,17 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
assert member.id == linked_member.id
|
||||
end
|
||||
|
||||
test "own_data user can read linked member (via special case bypass)" do
|
||||
test "own_data user can read linked member (via special case bypass)", %{actor: actor} do
|
||||
# own_data has Member.read scope :linked, but the special case ensures
|
||||
# users can ALWAYS read their linked member regardless of permission set.
|
||||
user = create_user_with_permission_set("own_data")
|
||||
linked_member = create_linked_member_for_user(user)
|
||||
user = create_user_with_permission_set("own_data", actor)
|
||||
linked_member = create_linked_member_for_user(user, actor)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user} = Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role])
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts)
|
||||
{:ok, user} =
|
||||
Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role], actor: actor)
|
||||
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts, actor: actor)
|
||||
|
||||
# Should succeed (special case bypass policy for :read takes precedence)
|
||||
{:ok, member} =
|
||||
|
|
@ -408,15 +437,19 @@ defmodule Mv.Membership.MemberPoliciesTest do
|
|||
assert member.id == linked_member.id
|
||||
end
|
||||
|
||||
test "own_data user can update linked member (via HasPermission :linked scope)" do
|
||||
test "own_data user can update linked member (via HasPermission :linked scope)", %{
|
||||
actor: actor
|
||||
} do
|
||||
# Update is NOT handled by special case - it's handled by HasPermission
|
||||
# with :linked scope. own_data has Member.update scope :linked.
|
||||
user = create_user_with_permission_set("own_data")
|
||||
linked_member = create_linked_member_for_user(user)
|
||||
user = create_user_with_permission_set("own_data", actor)
|
||||
linked_member = create_linked_member_for_user(user, actor)
|
||||
|
||||
# Reload user to get updated member_id
|
||||
{:ok, user} = Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role])
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts)
|
||||
{:ok, user} =
|
||||
Ash.get(Accounts.User, user.id, domain: Mv.Accounts, load: [:role], actor: actor)
|
||||
|
||||
{:ok, user} = Ash.load(user, :member, domain: Mv.Accounts, actor: actor)
|
||||
|
||||
# Should succeed via HasPermission check (not special case)
|
||||
{:ok, updated_member} =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue