Page Permission Router Plug closes #388 #390

Merged
moritz merged 16 commits from feature/388_page_permissions into main 2026-01-30 12:20:00 +01:00
2 changed files with 31 additions and 0 deletions
Showing only changes of commit 14fa873640 - Show all commits

View file

@ -103,6 +103,7 @@ defmodule Mv.Accounts.User do
# the specialized :update_user action below.
update :update do
primary? true
accept [:email]
# Required because custom validation functions (email validation, member relationship validation)
# cannot be executed atomically. These validations need to query the database and perform
@ -310,6 +311,14 @@ defmodule Mv.Accounts.User do
authorize_if expr(id == ^actor(:id))
end
# update_user allows :member argument (link/unlink). Only admins may use it to prevent
# privilege escalation (own_data could otherwise link to any member and get :linked scope).
policy action(:update_user) do
description "Only admins can update user with member link/unlink"
forbid_unless Mv.Authorization.Checks.ActorIsAdmin
authorize_if Mv.Authorization.Checks.ActorIsAdmin
end
# UPDATE/DESTROY via HasPermission (evaluates PermissionSets scope)
policy action_type([:read, :create, :update, :destroy]) do
description "Check permissions from user's role and permission set"

View file

@ -0,0 +1,22 @@
defmodule Mv.Authorization.Checks.ActorIsAdmin do
@moduledoc """
Policy check: true when the actor's role has permission_set_name "admin".
Used to restrict actions (e.g. User.update_user for member link/unlink) to admins only.
"""
use Ash.Policy.SimpleCheck
@impl true
def describe(_opts), do: "actor has admin permission set"
@impl true
def match?(nil, _context, _opts), do: false
def match?(actor, _context, _opts) do
ps_name =
get_in(actor, [Access.key(:role), Access.key(:permission_set_name)]) ||
get_in(actor, [Access.key("role"), Access.key("permission_set_name")])
ps_name == "admin"
end
end