While implementing what I thought was a simple signup/login system for a Ruby on Rails app, results haven't matched what tutorials have shown.
I'm trying to use bcrypt for authentication and PostgreSQL for the database.
I continually get 'ActionController::ParameterMissing (param is missing or the value is empty: name): ', even though it will show name as being input. '"users"=>{"name"=>"asdf", "password"=>"Qq!1asdfasdf", "password_confirmation"=>"Qq!1asdfasdf"}, "commit"=>"Submit"} (0.1ms)
output from the console when attempting to sign in
users controller
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
The table
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
and the signup form
class UsersController < ApplicationController
def new
end
def create
user = User.new(
name: params[:name],
password: params[:password],
password_confirmation: params[:password_confirmation])
if user.save
session[:user_id] = user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
end
the user model
class User < ActiveRecord::Base
PASSWORD_FORMAT = /\A
(?=.{10,}) # Must contain 10 or more characters
(?=.*\d) # Must contain a digit
(?=.*[a-z]) # Must contain a lower case character
(?=.*[A-Z]) # Must contain an upper case character
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
#formatting for password
USERNAME_FORMAT = /\A[a-z0-9A-Z\-_]{2,15}\z/ #Can contain lowercase and upercase letters, numbers, - and _, must be between 2 and 15 length
#username formatting
validates :name,
:presence => true,
:uniqueness => true,
:format => USERNAME_FORMAT
validates :password,
:presence => true,
:format => PASSWORD_FORMAT,
:confirmation => true,
:on => create
has_secure_password
end
I've tried troubleshooting, all similar questions haven't yielded an answer or fix.
EDIT: More clarity on issue
You need to use rails Strong Parameter like the following
class UsersController < ApplicationController
def new
end
def create
user = User.new(user_params)
if user.save
session[:user_id] = user.id
redirect_to root_path
else
redirect_to new_user_path
end
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
end
I think it's a problem of passing data between your form and your controller.
In your logs your parameters for user looks like: "users"=>{"name"=> ...} but it should be "user"
To pass data between your controller and your view, you need to use instance variable such as #user to make the new instance of User available in the view. (source)
In that way your controller should be:
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/'
else
redirect_to '/signup'
end
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
(with strong parameters like #fool-dev explained)
Then in your view, use this #user to pass the parameters to the controller:
<%= form_for #user do |f| %>
//...the form
<% end %>
Related
In the Sign up form of my Rails 6 application an Account with a nested User can be created.
class AccountsController < ApplicationController
def new
#account = Account.new
#account.users.build(
:owner => true,
:language => "FR"
)
end
def create
#account = Account.new(account_params)
if #account.save
redirect_to root_url, :notice => "Account created."
else
render :new
end
end
private
def account_params
safe_attributes = [
:name,
:users_attributes => [:first_name, :last_name, :email, :password, :owner, :language]
]
params.require(:account).permit(*safe_attributes)
end
end
What is the best way to define default values on the new user here?
Right now, I use hidden_fields for the default values in my sign up form thus making them publicly available. This is of course not what I want because it's very insecure.
Is there a better way to deal with this?
I know that there's Rails' with_defaults method but I couldn't get it to work on nested items so far.
try with:
account_params[:users_attributes] = account_params[:users_attributes].with_defaults({ first_name: 'John', last_name: 'Smith'})
in first line of create action
users_controller.rb
class UsersController < ApplicationController
include UsersHelper
def new
#user = User.new
end
def create
if isUsernameTaken?
render 'new'
elsif isEmailTaken?
render 'new'
else
#user = User.new(user_params)
if #user.save
else
render 'new'
end
end
end
private
def user_params
params.require(:user).permit(:username,:email,:password,:password_confirmation)
end
end
users_helper.rb
module UsersHelper
def isUsernameTaken?
!(User.find_by(username: params[:username]).nil?)
end
def isEmailTaken?
!(User.find_by(email: params[:email]).nil?)
end
end
The problem is isUsernameTaken? or isEmailTaken? never gets executed and always the else part gets executed even if I give same username again. Why is this happening?
This is because params[:username] and params[:email] are always blank in your controller. They should be params[:user][:username] or user_params[:username].
Anyway, those kind of checks belongs to the model, not the controller and there already are validators to do exactly what you want:
class User < ActiveRecord::Base
validates :email, :username, uniqueness: true
end
I am getting an error when I pass in my url to reset my password for example: localhost:3000/password_reset/SADASIJDSIDJ1231231/edit <---- gives me ActiveRecord::RecordNotFound and its pointing at in my password_reset_controller
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
This is my application controller (notice the "include SessionsHelper")
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
#om du lägger till kod här kmr inte sessions funka när du är inloggad wtf???
#tog bort detta protect_from_forgery with: :exception
include SessionsHelper
protect_from_forgery
private
def current_user
#current_user ||= User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token]
end
helper_method :current_user
end
This is my password_reset_controller
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
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.permit![:user])
redirect_to root_url, :notice => "Password has been reset!"
else
render :edit
end
end
end
HOW I SOLVED IT:
So thanks to jorge I knew password_reset_token wasn't generated in the database. So I went back to my model/user.rb
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
I added back "save!" it was "save" before. Then I got another error saying password can't be blank.
So I deleted these two lines
# validates :password, length: { minimum: 6 }, presence: true
#validates :password_confirmation, presence: true
Instead I added this line
validates_presence_of :password, :on => :create
Now everything works. Thanks for your patience jorge. I owe u one big.
Did you check in the database that the a user exists with password reset token "SADASIJDSIDJ1231231"?
Looks like following line is not finding a user:
#user = User.find_by_password_reset_token!(params[:id])
Please include your User class - the send_password_reset method may not be saving the token correctly.
I set up a basic sign in, login, sign out format for a rails app and I was going to give it a function so that if a person forgot their password they could get a email back. when I went click on submit password reset I got
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
Extracted source (around line #3):
1: To reset your password click the URL below.
2:
3: <%= edit_password_reset_url(#user.password_reset_token) %>
4:
5: If you did not request your password to be reset please ignore this email and your password will stay as it is.
Rails.root: /Users/cheatermoves/nightclass/mainproject/project
Application Trace | Framework Trace | Full Trace
app/views/user_mailer/password_reset.text.erb:3:in `_app_views_user_mailer_password_reset_text_erb__3613112772785486465_70118994937040'
app/mailers/user_mailer.rb:6:in `password_reset'
app/models/user.rb:17:in `send_password_reset'
app/controllers/password_resets_controller.rb:7:in `create'
just completed rails cast 250 and was doing 274. Everything was fine until I got this problem.
here is my controllers. Password resets:
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
end
sessions:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token
else
cookies[:auth_token] = user.auth_token
end
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
cookies.delete(:auth_token)
redirect_to root_url, :notice => "Logged out!"
end
end
users:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
end
and application:
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#current_user ||= User.find_by_auth_token( cookies[:auth_token]) if cookies[:auth_token]
end
helper_method :current_user
end
in environments/development.rb I have
config.action_mailer.default_url_options = { :host => "localhost:3000" }
here is my user model
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
validates_presence_of :password, :on => :create
before_create { generate_token(:auth_token) }
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
end
user_mailer.rb file
class UserMailer < ActionMailer::Base
default from: "from#example.com"
def password_reset(user)
#user = user
mail :to => user.email, :subject => "Password Reset"
end
end
anyone know what the problem is and how to fix it? I'm using rails 3.2.14 if anyone is wondering. Thanks!
Didn't read the part where you said that you already set your
config.default_url_options[:host] = "localhost:3000"
my bad, sorry
I have a standard RESTful controller that uses strong parameters.
class UsersController < ApplicationController
respond_to :html, :js
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(safe_params)
if #user.save
redirect_to #user, notice: t('users.controller.create.success')
else
render :new
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(safe_params)
redirect_to #user, notice: t('users.controller.update.success')
else
render :edit
end
end
def destroy
#user = User.find(params[:id])
if current_user != #user
#user.destroy
else
flash[:error] = t('users.controller.destroy.prevent_self_destroy')
end
redirect_to users_url
end
private
def safe_params
safe_attributes =
[
:first_name,
:last_name,
:email,
:password,
:password_confirmation,
]
if current_user.is?(:admin)
safe_attributes += [:role_ids]
end
params.require(:user).permit(*safe_attributes)
end
end
In my config/initializers I have the file strong_parameters.rb
ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
When I add a simple call to CanCan's load_and_authorize_resource I get
1) UsersController POST create with invalid params re-renders the 'new' template
Failure/Error: post :create, user: #attr
ActiveModel::ForbiddenAttributes:
ActiveModel::ForbiddenAttributes
# ./spec/controllers/users_controller_spec.rb:128:in `block (4 levels) in <top (required)>'
Where #attr in the test is defined as
before(:each) do
#attr =
{
first_name: "John",
last_name: "Doe",
email: "user#example.com",
password: "foobar",
password_confirmation: "foobar"
}
end
In the tests I have it all setup properly to login the user and give them the necessary roles for being an administrator so I know it's not that. I don't know why this is causing ForbiddenAttributes to trigger. I'm sure it's something simple I've overlooked. Has anyone else encountered this problem and found a solution to it?
I believe this is because CanCan will use its own getter method for the requested resource if you don't pre-load it with a before_filter. So you could add this to the controller and it should work:
class UsersController < ApplicationController
before_filter :new_user, :only => [:new, :create]
load_and_authorize_resource
def new_user
#user = User.new(safe_params)
end
end
(And then do the same for the edit/update actions.)
before_filter do
params[:user] = safe_params
end
load_and_authorize_resource