【问题标题】:How can I test that a user can't access a page with access reserved to another user?如何测试用户无法访问保留给其他用户的访问权限的页面?
【发布时间】:2014-05-28 11:28:43
【问题描述】:

我手动将某些交易的访问权限授予我在 Active Admin 面板上选择的客户。 它正在“手动”工作,但我不知道如何测试客户可以访问他的交易但不能访问其他客户的交易。

每个交易页面只能由与其相关联的客户(如果您愿意,归他们所有)使用 CanCan 功能查看:

class CustomerAbility
  include CanCan::Ability


  def initialize(customer)
    alias_action :show, :to => :read #this will have no change on the alias :read!

    customer ||= Customer.new # guest customer (not logged in)
    if customer.has_role? :superadmin 
      Log.info "Ability: customer is superadmin"
      can :manage, :all     
    else
      can :read, Deal do |Deal|
        # Only customers who have been granted access in Active Admin to a deal can read 
        deal.customers.include? customer 
      end
    end
  end

end

注意:客户和交易有一个 has_many through 关系(一个交易有很多客户/一个客户有很多交易)

这是我到目前为止的测试,但我认为这是不对的,因为我是 TDD 新手:)

require 'spec_helper'
require "cancan/matchers"

describe DealsController do      

  let(:customer)      { FactoryGirl.create(:customer) } 
  let(:deal)          { FactoryGirl.create(:deal, :customers => [customer]) } # in array as a deal has_many customers    

  context "As signed-in CUSTOMER" do 

    before do
      @customer = FactoryGirl.create(:customer) #the factory builds a basic customer i.e with 'prospect role' attributed by default
      @deal     = FactoryGirl.create(:deal, :customers => [@customer])
      sign_in_customer @customer

    end

    describe "the customer can read=view the page of a Deal HE OWNS " do
      it "can access the page" do
        get :deal_page, { :id => @deal.id }
        expect(current_path).to eq(???????)
        # WHAT TO DO HERE ??????
        page.should have_content('here is your deal, dear customer')
      end 
    end

    describe "the customer can NOT read the page of a Deal he does not own =owned by other customers and is redirected his customer panel" do
      it "can't access the page" do
        get :deal_page, { :id => @deal.id }
        expect(response).to redirect_to(customer_panel_path)
        # WHAT TO DO HERE ??????
        flash[:alert].should eql("Sorry but you could not access this page as it is not your Deal!")
      end 
    end

  end

问题是在客户应该可以访问交易页面的测试中,rspec 说他没有,因为他被重定向到主页。我知道问题出在哪里:我觉得 rspec 不知道这个创建的客户与这个创建的交易有关。

这里是我定义交易页面的地方:controllers/deals_controller.rb

def deal_page
    @deal = Deal.find(params[:id])
    authorize! :read, @deal # only allow customers with authorized access in AA; sends to customer_ability

    respond_to do |format|
      format.html 
      format.json { render json: @deal }
    end
  end

这似乎是一个非常基本的测试:我如何测试客户无法访问另一个客户页面(具有 has_many 关系客户/交易)但我不知道如何解决这个问题。

#编辑 如果对问题有帮助:

appplication_controller.rb

class ApplicationController < ActionController::Base

  protect_from_forgery

  # handle Cancan authorization exception
  rescue_from CanCan::AccessDenied do |exception|
    exception.default_message = t("errors.application_controller_exception_messages.only_open_to_admin")
    if current_user # if it's user redirect to main HP
      redirect_to root_path, :alert => exception.message
    else # if it's a Customer redirect him to client interface HP
      redirect_to customer_panel_path, :alert=> exception.message
    end
  end

  def current_ability 
    @current_ability ||= case
                         when current_user
                           UserAbility.new(current_user)
                         when current_customer 
                           CustomerAbility.new(current_customer)
                         end
  end

编辑#2

此失败的证据是以下测试成功何时应该失败:

it "can access the deal page" do
        get :deal_page, { :id => @deal.id }
        expect(current_path).to eq(customer_panel_path)            
      end

编辑#3

使用戴夫的建议,写道

before do
      @customer = FactoryGirl.create(:customer) #the factory builds a basic customer i.e with 'prospect role' attributed by default
      @deal    = FactoryGirl.create(:deal, :customers => [@customer])
      sign_in_customer @customer
    end
 (...)
 it "can access the DEAL he OWNS = HIS deals" do
        get :deal_page, { :id => @deal.id }
        expect(current_path).to eq(deal_page_path(@deal))
 end

但我得到错误:

DealsController As signed-in CUSTOMER with access to the deal page
 Failure/Error: expect(current_path).to eq(deal_page_path(@deal))

       expected: "/deals_page/2"
            got: "/customer_panel"

       (compared using ==)

好像我没能告诉他创建的客户与创建的交易相关联,所以客户应该能够访问它。

这里是详细的测试日志:

Deal Exists (0.8ms)  SELECT 1 AS one FROM "deals" WHERE LOWER("deals"."deal_code") = LOWER('CHA1FR001') LIMIT 1      
  SQL (2.1ms)  INSERT INTO "deals" ("admin_user_id", "client_contact_point_name", blabla") VALUES ($1, $2, blabla...) RETURNING "id"  [["admin_user_id", 1], ["client_contact_point_name", "henri Cool"], ["client_contact_point_profile_url", "http://example.com"], ....blabla...]
  (...blabla)
  Customer Exists (0.6ms)  SELECT 1 AS one FROM "customers" WHERE (LOWER("customers"."email") = LOWER('person_1@example.com') AND "customers"."id" != 1) LIMIT 1
  (...blabla)
Started GET "/customers/signin" for 127.0.0.1 at 2014-05-28 18:37:05 +0200
Processing by Customers::SessionsController#new as HTML
  Rendered customers/sessions/new.html.erb within layouts/lightbox (40.0ms)
  Rendered layouts/_metas.html.erb (0.4ms)
  Rendered layouts/_messages.html.erb (0.7ms)
  Rendered layouts/_footer.html.erb (1.2ms)
Completed 200 OK in 77ms (Views: 51.5ms | ActiveRecord: 0.0ms)
Started POST "/customers/signin" for 127.0.0.1 at 2014-05-28 18:37:05 +0200
Processing by Customers::SessionsController#create as HTML
  Parameters: {"utf8"=>"✓", "customer"=>{"email"=>"person_1@example.com", "password"=>"[FILTERED]"}, "commit"=>"Log In"}
  Customer Load (4.0ms)  SELECT "customers".* FROM "customers" WHERE "customers"."email" = 'person_1@example.com' ORDER BY "customers"."id" ASC LIMIT 1
  SQL (1.0ms)  UPDATE "customers" SET "remember_created_at" = $1, "updated_at" = $2 WHERE "customers"."id" = 1  [["remember_created_at", 2014-05-28 16:37:05 UTC], ["updated_at", 2014-05-28 18:37:05 +0200]]
  SQL (1.2ms)  UPDATE "customers" SET "last_sign_in_at" = $1, "current_sign_in_at" = $2, "last_sign_in_ip" = $3, "current_sign_in_ip" = $4, "sign_in_count" = $5, "updated_at" = $6 WHERE "customers"."id" = 1  [["last_sign_in_at", 2014-05-28 16:37:05 UTC], ["current_sign_in_at", 2014-05-28 16:37:05 UTC], ["last_sign_in_ip", "127.0.0.1"], ["current_sign_in_ip", "127.0.0.1"], ["sign_in_count", 1], ["updated_at", 2014-05-28 18:37:05 +0200]]
**Redirected to http://www.example.com/customer_panel**
Completed 302 Found in 33ms (ActiveRecord: 6.2ms)
Started GET "/customer_panel" for 127.0.0.1 at 2014-05-28 18:37:05 +0200
Processing by ClientreportingPagesController#index as HTML
  Customer Load (0.5ms)  SELECT "customers".* FROM "customers" WHERE "customers"."id" = 1 ORDER BY "customers"."id" ASC LIMIT 1
   (1.2ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "customers_roles" ON "roles"."id" = "customers_roles"."role_id" WHERE "customers_roles"."customer_id" = $1 AND (((roles.name = 'prospect') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))  [["customer_id", 1]]      
  Rendered layouts/_metas.html.erb (0.2ms)
   (0.8ms)  SELECT COUNT(*) FROM "roles" INNER JOIN "customers_roles" ON "roles"."id" = "customers_roles"."role_id" WHERE "customers_roles"."customer_id" = $1 AND (((roles.name = 'superadmin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL)))  [["customer_id", 1]]
  Rendered layouts/client_interface_partials
Completed 200 OK in 34ms (Views: 27.7ms | ActiveRecord: 2.4ms)
Processing by DealsController#deal_page as HTML
  Parameters: {"id"=>"2"}
**Completed 401 Unauthorized in 1ms**
  Rendered text template (0.1ms)
   (0.5ms)  ROLLBACK TO SAVEPOINT active_record_2
   (0.3ms)  ROLLBACK TO SAVEPOINT active_record_1
   (0.3ms)  ROLLBACK

2 行粗体对我来说似乎很奇怪:

  • 为什么 rspec 发送到 example.com/customer_panel(我在我的 spec_helper 文件中告诉 rspec 我在本地测试:Capybara.asset_host = 'http://localhost:3000')?

  • 为什么 rspec 最后会出现“Completed 401 Unauthorized in 1ms?

【问题讨论】:

  • 你在任何地方拯救CanCan::AccessDenied吗?
  • @DaveSchweisguth 嗨,是的,我愿意。我用 application_controller.rb 编辑了我的帖子,以便您可以看到。但我真正想测试的是结果:让 rspec 创建无法访问交易页面的客户,将他发送到那里,然后查看他是否确实未经授权并重定向到他的主页。
  • @DaveSchweisguth 比我之前的评论更准确。我真正想要测试的是结果:让 rspec 创建一个客户,并创建一个交易并告诉 rspec 这个创建的客户没有访问这个创建的交易,然后指示将该客户发送到交易页面,看看他是否确实没有授权并重定向到他的主页。

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


【解决方案1】:

在“客户可以阅读=查看他拥有的交易的页面”中,

expect(current_path).to eq(deal_path(@deal))

在“客户无法阅读他不拥有的交易的页面......”中,您以拥有交易的客户身份登录。创建其他客户并以该客户身份登录。

另外,您没有使用您在let 语句中定义的客户和交易,因此请删除它们。或者,最好删除 @customer@deal 分配并将 @customer@deal 替换为 customerdeal

【讨论】:

  • 问题出在'a customer CAN access',我不知道如何写当前页面是deal_page/id=X(见我最初的问题=> expect(current_path) .to eq(???????) 并且它不起作用,因为我被重定向到 customer_panel_homepage 好像 rspec 不明白他应该有权访问。请参阅编辑 2
  • 是的,我已经对非登录进行了测试,而且它们运行良好,所以我没有在这里提及它们
  • 好的,知道了。会试试这个。但是如果我将@customer 和@deal 替换为customer 并在'before do' 块中进行交易,我会得到:nil:NilClass 的未定义方法'id'。在那种情况下我应该改变“{:id => @deal.id}”吗?我试过 { :id => deal.id } 但也没有用
  • 您尝试:id =&gt; deal.id 时出现什么错误?我的观点是你应该使用let而不是在任何地方使用@(最好),或者在before中创建你的对象并在任何地方使用@,而不是两者兼而有之。
  • 恐怕我无法判断这个问题中所有不同版本的代码发生了什么。我认为你在这个问题上取得了一些进展,所以也许你应该用所有测试、代码和日志的当前版本开始一个新的问题。或者完全重写这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-03
  • 2015-03-06
相关资源
最近更新 更多