Skip to content

Using with Rails ​

Action Policy seamlessly integrates with Ruby on Rails applications.

In most cases, you do not have to do anything except writing policy files and adding authorize! calls.

NOTE: both controllers and channels extensions are built on top of the Action Policy behaviour mixin.

Generators ​

Action Policy provides a couple of useful Rails generators:

  • rails g action_policy:install — adds app/policies/application_policy.rb file
  • rails g action_policy:policy MODEL_NAME — adds a policy file and a policy test file for a given model (also creates an application_policy.rb if it's missing)
  • rails g action_policy:policy MODEL_NAME --parent=base_policy — adds a policy file that inherits from BasePolicy, and a policy test file for a given model (also creates an application_policy.rb if it's missing)

Controllers integration ​

Action Policy assumes that you have a current_user method which specifies the current authenticated subject (user).

You can turn off this behaviour by setting config.action_policy.controller_authorize_current_user = false in application.rb, or override it:

ruby
class ApplicationController < ActionController::Base
  authorize :user, through: :my_current_user
end

You can also pass a proc that will be executed in action context self to :through. This is useful if you're using ActiveSupport::CurrentAttributes or want to refer to instance variables:

ruby
class ApplicationController < ActionController::Base
  authorize :user, through: -> { @user || Current.user }
end

NOTE: The controller_authorize_current_user setting only affects the way authorization context is built in controllers but does not affect policy classes configuration. If you inherit from ActionPolicy::Base, you will still have the user required as an authorization context. Add authorize :user, optional: true to your base policy class to make it optional or use a custom base class.

Read more about authorization context.

If you don't want to include Action Policy in your controllers at all, you can disable the integration by setting config.action_policy.auto_inject_into_controller = false in application.rb.

verify_authorized hooks ​

Usually, you need all of your actions to be authorized. Action Policy provides a controller hook which ensures that an authorize! call has been made during the action:

ruby
class ApplicationController < ActionController::Base
  # adds an after_action callback to verify
  # that `authorize!` has been called.
  verify_authorized

  # you can also pass additional options,
  # like with a usual callback
  verify_authorized except: :index
end

You can skip this check when necessary:

ruby
class PostsController < ApplicationController
  skip_verify_authorized only: :show

  def index
    # or dynamically within an action
    skip_verify_authorized! if some_condition
  end
end

When an unauthorized action is encountered, the ActionPolicy::UnauthorizedAction error is raised.

verify_authorized_scoped hooks ​

Similarly, you might want to ensure that authorized_scope has been called during certain actions. Action Policy provides a verify_authorized_scoped hook for this:

ruby
class ApplicationController < ActionController::Base
  # adds an after_action callback to verify
  # that `authorized_scope` has been called.
  verify_authorized_scoped

  # you can also pass additional options,
  # like with a usual callback
  verify_authorized_scoped only: :index
end

You can skip this check when necessary:

ruby
class PostsController < ApplicationController
  skip_verify_authorized_scoped only: :show

  def index
    # or dynamically within an action
    skip_verify_authorized_scoped! if some_condition
  end
end

When an unscoped action is encountered, the ActionPolicy::UnscopedAction error is raised.

Resource-less authorize! ​

You can also call authorize! without a resource specified. In that case, Action Policy tries to infer the resource class from the controller name:

ruby
class PostsController < ApplicationController
  def index
    # Uses Post class as a resource implicitly.
    # NOTE: it just calls `controller_name.classify.safe_constantize`,
    # you can override this by defining `implicit_authorization_target` method.
    authorize!
  end
end

Usage with API and Metal controllers ​

Action Policy is only included into ActionController::Base. If you want to use it with other base Rails controllers, you have to include it manually:

ruby
class ApiController < ActionController::API
  include ActionPolicy::Controller

  # NOTE: you have to provide authorization context manually as well
  authorize :user, through: :current_user
end

Channels integration ​

Action Policy also integrates with Action Cable to help you authorize your channels actions:

ruby
class ChatChannel < ApplicationCable::Channel
  def follow(data)
    chat = Chat.find(data["chat_id"])

    # Verify against ChatPolicy#show? rule
    authorize! chat, to: :show?
    stream_from chat
  end
end

Action Policy assumes that you have current_user as a connection identifier.

You can turn off this behaviour by setting config.action_policy.channel_authorize_current_user = false in application.rb, or override it:

ruby
module ApplicationCable
  class Channel < ActionCable::Channel::Base
    # assuming that identifier is called `user`
    authorize :user
  end
end

Read more about authorization context.

In case you do not want to include Action Policy to channels at all, you can disable the integration by setting config.action_policy.auto_inject_into_channel = false in application.rb.