Rails: BCrypt::Errors::InvalidSalt: invalid salt - ruby-on-rails

Loosely following Ryan Bates's How I Test Railscast to implement sending an email confirmation token to users when they sign up.
class User < ActiveRecord::Base
has_secure_password
strip_attributes except: [:password, :password_confirmation]
...
def send_email_confirmation
generate_token(:email_token)
self.email_token_sent_at = Time.zone.now
save!
UserMailer.email_confirmation(self).deliver
end
private
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
This is failing (in feature specs and when manually clicking through sign-up process) with:
Failures:
1) UserPages sign up with valid information should create a user
Failure/Error: expect { click_button submit }.to change(User, :count).by(1)
BCrypt::Errors::InvalidSalt:
invalid salt
# ./app/models/user.rb:70:in `send_email_confirmation'
# ./app/controllers/users_controller.rb:27:in `create'
# ./spec/features/user_pages_spec.rb:165:in `block (5 levels) in <top (required)>'
# ./spec/features/user_pages_spec.rb:165:in `block (4 levels) in <top (required)>'
I've tried reinstalling bcrypt gem (as suggested elsewhere even though it was Devise related and I'm not using Devise): gem uninstall bcrypt-ruby and then gem install bcrypt-ruby but to no avail. ideas?

Gem strip_attributes is responsible for such behavior.
Solution
Exclude :password_digest attribute from stripping like below:
strip_attributes except: [:password_digest]

We hit this error and it was because we were trying to perform a write operation in a k8s pod that only had read access to the database.
I'm sure this isn't the answer to the original question, but I came across this question when we were trying to figure out what was causing this error for us so I wanted to share.

Related

RSpec 2.3 + Devise 1.0.11

I've got a really old Rails 2.3.18, ruby 1.9.3, rspec 1.x application which we are upgrading and it had restful-authentication in it. So I've replaced that with Devise 1.0.11.
I can login to the application, but my tests will not run;
Here is the test in question
require 'spec_helper'
describe CategoriesController do
context "As a logged in user" do
before do
login_user
current_firm = mock_model(Firm, :id => 1)
controller.stub!(:current_firm).and_return(current_firm)
end
describe "#index" do
it "should render index" do
get :index
response.should render_template('index')
end
end
end
end
Here is the error I get;
NoMethodError in 'CategoriesController As a logged in user#index should render index'
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]=
/home/map7/code/pdfcat/spec/spec_helper.rb:18:in `login_user'
spec/controllers/categories_controller_spec.rb:6:in `block (3 levels) in <top (required)>'
The error happens on this line;
[20, 29] in /usr/local/rbenv/versions/1.9.3-p551/lib/ruby/gems/1.9.1/gems/warden-0.10.7/lib/warden/session_serializer.rb
20 key
21 end
22
23 def store(user, scope)
24 return unless user
=> 25 session[key_for(scope)] = serialize(user)
26 end
The problem is 'session' is nil when I'm at this point.
I've pushed the full code to here: https://github.com/map7/pdfcat/tree/devise
My plan was to get devise working in the tests then I could jump to Rails 3.0 and continue the upgrade.
There's an old message in google groups that I think is relevant: https://groups.google.com/forum/#!topic/rspec/4AHuPtHFD34
It recommends using this:
before do
request.env['warden'].stub(:authenticate!) { double(User) }
end
I'd probably put it in rails_helper.rb so that it runs for all tests

Capybara::Webkit::InvalidResponseError: Javascript failed to execute

I converted an app from Rails 3.2 to 4.2 that was using the cancan gem. Googling around and checking on their github says it's enought to just replace gem cancan with gem cancancan without changing anything. This doesn't seem to work. My test fails:
Failures:
1) Redactable fields Manual redaction Redacting selected text in body
Failure/Error:
page.execute_script <<-EOS
document.getElementById('notice_#{#name}').focus();
document.getElementById('notice_#{#name}').select();
EOS
Capybara::Webkit::InvalidResponseError:
Javascript failed to execute
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-webkit-1.11.1/lib/capybara/webkit/browser.rb:305:in `check'
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-webkit-1.11.1/lib/capybara/webkit/browser.rb:211:in `command'
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-webkit-1.11.1/lib/capybara/webkit/browser.rb:232:in `execute_script'
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-webkit-1.11.1/lib/capybara/webkit/driver.rb:80:in `execute_script'
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-2.7.1/lib/capybara/session.rb:524:in `execute_script'
# ./spec/support/page_objects/redactable_field_on_page.rb:15:in `select'
# ./spec/support/page_objects/redactable_field_on_page.rb:22:in `select_and_redact'
# ./spec/integration/redaction_spec.rb:37:in `block (4 levels) in <top (required)>'
2) Redactable fields Manual redaction Restoring body from original
Failure/Error: page.find("#notice_#{#name}").click
Capybara::ElementNotFound:
Unable to find css "#notice_body"
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-2.7.1/lib/capybara/node/finders.rb:44:in `block in find'
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-2.7.1/lib/capybara/node/base.rb:85:in `synchronize'
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-2.7.1/lib/capybara/node/finders.rb:33:in `find'
# /Users/siaw23/.rvm/gems/ruby-2.1.9/gems/capybara-2.7.1/lib/capybara/session.rb:699:in `block (2 levels) in <class:Session>'
# ./spec/support/page_objects/redactable_field_on_page.rb:9:in `unredact'
# ./spec/integration/redaction_spec.rb:46:in `block (4 levels) in <top (required)>'
The elements being searched for can be reached manually after loggin in. This means neither cancancan nor cancan successfully applies :admin permission to my user that's created in the test. I used You are not authorized to access this page. and I get "You are not authorized to access this page."
The spec itself looks like this:
require 'rails_helper'
require 'support/notice_actions'
#lots of irrelevant code
private
def visit_redact_notice
user = create(:user, :admin)
visit '/users/sign_in'
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_on "Log in"
notice = create(:dmca, :redactable)
visit "/admin/notice/#{notice.id}/redact_notice"
notice
end
end
end
end
The visit_redact_notice method in the above spec is where everything is happening. I need help :). This is my repo if it might help: https://github.com/siaw23/lumendatabase
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
return unless user
if user.has_role?(Role.submitter)
can :submit, Notice
end
if user.has_role?(Role.redactor)
grant_admin_access
grant_redact
end
if user.has_role?(Role.publisher)
grant_admin_access
grant_redact
can :publish, Notice
end
if user.has_role?(Role.admin)
grant_admin_access
grant_redact
can :edit, :all
cannot :edit, [User, Role]
can :publish, Notice
can :rescind, Notice
can :pdf_requests, :all
end
if user.has_role?(Role.super_admin)
can :manage, :all
end
if user.has_role?(Role.researcher)
can :read, Notice
end
end
def grant_admin_access
can :read, :all
can :access, :rails_admin
can :dashboard
can :search, Entity
can :access, :original_files
end
def grant_redact
can :edit, Notice
can :redact_notice, Notice
can :redact_queue, Notice
end
end
A couple issues you might want to check out...
There's a new version of Devise out that allows authentication via feature / integration tests. I've always spent hours debugging actually getting step-by-step login functionality to work. With Devise 4.2, logging in via integration tests is now supported. You'll have to make some changes to the RSpec configuration for Devise, as noted in the documentation above for the sign_in method to work.
If at this point logging in as an admin still does not work, I would check to see if your RSpec configuration has use_transactional_fixtures set to true. If it does, I would strongly encourage you to let the database_cleaner gem manage your test database, and set use_transactional_fixtures to false.
One or both of those should get you around your authentication issue. My guess is that it's most likely an issue with the transactional fixtures, as Rails is likely cleaning up a transaction before you're fully authenticated (thus why I'd suspect #1 above won't work).

Updated to Rails 4.2.2 and got: User email_changed? method not found

I've just updated to rails 4.2.2 and can't create a user anymore. (it was working before).
I have the user's email delegated to another model like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :recoverable, :rememberable
...
belongs_to :contact, :inverse_of => :user
delegate :name, :email, :to => :contact, :allow_nil => true
# Validations
...
end
Now, when I try to create a User, I get the following error:
NoMethodError: undefined method `email_changed?' for #<User:0xddb3be0>
from /home/.../gems/activemodel-4.2.2/lib/active_model/attribute_methods.rb:433:in `method_missing'
If I add this to user.rb, it works again:
def email_changed?
false
end
Is there any problem with this solution? Is there a better alternative?
Obs:
In a more complete log, I got the following lines:
NoMethodError:
undefined method `email_changed?' for #<User:0xf75da14>
# /home/.../gems/activemodel-4.2.2/lib/active_model/attribute_methods.rb:433:in `method_missing'
# /home/.../gems/devise-3.5.1/lib/devise/models/recoverable.rb:35:in `block (2 levels) in <module:Recoverable>'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:446:in `instance_exec'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:446:in `block in make_lambda'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:164:in `call'
# /home.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:164:in `block in halting'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:504:in `call'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:504:in `block in call'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:504:in `each'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:504:in `call'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:92:in `_run_callbacks'
# /home/.../gems/activesupport-4.2.2/lib/active_support/callbacks.rb:776:in `_run_save_callbacks'
# /home/.../gems/activerecord-4.2.2/lib/active_record/callbacks.rb:302:in `create_or_update'
# /home/.../gems/activerecord-4.2.2/lib/active_record/persistence.rb:142:in `save!'
...
Is there something to do with the Devise's :recoverable module? (using devise-3.5.1)
I took a look into devise gem, recoverable module, and found this:
included do
before_update :clear_reset_password_token, if: :encrypted_password_changed?
before_save do
if email_changed? || encrypted_password_changed?
clear_reset_password_token
end
end
end
It was included by this commit. It clears the reset_password_token if the user's email changed which is correct.
In my particular case, the User doesn't have the option of changing his email and the email was delegated to another model. He can only change his password.
Since this is an odd use case, it doesn't seem to be that bad adding to User.rb this:
def email_changed?
false
end
There is a problem with this solution if some other important email verification is added by devise using the method email_changed? That might cause problems in the future as the method is "stubbed".

Cucumber: scenario for User is not signed up is failing

I ran cucumber for the devise-rspec-cucumber project but the following scenario is failing:
Scenario: User is not signed up
Given I do not exist as a user
When I sign in with valid credentials
Then I see an invalid login message
And I should be signed out
undefined method `flatten' for nil:NilClass (NoMethodError)
1 scenario (1 failed)
4 steps (4 passed)
I tried removing every step except the first one and it is still failing:
Given /^I do not exist as a user$/ do
create_visitor
delete_user
end
where
def create_visitor
#visitor ||= { :name => "Testy McUserton", :email => "example#example.com",
:password => "changeme", :password_confirmation => "changeme" }
end
def delete_user
#user ||= User.where(:email => #visitor[:email]).first
#user.destroy unless #user.nil?
end
But if I replace create_visitor with create_user, it will pass.
def create_user
create_visitor
delete_user
#user = FactoryGirl.create(:user, #visitor)
end
I am confused what's going on. It seems to me that the step definition is expecting some things that I am not providing.
I got the same error message before. I was sure that there's nothing wrong with my steps. Therefore I tried to upgrade the database_cleaner gem to the latest (1.0.1) since the error was caused by database_cleaner. And it did solve the problem. I would suggest you try this if your database_cleaner gem version is not 1.0.1. Just do
$ bundle update database_cleaner
The problem exist here:
create_visitor
delete_user
In the first step you created a visitor, but not an user. Then in second step you try to delete an user who does not exist. That's why the test failed.
I suggest you to review the logic and create correct steps.

Rspec problem mocking model in controller

I set up a controller which handles omniauth authentications which are worked into a custom built authentication system. i am trying to test the logic for how authentications are handled (ex: if user already has/does not have account, if user is/isn't currently logged in, etc.). as such i have a Authorization model and a authorizations controller. The action to create a authorization has this general outline:
class AuthorizationsController < ApplicationController
def create
omniauth = request.env['omniauth.auth']
authorization = Authorization.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
if authorization
# Authorization already established, log in user
elsif current_user
# User is logged in but wants to add another omniauth authentication
else
# Create user and associate them with omniauth authentication
end
end
end
I am trying to test this logic in Rspec but have been having issues. Heres is what I am working with in my spec:
describe AuthorizationsController do
render_views
describe "POST 'create'" do
describe "with an already existing authorization" do
it "should log the user in" do
#authmock = mock_model(Authorization)
Authorization.should_receive(:find_by_provider_and_uid).and_return(#authmock)
post :create, :provider => 'twitter'
current_user?(#authmock.user).should == true
response.should redirect_to(root_path)
end
end
end
end
I am under the impression that this should assign my mocked Authorization model (#authmock) to the local variable authorization in my controller when the assignment call is made, thus making 'if authorization' return true. However whenever I true to run this spec I get this error:
Failures:
1) AuthorizationsController POST 'create' with an already existing authorization should log the user in
Failure/Error: post :create, :provider => 'twitter'
NoMethodError:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
# ./app/controllers/authorizations_controller.rb:5:in `create'
# ./spec/controllers/authorizations_controller_spec.rb:16:in `block (4 levels) in <top (required)>'
Can anyone enlighten me as to what I am doing wrong here?
Edit:
since the question was raised as to whether or not the assignment of omniauth was causing issues, I commented out that line to see what would happen and got the following error:
1) AuthorizationsController POST 'create' with an already existing authorization should log the user in
Failure/Error: post :create, :provider => 'twitter'
NameError:
undefined local variable or method `omniauth' for #<AuthorizationsController:0xb41809c>
# ./app/controllers/authorizations_controller.rb:5:in `create'
# ./spec/controllers/authorizations_controller_spec.rb:16:in `block (4 levels) in <top (required)>'
which tells me that the problem is with the mock or stub as the find_by_provider_and_uid function is still being evaluated and is not stubbed when the test runs
Are you specing
current_user?(#authmock.user).should == true
or
response.should redirect_to(root_path)
I think that first expectation should not be tested here, because you've mocked 'if authorization' block, so you should spec what happens then!

Resources