Can't mass-assign protected attributes with ruby-on-rails - ruby-on-rails

I am trying to have a nested form on my users/new page, where it accepts user-attributes and also company-attributes. When you submit the form:
Here's what my error message reads:
ActiveModel::MassAssignmentSecurity::Error in UsersController#create
Can't mass-assign protected attributes: companies
app/controllers/users_controller.rb:12:in `create'
Here's the code for my form:
<%= form_for #user do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.fields_for :companies do |c| %>
<%= c.label :name, "Company Name"%>
<%= c.text_field :name %>
<% end %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<br>
<% if current_page?(signup_path) %>
<%= f.submit "Sign Up", class: "btn btn-large btn-primary" %> Or, <%= link_to "Login", login_path %>
<% else %>
<%= f.submit "Update User", class: "btn btn-large btn-primary" %>
<% end %>
<% end %>
Users Controller:
class UsersController < ApplicationController
def index
#user = User.all
end
def new
#user = User.new
end
def create
#user = User.create(params[:user])
if #user.save
session[:user_id] = #user.id #once user account has been created, a session is not automatically created. This fixes that by setting their session id. This could be put into Controller action to clean up duplication.
flash[:success] = "Your account has been created!"
redirect_to tasks_path
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#tasks = #user.tasks
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = #user.name.possessive + " profile has been updated"
redirect_to #user
else
render 'edit'
end
#if #task.update_attributes params[:task]
#redirect_to users_path
#flash[:success] = "User was successfully updated."
#end
end
def destroy
#user = User.find(params[:id])
unless current_user == #user
#user.destroy
flash[:success] = "The User has been deleted."
end
redirect_to users_path
flash[:error] = "Error. You can't delete yourself!"
end
end
Company Controller
class CompaniesController < ApplicationController
def index
#companies = Company.all
end
def new
#company = Company.new
end
def edit
#company = Company.find(params[:id])
end
def create
#company = Company.create(params[:company])
#if #company.save
#session[:user_id] = #user.id #once user account has been created, a session is not automatically created. This fixes that by setting their session id. This could be put into Controller action to clean up duplication.
#flash[:success] = "Your account has been created!"
#redirect_to tasks_path
#else
#render 'new'
#end
end
def show
#comnpany = Company.find(params[:id])
end
end
User model
class User < ActiveRecord::Base
has_secure_password
attr_accessible :name, :email, :password, :password_confirmation
has_many :tasks, dependent: :destroy
belongs_to :company
accepts_nested_attributes_for :company
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: { minimum: 6 }
#below not needed anymore, due to has_secure_password
#validates :password_confirmation, presence: true
end
Company Model
class Company < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :users
end

First for debugging put the bang on the create in the two controllers like so: create! Your log may spit out more.
Then, if that sucked, try it the old fashioned way of Building the two Objects and assigning each one with the params.
I assume also that this is it for attributes, no after saves that should have more to the schema then you are showing.
Lastly, you are missing
def new
#user = User.new
#company = #user.companies.build
end
Print out the params too, just in case it says something wonky but adding this line: as #Beerlington said should work too :company_attributes, maybe companies_attributes... spit balling here.

Add attr_accessible :companies to your user model attr list

Related

Upon submitting sign up form, ActiveModel::ForbiddenAttributesError

I've created a sign up form on rails, and upon clicking my submit button to save my user details, I get the following error:
ActiveModel::ForbiddenAttributesError
for this line: #user = User.new(params[:user])in my users controller.
See code below:
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
new.html.erb
<%= form_for(:user, :url => {:controller => 'users', :action => 'create'}) do |f| %>
</br> <%= f.text_field :firstname, placeholder: 'First Name' %>
</br> <%= f.text_field :lastname, placeholder: 'Last Name' %>
</br> <%= f.text_field :email, placeholder: 'Email' %>
</br> <%= f.password_field :password, placeholder: 'Password' %>
</br> <%= f.password_field :password_confirmation, placeholder: 'Confirm Password' %>
<%= f.submit :Register %>
<% end %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
</div>
routes.rb
Rails.application.routes.draw do
get 'users/new'
get 'pages/home'
get 'pages/howitworks'
get 'pages/about'
get 'pages/contact'
get 'pages/becomeauser'
get 'signup' => 'users#new'
resources :users
The params[:user] hash might have attributes in it that isn't a part of the User model. In your case, i'm guessing its password_confirmation.
Use strong params the next time - here
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:notice] = "You signed up successfully"
flash[:color]= "valid"
else
flash[:notice] = "Form is invalid"
flash[:color]= "invalid"
end
render "new"
end
end
private:
def user_params
params.require(:user).permit(:first_name, :last_name, :email, : password)
end
This means that you have Strong Parameters enabled. I would encourage you to read at least a part of this Rails Guide. Essentially, instead of using params[] to update or create records, you should use an intermediate method to ensure that users aren't sneaking information into your requests. This is much safer. For instance, someone could submit a "created_at" attribute with your form, and then be modifying fields which are not supposed to be edited. Your approach should instead be as follows:
Create a private user_params method in the controller which permits specified attributes
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
Replace your User.create(), User.new(), and User.update() references to params[:user] with user_params
# Do this instead of #user = User.new(params[:user])
#user = User.new(user_params)

How to solve strong parameters' contradiction when editing an object?

The problem I'm facing most likely has something to do with strong parameters. The thing is when I try to edit some user information and update it, an error appears which is not related with this form. That kind of error's supposed to pop up only when signing up or logging in.
For example, here is my database. And then I click 'Edit'.
After editing some information and submitting it, the error pops up.
controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to [#user, #task], notice: "Thank you for signing up!"
else
render "new"
end
end
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to {|format| format.js }
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
views/user/edit.html
<h1>Editing user</h1>
<%= form_for :user, url: #user, method: :patch do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#user.errors.count, "error") %> prohibited
this task from being saved:
</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :first_name %><br>
<%= f.text_field :first_name %>
</p>
<p>
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</p>
<p>
<%= f.submit %>
</p>
<%= link_to 'Back to List', users_path %>
<% end %>
models/user.rb
class User < ActiveRecord::Base
has_secure_password
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :first_name, presence: true,
length: {maximum: 20}
validates :last_name, presence: true,
length: {maximum: 40}
validates :email, presence: true,
format: {with: EMAIL_REGEX},
uniqueness: {case_sensitive: false}
validates :password,
length: {within: 6..40}
has_many :tasks
end
What do I do? Can anybody help?
The problem is not with the strong params. It is due to validation you have on password.
This
validates :password,length: {within: 6..40}
should be
validates :password,length: {within: 6..40}, on: :create
Yes, the problem is in strong params. You should never permit password and password_confirmation
def user_params
params.require(:user).permit(:first_name, :last_name, :email)
end
Just remove password and password_confirmation from user_params. You wouldn't want to mass assign those values.
And this would be a very bad practice to store a plain password in database. You should store encrypted or hashed password in database; it would enhance the security and integrity of your application.
To get more information about storing an encrypted or hashed password, please take a look at this and this links.

Can't log in to my database. Error: "Param is missing or the value is empty: user"

I'm having a trouble with my logging in. It highlights the user_params function as an error in users controller. What could it be?
/views/sessions/new.html.erb or login form
<h1>Log In</h1>
<%= form_tag users_path do %>
<div class="field">
<%= label_tag :email %><br />
<%= text_field :email, params[:email] %>
</div>
<div class="field">
<%= label_tag :password %><br />
<%= password_field_tag :password %>
</div>
<div class="actions"><%= submit_tag "Log In" %></div>
<% end %>
controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = 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 signup_path, notice: "Logged out!"
end
end
controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to user_tasks, notice: "Thank you for signing up!"
else
render "new"
end
end
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
def authorize
redirect_to login_url, alert: "Not authorized" if current_user.nil?
end
end
rake routes
Generated parameters
{"utf8"=>"✓", "authenticity_token"=>"jtdA0rITCiFjU9ECiKnDUMh/MHMpqv6u+Bd65PAfpARwYsmb9Dli32wpULfmYa7wpBQiFYxAGHF6flCfJgPOew==", "email"=>"dsadsa", "password"=>[FILTERED]", "commit"=>"Log In"}
Any help?
change this line and try please.
<%= text_field :email, params[:email] %>
to
<%= text_field_tag :email, params[:email] %>
Also majorly add this in your routes file to redirect it to sessions controller create action.
post '/authenticate' => 'sessions#create'
change this tag
<%= form_tag users_path do %>
to
<%= form_tag /authenticate do %>
When you look into the params, they are not coming from the user. Try changing your user_params like below
def user_params
params.permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
I suggest you keep the :user namespace for params in your form
<%= form_tag users_path do |form| %>
<%= fields_for :user do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email, params[:email] %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="actions"><%= submit_tag "Log In" %></div>
<% end %>
<% end %>
So you can still use params.require(:user) in your controller as usual
Parameters will look like this
{
"utf8"=>"✓",
"authenticity_token"=>"jtdA0rITCiFjU9ECiKnDUMh/MHMpqv6u+Bd65PAfpARwYsmb9Dli32wpULfmYa7wpBQiFYxAGHF6flCfJgPOew==",
"user" => {
"email"=>"dsadsa",
"password"=>[FILTERED],
},
"commit"=>"Log In"
}

Adding Email Confirmation to Rails App

I followed Michael Hartl's tutorial up to a point, then followed Ryan Bates' Railcast for adding reset password (#274). Perhaps "tried to follow" is more accurate since I can't get the reset password to work despite many hours of effort/searching/tial&error.
Had to deviate from Ryan's call: #user.update_attributes(params[:user]) to: #user.update_attributes(user_params) as I was getting a ForbiddenAttributesError.
I also had to comment-out the #validates :password, length: { minimum: 6 } in the User class as it failed on submission (even though the submitted password was greater than 6 in length).
The code doesn't currently generate any errors, and the redirect_to root_url, :notice => "Password has been reset!" works. However, the password is not updated in the db.
Any insight is very gratefully received.
app/models/user.rb
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
has_secure_password
#validates :password, length: { minimum: 6 }
before_create :create_remember_token
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.digest(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64(40)
end while User.exists?(column => self[column])
end
def create_remember_token
self.remember_token = User.digest(User.new_remember_token)
end
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :signed_in_user, only: [:index, :edit, :update]
before_action :correct_user, only: [:edit, :update]
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
sign_in #user
flash[:success] = "Welcome!"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :password_reset_token)
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
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
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(:id, :name, :email, :created_at, :updated_at, :password_digest, :remember_token, :admin, :auth_token, :password_reset_token, :password_reset_sent_at)
end
end
app/views/password_resets/edit.html.erb
<%= 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 %>
app/views/password_resets/new.html.erb
<h1>PasswordReset#new</h1>
<%= form_tag password_resets_path, :method => :post do %>
<div class="field">
<%= label_tag :email %>
<%= text_field_tag :email, params[:email] %>
</div>
<div class="actions"><%= submit_tag "Reset Password" %></div>
<% end %>
There is no model for password_resets.
In app/views/password_resets/edit.html.erb, your form describes :password and :password_confirmation. However, in your PasswordResetsController, you update using user_params, which describe :id, :name, :email, :created_at, :updated_at, :password_digest, :remember_token, :admin, :auth_token, :password_reset_token, :password_reset_sent_at. Note the lack of the attributes you are trying to set. In the console, you will probably see a message indicating that the desired attributes have been discarded. Many of the attributes that you have added to user_params do not make sense in any context, as they would never be set by the user in any circumstance, like :id, :created_at, :updated_at, :password_digest, :remember_token, :admin, :auth_token, :password_reset_token, :password_reset_sent_at. In fact, may of these are blatant security risks (do you want the user to decide if they are an admin?). You will probably have better luck if you change the user_params method in PasswordResetsController to look like:
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
Make sense?

Added Bio field to 'edit profile' - created migration but then what to add to model?

I've followed Hartl's rails tutorials to the of Chap 9 and now i'm building my own idea so i can get deep into rails.
Current issue - I've added a Bio filed to user profiles in edit and the text box appear on the Users profile but i can't figure out how to save the text added to the Bio text box. I've generated a migration called "add_bio_to_user_profile" (haven't raked yet) but i'm struggling to figure out what to add to the User.rb model. Does controller come into this to?
Migration "add_bio_to_user_profile"
class AddBioToUserProfile < ActiveRecord::Migration
def change
add_column :user_profiles, :, :string
end
end
Model/User.rb
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
before_create :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
#VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
#format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
def User.new_remember_token
SecureRandom.urlsafe_base64
end
def User.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end
private
def create_remember_token
self.remember_token = User.encrypt(User.new_remember_token)
end
end
controllers/user_controller.rb
class UsersController < ApplicationController
before_action :signed_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
end
def new
#user = User.find(params[:id])
end
def new
#user = User.new
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_url
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
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
show.html.erb
<% provide(:title, #user.name) %>
<div class="row">
<aside class="span4">
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
<div>
<textarea rows="3"></textarea>
</div>
</aside>
</div>
edit.html.erb
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :bio %>
<textarea rows="3"></textarea>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>
<%= gravatar_for #user %>
change
</div>
</div>
Formally, you dont have to do anything. Rails magic starts here. You run the migration, restart server if you are running production env, change the name of textbox to :bio in edit html.erb
<%=f.text_field :bio%>
When this form is submitted, it automatically gets saved in the table. Additionally, you can add validation on this field in model.
In edit.html.erb, make this change
<%= f.label :bio %>
<%=f.text_field :bio%>
and in show.html.erb
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
Bio: <%= #user.bio %>
One change in controller.rb
def user_params
params.require(:user).permit(:name, :email, :bio, :password,
:password_confirmation)
end
You want to add bio field to users table I guess.
Here is the migration.
class AddBioToUserProfile < ActiveRecord::Migration
def change
add_column :users, :bio, :string
end
end
While you are going for a migration, only model comes to play. and in this,
users is your table name and bio is the new column and string is the data-type.

Resources