security: remove is_system_role from public API
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.
This commit is contained in:
parent
73763b1f58
commit
5f13901ca5
4 changed files with 11 additions and 122 deletions
|
|
@ -17,6 +17,10 @@ defmodule Mv.Accounts.User do
|
||||||
# When a member is deleted, set the user's member_id to NULL
|
# When a member is deleted, set the user's member_id to NULL
|
||||||
# This allows users to continue existing even if their linked member is removed
|
# This allows users to continue existing even if their linked member is removed
|
||||||
reference :member, on_delete: :nilify
|
reference :member, on_delete: :nilify
|
||||||
|
|
||||||
|
# When a role is deleted, prevent deletion if users are assigned to it
|
||||||
|
# This protects critical roles from accidental deletion
|
||||||
|
reference :role, on_delete: :restrict
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,14 +61,16 @@ defmodule Mv.Authorization.Role do
|
||||||
|
|
||||||
create :create_role do
|
create :create_role do
|
||||||
primary? true
|
primary? true
|
||||||
accept [:name, :description, :permission_set_name, :is_system_role]
|
# 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
|
# Note: In Ash 3.0, require_atomic? is not available for create actions
|
||||||
# Custom validations will still work
|
# Custom validations will still work
|
||||||
end
|
end
|
||||||
|
|
||||||
update :update_role do
|
update :update_role do
|
||||||
primary? true
|
primary? true
|
||||||
accept [:name, :description, :permission_set_name, :is_system_role]
|
# 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
|
# Required because custom validation functions cannot be executed atomically
|
||||||
require_atomic? false
|
require_atomic? false
|
||||||
end
|
end
|
||||||
|
|
@ -85,7 +87,8 @@ defmodule Mv.Authorization.Role do
|
||||||
Mv.Authorization.PermissionSets.all_permission_sets()
|
Mv.Authorization.PermissionSets.all_permission_sets()
|
||||||
|> Enum.map(&Atom.to_string/1)
|
|> Enum.map(&Atom.to_string/1)
|
||||||
),
|
),
|
||||||
message: "must be one of: own_data, read_only, normal_user, admin"
|
message:
|
||||||
|
"must be one of: #{Mv.Authorization.PermissionSets.all_permission_sets() |> Enum.map_join(", ", &Atom.to_string/1)}"
|
||||||
|
|
||||||
validate fn changeset, _context ->
|
validate fn changeset, _context ->
|
||||||
if changeset.data.is_system_role do
|
if changeset.data.is_system_role do
|
||||||
|
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "fragment(\"gen_random_uuid()\")",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": true,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "id",
|
|
||||||
"type": "uuid"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "name",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": true,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "description",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "nil",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "permission_set_name",
|
|
||||||
"type": "text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "false",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "is_system_role",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "inserted_at",
|
|
||||||
"type": "utc_datetime_usec"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_nil?": false,
|
|
||||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
|
||||||
"generated?": false,
|
|
||||||
"precision": null,
|
|
||||||
"primary_key?": false,
|
|
||||||
"references": null,
|
|
||||||
"scale": null,
|
|
||||||
"size": null,
|
|
||||||
"source": "updated_at",
|
|
||||||
"type": "utc_datetime_usec"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"base_filter": null,
|
|
||||||
"check_constraints": [],
|
|
||||||
"custom_indexes": [],
|
|
||||||
"custom_statements": [],
|
|
||||||
"has_create_action": true,
|
|
||||||
"hash": "FFDA74F44B5F11381D4C1F4DACA54901A1E02C3D181A88484AEED4E1ADA21B87",
|
|
||||||
"identities": [
|
|
||||||
{
|
|
||||||
"all_tenants?": false,
|
|
||||||
"base_filter": null,
|
|
||||||
"index_name": "roles_unique_name_index",
|
|
||||||
"keys": [
|
|
||||||
{
|
|
||||||
"type": "atom",
|
|
||||||
"value": "name"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "unique_name",
|
|
||||||
"nils_distinct?": true,
|
|
||||||
"where": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"multitenancy": {
|
|
||||||
"attribute": null,
|
|
||||||
"global": null,
|
|
||||||
"strategy": null
|
|
||||||
},
|
|
||||||
"repo": "Elixir.Mv.Repo",
|
|
||||||
"schema": null,
|
|
||||||
"table": "roles"
|
|
||||||
}
|
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
"strategy": null
|
"strategy": null
|
||||||
},
|
},
|
||||||
"name": "users_role_id_fkey",
|
"name": "users_role_id_fkey",
|
||||||
"on_delete": null,
|
"on_delete": "restrict",
|
||||||
"on_update": null,
|
"on_update": null,
|
||||||
"primary_key?": true,
|
"primary_key?": true,
|
||||||
"schema": "public",
|
"schema": "public",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue