I am trying to get some controller tests passing but when they hit the update and delete action, CanCan keeps throwing the Access Denied error despite being set in the abilities. These errors only seem to occur for members, as admins work fine.
Abilities.rb
def initialize(user)
if user.has_role? :admin
can :manage, :all
elsif user.has_role? :member
can :manage, PaymentMethod, :user_id => user.id
end
end
User_Factory
FactoryGirl.define do
factory :user do
sequence(:first_name) { |n| "John_#{n}" }
sequence(:last_name) { |n| "Rambo_#{n}" }
sequence(:email) { |n| "john_rambo_#{n}@example.com" }
sequence(:username) { |n| "john_rambo_#{n}" }
date_of_birth "03/12/1982"
password 'password'
password_confirmation 'password'
picture_url File.open('spec/support/pictures/test.png')
address
factory :member do
sequence(:last_name) { |n| "Member_#{n}" }
roles :member
end
end
end
Controller_Spec.rb
describe "PUT /api/users/:user_id/payment_method/:id" do
before(:each) do
@user = FactoryGirl.create(:member)
sign_in_user @user
@payment_method = FactoryGirl.create(:credit_card, {:user_id => @user.id})
end
it "updates a users payment method" do
attr_to_change = {
brand: "mastercard",
user_id: @user.id,
id: @payment_method.id
}
put :update, attr_to_change
response.status.should == 200
JSON.parse(response.body)["payment_method"]["brand"]
.should == "mastercard"
end
end
describe "DELETE /api/users/:user_id/payment_methods/:id" do
before(:each) do
@user = FactoryGirl.create(:member)
sign_in_user @user
@payment_method = FactoryGirl.create(:credit_card, {:user_id => @user.id})
end
it "destroys a users payment method" do
delete :destroy, {:user_id => @user, :id => @payment_method.id}
response.status.should == 200
end
end
Controller
class Api::PaymentMethodsController < Api::ApiController
before_filter :clean_params, only: [:update, :create]
def index
@user = User.find(params["user_id"])
render json: @user.payment_methods
end
def update
pm_id = params.delete("id")
params.delete("user_id")
@payment_method = PaymentMethod.find(pm_id)
if @payment_method.update_attributes(params)
return render status: 200, json: @payment_method, root: :payment_method
else
return render status: 422, json: {success: false, errors: @payment_method.errors.full_messages.map{|error|{error: error}}}
end
end
def create
@payment_method = PaymentMethod.create_payment_method(params)
if @payment_method
render json: @payment_method, root: :payment_method
else
return render status: 422, json: {success: false, errors: @payment_method.errors.full_messages.map{|error|{error: error}}}
end
end
def destroy
@payment_method = PaymentMethod.find(params["id"])
if @payment_method.destroy
return render status: 200, json: {:message => "PaymentMethod Destroyed"}
else
return render status: 422, json: {success: false, errors: @payment_method.errors.full_messages.map{|error|{error: error}}}
end
end
def clean_params
["controller", "action"].each do |delete_me|
params.delete(delete_me)
end
params
end
end
ApiController
class Api::ApiController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
rescue_from CanCan::AccessDenied do |exception|
return render :status => 401, :json => {:success => false, :errors => [exception.message]}
end
end
Result of calling the delete action in the test:
delete :destroy, {:user_id => @user, :id => @payment_method.id}
#<ActionController::TestResponse:0x007fb999cf0080
@blank=false,
@block=nil,
@body=
["{\"success\":false,\"errors\":[\"You are not authorized to access this page.\"]}"],
@cache_control={},
@charset="utf-8",
@content_type=application/json,
@etag=nil,
@header={"Content-Type"=>"application/json; charset=utf-8"},
@length=0,
@request=
The other actions seem to work but for Update and Destroy, I keep getting that AccessDenied error. Any idea what I could be doing wrong?
@user
it's impossible to speculate. You can verify your user abilities in the console as a sanity check. – Dave Newton