Multiple require & permit strong parameters rails 4 - ruby-on-rails

In the below case, i am trying to use strong parameters. I want to require email_address, password and permit remember_me fields.
But using like below it only allows the LAST line in the method Ex:- In below case it only take params.permit(:remember_me)
private
def registration_params
params.require(:email_address)
params.require(:password)
params.permit(:remember_me)
end
Another Ex:- In this below case, if i rearrange it like below it will take only params.require(:email_address) where am i going wrong ?
def registration_params
params.require(:password)
params.permit(:remember_me)
params.require(:email_address)
end
UPDATE
Params hash be like
{
"utf8" => "✓",
"email_address" => "test1#gmail.com",
"password" => "password123",
"remember_me" => "true",
"commit" => "Log in",
"controller" => "registration",
"action" => "sign_in"
}

Ok found the answer through a friend ...one way to do this is
params.require(:email_address)
params.require(:password)
params.permit(
:email_address,
:password,
:remember_me
)
Works good.

Stong parameters are to prevent mass-assignment to Active Record models. Your parameters should be set up in a model backed form. Example from the Michael Hartl Tutorial:
REGISTRATION FORM
<%= form_for(#user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
This will create a parameter that looks like:
PARAMS
{
"utf8" => "✓",
"user" => { email: "test1#gmail.com", name:"Test Name", password: "password", password_confirmation: "password" },
"remember_me" => "true",
"commit" => "Log in",
"controller" => "registration",
"action" => "sign_in"
}
Then in your registration controller you can use strong parameters like:
STRONG PARAMETERS
params.require(:user).permit(:name, :email, :password, :password_confirmation)
It looks like in your case, you are handling a log in, in which case, you only need to use regular parameters to capture the login information.
SESSION CREATION
def sign_in
email = params[:email]
password = params[:password]
if User.authenticate!(email, password)
# do something
else
# do something different
end
end

Edit:
Here is the Rails way for you to handle logins and, I believe, cases where you need to 'require' multiple parameters and provide errors back to the user.
Unlike using strong params, this approach provides feedback to the user (using validation errors) when parameters are missing or blank. This is more user-friendly than throwing an exception.
Create an ActiveModel (not ActiveRecord) form backing object. This form backing object is where you specify which fields are required and when a call to valid? is performed, these fields will be validated.
With this, you will get nice user-friendly errors if:
email is missing
password is missing
email and password do not match
models/session.rb
class Session
include ActiveModel::Model
attr_accessor :password, :email, :remember_me
validates_presence_of :password, :email # these fields are required!
def authenticate
return false unless valid? # this checks that required params
# are present and adds errors to the
# errors object if not
if User.authenticate(:password, :email) # validate credentials
true
else
errors.add(:email, "and password didn't match") # wrong credentials. add error!
false
end
end
end
Create the controller. Here is what your controller would look like for logging in a user:
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
# GET /login
def new
#session = Session.new
end
# POST /login
def create
#session = Session.new(login_params)
if #session.authenticate
# do whatever you need to do to log the user in
# set remember_me cookie, etc.
redirect_to '/success', notice: 'You are logged in'
else
render :new # shows the form again, filled-in and with errors
end
end
private
def login_params
params.require(:session).permit(:email, :password, :remember_me)
end
end
Set up the view
app/views/sessions/new.html.erb
<% if #session.errors.any? %>
<ul>
<% #session.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= form_for #session, :url => login_path do |f| %>
<div>
<%= f.label :email, 'Email:' %>
</div>
<div>
<%= f.text_field :email %>
</div>
<div>
<%= f.label :password, 'Password:' %>
</div>
<div>
<%= f.password_field :password %>
</div>
<div>
<%= f.label :remember_me, 'Remember Me?' %>
<%= f.check_box :remember_me %>
</div>
<div>
<%= f.submit %>
</div>
<% end %>
Lastly, make sure the routes are configured
config/routes.rb
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'

2020 solution:
def registration_params
params.require([:email_address, :password]) #require all of these
params.permit(:email_address, :password, :remember_me) #return hash
end

Related

Devise Rails Password

I have a new application that the root user create another users, when i create the user with the model.
#user = User.new(:email => params[:email], :password => params[:password], :password_confirmation => params[:password_confirmation])
#user.save!
The users saves "correctly" on the table on "encrypted_password" the value and the others fields too, but when i try to login i have an error
"Invalid Email or Password" so when i read the record the password is.
encrypted_password: $2a$11$wFnpiA.l9HezNXfnAGkttuu2IGIXByETytLrEkdDsa8sBFrc8Bdmq
But i used another password the root password that actually works.
But on the table is :
encrypted_password: $2a$11$VKOAUk5pjILU1QHYmkpJSem9KKm70QJPS7Oj.nPM/pTuyu1tqZaQO
So, my model of the devise is not saving the correct encryption of the password.
My TestController:
class UsersController < ApplicationController
before_action :authenticate_user!
def index
#Users = User.all
end
def create
begin
#user = User.new(:email => params[:email], :password => params[:password_first], :password_confirmation => params[:password_confirmation])
#user.save!
flash[:notice] = "Usuario creado correctamente"
redirect_to action: 'index'
rescue Exception => e
flash[:alert] = "Error al crear al Usuario: " + e.message
redirect_to action: 'index'
end
end
end
What am i doing wrong?
Regards
BCrypt goes out of its way to scramble the password as much as possible to make it extremely difficult to reverse that hashing operation. For any given input string to BCrypt::Password.create there are around 680 trillion-trillion-trillion possible output strings (2128), so it's unlikely you'll ever get the same one twice:
hashes = 10.times.map { BCrypt::Password.create('test') }
# => [
# "$2a$10$vMrgjJHqvwnEKIs0fZ76pO3gbWL/0C3ExqK9HOpi/mHYu2.4GAO2K",
# "$2a$10$KxBOarDzRPHp7QF1GGqNnuplRs1B5rNVfp21IHx1/HzQ0YIcIkLRW",
# "$2a$10$emCdZAA.GU8GwQZkeJLfAuUTY2aEnhFmZ.GQAhDpJ.JGSh/m6s/k2",
# "$2a$10$6R6xmGyK7Tb1MKsQb00vpOJKwpi56aj98JLoBJhBN4vWSQb7zagQm",
# "$2a$10$r4qmb.C.vm88pL2nJK5TdOaWIboYaO6a1xHIRH.QDER6qYR6Ajvo.",
# "$2a$10$mlVWz4IHTgYHSf3tAgEgpenpDHtGWYev4EUENLs7hnLlm6ikPhUxy",
# "$2a$10$ixXdZZuc9rIVAozO8tyq5.wlsVOWBc6QWetNh3PvjPj2pGlqh.XOy",
# "$2a$10$zLzuevtOl.g4RbaHpdeTZ.k4qjE/1m4nh6gN4mhcIKQPSa5sBcG5u",
# "$2a$10$F/F71.DYEuzxS4W0w5m/a.IRpaVJxeh9sKUJ7DyQb5xU3SvFu1Ib.",
# "$2a$10$ILXg8R52ZtHHbQbT0FxSFOj8YNqpNLmrH.6FhM3RGMwIuBeP1YXHa" ]
This is alright though since the verification routine can handle checking password:
hashes.map { |h| BCrypt::Password.new(h) == 'test' }
# => [true, true, true, true, true, true, true, true, true, true]
They all match. It's important to note that verifying ten passwords does take a small but noticeable amount of time. This is what make BCrypt particularly well suited to password storage: Guessing is expensive so throwing huge dictionaries of words at a hash to see which match is extremely difficult. This is not the case with weaker hashes like MD5 or SHA1 where a million operations per second is completely feasible. BCrypt is deliberately about a half million times slower.
Start with a regular decent Rails crud setup and see if the issue does not solve itself.
# routes.rb
resources :users, only: [:create, :index]
class UsersController < ApplicationController
before_action :authenticate_user!
# GET /users
def index
#Users = User.all
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Usuario creado correctamente"
redirect_to action: :index
else
render :new
end
end
def user_params
params.require(:user)
.permit(:email, :password, :password_confirmation)
end
end
# app/views/users/_form.html.erb
<% form_for(#user || User.new) do |f| %>
<% if f.object.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(f.object.errors.count, "error") %> prohibited this article from being saved:</h2>
<ul>
<% f.object.errors.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.email_field :email %>
</div>
<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>
<% end %>
# app/views/users/new.html.erb
<%= render partial: 'form' %>
You can embed the partial in your root view or the users index by:
<%= render partial: 'users/form' %>
Note that you should render and not redirect if the record is invalid. If you redirect the users input and any validation messages you want to show are gone.

No route matches {:action=>"partner_update", :controller=>"users"}

i'm new in rails so i tried to do this to add a role, making a form to update and if the user update succsessfully add the role.
note: i'm using devise and rolify with cancancan
routes
get 'users/becomepartner' => 'users#becomepartner' do
collection do
patch 'update_partner'
end
end
and this is my users controller
def becomepartner
#user = current_user
end
def update_partner
#user = User.find(current_user.id)
if #user.update_with_password(partner_params)
self.add_role(:partner)
# Sign in the user by passing validation in case their password changed
bypass_sign_in(#user)
redirect_to root_path, notice: 'now you can create'
else
render :becomepartner
end
end
private
def partner_params
# NOTE: Using `strong_parameters` gem
params.require(:user).permit(:name, :style, :current_password, :email)
end
and this is my view becomepartner.html
<div>
<%= form_for(#user, :url => { :action => "update_partner" } ) do |f| %>
<div class="field">
<%= f.label :style %>
<%= f.text_field :style %>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.email_field :email %>
</div>
<div class="field">
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i>
<%= f.password_field :current_password, autocomplete: "off" %>
</div>
<% end %>
</div>
In form action you have mentioned action as partner_update but you don't have that action. Change it to update_partner.
You are using the collection route for another individual route, that is get 'users/becomepartner'. So this will generate the collection route with url like:
users/becomepartner/update_partner
You can confirm this by running rake routes.
But your forms action url will be different.
What you can do is create this collection route inside users resource route like:
resources :users do
collection do
patch 'update_partner'
end
end
Then in form_for url option, use update_partner_users_path
If you don't want the users resources routes, then just define a single route like:
get 'users/becomepartner' => 'users#becomepartner'
patch 'user/update_partner' => 'users#update_partner'
I have not ran any of this code, so if there is any syntax error then post it in comment.
In routes.rb,change
patch 'update_partner' to update 'update_partner' and remove collection as you are not operating on a collection.you have added it in a do block,instead add a one liner route path.
in the form,url must be update_partner and NOT partner_update .

Creating form_for for atributes User image and description

I have a rails application with devise, and I added to users a profile image, and a description. What I want to do is to create a page (DIFFERENT of the default registration/edit) where the users, after logged in, can set only this two atributes(image and description).
<%= form_for(:user, html: { method: :put, :multipart => true })) do |f| %>
<div class="form-group">
<%= f.label :Profile_Image %>
<%= f.file_field :image, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :Descrição %>
<%= f.text_area :description, class: "form-control", rows: "10" %>
</div>
<% end %>
I have already tried two different controllers and none of them worked:
def edit
end
def edit
#user = User.find(params[:id])
end
My config -> routes are:
get "edit" => "pages#edit"
post "edit" => "pages#edit"
But when i click submit it does nothing! I am new at rails and I am trying to figure this out for hours... How can I create a page to update only the image and the description? Thanks
You need an update method in your controller. Your edit method allows the form to render, but you need something like this:
def update
current_user.update(user_params)
end
Then you would have another method in your controller called user_params, which would look something like this. I was taught to put it under a private heading.
private
def user_params
params.require(:user).permit(:profile_image, :description)
end
I believe there is a shortcut way of including your params with your update method, but this will do.
Use registration controller devise and you should customize it.
You should have one method with the same name in one controller, you have two edit method. Change one edit method to update method ( reference : Allow users to edit their account )
pages_controller.rb
class PagesController < Devise::RegistrationsController
def edit
#user = current_user
end
def update
#user = current_user
successfully_updated = if needs_password?(#user, params)
#user.update_with_password(devise_parameter_sanitizer.for(:account_update))
else
params[:user].delete(:current_password)
#user.update_with_password(devise_parameter_sanitizer.for(:account_update))
end
if successfully_updated
set_flash_message :notice, :updated
# Sign in the user bypassing validation in case his password changed
sign_in #user, :bypass => true
redirect_to after_update_path_for(#user)
else
render "edit"
end
end
private
def needs_password?(user, params)
user.email != params[:user][:email] || params[:user][:password].present?
end
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:description, :image, :email, :password, :password_confirmation)
end
end
end
You have wrong http verb (post), you need PUT/PATCH not POST
devise_scope :user do
get "edit" => "pages#edit"
put "update" => "pages#update"
end
On your view looks like (example and not tested)
<%= form_for(#user, :url => update_pages_path, :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<div class="form-group">
<%= f.label :image, "Profile Image" %>
<%= f.file_field :image, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label description, "Descrição" %>
<%= f.text_area :description, class: "form-control", rows: "10" %>
</div>
<% end %>
<%= f.submit "Save Image" %>

Rails 4: Password Validation Error: "{:password=>["can't be blank"]}" even though form is filled out

I'm relatively new to Rails (using Rails 4), and am having a problem with validation for my user model. Even when the form is fully filled in with both the passwords, when I submit the code two errors print out:
{:password=>["can't be blank"], :password_confirmation=>["doesn't match Password"]}
I would like the user to be saved into the database, but these validation errors are preventing that from happening. What I would like to know is what I need to change in order to get rid of these errors.
I am printing out the params object and it looks like this (the authenticity token is omitted here):
params: {"utf8"=>"✓","authenticity_token"=>"[omitted]",
"user"=>{"username"=>"testuser1", "password"=>"test",
"password_confirmation"=>"test", "email_attributes"=>{"email"=>"d#d.com"},
"first_name"=>"test", "last_name"=>"user", "gender"=>"male", "city"=>"la",
"state"=>"ca", "country"=>"usa", "dob"=>"1980-11-20"},
"commit"=>"Create Account", "action"=>"create", "controller"=>"users"}
So it appears that the password and password_confirmation attributes are getting passed correctly. I am wondering if this may have to do with the virtual attribute password I have defined in the user model, but if that is the case I am still not quite sure how to solve this problem. Any help would be greatly appreciated. Let me know if I need to elaborate further.
Here is relevant code for reference:
Controller:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_email
end
def create
if #user = User.create(user_params)
logger.debug "#{#user.errors.messages}"
logger.debug "params: #{params}"
redirect_to :action => "new"
else
logger.debug "#{#user.errors.messages}"
logger.flush
redirect_to :action => "new"
end
end
private
def user_params
params.require(:user).permit(:username, :password, :password_confirmation, :first_name, :last_name, :gender, :dob, :city, :state, :country, :admin_level, email_attributes: [:email])
end
end
Model:
class User < ActiveRecord::Base
has_one :email
validates_presence_of :username, :email, :password
validates_confirmation_of :password, :on => :create
accepts_nested_attributes_for :email
def password_valid?(candidatePass)
candidatePassAndSalt = "#{candidatePass}#{self.salt}"
candidatePasswordDigest = Digest::SHA1.hexdigest(candidatePassAndSalt)
if (candidatePasswordDigest == self.password_digest)
return true
else
return false
end
end
def password
end
def password=(text)
self.salt = Random.new.rand
passAndSalt = "#{text}#{self.salt}"
self.password_digest = Digest::SHA1.hexdigest(passAndSalt)
end
end
View:
<%= form_for #user, url: {action: "create"}, html: {class: "user-creation-form"} do |f| %>
<%= f.text_field :username %>username<br/>
<%= f.password_field :password %>pw<br/>
<%= f.password_field :password_confirmation %>pwcopy<br/>
<%= f.fields_for :email do |email_form| %>
<%= email_form.text_field :email %>email<br />
<% end %>
<%= f.text_field :first_name %>first<br/>
<%= f.text_field :last_name %>last<br/>
<%= f.radio_button :gender, "male" %>
<%= f.label :gender_male, "M" %>
<%= f.radio_button :gender, "female" %>
<%= f.label :gender_female, "F" %><br />
<%= f.text_field :city %>city<br/>
<%= f.text_field :state %>state<br/>
<%= f.text_field :country %>country<br/>
<%= f.date_field :dob %>dob<br/>
<%= f.submit "Create Account" %><br/>
<% end %>
The issue is your empty getter:
def password
end
It always return nil.
2 small additions to the previous answer, which should resolve your issue by the way.
1) If you're using Rails >3 (I assume you are by looking at your user_params method in the controller) you don't have to specify all those password fields and validations.
ActiveRecord automatically includes this ActiveModel method :
has_secure_password
More details at : http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password
2) If the uncrypted password/password_confirmation are shown in your log files your app is insecure. Add this to your config/application.rb :
config.filter_parameters = [:password, :password_confirmation]
This should not be needed if you are using has_secure_password in your User model.

Pass model between controllers and views in Ruby on Rails

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

Resources