All my tests fail when I add before_filter authenticate_user! to my controllers. How do I get by this?
Thanks
You have to login a user in your tests. I don't know the authentication method you use, but i will make a wild guess. If it's devise, create a spec/support/controller_macros.rb and insert :
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = :user
#user = Factory.create(:user)
sign_in #user
end
end
end
I use factory girl to create a factory, but you can do it however you like. Then, in your tests add it like :
describe AlliancesController do
login_user
describe "GET 'show' without an id" do
......
put include Devise::TestHelpers to your spec
http://pupeno.com/2010/09/26/undefined-method-authenticate/
Related
I am using rails 5, rails_admin, devise and cancancan.
Everything works correctly, but when there is access denied, it shows a 'You are not authorized to access this page' error screen.
I want to redirect to root_path, I've been searching and I only found that I have to write in app/controllers/application_controller.rb this code:
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_path, :alert => exception.message
end
end
And I did, but I am still in the error message when not authorised. It does not redirect.
I think the rest of the code must be ok, because it works, but not redirecting to anywhere.
#config/initializers/rails_admin.rb
config.authorize_with :cancan
config.current_user_method(&:current_user)
.
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard
can :manage, :all
else
can :read, :all # allow everyone to read everything
end
end
end
I saw more people asking the same, but all of them are without answers. I found one with 3 answers, but I don't understand the accepted solution because it really do not explain any solution:
Cancan + Devise rescue_from not catching exception
It looks like ApplicationController isn't actually a parent of RailsAdmin::MainController by default. So, when RailsAdmin::MainController throws the CanCan::AccessDenied exception, it never actually touches ApplicationController, and the rescue block never kicks in.
You can explicitly declare ApplicationController as the parent for RailsAdmin::MainController in the rails_admin.rb config block with
config.parent_controller = 'ApplicationController'
You can also achieve this by extending the rails_admin controller. This is monkey patching, but can be useful if you don't want to set the parent controller to ApplicationController due to a particular reason.
Add following to config/initializers/rails_admin_cancan.rb file.
require 'rails_admin/main_controller'
module RailsAdmin
class MainController < RailsAdmin::ApplicationController
rescue_from CanCan::AccessDenied do |exception|
flash[:alert] = 'Access denied.'
redirect_to main_app.root_path
end
end
end
I have a before filter on my Products Controller:
before_filter :authorize, only: [:create, :edit, :update]
my authorize method is defined in my application_controller as:
def authorize
redirect_to login_url, alert: "Not authorized" if current_user.nil?
end
and current_user is defined as:
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
In my rspec, I am trying:
before(:each) do
session.stub!(:user_id).and_return("admin#email.com")
end
But I am still getting an error as follows:
ProductsController PUT update with valid params redirects to the product
Failure/Error: response.should redirect_to(product)
Expected response to be a redirect to <http://test.host/products/1> but was a redirect to <http://test.host/login>
. . . which means that my test is not logged in at the time of the request.
What am I missing here?
Is there a better way to approach this situation?
Considering you are testing the controller, and trying to keep it focussed on the controller you could stub the current_user method with a real user or a mock.
before(:each) do
ApplicationController.any_instance.stub(:current_user).and_return(#user = mock('user'))
end
With that you will have access to the user mock to apply further expectations and stubs if needed.
If the mock gets in the way, change it out for a real User object.
I want to use cancan to allow updating a certain attribute of a model but not others. Here is what my update action looks like.
class UsersController < ApplicationController
load_and_authorize_resource
def update
respond_with User.update(params[:id], params[:user])
end
end
if resource.instance_of? Teacher
can [:read, :update], User do |user|
resource.users.include? user
end
I want Teacher to be able to modify user.course_id but nothing else. How should I change
can :update, User
to do what I want to.
Hm.. I"m not sure if the 'action' in a cancan ability has to explicitly be an action in the controller. If it doesn't, you could do...
# ability.rb
if resource.instance_of? Teacher
can :update_course, User
end
# Controller
def update
params[:user].delete(:course_id) if cannot? :update_course, User
respond_with User.update(params[:id], params[:user])
end
I have a rails 3.1 app using Devise and CanCan to mange users and roles. I want to make sure that users can update their password, but not their roles. (So ordinary users can't give themselves an admin role, basically). I have overridden the Devise class "RegistrationsController" with some code like this:
def update
# this is my attempt to stop people from updating their roles
# and giving themselves "Admin" priveledges.
params.delete("role_ids")
super
end
I'm hoping this will prevent hackers from updating the "role_ids" field in the user to change their priviledges. (If there is a better way to achieve this, please say!) My problem is I can't seem to write a spec that will test that this code works. My spec looks like this:
require 'spec_helper'
describe RegistrationsController do
before (:each) do
#user = Factory(:user)
sign_in #user
end
it "should update the user attributes but not the roles" do
user_params = {"name" => "new_name", "role_ids" => ["2"],}
put :update, { :id => #user.id, :user => user_params}
#user = User.find(#user.id)
#user.name.should == "new_name"
#user.roles.should be_empty
end
end
The trouble is this test doesn't execute. I get an error message like this:
Failures:
1) RegistrationsController should update the user attributes but not the roles
Failure/Error: put :update, { :id => #user.id, :user => user_params}
AbstractController::ActionNotFound:
Could not find devise mapping for path "/user?id=29&user%5Bname%5D=new_name&user%5Brole_ids%5D%5B%5D=2".
Maybe you forgot to wrap your route inside the scope block? For example:
devise_scope :user do
match "/some/route" => "some_devise_controller"
end
# ./spec/controllers/registrations_controller_spec.rb:13:in `block (2 levels) in <top (required)>'
I don't understand what the error message is asking me to do. My routes seem fine and my application seems to work otherwise. Can anyone help?
Try this in your setup
#request.env["devise.mapping"] = Devise.mappings[:user]
For details see How To: Controllers and Views tests with Rails 3
I'm trying to move to rspec for testing, but I can't get controller testing to work with user authentication. Testing a route that doesn't require a user works.
require 'spec_helper'
describe UddsController do
include Devise::TestHelpers
render_views
before (:each) do
#user = User.new(:email => "test#user.com", :username => "test123")
#user.roles << Role.find_or_create_by_name("admin")
#user.save
sign_in #user
end
it "should get index" do
get :index
response.should be_success
end
I just get
2) UddsController should get index
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/udds_controller_spec.rb:21
I'm not using factory_girl. All the example fixes here and on google seem to. I don't see why what I've done wouldn't work. Error is useless in debugging.
I know this question has been asked to death. I've read every blog and forum I can find. I'm at a dead end.
It might be redirecting. be_success returns false for any non-2xx status codes (like 302). Try checking the value of response.status -- if it's redirecting, it probably means your authentication or authorization scheme is producing an unexpected result.
Try adding puts response.body before your assertion, and see what the page you are requesting says. If you can't figure it out from there, perhaps edit your question to include the output?
If you are using devise's "confirmable" module which makes users click a link in an email to confirm their address, then when you use "sign_in" in the tests you also have to fake email confirming. It looks something like this:
sign_in #user
#user.confim!