User: fix last-admin validation and forbid non-admin role_id change

- Last-admin only when target role is non-admin (admins may switch admin roles).
- Use Ash.Changeset.get_attribute for new role_id. Tests: admin role switch, non-admin update_user role_id forbidden.
This commit is contained in:
Moritz 2026-02-04 09:19:47 +01:00
parent dbd0a57292
commit 67ce514ba0
2 changed files with 75 additions and 23 deletions

View file

@ -105,6 +105,19 @@ defmodule Mv.Accounts.UserPoliciesTest do
Ash.destroy!(user, actor: user)
end
end
@tag permission_set: permission_set
test "cannot change role via update_user - forbidden (#{permission_set})", %{
user: user,
other_user: other_user
} do
other_role = Mv.Fixtures.role_fixture("read_only")
assert {:error, %Ash.Error.Forbidden{}} =
other_user
|> Ash.Changeset.for_update(:update_user, %{role_id: other_role.id})
|> Ash.update(actor: user, domain: Mv.Accounts)
end
end
end
@ -221,6 +234,31 @@ defmodule Mv.Accounts.UserPoliciesTest do
end),
"Expected last-admin validation message, got: #{inspect(error_messages)}"
end
test "admin can switch to another admin role (two roles with permission_set_name admin)", %{
actor: _actor
} do
# Two distinct roles both with permission_set_name "admin" (e.g. "Admin" and "Superadmin")
admin_role_a = Mv.Fixtures.role_fixture("admin")
admin_role_b = Mv.Fixtures.role_fixture("admin")
admin_user = Mv.Fixtures.user_with_role_fixture("admin")
# Ensure user has role_a so we can switch to role_b
{:ok, admin_user} =
admin_user
|> Ash.Changeset.for_update(:update_user, %{role_id: admin_role_a.id})
|> Ash.update(actor: admin_user)
assert admin_user.role_id == admin_role_a.id
# Switching to another admin role must be allowed (no last-admin error)
{:ok, updated} =
admin_user
|> Ash.Changeset.for_update(:update_user, %{role_id: admin_role_b.id})
|> Ash.update(actor: admin_user)
assert updated.role_id == admin_role_b.id
end
end
describe "AshAuthentication bypass" do