Rails - Unknown attribute password - ruby-on-rails

I have been following Michael Hartl's Ruby on Rails tutorial book to try and add users to my application. Reading chapter 6, I have added what I believe to be the necessary fields for my user, specifically password and password confirmation via "has_secure_password".
I thought that adding "has_secure_password" to my user model would include the attributes "password" and "password_confirmation" provided I add a "password_digest" to the model. I have done that as the book instructed me to. However, when I run a test, Rails gives me the following error:
Error:
UserTest#test_should_be_valid:
ActiveModel::UnknownAttributeError: unknown attribute 'password' for User.
test/models/user_test.rb:8:in `setup'
I tried this solution and it still gave me the same error, not recognizing the attributes "password" or "password_confirmation". I installed bcrypt using "gem install bcrypt" and included the following in my gem file:
gem 'bcrypt-ruby', :require => 'bcrypt'
I am using Rails 5 and it seems like "has_secure_password" is not supplying the password attributes that I need. Can anyone see what I missed or did wrong that caused "has_secure_password" to not work as intended? Thanks
User Model:
class User < ApplicationRecord
has_many :activities
class User < ActiveRecord::Base
attr_accessor :name, :email, :password, :password_confirmation
has_secure_password
validates :first_name, presence: true, length: {minimum: 1}
validates :last_name, presence: true, length: {minimum: 1}
validates :email, presence: true, uniqueness: true, length: {minimum: 5}
validates :username, presence: true, uniqueness: true, length: {minimum: 1}
validates :password_digest, length: {minimum: 6}
validates :password, :confirmation => true, length: {minimum: 4}
validates :password_confirmation, presence: true
#-----------------------New Stuff ---------------------------------------
acts_as_authentic do |c|
c.crypto_provider = Authlogic::CryptoProviders::Sha512
end
#------------------------------------------------------------------------
#---------------Unsure if working--------------
#validates_presence_of :password, :on => :create
#validates_presence_of :email
#validates_uniqueness_of :email
#----------------------------------------------
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
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
end
end
Apologies for the messy code on the model as I am still learning Rails.
User Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = 'Account created'
else
flash[:notice] ='ERROR: Account was not created'
redirect_to 'users/new'
end
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
User Table:
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "username"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "persistence_token"
t.string "password_digest"
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
end
User Test:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
def setup
#user = User.new(first_name: 'test', last_name: 'tester', password: '1234',email: 'test1#mail.com',
password: 'foobar', password_confirmation: 'foobar')
end
test 'should be valid' do
assert #user.valid?
end
end

Update:
I have tested this out and it works. So hope will work for you as well :) Looks like MiniTest doesn't work well with BCrypt. I received the same error - undefined password, but later implemented my change and it went further well.
Original answer:
As of your founded solution it made me think that this makes no sence - adding getter and especially setter methods for :password and :password_confirmation. Because has_secure_password creates those virtually that runs through BCrypt. So doesn't it goes around crypting / encrypting? If so it is not safe. So only option left for testing I see take the BYcript into the testing suite. I think something like this might do the trck:
In User Test:
require 'bcrypt'
def setup
#user = User.new(first_name: 'test', last_name: 'tester', password: BCrypt::Password.create("my password") ,email: 'test1#mail.com', password_confirmation: 'my password')
end
test 'should be valid' do
assert #user.valid?
end
Note that I removed duplicated password: 'foobar. Since with that particular test you are testing if User can be created, so shouldn't pass a different password or even duplicated attribute... Make another test for this (also checkout fixtures, they are great for creating test objects, as well as factories for more complicated cases).
And of course, remove the atr_accessor :password, :password_confirmation form your User model.
p.s. and please fix you code snippet for User class. Or is it really defined twice like this?:
class User < ApplicationRecord
has_many :activities
class User < ActiveRecord::Base

Related

Why is my Rails app returning "Password is too short (minimum is 6 characters)" when I have the correct amount of characters?

I am building a simple registration page.
Here is my User model with it's validations, and associations:
class User < ApplicationRecord
has_secure_password
has_many :posts
has_many :comments
has_many :likes
validates :username, presence: true, uniqueness: true
validates :password, presence: true, length: { minimum: 6 }
end
Here is my User migration:
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :name
t.string :username
t.string :password_digest
t.integer :age
t.integer :years_in_the_labor
t.string :title
t.string :location
t.string :ministry
t.timestamps
end
end
end
Here is also my User controller create method:
def create
#user = User.new(name: params[:name], title: params[:title], username: params[:username], password: [:password])
if #user.valid?
#user.save
render json: { status: 200, user: #user }
else
render json: { status: 401, message: #user.errors.full_messages }
end
end
I am sending the params through fetch, here's what the params are:
<ActionController::Parameters {"name"=>"Test Name User", "title"=>"Test Title User", "username"=>"test_username_user", "password"=>"password123", "controller"=>"users", "action"=>"create", "user"=>{"name"=>"Test Name User", "username"=>"test_username_user", "title"=>"Test Title User"}} permitted: false>
And I get this error:
message: ["Password is too short (minimum is 6 characters)"]
My password validation is not working, although I pass the correct amount of characters.
You're passing to User.new just [:password] instead of params[:password]
it is most often used to pass parameters only a private function like this:
private
def user_params
params.require(:user).permit(:name, :title, :username, :password)
end
And then in your create method:
#user = User.new(user_params)
Update
And instead of asking if the user is valid you can directly check if the user was saved
if #user.save
# your code for success
else
# your code for failure
end
Your password missed params[:password]
#user = User.new(name: params[:name], title: params[:title], username: params[:username], password: params[:password])
As it doesn't match any password, it is blank, which is something less than 6 characters.

Getting two errors for password presence in activerecord

I am testing my user input validation in my application and I am getting two errors in regards to my password presence.
This is what I have written for my model.
class User < ActiveRecord::Base
include Slugifiable
extend Slugifiable::Find
has_secure_password
has_many :posts
validates :email, uniqueness: true, presence: true
validates :username, uniqueness: true, presence: true
validates :password, presence: true
end
Below is my migration table:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :username
t.string :password
t.string :password_digest
end
end
end
Each time I run my application with no input it should give me three error messages: "Password can't be blank", "Email can't be blank", "Username can't be blank". Instead I get an extra "Password can't be blank" error. I am using a password_digest variable which is a salted hash of the users password once the data persists in the database.
has_secure_password comes with its own presence validation on the create action. Therefore, validating the presence of password is redundant and is causing you to get two "Password can't be blank" error messages.
Simply remove validates :password, presence: true or add a condition to the validation for a specific controller action/other context...ie
validates :password, presence: true, on: :some_action

Rails NoMethodError: undefined method `password_digest=

I am new to rails and have seen all possible answers for my problem, as this is asked quite frequently by other developers, yet I'm unable to resolve it. Please have a look.
I am getting this error when I try to add a data from the console
User.create(name: "Michael Hartl", email: "mhartl#example.com", phone: "0123456789", password: "foobar", password_confirmation: "foobar")
ERROR SHOWN
undefined method `password_digest=' for #<User:0x0000000375c788>
Did you mean? password=
Controller
def create
#candidate = User.new(user_params)
if #candidate.save
flash[:notice] = "New Candidate Added Successfully"
redirect_to(users_path)
else
render('new')
end
end
private
def user_params
#Whitelisting for strng parameters
params.require(:user).permit(:name, :email, :password, :password_confirmation, :qualification, :college, :stream, :phone)
end
Migration:
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :name, null: false
t.boolean :admin_user, default: false
t.string :email, null: false
t.string :password_digest, null: false
t.string :qualification
t.string :college
t.string :stream
t.string :phone
t.timestamps
end
end
end
Model
require 'bcrypt'
class User < ApplicationRecord
include BCrypt
has_many :results, dependent: :destroy
has_many :exams, through: :results
accepts_nested_attributes_for :results
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
before_save { self.email = email.downcase }
has_secure_password
validates :email, presence: true
validates :email, presence: true, length: { maximum: 255 },format: { with: VALID_EMAIL_REGEX },uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates_confirmation_of :password
validates_presence_of :password_confirmation
validates :name, presence: true
validates :phone, numericality: {only_integer: true}, length: {is: 10 , message: "length should be 10"}
scope :visible, lambda { where(:visible => true) }
scope :invisible, lambda { where(:visible => false) }
scope :sorted, lambda { order("id ASC") }
scope :newest_first, lambda { order("created_at DESC") }
scope :search, lambda {|query| where(["name LIKE ?", "%#{query}%"]) }
end
Have you checked this https://github.com/plataformatec/devise/issues/1845
As per described in the issue, you are using ActiveModel's has_secure_password with Devise. You must not mix the two.
May be removing has_secure_password from User model will resolve your issue.
The code was completely fine,
few server restarts and don't really know how but restarting my code editor ( Visual Studio Code ) worked for me.
I am leaving the question as it is, as I did reach to this code after going through several stackoverflow threads.
NB: For newbies like me, please see how to check for errors while inserting into database from console, it helps a lot. Synatctical example is as:
rails c
user = User.new ( ... . . . . .. )
user.save
user.errors
Thankyou
This happened to me as well, and I found out that sometimes vscode doesn't actually kill the process running. You will need to kill rails from the terminal.
use ps aux | grep rails to find out what processes are running and use kill -9 [rails-pid] to stop the process.
On your next attempt, everything should work fine.
This answer helped me.

Single Table Inheritance Errors - ActiveRecord::SubclassNotFound

My intent is to implement STI with two types: Staff and Clinician. My previous implementation was using roles with enums, and after doing my best to follow answers to similar questions, take out all references in tests etc. to enum roles and replace with references to types , I am getting many versions of the following error when I run my testing suite:
ERROR["test_valid_signup_information_with_account_activation", UsersSignupTest, 1.01794000000001]
test_valid_signup_information_with_account_activation#UsersSignupTest (1.02s)
ActiveRecord::SubclassNotFound: ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'Staff'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite User.inheritance_column to use another column for that information.
app/controllers/users_controller.rb:19:in `create'
test/integration/users_signup_test.rb:27:in `block (2 levels) in <class:UsersSignupTest>'
test/integration/users_signup_test.rb:26:in `block in <class:UsersSignupTest>'
Here are a couple areas where I am confused that could potentially be hiding issues:
In my user model user.rb, I think I am defining the sub Classes correctly (Staff and Clinician), but I'm unsure if I'm wrapping everything correctly. Does all the other code have to be contained in one of these classes? Am I misusing "end"?
class User < ApplicationRecord
end
class Staff < User
end
class Clinician < User
end
belongs_to :university
has_many :referral_requests
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :type, presence: true
validates :university_id, presence: true, if: lambda { self.type == 'Staff' }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
def feed
ReferralRequest.where("user_id = ?", id)
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
Here's the specific test code that is failing (one of many in the test suite that are failing - all the user parameters are defined similarly though). Am I passing the staff parameter appropriately?
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user#example.com",
university_id: 1 ,
type: "Staff",
password: "password",
password_confirmation: "password" } }
Here is my users table schema:
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.integer "university_id"
t.integer "role"
t.string "type"
t.index ["email"], name: "index_users_on_email", unique: true
end
Thanks very much for any ideas! I ask a lot of questions on here but it's only after trying to work through similar answers for quite a while.
Assuming that the code sample above is accurate, you are seeing this error because the user.rb file is invalid Ruby and failing to parse. You should also be seeing an interpreter error about that.
class User < ApplicationRecord
belongs_to :university
has_many :referral_requests
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :type, presence: true
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# etc...
end
class Staff < User
validates :university_id, presence: true
end
class Clinician < User
end
Standard class-inheritance practices apply, so if there is code in there that is only appropriate for a specific subclass it should move there (e.g. university_id validation moving to Staff).
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
These should be written as
def self.digest(string)
# ...
end
def self.new_token
# ...
end
or, alternatively,
class << self
def digest(string)
# ...
end
def new_token
# ...
end
end

User's Email Parameter Not Been Persisted Into The Database

I created a users model in a rails 4.1.8 application with the attributes email and password. I fired up rails console, user = User.new(email: "user#example.com" , password: "example") work but user.save saves the password and omits the email. below is the model, migrated database file and rails console log.
USER MODEL
class User < ActiveRecord::Base
before_save {self.email = email.downcase! }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format:{with: VALID_EMAIL_REGEX},
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }
end
DATABASE FILE (For the User)
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email, unique: true
t.string :password_digest
t.timestamps null: false
end
end
end
RAILS CONSOLE LOG
<r.new(email: "user#example.com" , password: "example")
=> #<User id: nil, email: "user#example.com", password_digest: "$2a$10$jxwd/oriT
z2HklHK4b4nf.P.DWb6s35YTO.EbYwup0I...", created_at: nil, updated_at: nil>
irb(main):008:0> user.save
(1.0ms) BEGIN
User Exists (1.0ms) SELECT 1 AS one FROM `users` WHERE `users`.`email` = 'user#example.com' LIMIT 1
SQL (46.0ms) INSERT INTO `users` (`created_at`, `password_digest`, `updated_at`) VALUES ('2015-07-20 20:54:35', '$2a$10$jxwd/oriTz2HklHK4b4nf.P.DWb6s35YTO.Eb
Ywup0I.gMTOLSNKa', '2015-07-20 20:54:35')
(89.1ms) COMMIT
=> true
USERS CONTROLLER
class UsersController < ApplicationController
def new
end
private
def user_params
params.require(:user).permit(:email)
end
end
Looking at the log the email parameter was not inserted into; please, any help with this is appreciated.
Try the following in your User model:
before_save {self.email = email.downcase } # no "!"
downcase! edits the variable on which it is called and does not return the downcased string unless there is something to downcase (i.e., if you use it on an all lower-case email, it returns nil and based on your console output, this is what is happening). downcase alone should be fine.
downcase! on the API

Resources