Consider the following case: you want to expose an API that allows to create a Post resource under authorization control, passing contextually an array of nested comments that require authorization too,
post: {
title: "Lorem Ipsum",
comments: [{body: "..."}, {body: "..."}]
}
Suppose to have the urgency of acting separately on post and comments authorization depending on specific conditions, relying on strong_parameters to verify which attributes to allow for mass updating. This topic was fairly discussed by CanCan users and different solution were proposed. At Sellf we decided to concentrate on satisfying the following needs:
- demand to
CanCanthe initialization of the resource, which means applying the attributes in the hash conditions specified through CanCan Abilities; - preserve the
paramssent by a client through the POST request; - avoid
ActiveSupport::HashWithIndifferentAccesserror due a lack of Strong Parameters validation; - authorizing
Postattributes in a first attempt (skipping nested comments authorization).
Basing on that, we decided to adopt the following solution that applies to Post controller:
class PostsController < ApplicationController
check_authorization
before_filter :custom_load_and_authorize_resource, only: [:create]
load_and_authorize_resource :post, except: [:create]
def create
Rails.logger.info "Here it is: " + @post.inspect
if @post.save
render status: 201,
json: @post
else
render status: 422,
json: @post.errors
end
end
def custom_load_and_authorize_resource
@post = Post.new
current_ability.attributes_for(:create, Post).each do |key, value|
@post.send("#{key}=", value)
end
filtered_params = params[:post].dup
filtered_params.delete(:comments)
@post.attributes = filtered_params
authorize! :create, @post
end
end
As you can notice, the custom_load_and_authorize_resource method parses the ability attributes, behaving like the standard implementation of CanCan load_and_authorize_resource method for create actions. What we do in addition is to duplicate the parameters and delete the unnecessary nested resource to restrict the authorization on post attributes.