Module: ConcernsOnRails::Controllers::Authorizable

Extended by:
ActiveSupport::Concern
Defined in:
lib/concerns_on_rails/controllers/authorizable.rb

Overview

Declarative, block-only per-action authorization gate. Each rule is a predicate; the first rule that applies to the current action and returns a falsey value halts the request with 403 (rendered via Respondable's render_error when available, otherwise an inline envelope).

class Api::BaseController < ApplicationController
include ConcernsOnRails::Controllers::Authorizable

authorize_by { current_user.present? }                       # every action
authorize_by(only: %i[update destroy]) { |_action, user| user.admin? }
require_role :admin, :editor, only: :publish                 # role sugar
end

The block is invoked with instance_exec so current_user (and any other helper) resolves on the controller. It is arity-safe: write it with zero, one (|action|), or two (|action, user|) parameters.

Non-goals (kept deliberately small): this is NOT a policy/ability framework. No policy objects, no ability DSL, no resource inference — reach for Pundit or CanCanCan when you outgrow a predicate per action.

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#authorization_denied(status:, message:) ⇒ Object

Public override point for how a denial is rendered. Fails CLOSED: when there is no response object to render into, raise — returning nil here (the pre-1.22 behavior) let the action run unauthorized.



98
99
100
101
102
103
104
105
# File 'lib/concerns_on_rails/controllers/authorizable.rb', line 98

def authorization_denied(status:, message:)
  unless respond_to?(:response) && response
    raise "ConcernsOnRails::Controllers::Authorizable: denial for '#{authorization_action_name}' " \
          "could not be rendered (no response object) — refusing to fail open"
  end

  ConcernsOnRails::Support::ErrorEnvelope.render(self, message: message, status: status, code: "forbidden")
end

#enforce_authorizationObject

Public so subclasses can override. Iterates the declared rules in order and denies on the first failing rule that applies to the current action.



85
86
87
88
89
90
91
92
93
# File 'lib/concerns_on_rails/controllers/authorizable.rb', line 85

def enforce_authorization
  self.class.authorizable_rules.each do |rule|
    next unless authorization_rule_applies?(rule)
    next if invoke_authorization_check(rule[:check])

    return authorization_denied(status: rule[:status], message: rule[:message])
  end
  nil
end