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.
Users always receive permissions through roles — there is no direct user-permission assignment.
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 and authorize/4 accept an optional :policy module implementing
PermitEx.Policy. The policy callback receives the full opts keyword list.
Unexpected return values or raised exceptions propagate to the caller — wrap
your policy in a try/rescue if you need guaranteed fail-closed semantics.
Optional features (all off by default)
config :permit_ex,
cache: true,
cache_ttl: :timer.minutes(5),
wildcards: true,
super_roles: ["super_admin"]
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.
Checks a permission and an optional resource policy.
Returns :ok or {:error, :unauthorized} when the scope has the role.
Returns true when the given scope or permission collection includes permission.
Returns true when every permission is granted.
Returns true when at least one of the permissions is granted.
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 id.
Gets a permission by name.
Gets a role by id.
Gets a global or context-specific role by name.
Adds one permission to a role without removing existing ones.
Adds many permissions to a role without removing existing ones.
Returns true when every role is present.
Returns true when at least one of the roles is present.
Returns true when the given scope or role collection includes role.
Invalidates cached scope data.
Invalidates the entire scope cache.
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 permission from a role.
Removes many permissions from a role.
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.
Updates a permission.
Updates a role. Locked roles require force?: true.
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 that have the given permission through any role in the context.
Lists user ids assigned to a role.
Types
@type role() :: PermitEx.Role.t() | Ecto.UUID.t() | String.t()
@type scope() :: %{ optional(:permissions) => Enumerable.t(), optional(:roles) => 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. Full opts are forwarded to the 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.
Checks a permission and an optional resource policy.
Returns :ok or {:error, reason}. Pass a policy with :policy.
PermitEx.authorize(scope, "orders:manage", order, policy: MyApp.OrderPolicy)
PermitEx.authorize(scope, "orders:manage", policy: MyApp.OrderPolicy, resource: order)
Returns :ok or {:error, :unauthorized} when the scope has the role.
Returns true when the given scope or permission collection includes permission.
When config :permit_ex, wildcards: true, granted permissions may use *
(orders:* or *). When super_roles is configured and the scope has one
of those roles, every permission check succeeds.
Returns true when every permission is granted.
Returns true when at least one of the permissions is granted.
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.
Locked roles return {:error, :role_locked} unless force?: true is passed.
Gets a permission by id.
Gets a permission by name.
Gets a role by id.
Gets a global or context-specific role by name.
Adds one permission to a role without removing existing ones.
Locked roles require force?: true.
Adds many permissions to a role without removing existing ones.
Locked roles require force?: true.
Returns true when every role is present.
Returns true when at least one of the roles is present.
Alias for can?/2.
Returns true when the given scope or role collection includes role.
Invalidates cached scope data.
Pass a context_id for one scope (nil = global). Pass :all as the second
argument to drop every cached scope for that user.
Invalidates the entire scope cache.
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 permission from a role.
Locked roles require force?: true.
Removes many permissions from a role.
Locked roles require force?: true.
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.
Results are cached when config :permit_ex, cache: true. Pass
skip_cache: true to force a database load.
Seeds permissions and roles in one transaction.
Pass context_id: to create context roles instead of global templates.
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"]}
]
)
PermitEx.seed!(definitions, context_id: workspace.id)
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.
Locked roles require force?: true.
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.
Updates a permission.
Updates a role. Locked roles require force?: true.
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 that have the given permission through any role in the context.
Lists user ids assigned to a role.