Am trying to update my database with signout time upon user sign out of the application but my solution does not work.
Here is what I have done below:
So I defined a method which I call in destroy action to update my database with signout time but it doesnt work, rather it throws argument error.
update_method
# the `save_signout_time` is what I have done to update logout_time upon sign out.
def save_signout_time(member_id)
SigninHistory.update(
logout_time: Time.now
)
end
def destroy
save_signout_time current_user.id
clear_all_sessions current_user.id
reset_session
redirect_to root_path
end
What am I missing? Or how do I go about this?
Your function save_signout_time takes a member_id but then you don't use that member_id for the update.
If you want to update a single record the way you have it architected right now, you need to:
SigninHistory.update(member_id, logout_time: Time.now)
Related
I'm continuing to tweak the Rails Getting Started project to get the authentication behavior I want.
I want to control what is allowed and not allowed at the level of specific actions rather than for a whole controller. For example, you don't need to be signed in to view posts (index / show), but you must be signed in to access the form to submit a new post (new) and to get a submitted post processed (create).
Since I would like people to be redirected to sign-in if they're not signed-in, and I'll be using that snippet over and over again in a million places, I put this in the application controller:
def authcheck
unless user_signed_in?
redirect_to new_user_session_path
end
end
For new posts, this seems to work:
def new
authcheck #see application controller
#post = Post.new
end
But for the case where I have two tabs open and I have the first one on the new post form, but I log out on the second one, then try to submit the post on the first form, I get an error about the user being null even though it seems to me like it should have been redirected:
def create
authcheck
#when not signed in, causes error "undefined method 'posts' for nil:NilClass"
#post = current_user.posts.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
Actually, I was getting an exception page originally but I changed exception to null_session in the application controller's protect_from_forgery with: :exception line.
Basically: Why isn't it redirecting to the sign-in page like it was when it was just about showing the form for a new post? And, from there, what might you suggest I should do about it?
I think I was labouring under a false impression. I thought that a redirect_to was the end of execution. But it doesn't seem to work that way - after a redirect line I could, for instance, tie up the server in an attempt to square the circle, even if it's not tied to spitting out a webpage.
I modified my "authcheck" to return true or false, then put everything in if blocks. The first example didn't fail the old way because it was just instantiating a new post but it didn't matter if it was actually saved or not.
Change to application controller routine:
def authcheck
unless user_signed_in?
redirect_to new_user_session_path
return false
end
return true
end
Changes to Posts controller routines:
def new
if authcheck #see application controller
#post = Post.new
end
end
def create
if authcheck
#post = current_user.posts.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
end
I suspect I'm not really doing things the accepted way, so I'll hold out a few days before accepting my own answer. :-)
I'm trying to let users submit a reservation request without being logged in. After they submit unauthed users are prompted to sign in or sign up. After signing up I'd like the form to be submitted and the (new registered) users to be taken to the checkout page.
Store location keeps the last page to return users after logging in. I need to figure out how to continue users on their intended path by submitting their forms and placing them on the checkout page after sign up/ sign in.
def store_location
#stores the last url. Used for post-login redirects to what the user was trying to do last.
if (!request.fullpath.match("/users/") && !request.xhr?) # don't store ajax calls
session[:previous_url] = request.fullpath
end
end
Ok, I think this is pretty dirty but I haven't been able to find another way to do this.
after_sign_in_path_for is a method Devise calls that allows you to send people to different pages after sign in.
I took all the create logic out of the controller and put it into a service object.
def after_sign_in_path_for(resource)
if session[:booking_params].present?
#booking = Booking::CreateBooking.new(user:current_user, params: session[:booking_params]).return_booking
checkout_booking_path(#booking.public_id)
else
session[:previous_url] || root_path
end
end
In the controller, the create method is split into two parts. If there is no current user I save their params into the session and they are sent to login. If there is the CreateBooking service object is called normally.
def create
if current_user.nil?
session[:booking_params] = params
redirect_to new_user_registration_path
else
#booking = Booking::CreateBooking.new(user:current_user, params:params).return_booking
respond_to do |format|
format.html { redirect_to checkout_booking_path(#booking.public_id) }
end
end
end
In the after_sign_in_path_for method I check for the session params and create the booking there.
Let me know if there is a better way to do this!
The RoR Security Guide states that you should "issue a new session identifier and declare the old one invalid after a successful login" using the reset_session method to counter session fixation.
I haven't been able to find any guidance on calling reset_session when using Authlogic. Is it simply a case of including the method in the controller method (as below)?
I'm just concerned about causing problems for Authlogic as I can see both user_credentials and user_credentials_id keys and values in the session hash prior to calling reset_session.
class UserSessionsController < ApplicationController
def create
#user_session = current_client.user_sessions.new(params[:user_session])
if #user_session.save
reset_session
flash[:success] = I18n.t(:msg_login_success)
redirect_back_or_default application_root_path
else
render :action => :new
end
end
For reference this is my current method:
def create
#user_session = current_client.user_sessions.new(params[:user_session])
if #user_session.save
# reset session to counter session fixation
# whilst retaining values except for those that the application has created specific to the current user
temp_session = session.merge("current_user" => {}).clone
reset_session
session.reverse_merge!(temp_session)
# set flash msg and redirect
flash[:success] = I18n.t(:msg_login_success)
redirect_back_or_default application_root_path
else
render :action => :new
end
end
With the call to reset_session still performed after a successful login as per the recommendation in http://guides.rubyonrails.org/security.html#session-fixation-countermeasures
yeah, resetting the session AFTER you log the user in (which is what looks like happening?) is definitely not right. You want to do it BEFORE you log the user in.
Ideally you'd want to do it before you log the user in but only if the login is actually going to be succesful -- but I'm not sure if you can get auth_logic to do that, I'm not very experienced with auth_logic, although it's a REALLY good question for auth_logic, if I were you I'd file it as a support ticket with auth_logic.
But in the meantime, you might want to just try putting the reset_session at the top of the action method, before #user_session = current_client.user_sessions.new(params[:user_session]). I think this will work, and at worse reset the session in some cases where you really didn't have to (if the user's credentials were invalid), but I don't think that will cause a serious problem. (uh-oh, unless it causes you to lose your validation errors?)
But again, not an auth_logic expert here. I don't expect you to accept this answer since I don't have the expertise to really answer it, just sharing what I think in case it helps you and gives you some pointers as to how to think about it.
I am trying to force a user to login once they call this update action in my article controller (I am trying to work with gradual engagement) but once they login, I want to still call this action instead of halting.
def update
#article.attributes = params[:article]
#article.save
#store this article as a session variable
session[:pending_article] = #article.body
respond_with(#article, :location => article_url(#article))
end
Right now I am using a before_filter for the action that requires the user to login
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to login_url
return false
end
end
However, I understand that before filters halt the original action once they redirect, so update never gets called. Basically, I want a user to be logged in to save an article but I want to save their work so I'm storing the article body in a session variable which I grab later. Is there a better way to require a user to login for an action but call it afterwards anyway?
In your require_user method you can do something like this:
session[:article] = params[:article]
Then in your login method (/sessions/create?) do this:
# this should take you back /articles/new,
# you may have to move your call to store_location
# or manually set session[:return_to]
redirect_back_or_default
Then in ArticlesController#new
def new
#article = Article.new(session[:article] || {})
end
Then the saved article params from the session are still there so the form is pre-filled out.
Be careful storing too much content in the session though. In Rails the default session store is a cookie, and cookies only hold about 4k of data. You may need to change your session store to pull this off.
I'm having a bit of problems with AuthLogic and current_user.
I have a Flex4 application using the Cairngorm framework as the front-end, and Ruby On Rails as the back-end.
I can log in fine through a browser, and when only using ROR. However, when I try it through my Flex4 application, it will fail the first time but work the second time.
What is happening, is inside the user_sessions_controller.rb I have a call to
self.current_user.to_xml;
The first time I call the create action, the current_user object returns nil. The second time I call it (without restarting the server, or browser) it will be the proper current user.
So this leads me to believe that the current_user is being set sometime after the render command inside my create action.
If I need my rails controller to return the current user in the create action, how would I go about doing that?
Was just having the exact same problem...not sure why this happens but I was trying to call current_user in my create method in my user_sessions_controller.rb, solved as per below...
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
current_user = UserSession.find.user
current_user.increment_login_count_for_current_memberships!
flash[:notice] = 'Sign in successful.'
redirect_to root_path
else
render action: "new"
end
end
Hope this helps!
This is because the helper methods current_user is typically defined as a before_action. What means, the before action did not run before you use it during the session create.
I also used this UserSession.find.user which is perfectly fine for this usecase.
Cheers,
Niklas