Remove is_system_role from accept lists in create_role and update_role actions. This field should only be set via seeds or internal actions to prevent users from creating unkillable roles through the public API.
142 lines
3.8 KiB
Elixir
142 lines
3.8 KiB
Elixir
defmodule Mv.Authorization.Role do
|
|
@moduledoc """
|
|
Represents a user role that references a permission set.
|
|
|
|
Roles are stored in the database and link users to permission sets.
|
|
Each role has a `permission_set_name` that references one of the four
|
|
hardcoded permission sets defined in `Mv.Authorization.PermissionSets`.
|
|
|
|
## Fields
|
|
|
|
- `name` - Unique role name (e.g., "Vorstand", "Admin")
|
|
- `description` - Human-readable description of the role
|
|
- `permission_set_name` - Must be one of: "own_data", "read_only", "normal_user", "admin"
|
|
- `is_system_role` - If true, role cannot be deleted (protects critical roles like "Mitglied")
|
|
|
|
## Relationships
|
|
|
|
- `has_many :users` - Users assigned to this role
|
|
|
|
## Validations
|
|
|
|
- `permission_set_name` must be a valid permission set (checked against PermissionSets.all_permission_sets/0)
|
|
- `name` must be unique
|
|
- System roles cannot be deleted (enforced via validation)
|
|
|
|
## Examples
|
|
|
|
# Create a new role
|
|
{:ok, role} = Mv.Authorization.create_role(%{
|
|
name: "Vorstand",
|
|
description: "Board member with read access",
|
|
permission_set_name: "read_only"
|
|
})
|
|
|
|
# List all roles
|
|
{:ok, roles} = Mv.Authorization.list_roles()
|
|
"""
|
|
use Ash.Resource,
|
|
domain: Mv.Authorization,
|
|
data_layer: AshPostgres.DataLayer
|
|
|
|
postgres do
|
|
table "roles"
|
|
repo Mv.Repo
|
|
|
|
references do
|
|
# Prevent deletion of roles that are assigned to users
|
|
reference :users, on_delete: :restrict
|
|
end
|
|
end
|
|
|
|
code_interface do
|
|
define :create_role
|
|
define :list_roles, action: :read
|
|
define :update_role
|
|
define :destroy_role, action: :destroy
|
|
end
|
|
|
|
actions do
|
|
defaults [:read]
|
|
|
|
create :create_role do
|
|
primary? true
|
|
# is_system_role is intentionally excluded - should only be set via seeds/internal actions
|
|
accept [:name, :description, :permission_set_name]
|
|
# Note: In Ash 3.0, require_atomic? is not available for create actions
|
|
# Custom validations will still work
|
|
end
|
|
|
|
update :update_role do
|
|
primary? true
|
|
# is_system_role is intentionally excluded - should only be set via seeds/internal actions
|
|
accept [:name, :description, :permission_set_name]
|
|
# Required because custom validation functions cannot be executed atomically
|
|
require_atomic? false
|
|
end
|
|
|
|
destroy :destroy do
|
|
# Required because custom validation functions cannot be executed atomically
|
|
require_atomic? false
|
|
end
|
|
end
|
|
|
|
validations do
|
|
validate one_of(
|
|
:permission_set_name,
|
|
Mv.Authorization.PermissionSets.all_permission_sets()
|
|
|> Enum.map(&Atom.to_string/1)
|
|
),
|
|
message:
|
|
"must be one of: #{Mv.Authorization.PermissionSets.all_permission_sets() |> Enum.map_join(", ", &Atom.to_string/1)}"
|
|
|
|
validate fn changeset, _context ->
|
|
if changeset.data.is_system_role do
|
|
{:error,
|
|
field: :is_system_role,
|
|
message:
|
|
"Cannot delete system role. System roles are required for the application to function."}
|
|
else
|
|
:ok
|
|
end
|
|
end,
|
|
on: [:destroy]
|
|
end
|
|
|
|
attributes do
|
|
uuid_v7_primary_key :id
|
|
|
|
attribute :name, :string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :description, :string do
|
|
allow_nil? true
|
|
public? true
|
|
end
|
|
|
|
attribute :permission_set_name, :string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :is_system_role, :boolean do
|
|
allow_nil? false
|
|
default false
|
|
public? true
|
|
end
|
|
|
|
timestamps()
|
|
end
|
|
|
|
relationships do
|
|
has_many :users, Mv.Accounts.User do
|
|
destination_attribute :role_id
|
|
end
|
|
end
|
|
|
|
identities do
|
|
identity :unique_name, [:name]
|
|
end
|
|
end
|