Cookie related rspec test problem while working through tutorial - ruby-on-rails

I've been working through Michael Hartl's Rails tutorial (which is unbelievably awesome by the way).
Anyway, everything has been going pretty well and I've nearly reached the end of chapter 10. The problem is that my rspec tests have started to generate some failures and I can't figure out what's wrong.
The first failure occurred when I was working through the section on destroying users. The test
before :each do
#user = Factory :user
end
describe "as a non-signed-in user" do
it "should deny access" do
delete :destroy, :id => #user
response.should redirect_to(signin_path)
end
end
gives the error:
UsersController DELETE 'destroy' as a non-signed-in user should deny access
Failure/Error: delete :destroy, :id => #user
NoMethodError:
undefined method `admin?' for nil:NilClass
# ./app/controllers/users_controller.rb:76:in `admin_user'
# ./spec/controllers/users_controller_spec.rb:308:in `block (4 levels) in <top (required)>'
Here's the code the message references in users_controller:
def admin_user
# the error tels me that current_user = NilClass
redirect_to(root_path) unless current_user.admin?
end
So I guess this would suggest that current_user isn't working correctly and is being set to nil. Now current_user involves a lot of methods of the SessionsHelper which (afaik) deal with setting the users ID in secure cookies and referencing the cookie as they move around the site. So this suggests that there is something wrong with the cookies.
I've checked the browser and the cookie is being set, I've also gone over every part of the code and it all replicates the tutorial exactly as far as I can tell.
Is there something else I should be looking at?
Appendix
Here is the contents of the SessionsHelper module:
module SessionsHelper
def sign_in user
# rails represents cookies as a hash and deals with the conversion for us
# making the cookie "signed" makes it impervious to attack
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
# this line calls the assignment operator below
self.current_user = user
end
def current_user=(user)
#current_user = user
end
# this is a getter method
def current_user
# this sets #current_user = to the user corresponding to the remember token
# but only if #current user is undefined. ie it only works once
#current_user ||= user_from_remember_token
end
def signed_in?
# return true if current_user is not nil
!current_user.nil?
end
def sign_out
cookies.delete(:remember_token)
self.current_user = nil
end
def current_user? user
# returns true if the user object == the current_user object
user == current_user
end
def authenticate
deny_access unless signed_in?
end
def deny_access
store_location
# this is a shortcut for flash notices: flash[:notice] = "Please sign in to access this page."
redirect_to signin_path, :notice => "Please sign in to access this page."
end
def redirect_back_or(default)
redirect_to(session[:return_to] || default)
clear_return_to
end
private
def user_from_remember_token
# the * allows us to give a 2 element array to a method expecting 2 seperate arguments
User.authenticate_with_salt(*remember_token)
end
def remember_token
# return [nil, nil] if the :remember_token cookie is nil
cookies.signed[:remember_token] || [nil, nil]
end
def store_location
# stores the url the browser was requesting
session[:return_to] = request.fullpath
end
def clear_return_to
session[:return_to] = nil
end
end

In your spec ypu are trying to delete user, while you are not logged in, so current_user is nil. You should prevent access to this action non singed in user.
class UsersController < ApplicationController
before_filter :authenticate, :only => [:index, :edit, :update, :destroy]
....
Just as it is described in example.

I made the same omission and this fixed worked. Thanks. I agree with ducky, Michael Hartl's Rails tutorial is tremendously awesome. It's almost a shame to +just+ call it a "Rails Tutorial." While the tutorial does indeed seem to be pointing me towards good Rails development habits, the text concisely contains much more. Very well done.

Related

Rails gem: Cancan on production is not working

I'm using cancancan.. and it works fine on development env .. but once i got it on the production the site works like if there's no gem installed at all.. no authorization handled.
I can't figure out what is wrong
--------------------edited
in the application controller there's
before_filter :authenticate_user!, if: :devise_controller?
rescue_from CanCan::AccessDenied do |exception|
# Make sure we don't redirect to an HTML page
# in API requests because this is absolutely a bad idea
unless request.accept == 'application/json'
flash[:error] = "Access denied."
redirect_to root_url
end
end
and in the ability file
def initialize(user)
# Define abilities for the passed in user here. For example:
#
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :index, Book
can :index, Category
end
end

Helper methods are not being seen (Rails 4 engine)

I have defined a helper method as such (for my rails engine):
module Xaaron
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
helper_method :current_user
helper_method :authenticate_user!
def current_user
#current_user ||= Xaaron::User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end
def authenticate_user!
if current_user
true
else
redirect_to xaaron.login_path
false
end
end
protected
def record_not_found
flash[:error] = 'Could not find specified role'
redirect_to xaaron.record_not_found_path
true
end
end
end
As far as I know everything above is correct in terms of creating helper methods. So now I need to use this helper method:
module Xaaron
class ApiKeysController < ActionController::Base
before_action :authenticate_user!
def index
#api_key = Xaaron::ApiKey.where(:user_id => current_user.id)
end
def create
#api_key = Xaaron::ApiKey.new(:user_id => current_user.id, :api_key => SecureRandom.hex(16))
create_api_key(#api_key)
end
def destroy
Xaaron::ApiKey.find(params[:id]).destroy
flash[:notice] = 'Api Key has been deleted.'
redirect_to xarron.api_keys_path
end
end
end
As you can see, before every action the user must be authenticated. So the authenticat_user!
method is then called.
Lets write a test for this
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
This, we expect, to send us back to the login path because we are not signed in, and as you will recall we are using authenticate before every action in the API Controller. What do we get instead:
1) Xaaron::ApiKeysController#create should not create an api key for those not logged in
Failure/Error: post :create
NoMethodError:
undefined method `authenticate_user!' for #<Xaaron::ApiKeysController:0x007f898e908a98>
# ./spec/controllers/api_keys_controller_spec.rb:9:in `block (3 levels) in <top (required)>'
Last I checked the way I defined a helper method is how rails casts has done it, how other stack questions have done it and how rails docs states to do it - unless I missed some majour step - why isn't this working?
Maybe I haven't seen a helper method set up like this before (I'm new to rails) but the helper methods I've seen are defined without controllers.
Usually I see a file like this in the helpers folder
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
def current_user=(user)
#current_user = user
end
...
and then
include SessionsHelper
In the application controller.
To me it looks like you're calling the controller a helper method, I'm not sure what the benefits of that would be - but I suppose I wouldn't.
Sorry if this wasn't helpful

How to create `authenticate_user' method without devise in ror

I am new in Ruby on Rails and i am using Ruby version 1.9.3 and Rails version 4.0.2.
My Query is:-
How to create `authenticate_user' method without devise in Ruby on Rails.
Below my routes
get "admin/users/sign_in" => "admin/users#sign_in"
Below My Application Controller:-
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from CanCan::AccessDenied do |exception|
flash[:alert] = "Access denied. You are not authorized to access the requested page."
redirect_to root_path and return
end
helper_method :current_user
before_filter :authenticate_user, :current_user
def current_user
# Note: we want to use "find_by_id" because it's OK to return a nil.
# If we were to use User.find, it would throw an exception if the user can't be found.
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
#current_user ||= User.find_by_authentication_token(cookies[:auth_token]) if cookies[:auth_token] && #current_user.nil?
#current_user
end
def authenticate_user
if #current_user.nil?
flash[:error] = 'You must be signed in to view that page.'
redirect_to :admin_users_sign_in
end
end
protected
#derive the model name from the controller. egs UsersController will return User
def self.permission
return name = self.name.gsub('Controller','').singularize.split('::').last.constantize.name rescue nil
end
def current_ability
#current_ability ||= Ability.new(current_user)
end
#load the permissions for the current user so that UI can be manipulated
def load_permissions
#current_permissions = current_user.role.permissions.collect{|i| [i.subject_class, i.action]}
end
end
Below code using my controller
before_filter :authenticate_user!
My authenticate_user method not redirect properly
redirect_to :admin_users_sign_in
admin_users_sign_in path define in routes see on top
Above the code every time say on browser "The page isn't redirecting properly"
Please help
I suspect the problem is due to this line:
redirect_to :admin_users_sign_in
You need to pass either an action & controller or a friendly name of the path to redirect_to.
Change your routes to be something like
get "admin/users/sign_in" => "admin/users#sign_in", :as => :admin_user_signin
Then you can do something like
redirect_to admin_user_signin_path
This looks an infinite loop.
You defined authenticate_user at ApplicationController level. So, when a visitor visited page 'foo', he is denied by this method because current_user is nil. Then he got redirected to admin sign in page, but that page has this before_filter as well, so he got redirected again, to the same page and never end.
To fix, move such filter to specific controllers which need protection. And do not set it in sign in/sign up page.
Side notes:
You've already used CanCan which has authorization on "read" as well. There is no point to use authenticate_user again for same functionality.

Rails Tutorial Michael hartl. Chapter 8 sign_in method, Why have we used writer method?

In Michael Hartls rails tutorial , I came across the following code :
module SessionsHelper
def current_user=(user)
#current_user = user
end
def current_user #get logged in user
#current_user||=User.find_by_remember_token(:remember_token)
end
def sign_in(user) #sign the user in by setting cookies
cookies.permanent[:remember_token]= user.remember_token
current_user = user
end
def signed_in?(user) #check whether user signed in
!current_user.nil?
end
end
My SessionsController create action looks like this :
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
sign_in user # sign the user in by setting cookies
else
flash.now[:error]= "Invalid email/password"
render 'new'
end
end
Why do we need a writer method def current_user=(user) in the SessionsHelper module ?
Can't we just assign the value of user to the #current_user variable directly in the sign_in method ?
Can it be done like this:
module SessionsHelper
# Notice that there is no def current_user=(user) method.
def current_user
#current_user||=User.find_by_remember_token(:remember_token)
end
def sign_in(user)
cookies.permanent[:remember_token]= user.remember_token
#current_user = user #set the current user directly withoout a writer method
end
def signed_in?(user)
!current_user.nil?
end
end
Sure you can do like that. But idea is to incapsulate instance variable and never assign it directly outside getter and setter methods and that's how ruby works. You actually cannot do SomeObject.#instanceVar=. you need setter for that.
So if in future you will need to set current_user eather you have to use sign_in method or create new method which will be like exactly setter. So why not to use it?

REST API: Custom method using cookies and sessions

I am trying to use REST API, so I want get a #current_user in APP2 from a RoR APP1.
In APP1/config/routes.rb I have this code:
resources :users do
collection do
get 'current'
end
end
In APP1/controllers/application_controller.rb I have this code:
before_filter :current_user
def current_user
if cookies[:remember_me]
current_user = user_from_cookie
else
current_user = User.find_by_id(session[:current_user_id])
end
unless !current_user.nil?
default_current_user = User.find_by_id(1)
end
return #current_user = current_user.nil? ? default_current_user : current_user
end
In APP1/controllers/users_controller.rb I have this code:
def index
...
end
def show
...
end
...
def current
respond_to do |format|
format.xml { render :xml => #current_user }
end
end
In APP2/models/user.rb I have this code:
class User < ActiveResource::Base
self.site = "http://APP1"
end
In APP2/controllers/application_controller.rb I have this code:
before_filter :current_profile
def current_profile
#current_profile = User.get(:current)
end
Now, if I Sign in my User2 in APP1 and I go to http://APP1/users/current.xml URL I get the correct #current_user (User2 object), but if I go to http://APP2/, even though I have 'before_filter's, the #current_profile will be always the default_current_user (User.find_by_id(1) object) instead of User2.
It seems do not care this code from APP1/controllers/application_controller.rb:
if cookies[:remember_me]
current_user = user_from_cookie
else
current_user = User.find_by_id(session[:current_user_id])
end
What is wrong?
EDITED
Maybe we can solve this problem through APP1/config/routes.rb parameters (?!):
Example: in APP1/config/routes.rb
resources :users do
collection do
get 'current', :current_user => #current_user # ?!
end
end
or something like that.
When you do a request to another website, the cookies of the current user are not accessible. The request is from server to server, so the application knows nothing about the user requesting it. I think a solution would be to send the parameters yourself and check for those.
If APP2 requests something from the APP1 via ActiveResource it is not the same as the APP1 logged in user requesting it. Users cookies are not 'forwarded'. Somebody correct me if this is nonsense. :)
Authentication part here http://api.rubyonrails.org/classes/ActiveResource/Base.html has a couple of valid options listed.

Resources