Skip to content

Authorization Rules

Salman Mahmud edited this page Aug 5, 2026 · 2 revisions

RailsIAM evaluates authorization based on what is defined in each authorization rule. It does not evaluate every role and permission a user owns; it only checks the requirements declared by the rule.

Each authorization rule is a possible path to access. First successful rule wins.

A useful way to think about authorization rules:

  • authorize roles: asks Does this user have the required role?
  • authorize permissions: asks Does this user have the required permission?
  • authorize roles + permissions: asks Does this user have both the required role and permission?

Role-based authorization

When a rule specifies roles, RailsIAM evaluates role membership.

The user must have one of the required roles (depending on role_match). Having additional permissions, including wildcard permissions (*), does not grant access if the required role is missing.

Example:

authorize roles: :admin

Case: If a user has super_admin role and * permissions, he will be denied

Reason: The rule only checks for the admin role. The wildcard permission does not bypass a missing role.

Permission-based authorization

When a rule specifies permissions, RailsIAM evaluates the user's effective permissions.

The user's role does not matter as long as the required permission exists.

 authorize permissions: "user:update"

Case 1: If a user has "sales" roles and * permissions, he will be granted.

authorize permissions: "user:update"

Case 2: A user with user:* will be allowed

Reason: The permission requirement is satisfied. * works on globally no matter what the permission name for the controller action. user:* works on namespace. any permission start with user: will pass.

RailsIAM supports permission wildcards:

*            → global permission, matches every permission

user:*       → namespace permission, matches user:create, user:update, user:delete

product:*    → matches product:create, product:update.

Role and permission authorization

When both roles and permissions are specified in the same rule, RailsIAM requires both conditions to be satisfied.

authorize roles: :manager, permissions: "user:update"

The user must:

  • Have the manager role
  • Have the user:update permission

A user having the permission is not enough because this rule also requires the manager role.

Multiple authorization rules

Multiple authorize declarations are evaluated using OR logic.

authorize roles: :manager, permissions: "user:update"

authorize permissions: "user:update"

Evaluation:

Rule 1: manager role AND user:update permission

OR

Rule 2: user:update permission

Results:

User Roles Permissions Result Reason
manager manager user:update First rule succeeds
employee employee user:update Second rule succeeds because permission exists
super_admin super_admin * Second rule succeeds because wildcard permission matches
manager manager user:view Neither rule is satisfied

This allows us to combine different access strategies:

Explicit denied permissions always win

RailsIAM supports explicit permission denial through user_denied_permissions.

Denied permissions always override permissions granted from role permissions, direct user permissions or wildcard permissions.

Example:

authorize permissions: "user:delete"

If user's role has permission user:delete or * or user:* but he also has denied permission user:delete, he will not be granted.


Authorization Examples

The following examples demonstrate the different ways RailsIAM authorizes access to controller actions.

For these examples, we'll use four different roles, each with its own role_permissions.

super_admin: ["*"]

admin: ["user:view", "user:create", "user:update", "user:delete", "user:activate", "user:deactivate"]

manager: ["user:*", "product:create", "product:update", "product:delete"]

sales: ["product:view", "product:analytics", "user:activity"]

We'll also use two users who both belong to the manager role to demonstrate how direct user permissions and denied permissions affect authorization.

User Role Direct User Permission Denied Permission
manager1 manager product:analytics
manager2 manager product:delete

As a result:

  • manager1 inherits all permissions from the manager role and additionally gains the product:analytics permission.
  • manager2 inherits all permissions from the manager role except product:delete, because user-denied permissions always take precedence over role and direct user permissions.

These users will be referenced throughout the examples below to illustrate how RailsIAM evaluates different authorization scenarios.

The following examples use the sample roles and users introduced above.

# Controller Action Authorization Rule Example User Result Why
1 AnyController#any_action authorize permissions: "any_permission" super_admin User's super_admin role provides the wildcard (*) permission, granting access to every permission check.
2 UsersController#create authorize roles: :super_admin super_admin User has the required super_admin role. No particular permission or action mentioned. so user will have access to every action.
3 UsersController#create authorize roles: :admin manager User does not have the required admin role.
4 UsersController#destroy authorize roles: [:admin, :manager] manager User has one of the allowed roles (role_match: :any, default behavior).
5 UsersController#destroy authorize roles: [:admin, :manager], role_match: :all manager User does not have all required roles.
6 UsersController#update authorize permissions: "user:update" manager Permission is inherited from the manager role.
7 ProductsController#analytics authorize permissions: "product:analytics" manager1
manager2

Permission is granted directly to the manager1 through user_permissions.
8 ProductsController#destroy authorize permissions: "product:delete" manager2
manager1

Permission is explicitly denied through user_denied_permissions for manager2. Denied permissions take precedence.
9 UsersController#create authorize permissions: "user:create" sales Permission is not granted by role permissions or direct user permissions.
10 UsersController#update authorize permissions: ["user:view", "user:update"] manager User has all required permissions (permission_match: :all, default behavior).
11 DashboardController#show authorize permissions: ["user:delete", "product:view"], permission_match: :any sales User has at least one of the required permissions.
12 ProductsController#update authorize roles: :manager, permissions: "product:update" manager User satisfies both the required role and permission.
13 ProductsController#update authorize roles: :sales, permissions: "product:update" sales User has the required role but does not have the required permission.
14 UsersController#update authorize permissions: "user:update" super_admin User's effective permissions contain *, which grants all permissions.
15 UsersController#update authorize permissions: "user:update" User with user:* Wildcard permission (user:*) matches user:update.
16 UsersController#different_action authorize permissions: "user:update" User with user:* Wildcard permissions apply regardless of controller action names.
17 UsersController#update authorize_resource manager Rails IAM automatically resolves the required permission as user:update.
18 UsersController#update authorize_resource sales Rails IAM resolves the required permission as user:update, which the user does not have.
19 SessionsController#create skip_authorization Anonymous Authorization is skipped for the entire controller.
20 SessionsController#create skip_authorization only: :create Anonymous The create action is explicitly excluded from authorization.
21 SessionsController#refresh skip_authorization except: :refresh Anonymous The refresh action raises an AuthorizationError
22 UsersController#export (No authorization rule) Anonymous Rails IAM follows a default-deny policy and raises an AuthorizationError. e.g., No authorization rule applied for users#export

skip_authorization does not disable authentication checks unless skip_authentication is also applied.

Notes and Best Practices

Every Endpoint Requires an Authorization Rule

By default, Rails IAM follows a default-deny approach.

Every protected controller action must have an authorization rule defined using authorize or authorize_resource.

Example:

class UsersController < ApplicationController
  def show; end
end

The above action will be denied because no authorization rule has been applied.

RailsIAM will raise an authorization rule error to prevent accidentally exposing unprotected endpoints.

Using authorize_resource

If your application follows the standard resource:action permission convention, you can use:

class UsersController < ApplicationController
  authorize_resource

  def show; end
  def send_invite; end
end

The required permission is automatically inferred as: user:show user:send_invite (singular).

No additional authorize rule is required.

Permission Names Do Not Have to Match Database Records

When using explicit authorization: authorize permissions: "user:view" the permission name in the authorization rule does not need to be identical to the permission description or database record name.

RailsIAM evaluates the permission code provided by the rule against the user's effective permissions.

However, when using authorize_resource, the inferred permission name must follow the convention: UsersController#show is always inferred as user:show.

Keep Permission Context with Description

The Permission model provides a description field.

When creating permissions, it is recommended to store additional context such as the related API endpoint.

RailsIam::Permission.create!( code: "user:show", description: "GET /api/users/:id")

This makes it easier to understand where a permission is used and helps prevent unused or forgotten permissions as the application grows.

Document Permissions Near Controller Actions

For better maintainability, you can document the required permission directly above controller actions.

class UsersController < ApplicationController
  authorize_resource

  # permission: user:show
  def show
  end
end

Keeping permission references close to the endpoint makes authorization rules easier to discover during development and code review.

Clone this wiki locally