尽管设置了能力,但为了更新和销毁而抛出CanCan AccessDenied错误

时间:2013-05-22 22:00:39

标签: ruby-on-rails-3 rspec cancan

我试图让一些控制器测试通过但是当他们点击更新和删除操作时,尽管设置在能力中,CanCan仍然会抛出拒绝访问错误。这些错误似乎只发生在成员身上,因为管理员工作正常。

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

控制器

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

在测试中调用删除操作的结果:

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=

其他操作似乎有效,但对于Update和Destroy,我一直收到AccessDenied错误。知道我可能做错了吗?

1 个答案:

答案 0 :(得分:1)

您的ApiController似乎是命名空间,您需要将before_filter更改为以下内容:

before_filter :authenticate_api_user!

然后,您需要调整Cancan以使用current_api_user而不是current_user:

def current_ability
  @current_ability ||= ::Ability.new(current_api_user)
end

这些链接有助于: