rails specify method for redirect_to - ruby-on-rails

During a post for a new model, I am checking for authentication via Authlogic. There is a before_filter on the create request. It is calling require_user. After the user session has been successfully created, the redirect_back_or_default(default) method is called. The problem is, The request needs to be posted to the stored uri. I have tried to store the method and input it into the redirect_to however it isn't working. Any ideas?
# called before successful authentication with before_filter
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
def store_location
session[:return_to] = request.request_uri
session[:return_to_method] = request.request_method
end
# called after successful authentication
def redirect_back_or_default(default)
redirect_to((session[:return_to] ? session[:return_to] : default), :method => session[:return_to_method])
session[:return_to] = nil
session[:return_to_method] = nil
end

You can't redirect to a post action, only get actions.

You could store the post object for later processing after authentication, but you really don't want to do that.
Why not simply ask for authentication on the #new method, rather than (or in addition to) the #create? That way the user is authenticated before they fill in the form.

Related

Ruby on Rails - Can't set cookies in some actions

I'm trying to set in cookies target_path that non authorized user tried to reach and after authorization redirect him to the target. Everything works fine and good, but then I tried to set as target edit_test_path or create_test_path and other methods with POST/PATCH/PUT requests and seems that no cookies are being set. What can be the case?
application.rb - I'm setting cookies here. authenticate_user! calling in almost every controller before_actions
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
helper_method :current_user,
:logged_in?
private
def authenticate_user!
unless current_user
cookies[:target_path] = request.path_info
redirect_to login_path, alert: 'Verify Email or Password'
end
end
def current_user
#current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
def logged_in?
current_user.present?
end
end
sessions_controller.rb - I'm trying to redirect to the target from cookies here
class SessionsController < ApplicationController
def new; end
def create
user = User.find_by(email: params[:email])
if user&.authenticate(params[:password])
session[:user_id] = user.id
cookies[:target_path] ? (redirect_to cookies[:target_path]) : (redirect_to root_path) # With verb POST cookies don't work
else
flash.now[:alert] = 'Verify Email or Password'
render :new
end
end
def exit
session[:user_id] = nil
redirect_to login_path
end
end
I don't think, you can do this with POST/PUT/PATCH requests. When you are doing redirect_to, rails sends 302 Found reponse with location specified in parameter of redirect_to in your case cookies[:target_path] or root_path.
Browser then understends it should do redirect and sends GET request to URL specified in location header. You cannot nor should try to tell it to do POST/PUT/PATCH requests - these types of requests usually also require some kind of data (eg submitted form) that goes along with the request. You lost all of these data during redirect to login page anyway.
What I am trying to say - use these redirects only for GET requests. It will not work for POST/PUT/PATCH.

How can I capture a return URL before a redirect?

In my Rails 4 application I have a before action called Authorize which will redirect the user to a login page if they are not logged in.
Is it possible to capture the current controller and method fired before the redirect is initiated by the Before Action?
ApplicationController.rb:
def authorize
unless User.find_by(id: session[:user_id])
redirect_to login_url, notice: "Please log in"
end
end
You can use request.referer but this doesn't always work. For perfect solution refer this answer
https://stackoverflow.com/a/10056022/1377943

Rails: redirect to previous page after login doesn't work

I am trying to redirect to the page from where I clicked login, but after logining in it doesn't redierect to previous page but stays on login page (although the user is already logged in).
Here is my code:
session_helper.rb
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 redirect_back_or(default)
redirect_to(session[:return_to] || default)
session.delete(:return_to)
end
def store_location
session[:return_to] = request.fullpath
end
end
sessions_controller.rb
class SessionsController < ApplicationController
include SessionsHelper
def new
end
def create
user = User.find_by_username(params[:session][:username])
if user && user.authenticate(params[:session][:password])
cookies.permanent[:remember_token] = user.remember_token
#redirect_to root_url,:notice => "Logged in!"
redirect_back_or user
else
flash[:error] = 'Invalid email/password combination' # Not quite right!
render 'new'
end
end
def destroy
cookies.delete(:remember_token)
#session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
end
I also tried to write in create function in sessions_controller.rb
redirect_to request.referer
but it doesn't work.
Am I missing something?
Thanks for your help!
The problem happens at store_location.
Though you havn't said in question, I guess you probably put this method in before_filter. So, no matter GET or POST or other request, the request hit this filter at first and store location.
Now, in this case, actually the user has two requests. One is to #new by GET, and the other is to #create by POST. In the later, his last request to #new was recorded as the going back location. So you'll see you always go back to #new :)
The solution is to filter the location to be stored.
def store_location
disable_pattern = /\A\/user*/
session[:return_to] = request.fullpath unless request.fullpath ~= disable_pattern
end
This pattern could solve current problem but not exclusive. In practice you may see even JS/JSON requests has been recorded, so you may need to add more restrictions according to the specific case. For example, only apply before_filter on #show or #index, use white list, etc.
I think request.referer may not have worked because of a typo in the method. It should be request.referrer.

Devise Resetting Session On Login

I've got the following defined in my application_controller.rb file:
def redirect_back_or(default)
redirect_to(session[:return_to] || default)
session.delete(:return_to)
end
def store_location
session[:return_to] = request.url
end
def after_sign_in_path_for(resource)
r = session[:return_to] || user_root_url
session.delete(:return_to)
r
end
I'm trying to send a user back to the place they came from before the sign_in, or, if no location is saved, send them to their root page.
I call this in one of my controllers:
if current_user.nil?
store_location
redirect_to home_login_url, alert: "You must be logged in to purchase subscriptions"
return
end
I stepped through the debugger and watched the session[:return_to] get set, but after I log on and the after_sign_in_path_for(resource) is called, session[:return_to] is null.
How can I persist the return_to variable across a login?
I'm running Rails 3.2.1 and Devise 2.1.2
Turns out I was overwriting Devise's entry in my hash. I think :return_to is what Devise uses. I ended up implementing this completely differently, but look for name conflicts if you have a problem

How to log in a user right after signing up?

I know I should put the code in the create action of the users controller, but I'm not sure what code I should put. I also assume it should call the create action in my sessions controller, but again I'm not sure how...
By the way I tried render :template => 'sessions/create' in the create action of the users controller, but I get this error when signing up:
Template is missing
Missing template sessions/create with {:locale=>[:en, :en], :formats=>[:html], :handlers=>[:rjs, :rhtml, :erb, :rxml, :builder]} in view paths "/rubyprograms/dreamstill/app/views", "/rubyprograms/dreamstill/vendor/plugins/facebox_render/app/views"
This is all in my application controller:
protected
# Returns the currently logged in user or nil if there isn't one
def current_user
return unless session[:user_id]
#current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
# Filter method to enforce a login requirement
# Apply as a before_filter on any controller you want to protect
def authenticate
logged_in? ? true : access_denied
end
# Predicate method to test for a logged in user
def logged_in?
current_user.is_a? User
end
# Make logged_in? available in templates as a helper
helper_method :logged_in?
def access_denied
respond_to do |format|
format.html do
flash[:alert] = "You must log in to peform this action."
redirect_to root_path
end
format.js do
render_to_facebox(:partial => 'sessions/login_box')
end
end
false
end
Somewhere in your controllers you have something that looks like this:
user = User.new
# set attributes
user.save
render :template => 'sessions/create' # Probably based on your question
All you need to do is update the session to:
user = User.new
# set attributes
if(user.save)
session[:user_id] = user.id
# Send them somewhere useful
else
# Handle the error
end
They're signed in once session[:user_id] is set.
Technically?
In your controller, after you create your user, this code:
#current_user = user
should get you going (looks like you're using restful_authentication).
Now, whether it's a good idea to log in a user automatically without verifying their email address / whatever else is up for debate.
You seem that you just begin with Rails right ? I would highly recommend that you use a gem like Devise to handle your user registrations.
However, if you insist on doing it manually, you would just need to create a session variable that verifies whether a user is logged in or not. Then, you can add a helper like current_user, to get the user if user session shows he/she is logged in.
I see that you have a sessions controller there. Are you trying to use restful_authentication ? If so, once more i highly recommend switching to Devise :)
OLD CODE USING RESTFUL AUTHENTICATION - SESSIONS CONTROLLER
# This controller handles the login/logout function of the site.
class SessionsController < ApplicationController
# Be sure to include AuthenticationSystem in Application Controller instead
include AuthenticatedSystem
# render new.erb.html
def new
end
def create
logout_keeping_session!
user = User.authenticate(params[:login], params[:password])
if user
# Protects against session fixation attacks, causes request forgery
# protection if user resubmits an earlier form using back
# button. Uncomment if you understand the tradeoffs.
# reset_session
self.current_user = user
new_cookie_flag = (params[:remember_me] == "1")
handle_remember_cookie! new_cookie_flag
flash[:notice] = "Logged in successfully"
redirect_to :controller=>'Town'
else
note_failed_signin
#login = params[:login]
#remember_me = params[:remember_me]
render :action => 'new'
end
end
def destroy
logout_killing_session!
flash[:notice] = "You have been logged out."
redirect_back_or_default('/')
end
protected
# Track failed login attempts
def note_failed_signin
flash[:error] = "Couldn't log you in as '#{params[:login]}'"
logger.warn "Failed login for '#{params[:login]}' from #{request.remote_ip} at #{Time.now.utc}"
end
end

Resources