Question

I'm currently trying to integrate inherited_resources and authority into my Rails app.

I'm a little bit stuck as to the best place to check the ability to do a controller action based on the resource. This code is given as an example in authority:

  def edit
    @llama = Llama.find(params[:id])
    authorize_action_for(@llama)        # Check to see if you're allowed to edit this llama. failure == SecurityViolation
  end

  def update
    @llama = Llama.find(params[:id])
    authorize_action_for(@llama)        # Check to see if you're allowed to edit this llama.
    @llama.attributes = params[:llama]  # Don't save the attributes before authorizing
    authorize_action_for(@llama)        # Check again, to see if the changes are allowed.
    if @llama.save?
    # etc
  end

Because in inherited_resources the finders are abstracted away, I thought it'd be nice to also tack the authorise_action_for checks onto these abstracted finders.

Note authority's double check in the case of an update (and presumably a create).

Was it helpful?

Solution

I'm relying on ActiveSupport::Concern to simplify the module. I store my concerns in a directory called concerns under app. I've called this one inherited_resources_with_authority.rb and you may need to modify your autoload_paths in application.rb to load files from this folder.

module InheritedResourcesWithAuthority

    extend ActiveSupport::Concern

    included do
        inherit_resources
        authorize_actions_for :resource_class

        alias_method_chain :resource, :authority
        alias_method_chain :build_resource, :authority
        alias_method_chain :update_resource, :authority
    end

    protected

    def resource_with_authority
        resource_without_authority
        authorize_action_for(get_resource_ivar)
    end

    def build_resource_with_authority
        build_resource_without_authority
        authorize_action_for(get_resource_ivar)
    end

    def update_resource_with_authority(object, attributes)
        object.assign_attributes(*attributes)
        authorize_action_for(object)
        object.save
    end

end

We're basically chaining important inherited_resources' abstract methods and inserting our authorisation code where necessary. The last one is the trickiest as we can't call the original method that we're chaining on to so we have to duplicate some of inherited_resources' code here.

To use this concern simply call include InheritedResourcesWithAuthority from your controller.

Note that you must not use the class inheritance method of activating inherited_resources on your controller as we're already using the other method in this concern.

Full writeup here: https://coderwall.com/p/tp5sig

Suggestions are definitely welcome :D

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top