request.url incorrect after ajax calls? - ruby-on-rails

I login to my Rails application using a login page which is reached either directly through a login link that uses the route
match 'ladmin/login' => 'ladmin#login'
or if I try to edit content.
In either case I get taken to the login page, which allows me to login and then returns me to the page I was trying to use or the app index page, but now as a logged in user.
Most of the time this works totally ok, I get logged in and returns to my content edit page or to the links index page as expected (if I had just use the 'login' link iself).
However I have been able to track down a bug whereby
if I use either of my ajax links the request.url is remembered incorrectly going forward
(to either toggle group shading or to toggle Summary/Details, then the next time (even if several clicks) that I try to login (assuming I am logged out initially) results in the blank page - though I am actually now logged in. Interestingly I notice that when this happens, the url that I end up at in the browser address bar is always localhost:3000/toggle_row_shading which seems like a clue to the problem - it seems like the request.url is remembered incorrectly .
I require digest/sha1 for authentication in the User model with various methods for authentication and password.
The relevant code would seem to be in my LadminController:
def login
session[:user_id] = nil
if request.post?
user = User.authenticate(params[:username], params[:password])
if user
session[:user_id] = user.id
session[:username] = user.username
uri = session[:original_uri]
session[:original_uri] = nil
redirect_to(uri || {:action => "index", :controller => :links})
else
flash.now[:notice] = "Invalid username/password combination"
end
end
end
I can temporarily got around it with
def login
session[:user_id] = nil
if request.post?
user = User.authenticate(params[:username], params[:password])
if user
session[:user_id] = user.id
session[:username] = user.username
redirect_to({:action => "index", :controller => :links})
else
flash.now[:notice] = "Invalid username/password combination"
end
end
end
However this 'forgets' the page I have come from, e.g. an 'edit' link and just uses links#index to put me on after login which is not ideal. It does get around the blank page problem though, as the steps to reproduce it now longer make it happen.
How can I have the login return me to the intended edit page and not give me the blank page?
btw my code for the ajax links is:
%a{href: '#', :data => {toggle_group_row_shading: 'toggle'}}
click to toggle
Do I need an extra route perhaps as the app started at rails 2.3.8 ?
My routes include
match 'toggle_full_details' => 'links#toggle_full_details'
match 'toggle_row_shading' => 'links#toggle_row_shading'
get 'verify_link/:id', to: 'links#verify_link', as: :verify_link
get 'unverify_link/:id', to: 'links#unverify_link', as: :unverify_link
should I have them all as gets perhaps?
My application controller includes:
before_filter :authorize, :except => :login
and
def authorize
unless User.find_by_id(session[:user_id])
session[:original_uri] = request.url #request.request_uri
flash[:notice] = "Please Log In!"
redirect_to :controller => 'ladmin', :action => 'login'
end
end

I have had this happen to me before. The problem I found was that for AJAX requests, the session[:original_uri] is still getting stored and... is bad. Basically, it's storing a uri for an ajax request which doesn't display very well after logging in. The code I ended up with is something like:
if request.get? && !request.xhr?
session[:original_uri] = request.url
end
This way we're not storing into the session[:original_uri] things that shouldn't be redirected to after logging in. You may find other things in your app to add to this conditional. For example, I had some download links that would render a send_file, but those were no good for storing for next login either.

Related

Devise: Redirect to previous page after authenticating user? (request.referer results in loop)

I am using Devise and Omniauth for facebook authentication. I am trying to redirect the User to the previous page after the User logs in either with Devise(regular) or Omniauth(fb).
"request.referer" works when the user uses the dropdown login on my navbar, but when the user tries to log in through the "http://localhost:3000/users/sign_in" url, "request.referer" gives me back an infinite loop error.
Here is the sequence. The problem is in (d).
(a) Unlogged-in User clicks Vote up for Mission.
(b) User is redirected to the "users/sign_in" url, because of the "before_filter :authenticate_user!" in the Missions Controller.
(c) User signs in either by typing in username/pword, or clicking Facebook icon.
(d) User should be redirected back to previous page, with the function VoteUp completed, but instead gives routing error
=> when I go back and refresh the page, the user is signed in, so I know it's just a problem with routing
MissionsController.rb
class MissionsController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :index]
def vote_for_mission
#mission = Mission.find(params[:id])
if #mission.voted_by?(current_user)
redirect_to request.referer, alert: 'You already voted on this mission.'
else
#mission.increment!(:karma)
#mission.active = true
#mission.real_author.increment!(:userpoints) unless #mission.real_author.blank?
current_user.vote_for(#mission)
redirect_to request.referer, notice: 'Your vote was successfully recorded.'
end
end
ApplicationsController.rb
class ApplicationController < ActionController::Base
protect_from_forgery
def after_sign_in_path_for(resource)
sign_in_url = "http://localhost:3000/users/sign_in" || "http://onvard.com/users/sign_in" ||
"http://www.onvard.com/users/sign_in" #url_for(:action => 'new', :controller => 'sessions', :only_path => false, :protocol => 'http')
if (request.referer == sign_in_url)
env['omniauth.origin'] || request.env['omniauth.origin'] || stored_location_for(resource) || getting_started_path || root_path
else
request.referer
end
end
The User is redirected to the "getting_started_path," which I put so I knew the previous options weren't working. The 'else' case, which is the dropdown login form I created with jquery, redirects the user to the previous page perfectly fine, but when I try to put the request.referer in the 'request.referer==sign_in_url' case, it gives me an infinite loop.
How would I redirect the User to the previous page even when the user logs in through the 'users/sign_in' url??
You can't simply redirect back, because the sign_in_url is meant to only accept users that are not logged in, and after logging in your app is sending a logged in user back to that page.
If you need to redirect the user to a specific location after login you should store the original place the user tried to go before the redirect to the login page, then after the login, check if the user should go to a different place after logging in, and redirect him there.
I'm addressing this reply to future generations who use devise and have the same problem of OP: you should be able to solve it by using #stored_location_for, that returns the path that the user wanted to reach before being redirected to the sign in page.

LDAP authentication always returning true

Im following a tutorial online (http://my.opera.com/learnror/blog/ldap-binding-and-authentication-ror) to setup authentication against an LDAp active directory (have a pretty tough time with it too). Anyway, i got the login form and everything setup but for some reason, no matter what i type into the form, (even wrong/nonexistent credentials), it comes back as true! Can anyone help?
OR can anyone provide away to have some sort of debugger run the code line by line (like done with jaavscript debugger). Heres the code that authenticates the login form: (the LDAP module is in a separate lib file):
def authenticate
if session[:person] = LDAP.authenticate(params[:login][:name], params[:login][:p
assword])
session[:username] = params[:login][:name]
if session[:return_to]
redirect_to(session[:return_to])
session[:return_to] = nil
else
redirect_to :controller => 'login' , :action => 'index'
end
else
flash[:notice] = "Login failed!"
redirect_to :action => "index"
end
It looks to me like you used = instead of == in if session[:person] = LDAP… == means equals, = means assignment.

How to set a home page for logged in users and non logged in users?

How would I set in my rails app so that if a first time user comes to my site www.example.com they see a page that they can sign in but if an already logged in goes to www.example.com it now displays their own posts but still at the same www.example.com url.
Would I do something like render template based if they are logged in or is there some other way to do this?
You can set the users#home to be the root URL:
UsersController:
def home
if logged_in?
#blogs = current_user.blogs
render :action => 'logged_in'
else
render :action => 'non_logged_in'
end
end
Have 2 files in the app/views/users folder:
logged_in.html.erb & non_logged_in.html.erb
A great article was writen by Steve Richert. He is using advanced constraint when defining the route, see here
It depends on how you are making your log in logic
Usually you should have two actions, one for home/login form and another for user logged in home. You can make a before_filter on your application controller, so you can test if the user is logged in or not and then redirect him to home (logged out) if not.
If you are not using your own code or another solution I would like to recommend you this gem called devise, it implements a lot of login logic itself and is easy to change too.
EDIT: I think this solutions is better than the others that were presented and I didn't put the code (although it is quite the same code of the before_filter link), so here it is:
class ApplicationController < ActionController::Base
before_filter :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
render :controller => 'home', :action => 'not_logged_in'
else
# whatever code you need to load from user
render :controller => 'home', :action => 'logged_in'
end
end
end
This solutions works perfectly because it tests if the user is logged in in every controller/action he tries to access.

Rails - passing parameters in a redirect_to - is session the only way?

I have a controller set as the root of my app. It accepts in a parameter called uid and checks to see if the user exists. If not, I want it to redirect to the new user page and pre-populate the uid field with the uid in the parameter.
In my root_controller:
def index
if params[:uid]
#user = User.find_by_uid(params[:uid])
if (!#user.blank?)
# do some stuff
else
session[:user_id] = params[:uid]
redirect_to(new_user_path, :notice => 'Please register as a new user')
end
else
# error handling
end
end
In my users_controller, GET /users/new action:
def new
#user = User.new
#user.uid = session[:user_id]
# standard respond_to stuff here
end
This all works fine, but is this an acceptable way to do this? I originally tried passing the uid in the redirect statement, like:
redirect_to(new_user_path, :notice => 'Please register as a new user', :uid => params[:uid])
or even testing it with:
redirect_to(new_user_path, :notice => 'Please register as a new user', :uid => 'ABCD')
but neither seemed to pass the value to users_controller...I couldn't access it using params[:uid] from that controller.
Is session a proper place to store stuff like this, or is there a better way to pass it via the redirect? Thanks!
A session is fine to store that kind of information. Depending on what you are doing with the uid it might actually be dangerous to allow it to be read from the URL. Imagine if the end user was malicious and started putting other user's IDs into there.
For messages that should only last until the next request Rails actually has the flash object which will carry it over for you.
http://guides.rubyonrails.org/action_controller_overview.html#the-flash
That said, if you want to redirect to a url and pass some params, do so like this:
redirect_to(new_user_path(:notice => 'Please register as a new user', :uid => 'ABCD'))
The params you want to pass are arguments to the new_user_path method, not the redirect_to method.

RESTful Authentication: Stumped trying to do simple redirect on cookie based authentication

I have a RoR application that's using the RESTful Authentication plug-in. Everything works great. I recently enabled cookie based authentication and that works fine too. The problem is that I want to change the default landing page when the user is authenticated using a cookie. I want to have a cookie authenticated user redirected to the same page they are redirected to upon successful login from the login form. They are always directed to the original request URL. I'm racking my brain on this as I thought I understood how it works and every change I make seems to have no impact.
I suspect this is something simple but I'm obviously missing it. I'd appreciate any feedback, guidance or suggestions you might offer.
I solved the problem but it's a bit ugly in my opinion. Here's what I did.
In the cookie authentication method I set a session variable indicating the cookie login method was used.
def login_from_cookie
user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
session[:cookie_login] = true **# this is my addition**
self.current_user = user
handle_remember_cookie! false # freshen cookie token (keeping date)
self.current_user
end
end
Then in the :before_filter set_current_user I just check for that variable and redirect if it is set making sure to set the variable to nil.
def set_current_user
Authorization.current_user = current_user
if session[:cookie_login]
redirect_to :controller => :users, :action => :search
session[:cookie_login] = false
end
end
It's not pretty but it does work. I'm definitely open to any suggestions about how to clean this up.
You could add this line to the session controller after a successful login:
redirect_to :controller => 'dashboard', :action => 'index'
I'm using Bort so maybe this isn't part of Restful_Authentication itself but there is a successful_login method in the sessions controller that uses this restful_auth method:
redirect_back_or_default( root_path )
which is in defined in authenticated_system.rb
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
Can't you just have your routes setup so that
map.root :controller => :users, :action => :search
And then have a before_filter that checks to make sure that some "logged in" parameter is set? This param would just need to be set whenever the user logs in, either via cookie or via normal means. Then, whether the cookie authentication happens or normal auth happens, it will go to the default page. Maybe I'm misunderstanding the problem.
Restful Authentication stores the original URL that was trying to be accessed when the request is made. All of you have to do is prevent it from storing that value OR clear that value when a cookie authentication is performed and then the user will get redirected back to your default page.
I would probably do it like this in authenticated_system.rb
def login_from_cookie
user = cookies[:auth_token] && User.find_by_remember_token(cookies[:auth_token])
if user && user.remember_token?
self.current_user = user
session[:return_to] = nil # This clears out the return value so the user will get redirected to the default path
handle_remember_cookie! false # freshen cookie token (keeping date)
self.current_user
end
end
The is session[:return_to] = nil
Then just make sure you have set your default path in your sessions controller and you should be all set. The code in your sessions controller should be something like this:
redirect_back_or_default(the_path_you_want_to_send_them_to)

Resources