Password, Password_Confirmation ActiveModel::MassAssignmentSecurity::Error: - ruby-on-rails

I'm working through the Rails Tutorial and I've gotten stuck. I'm trying to use a password and password_confirmation.
I'm getting the error(s):
15) User when password confirmation is nil
Failure/Error: #user = User.new(name: "Example User", email: "user#example.com", password: "foobar", password_confirmation: "foobar")
ActiveModel::MassAssignmentSecurity::Error:
Can't mass-assign protected attributes: password, password_confirmation
# ./spec/models/user_spec.rb:5:in `new'
# ./spec/models/user_spec.rb:5:in `block (2 levels) in <top (required)>'
Finished in 0.21758 seconds
25 examples, 15 failures
Failed examples:
rspec ./spec/models/user_spec.rb:8 # User
rspec ./spec/models/user_spec.rb:9 # User
rspec ./spec/models/user_spec.rb:10 # User
rspec ./spec/models/user_spec.rb:11 # User
rspec ./spec/models/user_spec.rb:12 # User
rspec ./spec/models/user_spec.rb:14 # User
rspec ./spec/models/user_spec.rb:17 # User when name is not present
rspec ./spec/models/user_spec.rb:21 # User when name is too long
rspec ./spec/models/user_spec.rb:25 # User when email format is invalid should be invalid
rspec ./spec/models/user_spec.rb:33 # User when email format is invalid when email format is valid should be valid
rspec ./spec/models/user_spec.rb:47 # User when email address is already taken
rspec ./spec/models/user_spec.rb:55 # User when email address is already taken
rspec ./spec/models/user_spec.rb:59 # User when password is not present
rspec ./spec/models/user_spec.rb:63 # User when password doesn't match confirmation
rspec ./spec/models/user_spec.rb:67 # User when password confirmation is nil
All of the errors are for the same reason.
User.rb
class User < ActiveRecord::Base
attr_accessible :email, :name
before_save { |user| user.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
has_many :event
end
user_spec.rb
require 'spec_helper'
describe User do
before do
#user = User.new(name: "Example User", email: "user#example.com", password: "foobar", password_confirmation: "foobar")
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest) }
it { should respond_to(:password) }
it { should respond_to(:password_confirmation) }
Any ideas would be appreciated.

Add password, :password_confirmation to attr_accessible in user.rb
attr_accessible :name, :email, :password, :password_confirmation
attr_accessible method takes list of attributes to be accessible. the other attribute will be protected see Mass Assignment for the reason.

Related

Testing conditional validation with controller inputs in Rails

I have custom validator for password that takes a updating_password field from the controller
attr_accessor :updating_password
validates :password, presence: true, if: :should_validate_password?
validates :password, length: { minimum: 6 }, if: :should_validate_password?
def should_validate_password?
updating_password || new_record?
end
I want to stub out the updating_password field in my User model RSpec test, something like
before(:each) do
#user_valid = FactoryGirl.create(:user)
end
it "validates for password when updating_password is true" do
old_password = #user_valid.password
subject { #user_valid }
allow(subject).to receive(:updating_password).and_return(true)
#user_valid.update(password: "short", password_confirmation: "short")
expect(#user_valid.password).to eql(old_password)
end
The password should not be updated in this case because it is too short but the test is failing. Any help would be appreciated
I'd recommend not stubbing out your model validations. Instead you might test what you're trying to do like this
# spec/models/user_spec.rb
describe User do
describe 'validations' do
context 'while updating password' do
let(:user){ FactoryGirl.create(:user, updating_password: true) }
it 'requires password to be at least 6 characters long' do
expect {user.update!(password: 'short')}.to raise_error(ActiveRecord:RecordInvalid)
end
it 'requires password to be present' do
expect {user.update!(password: nil))}.to raise_error(ActiveRecord:RecordInvalid)
end
end
end
end

Shoulda-matchers test with FactoryGirl, password_confirmation not set?

I am testing my account model with shoulda-matchers using a entity created with FactoryGirl.
The code of both files look like this:
TEST FILE
require 'spec_helper'
describe Account do
before { #account = FactoryGirl.build(:account) }
subject { #account }
it { should validate_presence_of(:email) }
it { should validate_presence_of(:password) }
it { should validate_presence_of(:password_confirmation).
with_message(#account.password_confirmation+' '+#account.password) }
it { should allow_value('example#domain.com').for(:email) }
it { should be_valid }
end
FACTORY
FactoryGirl.define do
factory :account do
email { FFaker::Internet.email }
password "12345678"
password_confirmation "12345678"
end
end
My error is the following:
1) Account should require password_confirmation to be set
Failure/Error: it { should validate_presence_of(:password_confirmation).
Expected errors to include "12345678 12345678" when password_confirmation is set to nil, got errors: ["password_confirmation can't be blank (nil)"]
# ./spec/models/account_spec.rb:9:in `block (2 levels) in <top (required)>'
rspec ./spec/models/account_spec.rb:9 # Account should require password_confirmation to be set
I am using devise which should check for password confirmation. I know its probably because of something stupid, but I really can't figure out what's wrong with it.
Devise, doesn't validate the presence of password_confirmation it just validates the confirmation of password see Validatable line 34: https://github.com/plataformatec/devise/blob/v3.5.3/lib/devise/models/validatable.rb#L34
validates_confirmation_of :password, if: :password_required?
EDIT: You could also see that there are no validators on password_confirmation running: User.validators_on(:password_confirmation)

Seeding fails with validation error

To my User model I have added a new variable new_email. To this end I added:
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :new_email, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX }
In migration file:
t.string :new_email
I have not added anything in my seeds file regarding this new variable. On seeding I get the error: ActiveRecord::RecordInvalid: Validation failed: New email is invalid. If I remove the validates line from the model file it seeds succesfully. I have tried resetting my db: rake db:drop, rake db:create, rake db:migrate and rake db:seed, but with the same result. What could be causing this error?
The controller methods using params:
def create
#user = User.new(usernew_params)
if #user.save
#user.send_activation_email
flash[:success] = "A confirmation email has been sent to you"
redirect_to root_url
else
render 'new'
end
end
def update
#user = User.friendly.find(params[:id])
if #user.update_attributes(userupdate_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
def usernew_params
params.require(:user).permit(:email,
:username,
:password,
:password_confirmation)
end
def userupdate_params
params.require(:user).permit(:email,
:email_new,
:avatar,
:organization,
:activated,
:password,
:password_confirmation)
end
And my seeds file (left out other models without a relationship to this model):
99.times do |n|
username = "fakename#{n+1}"
email = "example-#{n+1}#railstutorial.org"
password = "password"
organization = Faker::Company.name
User.create!(username: username,
email: email,
password: password,
password_confirmation: password,
activated: true,
activated_at: Time.zone.now,
organization: organization)
end
The issue is that you are validating new_email on every request and not allowing it to be blank. Since it is not set in your seed file and you are validating based on a Regexp this will fail every time.
Based on your use case described I would recommend this
validates :new_email, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
allow_blank: true,
on: :update
This will do 2 things:
It will allow new_email to be blank "" or omitted completely nil without failing.
Also since you are not even accepting new_email on create through the controller this validation will only run on update.
While validations that allow_blank are fairly lightweight I still try to advise against running code when it is not needed that is why I added the on parameter.

Rspec be_valid fails on a valid test

I'm learning TDD with Rails 4 and rspec. I've made some test cases for my user model to check the password lengths. I have two tests so far that checks whether a user input a password that was too short and one where the password is between 6 - 10 characters.
So far, the "password is too short" test passes:
it "validation says password too short if password is less than 6 characters" do
short_password = User.create(email: "tester#gmail.com", password: "12345")
expect(short_password).not_to be_valid
end
However, on the test where I do have a valid password, it fails:
it "validation allows passwords larger than 6 and less than 10" do
good_password = User.create(email: "tester2#gmail.com", password: "blahblah")
expect(good_password).to be_valid
end
And I get this error:
Failure/Error: expect(good_password).to be_valid
expected #<User id: 1, email: "tester2#gmail.com",
created_at: "2014-06-21 02:43:42", updated_at: "2014-06-21 02:43:42",
password_digest: nil, password: nil, password_hash: "$2a$10$7u0xdDEcc6KJcAi32LBW7uzV9n7xYbfOhZWdcOnU5Cdm...",
password_salt: "$2a$10$7u0xdDEcc6KJcAi32LBW7u"> to be valid,
but got errors: Password can't be blank, Password is too short (minimum is 6 characters)
# ./spec/models/user_spec.rb:12:in `block (3 levels) in <top (required)>'
Here's my model code:
class User < ActiveRecord::Base
has_many :pets, dependent: :destroy
accepts_nested_attributes_for :pets, :allow_destroy => true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: true
validates :password, presence: true, :length => 6..10, :confirmation => true
#callbacks
before_save :encrypt_password
after_save :clear_password
#method to authenticate the user and password
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
#method to encrypt password
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
#clears password
def clear_password
self.password = nil
end
end
I'm confused on why the password is nil when I create the test object.
You have a password presence requirement on your model, but then you have an after_save hook that nilifies the password and puts the record into an invalid state. The first test passes because your records are always being put into an invalid state by the after_save hook. You need to rethink how you're handling password storage; once you resolve that, here are some code samples to help give you some ways to test this:
# Set up a :user factory in spec/factories.rb; it should look something like:
FactoryBot.define do
factory :user do
sequence(:email) { |n| "tester+#{n}#gmail.com" }
password { SecureRandom.hex(6) }
end
end
# In your spec:
let(:user) { create :user, password: password }
context 'password' do
context 'length < 6' do
let(:password) { '12345' }
it { expect(user).not_to be_valid }
it { user.errors.message[:password]).to include('something') }
end
context 'length >= 6' do
context 'length < 10' do
let(:password) { 'blahblah' }
it { expect(user).to be_valid }
end
context 'length >= 10' do
let(:password) { 'blahblahblah' }
it { expect(user).not_to be_valid }
end
end
end
You can also use shoulda matchers:
it { should_not allow_value('12345').for(:password) }
it { should allow_value('12345blah').for(:password) }
The most likely problem is the password field is not mass assignable. That is why password is nil in the output message.
Try this instead:
it "validation allows passwords larger than 6 and less than 10" do
good_password = User.create(email: "tester2#gmail.com")
good_password.password = "blahblah"
expect(good_password).to be_valid
end
Note that your first test is passing accidentally - it has the same problem as the second test (password isn't being assigned). This means you aren't actually testing that the password is rejected when less than 6 characters atm.
See this article on mass assignment for more details.
EDIT: Leo Correa's comment may suggest this may not be the case for you. Posting your model code would help...

Rails, undefined method error. But I can use this method in rails console

In my rspec testing. I got NoMethodError:
undefined method `password_digest='.
But in rails console. I do can use .password_digest method.
I am so confused. I searched google. Got sever threads about this. even in stackoverflow. But those don't help. I do have password_digest as a field in my database.
I can even set my password_digest
see:
1.9.2-p290 :005 > user.password_digest
=> "$2a$10$X8CSsstOqZKKA6qVHpW9.uH5Lzd7dxfGNCAxvIbePpcfBg8KFbD4y"
1.9.2-p290 :006 > user.password_digest = 1
=> 1
1.9.2-p290 :007 > user.password_digest
=> 1
And also, in my app. everything seems fine. That's weird.
Please help...
1.9.2-p290 :002 > User.first.password_digest
User Load (0.3ms) SELECT "users".* FROM "users" LIMIT 1
=> "$2a$10$NCfX2SgKeqGARJ68StxRJuCbbbK7g18n5FPxbHY5THwg4pAdHUvui"
1.9.2-p290 :003 >
[5]+ Stopped rails console
luke#Macbook-Pro~/Documents/workspace/RoR/rails_projects/sample_app2012$ bundle exec rspec spec/models/user_spec.rb
DEPRECATION WARNING: The InstanceMethods module inside ActiveSupport::Concern will be no longer included automatically. Please define instance methods directly in #<Class:0x000001029f6688> instead. (called from <top (required)> at /Users/luke/Documents/workspace/RoR/rails_projects/sample_app2012/spec/models/user_spec.rb:14)
DEPRECATION WARNING: The InstanceMethods module inside ActiveSupport::Concern will be no longer included automatically. Please define instance methods directly in #<Class:0x000001029f6688> instead. (called from <top (required)> at /Users/luke/Documents/workspace/RoR/rails_projects/sample_app2012/spec/models/user_spec.rb:14)
FFFFFFFFFFFFFFFFFFFFF
Failures:
1) User
Failure/Error: #user = User.new(name: "Example User", email: "user#example.com",
NoMethodError:
undefined method `password_digest=' for #<User:0x00000100beee60>
# ./spec/models/user_spec.rb:17:in `new'
# ./spec/models/user_spec.rb:17:in `block (2 levels) in <top (required)>'
..... There are 19 more failures, to save space, i didn't paste them here.
And this is my user_spec.rb code:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
require 'spec_helper'
describe User do
before(:each) do
#user = User.new(name: "Example User", email: "user#example.com",
password: "foobar", password_confirmation: "foobar")
end
subject { #user }
it { should respond_to(:name) }
it { should respond_to(:email) }
it { should respond_to(:password_digest)}
it { should respond_to(:password)}
it { should respond_to(:password_confirmation)}
it { should respond_to(:remember_token) }
it { should respond_to(:authenticate) }
it { should be_valid }
describe "when name is not present" do
before { #user.name = " " }
it { should_not be_valid }
end
describe "when email is not present" do
before { #user.email = " " }
it { should_not be_valid }
end
describe "when name is too long" do
before { #user.name = "a" * 51 }
it { should_not be_valid }
end
describe "when email format is valid" do
valid_addresses = %w[user#foo.com THE_USER#foo.bar.org first.last#foo.jp]
valid_addresses.each do |valid_address|
before { #user.email = valid_address}
it {should be_valid }
end
end
describe "when email address is already taken" do
before do
user_with_same_email = #user.dup
user_with_same_email.email = #user.email.upcase
user_with_same_email.save
end
it { should_not be_valid }
end
describe "when password is not present" do
before { #user.password = #user.password_confirmation = " " }
it {should_not be_valid}
end
describe "when password doesn't match confirmation" do
before { #user.password_confirmation = "mismatch" }
it { should_not be_valid }
end
describe "return value of authenticate method" do
before { #user.save }
let(:found_user) { User.find_by_email(#user.email) }
describe "with valid password" do
it { should == found_user.authenticate(#user.password) }
end
describe "with invalid password" do
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
it { should_not == user_for_invalid_password }
specify { user_for_invalid_password.should be_false }
end
describe "remember token" do
before { #user.save }
its(:remember_token) { should_not be_blank }
end
end
end
And this is my user.rb under modles:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
class User < ActiveRecord::Base
has_secure_password
attr_accessible :name, :email, :password, :password_confirmation
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}
end
Was just stuck on this for a while as well.
Don't forget
rake db:test:prepare after new migrations!
class User has method password_diges but has no method password_digest=
this is not the same methods, so instance of User shall not be responsible on password_digest
it shoud return password_digest but should_not respond_to password_digest i think
You can use User.first.password_digest in console because password_digest and password_digest= isn't the same methods.
password_digest= same as:
def password_digest=(digest)
#password_digest = digest
end
otherwise password_digest:
def password_digest
#password_digest
end
In your console, you're trying the password_digest method, not the password_digest= method. Are you able to execute the following in the console? User.first.password_digest = "$2a$10$NCfX2SgKeqGARJ68StxRJuCbbbK7g18n5FPxbHY5THwg4pAdHUvui"
I think your issue may be rooted in the fact that you're specifying only certain attributes in your User model as being attr_accessible. Try adding :password_digest to that list and see if your test passes.
Hi I am new to RoR and I learned RailsTutorial and encountered this issue too. After a long search in Google I found my db/test.sqlite3 hasn't been updated. which means there was 'password_digest' in my db/development.sqlite3 but there wasn't that column in my db/test.sqlite3.
And after I run the command 'rake db:reset' and 'rake db:test:prepare', the issue was gone.
I had the exactly same issue yesterday. The console in development and in test environment work well. And the web app itself works great as well. But the test (almost the same test as the question described) fail for no method error.
I've tried to stop spring, recreate test database, kill memcached. But it doesn't work.
Finally, I added some data into the fixture file test/fixtures/users.yml.
harry:
name: Harry
email: harry#example.com
password_digest: <%= User.digest('password') %>
It was empty before. Then the tests passed. I made the user.yml empty afterwards.The tests still passed.
I don't know why yet. But hope it helps.

Resources