Argument Error in Password Resets Controller - ruby-on-rails-3

I'm following the tutorial in Railscast to create a "forgot my password" link. I'm running into problems when creating a cookie for the password reset. Here is the error:
ArgumentError in PasswordResetsController#create
wrong number of arguments (1 for 0)
Rails.root: C:/Sites/application
Application Trace | Framework Trace | Full Trace
app/models/user.rb:37:in `create_remember_token'
app/models/user.rb:29:in `send_password_reset'
app/controllers/password_resets_controller.rb:7:in `create'
../app/models/user.rb - line 28 is remember_token(:password_reset_token)
before_save :create_remember_token
before_create { create_remember_token(:remember_token) }
def send_password_reset
create_remember_token(:password_reset_token)
save!
UserMailer.password_reset(self).deliver
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
../app/controllers/password_resets_controller.rb
class PasswordResetsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
user.send_password_reset if user
redirect_to root_url, :notice => "Email sent with password reset instructions."
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
end
../app/helpers/sessions_helper.rb
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
current_user = user
end
def current_user
#current_user ||= user_from_remember_token
end
def sign_out
current_user = nil
cookies.delete(:remember_token)
end
private
def user_from_remember_token
remember_token = cookies[:remember_token]
User.find_by_remember_token(remember_token) unless remember_token.nil?
end
end
If I change the way I find the user in my controller to: user = User.find_by_email(params[:session][:email]) the first error dissapears. I can only think that this is telling me that I'm calling the user incorrectly, and not assigning the new token to it on reset.

Just looking at the Railscast, where you have the "remember_token" method, they use a "generate_token" method that was added to the User class. That kind of typo might not be the problem, of course, but if it were the error message would make sense (a method that didn't previously exist wouldn't expect any arguments).

Related

Bcrypt with two different user models on rails 5.2

I have two migration tables: parents and teachers. And I use Bcrypt for registration. I can't figure out what I should do with log in and sessions_controller (sessions helper). I can register new user and when user registers he can only see Sign Out link in navbar. However, I can not sign out user, I am not sure how I define the methods in sessions controller and session helper. Maybe somebody can help me with this?
I can not find information about bcrypt with different user models - is this thing not popular or is this so easy and I am just stupid and do not understand a thing??
class SessionsController < ApplicationController
include SessionsHelper
def new
end
def create
teacher = Teacher.find_by(email: params[:session][:email])
parent = Parent.find_by(email: params[:session][:email])
if teacher && teacher.authenticate(params[:session][:password])
log_in teacher
redirect_to documents_path
flash[:notice] = "Welcome!"
elsif parent && parent.authenticate(params[:session][:password])
log_in parent
redirect_to root_path
flash[:notice] = "Welcome!"
else
flash[:alert] = "Please log in again!"
render 'new'
end
end
def destroy
if log_out parent
redirect_to root_path
elsif log_out teacher
redirect_to root_path
end
end
end
And here is my sessions helper:
module SessionsHelper
# Logs in the given user.
def log_in(parent)
session[:parent_id] = parent.id
end
def log_in(teacher)
session[:teacher_id] = teacher.id
end
# Returns the current logged-in user (if any).
def current_teacher
#current_teacher ||= Teacher.find_by(id: session[:teacher_id])
end
def current_parent
#current_parent ||= Parent.find_by(id: session[:parent_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?(teacher)
!current_teacher.nil?
end
def logged_in?(parent)
!current_parent.nil?
end
def log_out(teacher)
session.delete(:teacher_id)
#current_teacher = nil
end
def log_out(parent)
session.delete(:parent_id)
#current_parent = nil
end
end
I don't know the details of the your application, but I can explain the general case.
First of all, controller which has log in function is not have to be named sessions_controller, name is anything OK.
And Bcrypt is basically only a library for encrypting passwords. Main process is checking the password entered by user is valid or not without decrypting. There is no clear answer how to implement controller logic.
Apparently, user is divided into two types, Teacher and Parent, and probably each has different functions. So essentially, I want to divide the two login processes into separate controllers or actions. Because each login process is not the same one.
But Teacher and Parent will log in with the same URL if user have to login from the same page due to the UI restriction. If you are in such circumstances, implementing in the same controller & action will be appropriate.
After all, it depends on how to design your application. So your code is not always wrong.
However, look at your code, Teacher or Parent is judged only by e-mail, it is doubtful whether this is a proper approach. I have not seen many websites where users with different privileges log in from the same page.
I think that it is basically divide the login page depending on Teacher or Parent. If you divide the login page, example is as follows.
class TeachersController < ApplicationController
include SessionsHelper
def login
end
def login_action
teacher = Teacher.find_by(email: params[:teacher][:email])
if teacher && teacher.authenticate(params[:teacher][:password])
log_in teacher
flash[:notice] = 'Welcome!'
redirect_to root_path
else
flash[:notice] = 'Invalid log in information!'
redirect_to action: :login
end
end
def logout
teacher = Teacher.find(session[:teacher_id])
log_out teacher
redirect_to root_path
end
end
class ParentsController < ApplicationController
include SessionsHelper
def login
end
def login_action
parent = Parent.find_by(email: params[:parent][:email])
if parent && parent.authenticate(params[:parent][:password])
log_in parent
flash[:notice] = 'Welcome!'
redirect_to root_path
else
flash[:notice] = 'Invalid log in information!'
redirect_to action: :login
end
end
def logout
parent = Parent.find(session[:parent_id])
log_out parent
redirect_to root_path
end
end
Although this is not the main issue, did you write sessions_helper in the helpers directory?
Usually, helper is used to implement for view logic, so if you want to share method in controller, use ActiveSupport::Concern in the concerns directory.

find_by_* method is not returning the object

I am trying to set up a simple authentication for my rails application. I have a security_users scaffold and have created some users.
When, I am trying to log in using some of these accounts it seams that the "find_by_*" method is not able to detect the current one.
This is how my session controller looks like (I have comment the password check in purpose in order to debug the issue):
class SessionsController < ApplicationController
def new
end
def create
#security_user = SecurityUser.find_by_email(params[:email])
if #security_user #&& #security_user.authenticate(params[:password])
session[:security_user_id] = #security_user.id
redirect_to root_url, notice: "Logged in!"
else
flash.now.alert = "Email or password is invalid"
render 'new'
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, notice: "Logged out!"
end
end
So, when I try to create a session (to log in) I am redirect to the session 'new' template. This is the debug information:
which seems to be all right. Why the following statement could not find the record:
SecurityUser.find_by_email(params[:email])
EDIT:
When I entered the line above in the console it is returning the record:
First off, unless this is a simple exercise in Rails authentication, you should use Devise or AuthLogic at this stage.
Second, are you sure that params[:email] contains the email you are looking for? From your params, it looks to me like you want to use params[:session][:email].
Third, you should move this down into the model. For example:
class SecurityUser < ActiveRecord::Base
def self.authenticate(params)
user = where(email: params[:email]).first
(user && user.password == params[:password]) ? user : false
end
end
And in the controller:
#user = SecurityUser.authenticate params[:session]
session[:user_id] = user.id if #user
Note above that the password is not hashed - you should not save a plain text password - but that's not what this is about.
Also note that now you should use where().first instead of find_by.

Set Session Expiry - Rails 3

I have an application in which I used the tutorial offered by http://ruby.railstutorial.org/chapters/a-demo-app?version=3.0#sec:planning_the_application and http://ruby.railstutorial.org/chapters/sign-in-sign-out#sec-signin_failure (Rails 3.2) to create my user and session model.
I want to implement a expiration if user is in-active for 5 minutes or so. I have tried:
config.timeout_in = 5.minutes in my **application.rb**
and
config.session_store :cookie_store, key: 'blanked_out', :expire_after => 2.minutes in my **session_store.rb**
None of these seem to be working.
Any suggestions?
Sessions Helper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
self.current_user = user
end
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token])
end
Did u try this?
EDIT:
MAX_SESSION_TIME = {enter time}
before_filter :verify_session
def verify_session
if !session[:expire].nil? and session[:expire] < Time.now
# Session has expired. Clear or reset session.
reset_session
end
# Assign a new expiry time, whether the session has expired or not.
session[:expire] = MAX_SESSION_TIME.seconds.from_now
return true
end
I came across the following while researching a CookieStore issue. I think it answers your question:
https://www.coffeepowered.net/2013/09/26/rails-session-cookies/
Here's the code Chris Heald suggests for adding session timeout with CookieStore (note this would go in your application_controller.rb file:
class ApplicationController
before_filter :validate_session_timestamp
after_filter :persist_session_timestamp
SESSION_TTL = 48.hours
def validate_session_timestamp
if user? && session.key?(:ttl) && session[:ttl] < SESSION_TTL.ago
reset_session
current_user = nil
redirect_to login_path
end
end
def persist_session_timestamp
session[:ttl] = Time.now if user?
end
end

Rails remember_token session not working

I am new to Ruby on Rails. I am learning it through Michel Hartl's reference doc available on the internet. However i ran into a problem during sign-in of users, I suspect the before_save is not calling the create_remember_token method. I am trying to debug this issue from a very long time. My code is as follows:
user.rb file:
# == Schema Information
#
# Table name: users
#
# id :integer(4) not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# password_digest :string(255)
# username :string(255)
# remember_token :string(255)
#
class User < ActiveRecord::Base
def to_param
username
end
attr_accessible :name, :email, :password, :password_confirmation, :username
has_secure_password
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
session_helper.rb
module SessionsHelper
def sign_in(user)
cookies.permanent[:remember_token] = user.remember_token
current_user = user
end
def current_user=(user)
#current_user = user
end
def current_user
#current_user ||= user_from_remember_token
end
def signed_in?
!current_user.nil?
end
private
def user_from_remember_token
remember_token = cookies[:remember_token]
User.find_by_remember_token(remember_token) unless remember_token.nil?
end
end
sessions_controller.rb:
class SessionsController < ApplicationController
def new
end
def create
user=User.find_by_username(params[:session][:username])
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_to "/#/#{params[:session][:username]}"
else
flash.now[:error] = "Login failed! Please try again!"
render 'new'
end
end
def destroy
end
end
Please help me and let me know as to where the problem exists.
Cheers :)
What exactly is the problem that you are having during sign in?
The reason I ask is that the create_remember_token method is not supposed to be called during the sign-in process. It gets called when the user is saved (sign-up).
The sign-in process just fetches the token from the users table and copies it into the permanent cookie. The sign-in process looks roughly like this:
sign_in user --> session_helper.rb --> def sign_in(user) --> cookies.permanent[:remember_token] = user.remember_token --> back to session_controller.rb --> redirect_to ...
The before_save callbacks should only be called when the user is created (ie. when they sign up, not when they sign in). You don't show the whole sign-up/user creation flow in your code sample, but i assume it exists.
You can test whether create_remember_token is called (when the user is created) by running this command from the Rails console:
User._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:create_remember_token)
That should return true if and only if the create_remember_token function is being run on save. More details in the 'Debugging callbacks' section of the Callbacks API Documentation.
If the callback is called during the signup flow, the remember_token should be saved to the DB then. You can verify that by inspecting the user with the Rails console (User.last, after creating your test user).
What does your sign-up/user creation flow look like? What repro steps produce the problem you're describing?
You might have not used csrf meta tag in your layout. Try removing ' protect from forgery ' in application_controller.rb in controllers folder. Then try running the app again, if it works then you didnt add tags to your layout.
I had posted this question a year ago.
Now I am using the Devise gem for authentication. Everything is working as expected.
Thanks

How to create current user method without using any gem or plugin?

i know it's a silly one but i want to know how can we create a current_user method to get access throughout the app without using any gem or plugin ? To test it i created an app that make a user able to share files and folders.How to create such method that a user can only access his folder and files?Here is my code sample:
Login controller:
class LoginController < ApplicationController
layout 'signup'
#to skip checking the authentication and authorization.
skip_before_filter :check_authentication, :check_authorization
def index
end
def authenticate
if request.post?
user = User.authenticate(params[:username],params[:password])
if user
session[:current_user_id]=user.id
session[:name]= user.first_name
puts "session name #{session[:name]}"
redirect_to(:subdomain => user.company.subdomain, :controller => :dashboard)
else
flash.now[:notice] = "Invalid user/password combination"
end
end
end
def destroy
session[:current_user_id] = nil
reset_session
flash[:notice] = "You have been successfully logged out."
redirect_to root_url
end
end
User model:
require 'digest/sha1'
class User < ActiveRecord::Base
#sharering method start
after_create :check_and_assign_shared_ids_to_shared_folders
#this is to make sure the new user ,of which the email addresses already used to share folders by others, to have access to those folders
def check_and_assign_shared_ids_to_shared_folders
#First checking if the new user's email exists in any of ShareFolder records
shared_folders_with_same_email = SharedFolder.find_all_by_shared_email(self.email)
if shared_folders_with_same_email
#loop and update the shared user id with this new user id
shared_folders_with_same_email.each do |shared_folder|
shared_folder.shared_user_id = self.id
shared_folder.save
end
end
end
#to check if a user has acess to this specific folder
def has_share_access?(folder)
#has share access if the folder is one of one of his own
return true if self.folders.include?(folder)
#has share access if the folder is one of the shared_folders_by_others
return true if self.shared_folders_by_others.include?(folder)
#for checking sub folders under one of the being_shared_folders
return_value = false
folder.ancestors.each do |ancestor_folder|
return_value = self.being_shared_folders.include?(ancestor_folder)
if return_value #if it's true
return true
end
end
return false
end
#sharing method end
def self.authenticate(name, password)
user = self.find_by_username(name)
if user
expected_password = encrypt_password(password, user.salt)
if user.hashed_password != expected_password
user = nil
end
end
user
end
#'password' is a virtual attribute
def password
#password
end
def password= (pwd)
#password =pwd
return if pwd.blank?
create_new_salt
self.hashed_password = User.encrypt_password( self.password, self.salt)
end
def self.users_in_company(user_id)
User.find(user_id).company.users
end
private
def password_non_blank
errors.add(:password, "Missing password, please enter your password") if hashed_password.blank?
end
def create_new_salt
self.salt = self.object_id.to_s + rand.to_s
end
def self.encrypt_password(password, salt)
string_to_hash = password +"prftnxt" + salt
Digest::SHA1.hexdigest(string_to_hash)
end
end
i want to access all files as "current_user.files" is it possible without any gem?
Application helper:
module ApplicationHelper
#for current user to use through out the app
def current_user
#current_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id]) # Use find_by_id to get nil instead of an error if user doesn't exist
end
end
Application controller:
class ApplicationController < ActionController::Base
include UrlHelper
#include ApplicationHelper
helper_method :current_user #make this method available in views
protect_from_forgery
# def current_user
# #current_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id]) # Use find_by_id to get nil instead of an error if user doesn't exist
# end
end
and in task controller:
class TasksController < ApplicationController
# GET /tasks
# GET /tasks.xml
def index
#menu = "Timesheet"
#page_name = "Manage Task"
company_id = Company.find_by_subdomain(request.subdomain)
#users = User.find_all_by_company_id(company_id)
#tasks = current_user.tasks.all#Task.all
#task = Task.new
respond_to do |format|
format.html # index.html.erb
format.html # new.html.erb
format.xml { render :xml => #tasks }
end
end
end
and my error message i got:
NameError in TasksController#index
undefined local variable or method `current_user' for #<TasksController:0xfa7e638>
that's not so hard ;) just define the method you need:
class ApplicationController < ...
def current_user
#current_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id]) # Use find_by_id to get nil instead of an error if user doesn't exist
end
helper_method :current_user #make this method available in views
end
Hi friends i have found the way to create current_user method without using any gem or plugin:
In my application_helper.rb i did this :
module ApplicationHelper
def current_user
User.find(session[:current_user_id])
end
end
and at the end in my application controller.rb i called this, because from here i can access it through the application:
class ApplicationController < ActionController::Base
include UrlHelper
include ApplicationHelper
helper_method :current_user
end
and now i can access any data related to current user:
like :
#tasks = current_user.tasks
Thanks to all my friends for their valuable answers.