I must be missing something... my views are being rendered, but it appears like code in the controller isn't being executed. I can't find what's wrong.
When I click the login button in login.html.erb, the login_process method of the PublicController should be called to authenticate the user. That doesn't seem to happen as the view login_process.html.erb just displays, which shouldn't be possible.
I've even tried putting a flash[:notice] = "heyhey" on the login_screen action so it appears on the login form, but that doesn't happen either.
What have I done wrong?
Is it something in the routes.rb?
routes.rb
root :to => "public#index"
get "login",
:to => "public#login",
:as => "login_screen"
post "login_process",
:to => "public#login_process",
:as => "login_process"
get "logout",
:to => "public#logout",
:as => "logout"
public_controller.rb
class PublicController < ApplicationController
def login_screen
flash[:notice] = "heyhey"
end
def login_process
authenticated_user = User.authenticate params[:email], params[:password]
if authenticated_user
session[:user_id] = authenticated_user.id
session.countdown_start(1.minute)
redirect_to :public => :index
else
flash[:notice] = 'Email/Password incorrect. Receive a new password.'
flash[:color] = "invalid"
end
render :login_screen
end
def logout
session.countdown_abort
flash[:notice] = "You are now logged out"
redirect_to :login_screen
end
def count
render :text => session.countdown_count.to_i
end
end
views/public/login.html.erb
<h1>Login</h1>
<p id="notice"><%= notice %></p>
<%= form_tag :login_process do %>
<div class="field">
<%= label_tag :email %><br>
<%= text_field_tag :email %>
</div>
<div class="field">
<%= label_tag :password %><br>
<%= password_field_tag :password %>
</div>
<div class="actions">
<%= submit_tag "Login" %>
</div>
<% end %>
<h1>Login</h1>
<p id="notice"><%= notice %></p>
<%= form_tag :login_process do %>
<div class="field">
<%= label_tag :email %><br>
<%= text_field_tag :email %>
</div>
<div class="field">
<%= label_tag :password %><br>
<%= password_field_tag :password %>
</div>
<div class="actions">
<%= submit_tag "Login" %>
</div>
<% end %>
login_process.html.erb
I never expect to see this page as the user should be directed elsewhere
<p>login_process.html.erb</p>
<p id="notice"><%= notice %></p>
Update 1:
I've modified the authenticate action in the User model to be:
def authenticate(:email, :password)
#x = new User
#x.id = 123
return false #x
end
and login_process on PublicController to be:
def login_process
if true
flash[:notice] = 'boom'
else
flash[:notice] = 'Email/Password incorrect. Receive a new password.'
flash[:color] = "invalid"
end
redirect_to :login_screen
end
Unfortunately though... same issue. The login_process.html.erb is
displayed with no flash notices either.
Versions:
Ruby 2.0.0p247
Rails 4.0.0
The code in your controller is probably executing just fine.
I think the authenticate class method on User is not giving you the correct result. That's why it doesn't go through the if authenticated_user and just renders the login_process view. You don't have to call this render command though, it's done for you if you do nothing.
There's a problem with the login_process method though. You can't have a render call and a redirect_to be called together, now it can happen in your case, if the user is authenticated. Put the render :login_screen in the else case, otherwise you get the AbstractController::DoubleRenderError error saying: Render and/or redirect were called multiple times in this action. [etc.]
This is what you would get as an error if the user was authenticated.
Edit: Running this locally (stubbing User.authenticate) on a new Rails project it works. Flash notices appear and everything.
Also: render :login_screen is probably render :login, no? in login_process?
If nothing works still, what's your: Ruby version, Rails version, put authenticated_user = false # or true in login_process to see if it behaves differently.
redirect_to :public => :index
It seems like you are redirecting from login_process to /login_process?public=index, which I don't think is your intention.
If you are meaning to redirect to public#index, try
redirect_to root_path
(But I can't see that action in your public controller.)
I believe the problem is that you call two renders in one action. You can only render one page per action.
def login_process
authenticated_user = User.authenticate params[:email], params[:password]
if authenticated_user
session[:user_id] = authenticated_user.id
session.countdown_start(1.minute)
redirect_to :public => :index
else
flash[:notice] = 'Email/Password incorrect. Receive a new password.'
flash[:color] = "invalid"
end
render :login_screen
end
The user is authenticated and the redirect_to should bring you to the public/index, however, you call render :login_screen after the if statement, so it will execute that portion of the code no matter what and bring you back to the login.
Related
When a user clicks on "Forgot My Password" on the login screen, they are redirected to a route '/password-reset'. Right now, I'm trying to understand how to right the form for entering your email to receive and SMS from Twilio with a code.
<div class="form-group", style="width:50%;">
<%= form_for #user, url: password_patch_path(current_user) do |f| %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: "form-control" %>
</div>
<%= f.submit "Get Confirmation Code", class: "btn btn-default" %>
<% end %>
</div>
The issue I'm running into is that #user is nil, and I'm uncertain if the url is correct at the beginning of the form for. It makes sense to me that #user is nil because no one is logged in, so I'm not sure what that should be.
My routes are
get '/password-reset', :to => 'passwords#edit', as: :password_reset
post '/password-reset', :to => 'passwords#reset', as: :password_edit
patch '/password-confirmation', :to => 'passwords#update', as: :password_patch
and my passwords controller looks like
class PasswordsController < ApplicationController
before_action :authenticated?, only: [:edit, :update]
def reset
ConfirmationSender.send_confirmation_to(current_user)
redirect_to new_confirmation_path
end
def edit
#user = current_user
end
def update
if passwords_not_empty? && passwords_equal?
current_user.update(password_params)
redirect_to users_dashboard_path(current_user.username), success: "Password Updated"
session[:authenticated] = false
else
redirect_to password_edit_path(current_user.username), warning: "Error, please try again."
end
end
private
def password_params
params.require(:user).permit(:password, :password_confirmation)
end
def passwords_not_empty?
params[:user][:password].length > 0 && params[:user][:password_confirmation].length > 0
end
def passwords_equal?
params[:user][:password] == params[:user][:password_confirmation]
end
def authenticated?
render :file => "#{Rails.root}/public/404.html", :status => 404 unless session[:authenticated]
end
end
You are right that there will be no current_user if a user forgot his/her password. I would redesign as follows:
PasswordsContoller
class PasswordsController < ApplicationController
before_action :authenticated?, only: [:update]
def reset
#user = User.find_by(email: params[:user][:email])
if #user.present?
ConfirmationSender.send_confirmation_to(#user)
redirect_to new_confirmation_path
else
redirect_to password_reset_path, warning: "Email not found."
end
end
def edit
#user = User.new
end
...
end
Form
<div class="form-group", style="width:50%;">
<%= form_for #user, url: password_edit_path do |f| %>
<div class="form-group">
<%= f.label :email %>
<%= f.email_field :email, class: "form-control" %>
</div>
<%= f.submit "Get Confirmation Code", class: "btn btn-default" %>
<% end %>
</div>
The new edit method seeds the form with a blank user. The new reset method looks up the user by email and sends the token if the user is found. If not, it displays an email not found flash message and redirects back to the forgotten password form.
This also makes the form use the correct path for requesting a password confirmation.
I have signup form on my home screen. If user inputs invalid data I redirect him to /signin page. On this page I can see filled fields, but errors descriptions are empty.
Here is my UsersController:
class UsersController < ApplicationController
def new
#user = User.new(params[:user])
end
def create
#user = User.new(params[:user])
print #user
if #user.save
else
render 'new'
end
end
end
Method I use to show errors
module ApplicationHelper
def errors_for(model, attribute)
if model.errors[attribute].present?
content_tag :div, :class => 'well error' do
content_tag :ul do
model.errors[attribute].collect {|item| concat(content_tag(:li, item))}
end
end
end
end
end
My form partial:
<%= f.label :user_name %>
<%= f.text_field :user_name, :class=>"input-medium" %>
<%= errors_for(#user, :user_name) %>
<%= f.label :email %>
<%= f.text_field :email, :class=>"input-medium " %>
<%= errors_for(#user, :email) %>
<%= f.label :password %>
<%= f.password_field :password, :class=>"input-medium" %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, :class=>"input-medium" %>
and my signup view:
<section class="centered user-form-container">
<div class="user-form well pull-left">
<div class="centered">
<h1>Sign up</h1>
<%= form_for(#user, :action=>"create") do |f| %>
<%= render 'signup', :f=>f %>
<%= f.submit "Sign up" %>
<% end %>
</div>
</div>
</section>
In this situation, I believe you need to use flash.now, something like this:
Per the rails docs:
By default, adding values to the flash will make them available to the next request, but sometimes you may want to access those values in the same request. For example, if the create action fails to save a resource and you render the new template directly, that’s not going to result in a new request, but you may still want to display a message using the flash. To do this, you can use flash.now in the same way you use the normal flash:
def create
#user = User.new(params[:user])
print #user
if #user.save
else
# start with this, then expand the error text
flash.now[:error] = "Could not save user"
render 'new'
end
end
You would do this in your validation method.
If you are using a standard rails validation you would do this:
validates_presence_of :foo, :message => 'Message you want to display here'
If you are doing a custom validation then this:
def my_validation_method
begin
my_validation_code_here
rescue
self.errors[:base] << 'Message you want to display here'
end
end
def new
#user = User.new(params[:user])
if (!params[:user].nil?)
#user.valid?
end
end
I've got two controllers: admin and customers, plus one more called sessions for handling login and authentication. I'm trying to use one login form so that when an admin logs in, they are redirected to their part of the site, and if a customer logs in, they are taken to their part.
Edit: changed the params to login by email, but now getting the error No route matches {:action=>"show", :controller=>"customers"} when I try to log in as a customer :S!!
Code:
class SessionsController < ApplicationController
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_url
else
redirect_to login_url, alert: "Invalid user/password combination"
end
end
def destroy
session[:user_id] = nil
session[:customer_id] = nil
redirect_to store_url, notice: "Logged out"
end
end
Code for the login page (stored in app/sessions/new.html.erb):
<div class="depot_form">
<% if flash[:alert] %>
<p id="notice"><%= flash[:alert] %></p>
<% end %>
<%= form_tag do %>
<fieldset>
<legend>Please Log In</legend>
<div>
<%= label_tag :email, 'Email:' %>
<%= text_field_tag :email, params[:email] %>
</div>
<div>
<%= label_tag :password, 'Password:' %>
<%= password_field_tag :password, params[:password] %>
</div>
<div>
<%= submit_tag "Login" %>
</div>
</fieldset>
<% end %>
</div>
Also, if relevant, I've got this in the config/routes file:
controller :sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
Edit: changed the params to login by email, but now getting the error No route matches {:action=>"show", :controller=>"customers"} when I try to log in as a customer :S!!
The problem is that your params[:email] does not exist at all! Try using the params[:name], and hope that your customer knows that he has to write his email into the name field.
user = User.find_by_name(params[:name])
customer = Customer.find_by_email(params[:name])
If you only have one login form, then the input for the username/email will either end up in the params hash as either
params[:email]
or
params[:name]
But it looks like params[:email] doesn't exist since you're only using one form. Since you can successfully authenticate as an admin, I would guess that the input for the username is titled "name", so in your params hash, when you authenticate as a customer, your authenticating against
params[:name] #this is what I think the input on the form is named
instead of
params[:email] #which is what it looks like your code is looking for.
Can you post the view code?
I'm trying to set up a simple login using AuthLogic into my User table. Every time I try, the login fails and I don't know why. I'm sure this is a simple error but I've been hitting a brick wall with it for a while.
#user_sessions_controller
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Login successful!"
else
flash[:notice] = "We couldn't log you in. Please try again!"
redirect_to :controller => "index", :action => "index"
end
end
#_user_login.html.erb (this is the partial from my index page where Users log in)
<% form_tag user_session_path do %>
<p><label for="login">Login:</label>
<%= text_field_tag "login", nil, :class => "inputBox", :id => "login",
</p>
<p><label for="password">Password: </label>
<%= password_field_tag "password", nil, :class => "inputBox", :id => "password",
</p>
<p><%= submit_tag "submit", :class => "submit" %></p>
<% end %>
I had Faker generate some data for my user table but I cannot log in! Every time I try it just redirects to index. Where am I going wrong? Thanks everybody.
------UPDATE------
I implemented Jakub Hampl's suggestion with form_for just now - I'm getting a new error.
ActionView::TemplateError (called id for nil, which would mistakenly be 4 -
1: <% form_for #user_session do |f| %>
2: <% if flash[:notice] -%>
3: <p class="notice"><%= flash[:notice] %></p>
4: <% end -%>
app/views/index/_user_login.html.erb:1
app/views/layouts/index.html.erb:65
app/controllers/index_controller.rb:3:in `index'
Rendered rescues/_trace (86.0ms)
Rendered rescues/_request_and_response (1.0ms)
Rendering rescues/layout (internal_server_error)
I have not changed the controller at all. Thank you everyone who is responding to this topic - it's incredibly helpful to me. What can I do now to get past this hurdle?
------UPDATE #2------
Here is my application controller.
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to login_path
return false
end
end
def require_no_user
if current_user
store_location
flash[:notice] = "You must be logged out to access this page"
redirect_to root_url
return false
end
end
Which one of these should be changed to #user_session?
A good idea is to use form_for if you possibly can:
<% form_for #user_session do |f| %>
<p>
<%= f.label :username %>
<%= f.text_field :username, :class => :inputBox %>
</p>
<p>
<%= f.label :password %>
<%= f.password_field :password, :class => :inputBox %>
</p>
<p><%= f.submit "submit", :class => :submit %></p>
<% end %>
Apart from that hard to say. Does your log file give any detail (aka errors)? Also try to add the errors on the table to you flash so that you can see what's going wrong.
Since it seems fro your last update that #user_session is not set, just go ahead and create one: <% form_for UserSession.new do |f| %>.
in your case, params[:user_session] is empty because it's not being set in your view. I think Jakub Hampl's suggestion to use form_for is the best way, but you can also stay with form_tag by setting input names to user_session[login] and user_session[password], OR you can change the line in your action to #user_session = UserSession.new(:login => params[:login], :password => params[:password])
.
This is my first time doing validation on a rails application. I saw many tutorials which made it seem easy. I don't know why I cant get it to work.
Below is my setup.
Controller Admin (action = login)
def login
session[:user_id] = nil
if request.post?
#user = User.authenticate(params[:userId], params[:password])
if true
session[:user_id] = #user.user_id
flash.now[:notice] = "Login Successful"
redirect_to(:controller => "pages", :action => "mainpage")
else
flash.now[:notice] = "Invalid user/password combination"
end
end
end
So first time user comes to admin/login they are just presented with a form below
login.erb.html
<% form_for :user do |f| %>
<p><label for="name">User ID:</label>
<%= f.text_field :userid %>
</p>
<p><label for="password">Password:</label>
<%= f.password_field :password%>
</p>
<p style="padding-left:100px">
<%= submit_tag 'Login' %>
</p>
<% end %>
My User model:
class User < ActiveRecord::Base
validates_presence_of :userid, :password
def self.authenticate(userid, password)
user = self.find_by_userid_and_password(userid, password)
user
end
end
Actual field names for userId and password in my DB: userid password
I am expecting behavior that when user does not enter anything in the fields and just clicks submit. it will tell them that userid and password are required fields. However, this is not happening
From the console I can see the messages:
>> #user = User.new(:userid => "", :password => "dsf")
=> #<User id: nil, userid: "", password: "dsf", created_at: nil, updated_at: nil>
>> #user.save
=> false
>> #user.errors.full_messages
=> ["Userid can't be blank"]
So error is somewhere in my form submit...
UPDATE: validations only happen when u SAVE the object....here I am not saving anything. So in this case I have to do javascript validations?
It's the if true line. Change it to
if #user = User.authenticate(params[:userId], params[:password])
or
#user = User.authenticate(params[:userId], params[:password])
if #user
...
end
I'd also add redirect_to login_path to the failure case.
You can also slim down your auth method:
def self.authenticate(userid, password)
find_by_userid_and_password(userid, password)
end
It turns out, there are several issues here, and I'll try to cover them all. Let's start with your model:
class User < ActiveRecord::Base
validates_presence_of :userid, :password
def self.authenticate(userid, password)
self.find_by_userid_and_password(userid, password)
end
end
The validation doesn't come into play for logging in, only for creating and updating user records. The authentication has been trimmed, because ruby automatically returns the last calculated value in a method.
Next, the login action of your controller:
def login
session[:user_id] = nil
if request.post?
if #user = User.authenticate(params[:userId], params[:password])
session[:user_id] = #user.user_id
flash[:notice] = "Login Successful"
redirect_to(:controller => "pages", :action => "mainpage")
else
flash.now[:error] = "Invalid user/password combination"
end
end
end
Notice we don't use flash.now on a redirect - flash.now is only if you're NOT redirecting, to keep rails from showing the message twice.
Finally, you shouldn't be using form_for, because this is not a restful resource form. You're not creating or editing a user, so use form_tag instead:
<% form_tag url_for(:controller => :users, :action => :login), :method => :post do %>
<%= content_tag(:p, flash[:error]) if flash[:error] %>
<p><label for="name">User ID:</label>
<%= text_field_tag :userid %>
</p>
<p><label for="password">Password:</label>
<%= password_field_tag :password%>
</p>
<p style="padding-left:100px">
<%= submit_tag 'Login' %>
</p>
<% end %>
This will do what you want. This is a great learning exercise, but if you're serious about user authentication in a production application, checkout rails plugins like restful_authentication or clearance that do this for you in a much more sophisticated (and RESTful) way.