guys. I have a problem when doing the authlogic login validation. I have the magical "active" field in the user model, say if active is false, when login with correct password, it will pop up the message like "You are suspended, please contact your administration" (I defined this message in I18n file); when login with wrong password, it will pop up the the not_active message plus password_invalid message like "password invalid". I think that is because authlogic did the validation both for "active" field and password and it seems password validation comes first.
My question is, how can bypass the password validation if 'active' is false. Or, can I only show not_active message? my code like:
if #user_session.save
redirect_to home_path
else
render :json => {:success => false, :error => #user_session.errors.full_messages.join("\n")}
end
OK, so I don't like this as a user-experience, but if you really want to, do something like:
before_filter :restrict_inactive_users, :on=>:create
def restrict_inactive_users
#user = User.find_by_login(params[:user_session][:login]) rescue nil
return unless #user
unless #user.active?
flash[:error] = "You are suspended, please contact your administration"
render :action=>:new
return false
end
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
redirect_to home_path
else
render :json => {:success => false, :error => #user_session.errors.full_messages.join("\n")}
end
end
Today I thought out a solution which doesn't bypass the password validation but just delete the password error message from user_session. Code like:
if #user_session.save
redirect_to home_path
else
#user_session.errors.delete(:password) unless #user_session.attempted_record.active
render :json => {:success => false, :error => #user_session.errors.full_messages.join("\n")}
end
Start with fetching the user by the identifier of your choice, like the email or user name.
If the user is not active you can remove the other errors before redirecting back to the login page.
#user_session.errors.clear
Then the errors will not show when the page is rerendered. But you must provide a custom error message, for example via the flash.now[:error] or your json response.
Related
I'm trying to change a users password and it successfully changes but it doesn't let me do anything afterwards because the user becomes unauthorized... I'm probably missing a piece that reauthenticates them.
This is my code to update their password
def password
if current_user.valid_password?(params[:current_password])
current_user.password = params[:new_password]
if current_user.save
#sign them in
#tried doing this to sign them in again but didn't work
sign_in(:user, current_user)
response.headers['X-CSRF-Token'] = form_authenticity_token
response.headers['X-Pyne-Auth'] = current_user.authentication_token
render :json => {:success => true} and return
else
render :json => {:success => false, error: "Unexpected error while trying to save user. Please try again."} and return
end
else
render :json => {:success => false, error: "Your current password is incorrect. Please try again"} and return
end
end
I can update the password but have trouble accessing the app again because the user becomes unauthorized.
Thank you
Try bypass_sign_in(#user) as suggested in the Devise wiki.
I have a User model:
class User < ActiveRecord::Base
has_secure_password
# validation lets users update accounts without entering password
validates :password, presence: { on: :create }, allow_blank: { on: :update }
validates :password_confirmation, presence: { if: :password_digest_changed? }
end
I also have a password_reset_controller:
def update
# this is emailed to the user by the create action - not shown
#user=User.find_by_password_reset_token!(params[:id])
if #user.update_attributes(params[:user])
# user is signed in if password and confirmation pass validations
sign_in #user
redirect_to root_url, :notice => "Password has been reset."
else
flash.now[:error] = "Something went wrong, please try again."
render :edit
end
end
Can you see the problem here? A user can submit a blank a password/confirmation and rails will sign them in, because the User model allows blank on update.
It's not a security concern, since an attacker would still need access to a user's email account before they could get anywhere near this action, but my problem is that a user submitting 6 blank chars would be signed in, and their password would not be changed for them, which could lead to confusion later on.
So, I've come up with the following solution, and I'd like to check if there's a better way of doing it, before I push to production:
def update
#user=User.find_by_password_reset_token!(params[:id])
# if user submits blank password, add an error, and render edit action
if params[:user][:password].blank?
#user.errors.add(:password_digest, "can't be blank.")
render :edit
elsif #user.update_attributes(params[:user])
sign_in #user
redirect_to root_url, :notice => "Password has been reset."
else
flash.now[:error] = "Something went wrong, please try again."
render :edit
end
end
Should I be checking for nil as well as blank? Are there any rails patterns or idiomatic ruby techniques for solving this?
[Fwiw, I've got required: true on the html inputs, but want this handled server side too.]
Please try this:
we can use - present?
Ex:
if !params[:user][:password].present?
I'm having a problem with a notice appearing when it shouldn't. When I click a link in my app to login, it's flashing the 'Invalid username/password combination' notice, even though I haven't typed anything in. I understand why I'm getting the message - it's because when I click the link, I haven't typed in a matching username and password, so the error fires. But I'm not sure how to fix this. I want the error to appear when the user does type in the wrong combo, but not when the page first appears.
In the code, the 'user' refers to an admin, and the customer is a customer. I'm using the same login page for both types of people.
Also, what I'd really like is that when the user does type in the wrong combination, their email address will stay in the field so that they don't have to type it in again. How would I go about doing this?
Thanks!!
Here's the updated controller code:
class SessionsController < ApplicationController
skip_before_filter :authorize
def new
end
def create
user = User.find_by_email(params[:email])
customer = Customer.find_by_email(params[:email])
if user and user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to admin_url
elsif customer and customer.authenticate(params[:password])
session[:customer_id] = customer.id
redirect_to customer_path(session[:customer_id])
else
render :new, notice: "Invalid email/password combination"
end
end
def destroy
session[:user_id] = nil
session[:customer_id] = nil
redirect_to store_url, notice: "Logged out"
end
end
Set the flash notice only when login parameters where sent with the request:
# ...
else
flash[:notice] = "Invalid username/password combination" if params[:email] || params[:password]
redirect_to login_url
end
I suggest you wrap everything in if params and rerender the view instead of redirecting to preserve the email.
if params
if user ...
...
elsif ...
...
else
render :new
Try replacing your last else statement with this:
else
if params[:email] || params[:password]
flash[:notice] = "Invalid username/password combination"
redirect_to login_url
end
end
That should do the trick. In regards to your question about the email address remaining in the field when the user enters an incorrect password, check this link: here
The general gist of the solution would be to use 'render' instead of 'redirect_to' in your code to reload the page.
In my Rails app I'm trying to produce a separate flash.now[:alert] for invalid :email and :password, respectively. So if a user enters the correct email but wrong password, the :alert warns the user of an invalid password and vice versa. Here's what I have in my SessionsController:
def create
if user = User.authenticate(params[:email], params[:password])
session[:user_id] = user.id
redirect_to user.profile, :notice => "Logged in successfully"
elsif user.email != params[:email]
session[:email] = #user.email
flash.now[:alert] = "Invalid email. Try again!"
render :action => 'new'
else
session[:password] = #user.password
flash.now[:alert] = "Invalid password. Try again!"
render :action => 'new'
end
end
Rendering this gives me an undefined method for email. Can anyone help me figure out what I'm doing wrong?
DISCLAIMER: Obviously this is a really bad idea as an attacker could keep on trying emails until he found one that did match and then he could start trying passwords for this email he knows exists at your database, but you're asking, so it's up to you deciding to do this or not.
Your authenticate method obviously only returns the user if the email and password did match, change your authenticate method to return a boolean and a user if there is any available. It would look somewhat like this:
def authenticate(email, password)
u = first(:conditions => {:email => email, :state => 'active'})
u && u.authenticated?(password) ? [true, u] : [false, u]
end
Then, at your controller:
def create
result , user = User.authenticate(params[:email], params[:password])
if result
session[:user_id] = user.id
redirect_to user.profile, :notice => "Logged in successfully"
elsif user
session[:email] = #user.email
flash.now[:alert] = "Invalid email. Try again!"
render :action => 'new'
else
session[:password] = #user.password
flash.now[:alert] = "Invalid password. Try again!"
render :action => 'new'
end
end
And this should work.
When i run in my page this code :
<% user = User.find(session[:userid]) %>
i get the error :
line #1 raised:
Couldn't find User without an ID
although i have in my authentification in my sessions_controller this :
def create
if user = User.authenticate(params[:username],params[:password])
session[:user_id]= user.id
session[:language_id]= user.language_id
User.find(user.id).update_attributes(:last_login => Time.now)
redirect_to root_path , :notice => (I18n.t :"session.login_success")
else
flash.now[:alert] = (I18n.t :"session.error")
render :action => 'new'
end
end
and the session should contain the userid
In your log-in you set session[:user_id], and you try to find session[:userid] (note the spurious underscore). That's why.
Your controller is looking for params session[:user_id], but in your views it is session[:userid] so it will complain that session[:user_id] is nil.