Here is my code:
password_resets_controller.rb
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update]
def new
end
def create
# #user = User.find_by(email: params[:password_resets] [:email].downcase)
#user = User.find_by(email: params[:password_reset][:email].downcase)
if #user
#user.create_reset_digest
#user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:member][:password].empty?
#user.errors.add(:password, "Can't be empty")
render 'edit'
elsif #user.update_attributes(user_params)
# log_in #user
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:member).permit(:password, :password_confirmation)
end
def get_user
#user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless #user
redirect_to root_url
end
end
# Check expiration of reset token.
def check_expiration
if #user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
Thr situation is after I enter the path of password reset token, I put the new password and password_confirmation. Then I click update. However, this error page shows. I really don't know how to figure this out.
Here is my edit file:
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user, url: password_reset_path(params[:id])) do |f| %>
<%= render 'shared/error_messages' %>
<%= hidden_field_tag :email, #user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Update password", class: "btn btn-primary" %>
<% end %>
</div>
Here is the log:
Here is my User table:
Here is the log after change password_reset_path(params[:id]) to password_reset_path(#user)
The problem is because your form sends member key instead of user, as your logs show:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"...",
"email"=>"naomi.wuuu#gamil.com",
"member"=>{
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"
},
...
}
So, since there is no user key, params[:user] returns nil and, since it can't respond to [], results in the error:
undefined method `[]' for nil:NilClass
Looking at your controller and view I can't see why member is being used instead of user, but you can force your form to send user key using as: "user", like this:
<%= form_for(#user, url: password_reset_path(params[:id]), as: "user") do |f| %>
With this in place, your parameters will be now:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"...",
"email"=>"naomi.wuuu#gamil.com",
"user"=>{
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"
},
...
}
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.
This is my first Rails app and have hit another wall. I have a User model and a Country model. They have a many-to-many relationship, which I join together with a Trip model.
A user can maintain a list of countries that they have been to. On the Country page, I want to have a simple bootstrap button so the current_user can add or remove the country to their list.
I am using a partial that looks like the below to at least render buttons on all the pages.
_add_remove_countries.html.erb
<% if #user.countries.exists?(#country.id) %>
<%= form_for(#user) do |f| %>
<%= f.submit "Remove Country", class: "btn btn-info" %>
<% end %>
<% else %>
<%= form_for(#user) do |f| %>
<%= f.submit "Add Country", class: "btn btn-info" %>
<% end %>
<% end %>
I have tried a few different things, with no luck so I have just reverted to the basic structure. I am currently using a form_for, however that is just what has worked best so far, I am not tied to that solution.
Below are my controllers if needed, I have not set up a Trips controller as I am only using it to join the User and Country Model (maybe I need to set one up?).
users_controller.rb
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#countries = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
render 'new'
end
end
def update
redirect_to user_path
end
private
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
end
countries_controller.rb
class CountriesController < ApplicationController
before_action :require_user, only: [:index, :show]
def index
#countries = Country.all
#sort = CS.countries.sort_by {|key, value| value}
#sort = #sort.first #sort.size - 2
end
def show
#country = Country.find(params[:id])
#user = User.find(session[:user_id])
end
end
my suggestion using collection_select (and click link in case you would like to know more about collection_select) to add countries to user while editing user, below is sample code to help (using edit method)
user_controller
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#countries = Country.all
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to #user
else
render 'new'
end
end
# ---> here additional code to edit method
def edit
#user = User.find(params[:id])
#countries = Country.all
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to user_path
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:username,
:email,
:password,
:password_confirmation,
:country_ids => [])
# country_ids is an array that will save data for countries that user have been to
end
end
now this is the fun one, in your views\user\edit.html.erb
<%= form_for #user do |f| %>
<!-- simple -->
<p>Email : </p>
<p><%= f.text_field :email %></p>
<!-- if you using bootstrap -->
<div class="row form-group">
<%= f.label "email", :class => 'control-label col-sm-3' %>
<div class="col-sm-5">
<%= f.text_field :email, :class => 'form-control' %>
</div>
</div>
<!-- other inputs (password / password_confirmation) -->
<%= f.collection_select :country_ids, #countries, :id, :name, {}, { multiple: true, class: 'form-control' } %>
<% end %>
I have my rails app and when I try to login (I have created user called "test") I see this in the console:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"tlKwtMBNJ4LzJuJq13bUscAGpumdr+HVmUlGlfIudT9032DMXNxqa0d2VCxCvDZRDe1D6pFfaTafSRiL6tUvhw==", "session"=>{"login"=>"", "password"=>"[FILTERED]"}, "commit"=>"Log in"}
User Load (1.7ms) SELECT `users`.* FROM `users` WHERE `users`.`login` IS NULL LIMIT 1
I see that in the session parameters application can't get user login (and maybe password too). Below are my user and session controllers:
class UsersController < ApplicationController
before_action :require_admin
def new
#users = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
current_user = #user.id
redirect_to #user
else
redirect_to '/login'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :login)
end
end
Session controller:
class SessionsController < ApplicationController
def new
end
def create
#user = User.find_by_login(params[:login])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to '/'
else
flash[:error] = 'err'
redirect_to '/login'
end
end
def destroy
session[:user_id] = nil
redirect_to root_url
end
end
I have also tested the user creation and the record is in the database.
#update here is my view for the login form
<%= form_for(:session, url: login_path) do |f| %>
<div class="hidden-sm hidden-xs col-md-12 col-lg-12 ">
<%= f.text_field :login, :placeholder => "login" %>
<%= f.password_field :password, :placeholder => "password" %>
<%= f.submit "Log in", class: "btn-submit"%>
</div>
You have nested hash in your params(login param is under session key).
Try
def create
#user = User.find_by_login(params[:session][:login])
if #user && #user.authenticate(params[:session][:password])
session[:user_id] = #user.id
redirect_to '/'
else
flash[:error] = 'err'
redirect_to '/login'
end
end
Also, in your example login param is empty - i guess it's not being supplied from the form.
#update: using form tag
<%= form_tag login_path, method: :post do %>
<div class="hidden-sm hidden-xs col-md-12 col-lg-12 ">
<%= text_field_tag :login, :placeholder => "login" %>
<%= password_field_tag :password, :placeholder => "password" %>
<%= submit_tag "Log in", class: "btn-submit"%>
</div>
<% end %>
and for create method(since params are not nested anymore):
def create
#user = User.find_by_login(params[:login])
if #user && #user.authenticate(params[:password])
session[:user_id] = #user.id
redirect_to '/'
else
flash[:error] = 'err'
redirect_to '/login'
end
end
I want to implement a reset password functionality so I have followed this railscast, I receive the mail with the link to redirect to an edit password page but I get an error here.
View
<h1>Reset Password</h1>
<%= form_for #user, :url => password_reset_path(params[:id]) do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #user.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
</div>
<div class="actions"><%= f.submit "Update Password" %></div>
<% end %>
The error is :First argument in form cannot contain nil or be empty
I'm assuming that #user is empty, I'm new on RoR and I don't know why I get this error
Password Controller
class PasswordResetsController < ApplicationController
def new
render :layout => false
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to :connect, :notice => "An E-mail has been send"
end
def edit
render :layout => false
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password ↵
reset has expired."
elsif #user.update_attributes(params[:user])
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
end
Change your def edit to
def edit
#user = User.find_by_password_reset_token!(params[:id])
render :layout => false
end
you have to add
#user = User.new
to your new method.
you have also another error for your create method. there is no user creation.
class PasswordResetsController < ApplicationController
def new
#user = User.new
render :layout => false
end
def create
#user = User.new user_params
if #user.save
# your code to render success
else
# your code to render error
end
end
private
def user_params
params.require(:user).permit(:email) # add more
end
end
This is the answer to '#user.update_attributes(params[:user])' with forbidden attributes error.
Rails 4 has new feature known as strong parameters.
Change your password controller to:
class PasswordResetsController < ApplicationController
def new
render :layout => false
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to :connect, :notice => "An E-mail has been send"
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
render :layout => false
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_sent_at < 2.hours.ago
redirect_to new_password_reset_path, :alert => "Password ↵
reset has expired."
elsif #user.update_attributes(user_params)
redirect_to root_url, :notice => "Password has been reset."
else
render :edit
end
end
private
def user_params
params.require(:user).permit(:name, :email_id, :password)
end
end
I've spent several days on it, but couldn't carry out. I wonder how can I make logging in as admin using a Bootstrap Modal either when clicking on a link or visiting /admin page.
I made an admin namespace
namespace :admin do
resources :users
resources :shops
match '/index' => 'shops#index', :via => :get, :as => 'index'
get '', to: 'sessions#new', as: '/'
end
and also Users & Shop controllers and views in /admin folder.
Different solutions on the web couldn't work and that's why I decided to make it from the very beginning.
Just say if any other information about application is needed. Relying on your knowledge, I will be grateful for any help!
PS: Authentication method in application is taken from Hartl's tutorial (there's no authentication libraries).
Login page (/admin), views/admin/sessions/new.html.erb:
<% provide(:title, "Sign in") %>
<h1>Sign in as admin</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(:session, url: sessions_path) do |f| %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Sign in", class: "btn btn-large btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
And also, if non-admin tries to access /admin page, he will be redirected ro the 'login as admin' page. controllers/admin/users_controller.rb has admin_user for show action:
class Admin::UsersController < ApplicationController
before_action :signed_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: [:show, :destroy]
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Welcome to the Hotels Advisor!"
redirect_to root_url
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to [:admin, #user]
else
render 'edit'
end
end
def destroy
user = User.find(params[:id])
unless current_user?(user)
user.destroy
flash[:success] = "User deleted."
end
redirect_to admin_users_url
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
def creating_forbidden
if signed_in?
redirect_to root_url, notice: "You are already regsitered."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end