Module: BetterService::Concerns::Serviceable::Authorizable

Extended by:
ActiveSupport::Concern
Included in:
Services::Base
Defined in:
lib/better_service/concerns/serviceable/authorizable.rb

Overview

Authorizable adds authorization support to services.

Use the authorize_with DSL to define authorization logic that runs BEFORE the search phase (fail fast principle).

The authorization block has access to:

  • user: The current user object

  • params: The validated parameters

If authorization fails (block returns false/nil), the service stops immediately and raises AuthorizationError with code :unauthorized.

Example:

class Product::UpdateService < BetterService::UpdateService
  authorize_with do
    user.admin? || product_belongs_to_user?
  end

  def product_belongs_to_user?
    Product.find(params[:id]).user_id == user.id
  end
end

Works with any authorization library (Pundit, CanCanCan, custom):

# Pundit style
authorize_with do
  ProductPolicy.new(user, resource).update?
end

# CanCanCan style
authorize_with do
  Ability.new(user).can?(:update, :product)
end

# Custom logic
authorize_with do
  user.has_role?(:editor) && params[:status] != 'published'
end

Instance Method Summary collapse

Instance Method Details

#authorize!void

This method returns an undefined value.

Execute authorization check if defined.

Runs the authorization block defined with authorize_with. Has access to user and params.

Raises:



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/better_service/concerns/serviceable/authorizable.rb', line 85

def authorize!
  return unless self.class._authorize_block

  authorized = instance_exec(&self.class._authorize_block)

  return if authorized

  # Raise AuthorizationError instead of returning hash
  raise Errors::Runtime::AuthorizationError.new(
    "Not authorized to perform this action",
    code: ErrorCodes::UNAUTHORIZED,
    context: {
      service: self.class.name,
      user: user&.id || "nil",
      params: @params
    }
  )
end