Role and permission management for Ecto and Phoenix applications.
PermitEx keeps the core authorization model intentionally small:
users receive roles globally or inside an optional context, roles receive
permissions, and permissions are checked against the current scope.
Atom safety
Permission and role identifiers can be passed as atoms for convenience
(can?(scope, :orders_manage)), but atoms must always be compile-time
literals. Never derive them from user input via String.to_atom/1 — the
atom table is not garbage-collected and exhausting it crashes the VM. Use
strings for any value that originates from user input or external data.
Policy contract
allowed?/4 accepts an optional :policy module implementing
PermitEx.Policy. The policy callback must return a boolean or
{:error, reason}. Unexpected return values or raised exceptions propagate
to the caller — wrap your policy in a try/rescue if you need guaranteed
fail-closed semantics on errors.
Summary
Functions
Checks a permission and an optional resource policy.
Assigns one role to a user in a context.
Assigns many roles to a user without removing existing roles.
Returns :ok or {:error, :unauthorized} for command-style flows.
Returns true when the given scope or permission collection includes permission.
Clones global role templates into a context.
Creates a permission.
Creates a global role or a context role when context_id is present.
Deletes a permission.
Deletes a role.
Gets a permission by name.
Gets a global or context-specific role by name.
Returns true when the given scope or role collection includes role.
Lists permissions ordered by name.
Lists permissions assigned to a role.
Lists global roles and roles for the given context, with permissions preloaded.
Lists role assignments for a user.
Loads permission names for the user in a context.
Removes one role from a user in a context.
Returns a map of role name to permission names for the given context.
Loads roles assigned to a user in a context.
Lists roles that belong to one context.
Loads roles and permissions for a user in a single query.
Seeds permissions and roles in one transaction.
Replaces all permissions assigned to a role.
Replaces all roles assigned to a user in a context.
Creates or updates a context role by name.
Creates or updates a permission by name.
Creates or updates a global role by name.
Lists user ids assigned to a role.
Types
@type role() :: PermitEx.Role.t() | Ecto.UUID.t() | String.t()
@type scope() :: %{optional(:permissions) => Enumerable.t()}
Functions
Checks a permission and an optional resource policy.
Pass a policy module with :policy. The policy module must implement
PermitEx.Policy.authorize/3.
Assigns one role to a user in a context.
Assigns many roles to a user without removing existing roles.
Returns :ok or {:error, :unauthorized} for command-style flows.
Returns true when the given scope or permission collection includes permission.
Clones global role templates into a context.
A global role is any role with context_id == nil. The cloned context role
receives the same name, description, locked flag, and permissions. Existing
context roles are updated idempotently.
PermitEx.clone_roles_to_context(workspace.id)
PermitEx.clone_roles_to_context(workspace.id, roles: ["admin", "viewer"])
Creates a permission.
Creates a global role or a context role when context_id is present.
Deletes a permission.
Deletes a role.
Gets a permission by name.
Gets a global or context-specific role by name.
Alias for can?/2.
Returns true when the given scope or role collection includes role.
Lists permissions ordered by name.
Lists permissions assigned to a role.
Lists global roles and roles for the given context, with permissions preloaded.
Lists role assignments for a user.
Loads permission names for the user in a context.
Removes one role from a user in a context.
Returns a map of role name to permission names for the given context.
Useful for rendering admin permission matrices and exposing role definitions via API. Roles with no permissions appear with an empty list.
PermitEx.role_matrix()
#=> %{"admin" => ["orders:manage", "orders:view"], "viewer" => ["orders:view"]}
PermitEx.role_matrix(workspace.id)
Loads roles assigned to a user in a context.
Lists roles that belong to one context.
Loads roles and permissions for a user in a single query.
Seeds permissions and roles in one transaction.
Expected shape:
PermitEx.seed!(
permissions: [
{"orders:view", "View orders"},
{"orders:manage", "Manage orders"}
],
roles: [
{"admin", "Context admin", ["orders:view", "orders:manage"]},
{"viewer", "Read-only user", ["orders:view"]}
]
)
Alias for clone_roles_to_context/2.
Alias for sync_role_permissions/3.
Replaces all permissions assigned to a role.
Accepts a role struct, role id, or role name. Permissions can be names, ids,
atoms, or %PermitEx.Permission{} structs. Missing permissions return
{:error, {:permissions_not_found, missing}} unless allow_missing?: true
is passed.
Alias for sync_user_roles/4.
Replaces all roles assigned to a user in a context.
This is the Spatie-style syncRoles equivalent. It accepts role structs, ids
or names and leaves the user with exactly the resolved roles.
Creates or updates a context role by name.
Creates or updates a permission by name.
Creates or updates a global role by name.
Lists user ids assigned to a role.