Mathieu
Mathieu

Reputation: 4787

How can I test that a user can't access a page with access reserved to another user?

I manually give access to certain Deals to customers I choose on Active Admin panel. It's working "manually" but I don't know how to test that a customer can access his Deal but can't access another customer's deals.

Each deal pages can only be viewed by customer associated with it (owned by them if you want) by using CanCan abilities:

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

Note: Customer and Deals have a has_many through relationships (a deal has many customers / a customer has many deals)

Here is the test i have so far but it's not right i think as i'm a TDD newbie:)

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

The issue is that in the tests where customer SHOULD have access to the deal page, rspec says he does not as he is redirected to the homepage. I know where the problem is: i feel rspec does not know that this created customer is assocciated with this created deal.

And here is where i define the Deal page: 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

It seems a pretty basic test: how can i test that a customer can't access another customer pages (with a has_many relationship customer/deals) but i don'"t know how to tackle this issue.

# Edit If it helps to the issue:

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

EDIT #2

Evidence of this failure is that the following test SUCCEED when is should FAIL:

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

Edit #3

Using Dave advice, wrote

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

but i get error:

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 ==)

It's like I don't manage to tell him that the created customer is associated with the created deal so the customer should be able to access it.

Here is the detailed test log:

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('[email protected]') 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"=>"[email protected]", "password"=>"[FILTERED]"}, "commit"=>"Log In"}
  Customer Load (4.0ms)  SELECT "customers".* FROM "customers" WHERE "customers"."email" = '[email protected]' 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 lines in BOLD seem strange to me:

Upvotes: 2

Views: 948

Answers (1)

Dave Schweisguth
Dave Schweisguth

Reputation: 37617

In "the customer can read=view the page of a Deal HE OWNS",

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

In "the customer can NOT read the page of a Deal he does not own ...", you're logging in as the customer that owns the deal. Make a different customer and log in as that one.

Also, you're not using the customer and deal that you define in the let statements, so delete those. Or, better, delete the @customer and @deal assignments and replace @customer and @deal with customer and deal.

Upvotes: 1

Related Questions