feat: implement PermissionSets module with all 4 permission sets

- Add types for scope, action, resource_permission, permission_set
- Implement get_permissions/1 for all 4 sets (own_data, read_only, normal_user, admin)
- Implement valid_permission_set?/1 for string and atom validation
- Implement permission_set_name_to_atom/1 with error handling
This commit is contained in:
Moritz 2026-01-06 19:50:00 +01:00
parent 634d3bd446
commit 3a0fb4e84f
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 813 additions and 5 deletions

View file

@ -2,23 +2,60 @@ defmodule Mv.Authorization.PermissionSets do
@moduledoc """
Defines the four hardcoded permission sets for the application.
This is a minimal stub implementation. The full implementation
with all permission details will be added in a subsequent issue.
Each permission set specifies:
- Resource permissions (what CRUD operations on which resources)
- Page permissions (which LiveView pages can be accessed)
- Scopes (own, linked, all)
## Permission Sets
1. **own_data** - Default for "Mitglied" role
- Can only access own user data and linked member/custom field values
- Cannot create new members or manage system
2. **read_only** - For "Vorstand" and "Buchhaltung" roles
- Can read all member data
- Cannot create, update, or delete
3. **normal_user** - For "Kassenwart" role
- Create/Read/Update members (no delete for safety), full CRUD on custom field values
- Cannot manage custom fields or users
4. **admin** - For "Admin" role
- Unrestricted access to all resources
- Can manage users, roles, custom fields
## Usage
# Get list of all valid permission set names
PermissionSets.all_permission_sets()
# => [:own_data, :read_only, :normal_user, :admin]
# Get permissions for a role's permission set
permissions = PermissionSets.get_permissions(:admin)
# Check if a permission set name is valid
PermissionSets.valid_permission_set?("read_only") # => true
# Convert string to atom safely
{:ok, atom} = PermissionSets.permission_set_name_to_atom("own_data")
## Performance
All functions are pure and compile-time. Permission lookups are < 1 microsecond.
"""
@type scope :: :own | :linked | :all
@type action :: :read | :create | :update | :destroy
@type resource_permission :: %{
resource: String.t(),
action: action(),
scope: scope(),
granted: boolean()
}
@type permission_set :: %{
resources: [resource_permission()],
pages: [String.t()]
}
@doc """
Returns the list of all valid permission set names.
@ -31,4 +68,207 @@ defmodule Mv.Authorization.PermissionSets do
def all_permission_sets do
[:own_data, :read_only, :normal_user, :admin]
end
@doc """
Returns permissions for the given permission set.
## Examples
iex> permissions = PermissionSets.get_permissions(:admin)
iex> Enum.any?(permissions.resources, fn p ->
...> p.resource == "User" and p.action == :destroy
...> end)
true
iex> PermissionSets.get_permissions(:invalid)
** (FunctionClauseError) no function clause matching
"""
@spec get_permissions(atom()) :: permission_set()
def get_permissions(:own_data) do
%{
resources: [
# User: Can always read/update own credentials
%{resource: "User", action: :read, scope: :own, granted: true},
%{resource: "User", action: :update, scope: :own, granted: true},
# Member: Can read/update linked member
%{resource: "Member", action: :read, scope: :linked, granted: true},
%{resource: "Member", action: :update, scope: :linked, granted: true},
# CustomFieldValue: Can read/update custom field values of linked member
%{resource: "CustomFieldValue", action: :read, scope: :linked, granted: true},
%{resource: "CustomFieldValue", action: :update, scope: :linked, granted: true},
# CustomField: Can read all (needed for forms)
%{resource: "CustomField", action: :read, scope: :all, granted: true}
],
pages: [
# Home page
"/",
# Own profile
"/profile",
# Linked member detail (filtered by policy)
"/members/:id"
]
}
end
def get_permissions(:read_only) do
%{
resources: [
# User: Can read/update own credentials only
%{resource: "User", action: :read, scope: :own, granted: true},
%{resource: "User", action: :update, scope: :own, granted: true},
# Member: Can read all members, no modifications
%{resource: "Member", action: :read, scope: :all, granted: true},
# CustomFieldValue: Can read all custom field values
%{resource: "CustomFieldValue", action: :read, scope: :all, granted: true},
# CustomField: Can read all
%{resource: "CustomField", action: :read, scope: :all, granted: true}
],
pages: [
"/",
# Member list
"/members",
# Member detail
"/members/:id",
# Custom field values overview
"/custom_field_values"
]
}
end
def get_permissions(:normal_user) do
%{
resources: [
# User: Can read/update own credentials only
%{resource: "User", action: :read, scope: :own, granted: true},
%{resource: "User", action: :update, scope: :own, granted: true},
# Member: Full CRUD except destroy (safety)
%{resource: "Member", action: :read, scope: :all, granted: true},
%{resource: "Member", action: :create, scope: :all, granted: true},
%{resource: "Member", action: :update, scope: :all, granted: true},
# Note: destroy intentionally omitted for safety
# CustomFieldValue: Full CRUD
%{resource: "CustomFieldValue", action: :read, scope: :all, granted: true},
%{resource: "CustomFieldValue", action: :create, scope: :all, granted: true},
%{resource: "CustomFieldValue", action: :update, scope: :all, granted: true},
%{resource: "CustomFieldValue", action: :destroy, scope: :all, granted: true},
# CustomField: Read only (admin manages definitions)
%{resource: "CustomField", action: :read, scope: :all, granted: true}
],
pages: [
"/",
"/members",
# Create member
"/members/new",
"/members/:id",
# Edit member
"/members/:id/edit",
"/custom_field_values",
"/custom_field_values/new",
"/custom_field_values/:id/edit"
]
}
end
def get_permissions(:admin) do
%{
resources: [
# User: Full management including other users
%{resource: "User", action: :read, scope: :all, granted: true},
%{resource: "User", action: :create, scope: :all, granted: true},
%{resource: "User", action: :update, scope: :all, granted: true},
%{resource: "User", action: :destroy, scope: :all, granted: true},
# Member: Full CRUD
%{resource: "Member", action: :read, scope: :all, granted: true},
%{resource: "Member", action: :create, scope: :all, granted: true},
%{resource: "Member", action: :update, scope: :all, granted: true},
%{resource: "Member", action: :destroy, scope: :all, granted: true},
# CustomFieldValue: Full CRUD
%{resource: "CustomFieldValue", action: :read, scope: :all, granted: true},
%{resource: "CustomFieldValue", action: :create, scope: :all, granted: true},
%{resource: "CustomFieldValue", action: :update, scope: :all, granted: true},
%{resource: "CustomFieldValue", action: :destroy, scope: :all, granted: true},
# CustomField: Full CRUD (admin manages custom field definitions)
%{resource: "CustomField", action: :read, scope: :all, granted: true},
%{resource: "CustomField", action: :create, scope: :all, granted: true},
%{resource: "CustomField", action: :update, scope: :all, granted: true},
%{resource: "CustomField", action: :destroy, scope: :all, granted: true},
# Role: Full CRUD (admin manages roles)
%{resource: "Role", action: :read, scope: :all, granted: true},
%{resource: "Role", action: :create, scope: :all, granted: true},
%{resource: "Role", action: :update, scope: :all, granted: true},
%{resource: "Role", action: :destroy, scope: :all, granted: true}
],
pages: [
# Wildcard: Admin can access all pages
"*"
]
}
end
@doc """
Checks if a permission set name (string or atom) is valid.
## Examples
iex> PermissionSets.valid_permission_set?("admin")
true
iex> PermissionSets.valid_permission_set?(:read_only)
true
iex> PermissionSets.valid_permission_set?("invalid")
false
"""
@spec valid_permission_set?(String.t() | atom()) :: boolean()
def valid_permission_set?(name) when is_binary(name) do
case permission_set_name_to_atom(name) do
{:ok, _atom} -> true
{:error, _} -> false
end
end
def valid_permission_set?(name) when is_atom(name) do
name in all_permission_sets()
end
def valid_permission_set?(_), do: false
@doc """
Converts a permission set name string to atom safely.
## Examples
iex> PermissionSets.permission_set_name_to_atom("admin")
{:ok, :admin}
iex> PermissionSets.permission_set_name_to_atom("invalid")
{:error, :invalid_permission_set}
"""
@spec permission_set_name_to_atom(String.t()) ::
{:ok, atom()} | {:error, :invalid_permission_set}
def permission_set_name_to_atom(name) when is_binary(name) do
atom = String.to_existing_atom(name)
if valid_permission_set?(atom) do
{:ok, atom}
else
{:error, :invalid_permission_set}
end
rescue
ArgumentError -> {:error, :invalid_permission_set}
end
end

View file

@ -0,0 +1,568 @@
defmodule Mv.Authorization.PermissionSetsTest do
@moduledoc """
Tests for the PermissionSets module that defines hardcoded permission sets.
"""
use ExUnit.Case, async: true
alias Mv.Authorization.PermissionSets
describe "all_permission_sets/0" do
test "returns all four permission sets" do
sets = PermissionSets.all_permission_sets()
assert length(sets) == 4
assert :own_data in sets
assert :read_only in sets
assert :normal_user in sets
assert :admin in sets
end
end
describe "get_permissions/1" do
test "returns map with :resources and :pages keys for :own_data" do
permissions = PermissionSets.get_permissions(:own_data)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "returns map with :resources and :pages keys for :read_only" do
permissions = PermissionSets.get_permissions(:read_only)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "returns map with :resources and :pages keys for :normal_user" do
permissions = PermissionSets.get_permissions(:normal_user)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "returns map with :resources and :pages keys for :admin" do
permissions = PermissionSets.get_permissions(:admin)
assert Map.has_key?(permissions, :resources)
assert Map.has_key?(permissions, :pages)
assert is_list(permissions.resources)
assert is_list(permissions.pages)
end
test "each resource permission has required keys" do
permissions = PermissionSets.get_permissions(:own_data)
Enum.each(permissions.resources, fn perm ->
assert Map.has_key?(perm, :resource)
assert Map.has_key?(perm, :action)
assert Map.has_key?(perm, :scope)
assert Map.has_key?(perm, :granted)
assert is_binary(perm.resource)
assert perm.action in [:read, :create, :update, :destroy]
assert perm.scope in [:own, :linked, :all]
assert is_boolean(perm.granted)
end)
end
test "pages lists are non-empty for all permission sets" do
for set <- [:own_data, :read_only, :normal_user, :admin] do
permissions = PermissionSets.get_permissions(set)
assert permissions.pages != [],
"Permission set #{set} should have at least one page"
end
end
end
describe "get_permissions/1 - :own_data permission content" do
test "allows User read/update with scope :own" do
permissions = PermissionSets.get_permissions(:own_data)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
assert user_read.scope == :own
assert user_read.granted == true
assert user_update.scope == :own
assert user_update.granted == true
end
test "allows Member read/update with scope :linked" do
permissions = PermissionSets.get_permissions(:own_data)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
assert member_read.scope == :linked
assert member_read.granted == true
assert member_update.scope == :linked
assert member_update.granted == true
end
test "allows CustomFieldValue read/update with scope :linked" do
permissions = PermissionSets.get_permissions(:own_data)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
assert cfv_read.scope == :linked
assert cfv_read.granted == true
assert cfv_update.scope == :linked
assert cfv_update.granted == true
end
test "allows CustomField read with scope :all" do
permissions = PermissionSets.get_permissions(:own_data)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
assert cf_read.scope == :all
assert cf_read.granted == true
end
test "includes correct pages" do
permissions = PermissionSets.get_permissions(:own_data)
assert "/" in permissions.pages
assert "/profile" in permissions.pages
assert "/members/:id" in permissions.pages
end
end
describe "get_permissions/1 - :read_only permission content" do
test "allows User read/update with scope :own" do
permissions = PermissionSets.get_permissions(:read_only)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
assert user_read.scope == :own
assert user_read.granted == true
assert user_update.scope == :own
assert user_update.granted == true
end
test "allows Member read with scope :all" do
permissions = PermissionSets.get_permissions(:read_only)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
assert member_read.scope == :all
assert member_read.granted == true
end
test "does NOT allow Member create/update/destroy" do
permissions = PermissionSets.get_permissions(:read_only)
member_create =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
member_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "Member" && p.action == :destroy
end)
assert member_create == nil || member_create.granted == false
assert member_update == nil || member_update.granted == false
assert member_destroy == nil || member_destroy.granted == false
end
test "allows CustomFieldValue read with scope :all" do
permissions = PermissionSets.get_permissions(:read_only)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
assert cfv_read.scope == :all
assert cfv_read.granted == true
end
test "does NOT allow CustomFieldValue create/update/destroy" do
permissions = PermissionSets.get_permissions(:read_only)
cfv_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :create
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
cfv_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :destroy
end)
assert cfv_create == nil || cfv_create.granted == false
assert cfv_update == nil || cfv_update.granted == false
assert cfv_destroy == nil || cfv_destroy.granted == false
end
test "allows CustomField read with scope :all" do
permissions = PermissionSets.get_permissions(:read_only)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
assert cf_read.scope == :all
assert cf_read.granted == true
end
test "includes correct pages" do
permissions = PermissionSets.get_permissions(:read_only)
assert "/" in permissions.pages
assert "/members" in permissions.pages
assert "/members/:id" in permissions.pages
assert "/custom_field_values" in permissions.pages
end
end
describe "get_permissions/1 - :normal_user permission content" do
test "allows User read/update with scope :own" do
permissions = PermissionSets.get_permissions(:normal_user)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
assert user_read.scope == :own
assert user_read.granted == true
assert user_update.scope == :own
assert user_update.granted == true
end
test "allows Member read/create/update with scope :all" do
permissions = PermissionSets.get_permissions(:normal_user)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
member_create =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
assert member_read.scope == :all
assert member_read.granted == true
assert member_create.scope == :all
assert member_create.granted == true
assert member_update.scope == :all
assert member_update.granted == true
end
test "does NOT allow Member destroy (safety)" do
permissions = PermissionSets.get_permissions(:normal_user)
member_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "Member" && p.action == :destroy
end)
assert member_destroy == nil || member_destroy.granted == false
end
test "allows CustomFieldValue full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:normal_user)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
cfv_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :create
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
cfv_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :destroy
end)
assert cfv_read.scope == :all
assert cfv_read.granted == true
assert cfv_create.scope == :all
assert cfv_create.granted == true
assert cfv_update.scope == :all
assert cfv_update.granted == true
assert cfv_destroy.scope == :all
assert cfv_destroy.granted == true
end
test "allows CustomField read with scope :all" do
permissions = PermissionSets.get_permissions(:normal_user)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
assert cf_read.scope == :all
assert cf_read.granted == true
end
test "includes correct pages" do
permissions = PermissionSets.get_permissions(:normal_user)
assert "/" in permissions.pages
assert "/members" in permissions.pages
assert "/members/new" in permissions.pages
assert "/members/:id" in permissions.pages
assert "/members/:id/edit" in permissions.pages
assert "/custom_field_values" in permissions.pages
assert "/custom_field_values/new" in permissions.pages
assert "/custom_field_values/:id/edit" in permissions.pages
end
end
describe "get_permissions/1 - :admin permission content" do
test "allows User full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
user_read =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :read end)
user_create =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :create end)
user_update =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :update end)
user_destroy =
Enum.find(permissions.resources, fn p -> p.resource == "User" && p.action == :destroy end)
assert user_read.scope == :all
assert user_read.granted == true
assert user_create.scope == :all
assert user_create.granted == true
assert user_update.scope == :all
assert user_update.granted == true
assert user_destroy.scope == :all
assert user_destroy.granted == true
end
test "allows Member full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
member_read =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :read end)
member_create =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :create end)
member_update =
Enum.find(permissions.resources, fn p -> p.resource == "Member" && p.action == :update end)
member_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "Member" && p.action == :destroy
end)
assert member_read.scope == :all
assert member_read.granted == true
assert member_create.scope == :all
assert member_create.granted == true
assert member_update.scope == :all
assert member_update.granted == true
assert member_destroy.scope == :all
assert member_destroy.granted == true
end
test "allows CustomFieldValue full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
cfv_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :read
end)
cfv_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :create
end)
cfv_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :update
end)
cfv_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomFieldValue" && p.action == :destroy
end)
assert cfv_read.scope == :all
assert cfv_read.granted == true
assert cfv_create.scope == :all
assert cfv_create.granted == true
assert cfv_update.scope == :all
assert cfv_update.granted == true
assert cfv_destroy.scope == :all
assert cfv_destroy.granted == true
end
test "allows CustomField full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
cf_read =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :read
end)
cf_create =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :create
end)
cf_update =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :update
end)
cf_destroy =
Enum.find(permissions.resources, fn p ->
p.resource == "CustomField" && p.action == :destroy
end)
assert cf_read.scope == :all
assert cf_read.granted == true
assert cf_create.scope == :all
assert cf_create.granted == true
assert cf_update.scope == :all
assert cf_update.granted == true
assert cf_destroy.scope == :all
assert cf_destroy.granted == true
end
test "allows Role full CRUD with scope :all" do
permissions = PermissionSets.get_permissions(:admin)
role_read =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :read end)
role_create =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :create end)
role_update =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :update end)
role_destroy =
Enum.find(permissions.resources, fn p -> p.resource == "Role" && p.action == :destroy end)
assert role_read.scope == :all
assert role_read.granted == true
assert role_create.scope == :all
assert role_create.granted == true
assert role_update.scope == :all
assert role_update.granted == true
assert role_destroy.scope == :all
assert role_destroy.granted == true
end
test "has wildcard page permission" do
permissions = PermissionSets.get_permissions(:admin)
assert "*" in permissions.pages
end
end
describe "valid_permission_set?/1" do
test "returns true for valid permission set string" do
assert PermissionSets.valid_permission_set?("own_data") == true
assert PermissionSets.valid_permission_set?("read_only") == true
assert PermissionSets.valid_permission_set?("normal_user") == true
assert PermissionSets.valid_permission_set?("admin") == true
end
test "returns true for valid permission set atom" do
assert PermissionSets.valid_permission_set?(:own_data) == true
assert PermissionSets.valid_permission_set?(:read_only) == true
assert PermissionSets.valid_permission_set?(:normal_user) == true
assert PermissionSets.valid_permission_set?(:admin) == true
end
test "returns false for invalid permission set string" do
assert PermissionSets.valid_permission_set?("invalid") == false
assert PermissionSets.valid_permission_set?("") == false
assert PermissionSets.valid_permission_set?("admin_user") == false
end
test "returns false for invalid permission set atom" do
assert PermissionSets.valid_permission_set?(:invalid) == false
assert PermissionSets.valid_permission_set?(:unknown) == false
end
test "returns false for nil input" do
assert PermissionSets.valid_permission_set?(nil) == false
end
end
describe "permission_set_name_to_atom/1" do
test "returns {:ok, atom} for valid permission set name" do
assert PermissionSets.permission_set_name_to_atom("own_data") == {:ok, :own_data}
assert PermissionSets.permission_set_name_to_atom("read_only") == {:ok, :read_only}
assert PermissionSets.permission_set_name_to_atom("normal_user") == {:ok, :normal_user}
assert PermissionSets.permission_set_name_to_atom("admin") == {:ok, :admin}
end
test "returns {:error, :invalid_permission_set} for invalid permission set name" do
assert PermissionSets.permission_set_name_to_atom("invalid") ==
{:error, :invalid_permission_set}
assert PermissionSets.permission_set_name_to_atom("") == {:error, :invalid_permission_set}
assert PermissionSets.permission_set_name_to_atom("admin_user") ==
{:error, :invalid_permission_set}
end
test "handles non-existent atom gracefully" do
# String.to_existing_atom will raise ArgumentError for non-existent atoms
assert PermissionSets.permission_set_name_to_atom("nonexistent_atom_12345") ==
{:error, :invalid_permission_set}
end
end
end