Rails 4 user model will not save with hash data from oauth - ruby-on-rails

I am trying to add oauth to an existing login for my project, but when I retrieve the hash data and try to save the user params with user.save!, the validation rules fail.
user.rb
has_secure_password
validates :email, :format => { :with => /\A[^# ]+#[^# ]+\.[^# ]+\Z/ },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true, :on => :create
validates :username,
:format => { :with => /\A[A-Za-z0-9][A-Za-z0-9_-]{0,24}\Z/ },
:uniqueness => { :case_sensitive => false }
...
class << self
def from_omniauth(auth_hash)
user = find_or_create_by(uid: auth_hash['uid'], provider: auth_hash['provider'])
user.name = auth_hash['info']['name']
user.email = auth_hash['info']['email']
user.username = auth_hash['info']['email']
user.save!
user
end
end
The output of user.errors.full_messages gives me ["Password can't be blank", "Password can't be blank", "Email is invalid", "Username is invalid"].
What I don't understand is why the validations are failing if the data parameters have been defined (i.e. user.email) and hold the correct values.
What am I missing?

your problem is the find_or_create_by method.
this will looking for the user with uid and provider otherwise try to create it.
but without vaild username, and so on, it will always fail if there is no user with uid and provider
update:
you try to find a user with an uid and a provider. if
find_or_create_by find a valid user, it will return it. with this
you can update the data.
BUT if find_or_create_by did not find a valid user, it will create a user with the given parameter uid and provider. but to
create a valid user, your model needs a valid username, a valid
password, and a valid email
you could do something like this
def from_omniauth(auth_hash)
user = User.find_by(uid: auth_hash['uid'], provider:auth_hash['provider']) || User.new(uid: auth_hash['uid'], provider:auth_hash['provider'], password: 'your password methods')
user.name = auth_hash['info']['name']
user.email = auth_hash['info']['email']
user.username = auth_hash['info']['email']
user.save!
user
end

Related

Devise testing expired reset token

I need some help here
I'm testing weather a expired reset_password_token should update or not a user account. I'm setting reset_password_sent_at attribute in User model to a expired value, but the user's password are been updated anyway. I'm using Devise's recoverable module.
factories/user.rb
factory :user_expired_unconfirmed_password do
email { Faker::Internet.email }
password { default_password }
password_confirmation { default_password }
confirmed_at { 2.days.ago }
reset_password_token { nil }
reset_password_sent_at { nil }
updated_at { 2.hours.ago }
end
passwords_spec.rb
RSpec.describe Devise::PasswordsController, type: :request do
it "creater users with expired reset_password_token (6 hours max.) shouldn't update password" do
user = FactoryBot.create(:user_expired_unconfirmed_password)
reset_password_token = user.send_reset_password_instructions
old_passw = user.encrypted_password
new_passw = 'test_new_passw_123'
# expire token
user.reset_password_sent_at = 7.hours.ago
#### debbug ###
# user.reload # if uncommented, got true in the below line
puts user.reset_password_period_valid? # got false
put user_password_path,
:params => {
"user" => {
"reset_password_token" => reset_password_token,
"password" => new_passw,
"password_confirmation" => new_passw
}
}
expect(user.reload.encrypted_password).to eq old_passw # got false here
end
end
P.s: Using the method reset_password_period_valid? I got false and true if I reload the user, but independent of that it's not passing the assertion.
Any idea what would be? I tested these helpers methods in Model tests with valid and expire reset_password_token to change the password and it worked right.
Try saving the user with the new value of the reset_password_token
RSpec.describe Devise::PasswordsController, type: :request do
it "creater users with expired reset_password_token (6 hours max.) shouldn't update password" do
user = FactoryBot.create(:user_expired_unconfirmed_password)
reset_password_token = user.send_reset_password_instructions
# expire token
user.reset_password_sent_at = 7.hours.ago
user.save
#### debbug ###
user.reload
puts user.reset_password_period_valid? # You should get false
old_passw = user.encrypted_password
new_passw = 'test_new_passw_123'
put user_password_path,
:params => {
"user" => {
"reset_password_token" => reset_password_token,
"password" => new_passw,
"password_confirmation" => new_passw
}
}
expect(user.reload.encrypted_password).to eq old_passw # got false here
end
end

Create different users in Ruby with iteration

I'm new in Ruby. I want to create different users in ruby using iteration.
def createuser(*args)
obj = H['userClass']
obj.login = H['login']
obj.password = a.password = #default_passwd
obj.email = 'test#example.com'
obj.role = MasterUser::ROLE_MASTER_USER
end
For example I want to call this method and send these arguments:
H = Hash["userClass" => MasterUser.new, "login" => admin]
createuser(H)
What is the proper way to implement this?
Here's a modified version. It should bring you closer to your goal, while still being recognizable :
def create_user(parameters)
klass = parameters['user_class']
user = klass.new
user.login = parameters['login']
user.password = #default_passwd
user.email = 'test#example.com'
user.role = klass::ROLE_MASTER_USER
user
end
user_params = {"user_class" => MasterUser, "login" => 'admin'}
new_user = create_user(user_params)
I'd probably do something like this:
class UserFactory
attr_accessor :user
def initialize(klass)
#user = klass.new
end
def create(params = {})
user.login = params.fetch :login
user.password = params.fetch :password, 'default_password'
user.email = params.fetch :email
# user.role should just be initialised on the klass.new call, no need to do that here
# etc...
end
end
class MasterUser
ROLE = 'master_role'
attr_accessor :login, :password, :email, :role
def initialize
self.role = ROLE
end
end
which you would call like:
UserFactory.new(MasterUser).create(login: 'george', password: 'secret', email: 'me#george.com')
The reason I'd use params.fetch :login, instead of just reading it, is that in Ruby accessing a hash by a key that it doesn't have returns nil, while trying to fetch it will throw an error.
For example:
a = {}
a[:foo] #=> nil
a.fetch :foo #=> throw a KeyError
So that is a way of enforcing that the argument hash has the right keys.

How could patch method in Rails update only some attributes while the other attributes are blank?

I have these questions while reading the Ruby On Rails Tutorial in
here
The validation of User class is:
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, length: { maximum: 255 }
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
.
.
.
end
In test,while patching a updated user information to the route of the user like this:
def setup
#user = users(:michael)
end
.
.
.
test "successful edit" do
get edit_user_path(#user)
assert_template 'users/edit'
name = "Foo Bar"
email = "foo#bar.com"
patch user_path(#user), user: { name: name,
email: email,
password: "",
password_confirmation: "" }
assert_not flash.empty?
assert_redirected_to #user
#user.reload
assert_equal name, #user.name
email, #user.email
end
The test would pass and only the user's name and email would be updated and password won't change.
If the validation of the password doesn't include the "allow_blank:true",this test would fail.
So I don't understand that: When the test passed which means the password could be blank, why it wouldn't change the password to be blank? How could Rails know I just want to update some of the attributes?
has_secure_password adds a password= setter method method to your model which discards empty? input when setting the password.
irb(main):012:0> "".empty?
=> true
This prevents users from choosing a blank password. If you dont want to take my word for it you can easily test this:
test "does not change password to empty string" do
patch user_path(#user), user: { name: name,
email: email,
password: "",
password_confirmation: "" }
#user.reload
assert_false #user.authenticate("")
end
However what your validation does do is that if the user sets a password it must be over 6 characters:
test "does not allow a password less than 6 characters" do
patch user_path(#user), user: { name: name,
email: email,
password: "abc",
password_confirmation: "abc" }
assert assigns(:user).errors.key?(:password)
end
(PS. this is something that is better tested in a model test than a controller test)

How to write Rspec Testing for devise authenticate methods

def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions).first
end
end
Above is the method for authenticate and that code is from Model.
I unable to understand how to pass warden_conditions as parameter in this method for testing.
Can you please help me how to pass warden_condition as parameter for Rspec (Unit) Testiong?
Thanks
That seems to be a class method, and warden_conditions seems to be just a hash, so you can use something like this
let(:email) { "foo#bar.com" }
let(:warden_conditions) { { login: email } }
it "finds user by email" do
user = User.create(email: email)
authenticated = User.find_for_database_authentication(opts)
expect(authenticated).to eql user
end
it "finds user by username" do
user = User.create(username: email)
authenticated = User.find_for_database_authentication(opts)
expect(authenticated).to eql user
end

why is rspec telling me that this user is valid

I have the following classes in Rails and am writing some rspec tests (any critiques are more than welcome as I'm a nOOb at rspec).
class User.rb
class User < ActiveRecord::Base
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true ,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => true },
:on => :create
end
and in factories.rb
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "my-name#{n}" }
sequence(:email) { |n| "blue#{n}#12blue.com" }
end
end
and in my rspec (users_spec.rb):
require 'spec_helper'
describe User do
let(:user) { FactoryGirl.build(:user) }
it { user.should be_valid }
it { user.should be_a(User) }
it { user.should respond_to(:email) }
it { user.email = " " }
it { user.should_not be_valid } # this is causing the error
end
and get
1) User
Failure/Error: it { user.should_not be_valid }
expected valid? to return false, got true
But based upon the validates, user should be not be valid. What is going on here? What am I not getting (and I know it's my fault)?
thx
I assume that the test failure surprises you because you think the user email should be " ".
In rspec every example is independent. This means that anything you did in a previous example is forgotten.
In your case your second to last example runs, builds a new, valid activerecord user whose email is "blue4#12blue.com", overwrites that email with " " and then passes since it makes no assertions.
Then your last example runs, builds a new, valid activerecord user who's email is "blue5#12blue.com" and fails because the user is valid, it's email has not been overwritten.
You probably want something like this:
it 'should validate the email' do
user.email = " "
user.should_not be_valid
end

Resources