Disable password validation - ruby-on-rails

If it's seen that a user is trying to log in with Facebook, I'd like to not require a password when logging in. However, the Authlogic (obviously) checks for a password when logging in. How can I disable password validation in this case?
Below is a short code snippet of the code that I'm working with. login_params changes depending on the method by which the user is logging in -- either by form fields or by OmniAuth.
login_params = if facebook_login?
if homeowner = get_facebook_user
{
email: homeowner.email,
password: homeowner.password
}
end
else
user_session_params
end

You could use a lambda in the validation to run it conditionally. For example:
validates :password, presence: true, if: lambda { !isNotFacebookLogin? }

Related

Rails 5 validation on update fires on create too

I am using factory bot gem to build my factory objects for rspec test. And I have a validation like this in my model :
validates :reason_for_change, presence: true, on: :update
The spec test fails because the object itself cannot be created :
Failure/Error: let(:user) { create(:super_user) }
ActiveRecord::RecordInvalid:
Validation failed: Profile reason for change can't be blank
I have also tried this:
validates :reason_for_change, presence: true, unless: :new_record?
Same error. This as well :
validates :reason_for_change, presence: true, if: :my_custom_method
def my_custom_method
!new_record?
end
Same result. However when I used the last validation and put a breakpoint inside my custom method like this :
def my_custom_method
binding.pry
end
And was manually first checking is this a new_record?, whenever it would evaluate to true, I would return false (manually typing). And for every false evaluated, I'd return true (manually typing).
In this case all tests pass. What is going on here? I am using rails 5.0.1
Update 1:
Here is my super user factory :
factory :super_user do
transient do
email { nil }
end
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
gender { 'male' }
age { Random.rand(18..40) }
ethnicity { '' }
avatar { Rack::Test::UploadedFile.new(Rails.root.join('spec/files/1.png')) }
end
Also I don't think I should modify factory, just to make this work. This should work.
A copy-paste of my comment:
Please check the logs. I believe, you will see there something like this:
INSERT INTO super_users () VALUES(...)
UPDATE super_users SET filed = val
If so - please check another callbacks that may be run when record is saved. My guess is that you have another callback that updates same record in, for example, after_save callback.
You should disable that validation first before trying my suggestion.

Adding JWT to current application

I've been trying to research implementing JWT into my application and a little confused. Currently, I'm using BCrypt in order to Hash and Salt. My file looks like this in the User model
class User < ApplicationRecord
has_secure_password
validates :username, :email, :password_digest, presence: true
validates :password, length: { minimum: 6, allow_nil: true }
attr_reader :password
after_initialize :ensure_session_token
def self.find_by_credentials(email, password)
user = User.find_by(email: email)
user && user.is_password?(password) ? user : nil
end
def self.generate_session_token
SecureRandom.urlsafe_base64
end
def password=(password)
#password = password
self.password_digest = BCrypt::Password.create(password)
end
def is_password?(password)
BCrypt::Password.new(self.password_digest).is_password?(password)
end
def reset_session_token!
self.session_token = User.generate_session_token
self.save!
self.session_token
end
def ensure_session_token
self.session_token ||= User.generate_session_token
end
end
What I'm trying to ask is would I need to create a new method to further encrypt the password output from using BCrypt? Like I can't find any articles where a user encrypts a user password with BCrypt then uses JWT. All I'm seeing is people mentioning adding BCrypt by adding has_secure_password to the user model and basically creating hashing methods with JWT instead.
My question is replace BCrypt with JWT or what are some recommendation in regards to securing a user password with both JWT and BCrypt? also, any beginner friendly articles would be appreciated.
Thanks for all your help and explanation.
JWT is quite a different way compared to the method of logging in a user and using sessions and cookies to authenticate future requests.
If you think of it like this, user comes to your application and goes to login (like normal, with Devise for example). You receive their username and password on the login and check agasint the BCrypt hash in the database. If they successfully login, you then provide them with a JWT token. Inside this token is encoded their user id.
When they make future requests to your application (normally from an API) then they will provide the token instead of their username password. Your server has a secret and can decrypt this token to check if it is valid and can then use the user_id inside to know that it is the correct user. This will allow them to access any resources that they have access to.
JWT is normally used for javascript front ends or Smart phone apps that want to have long login times (rather than session or cookie). The token is also stateless so as long as the server has the secret, it can check its valid and decrypt it.
Here is a more detailed explanation: https://github.com/dwyl/learn-json-web-tokens
Here is a good guide for setting up JWT with devise: https://medium.com/#mazik.wyry/rails-5-api-jwt-setup-in-minutes-using-devise-71670fd4ed03

Allow only specific domain names to register in Rails App (Devise gem)

I have read about similar case here: Validation specific email specific domain devise 3 ruby on rails 4
However, it's only one email domain (e.x. example.com). What I want is to alloww some email domains that I have (ex. domain1.com, domain2.com, domain3.com, etc.). How to achieve that?
you can add the following code in your model
validate :email_domain
def email_domain
domain = email.split("#").last
if !email.blank?
errors.add(:email, "Invalid Domain") if domain != "gmail.com"
end
end
You can add as many domain you want to allow.
I use the code by Pavan in other thread (Allow only specific emails to register in Rails app (Devise)) and it's working.
To allow specific email, I use these code to my user model:
validates :email, format: { with: /\b[A-Z0-9._%a-z\-]+#xyz\.com\z/, message: "must be a xyz.com account" }
APPROVED_DOMAINS = ["domain_one.com", "domain2.com"]
* or you can load this from somewhere else
validates :email, presence: true, if: :domain_check
def domain_check
unless APPROVED_DOMAINS.any? { |word| email.end_with?(word)}
errors.add(:email, "is not from a valid domain")
end
end

Allow admins to edit users details including password [duplicate]

I've looking for a way to allow users to change their settings (User model) without having to change their password (they still have to enter their current password). Devise out of the box seems to allow this, but when you remove the validatable module and setup custom validations, it seems you need to work around a bit.
I have setup the following validation in my user model :
validates :password, length: { in: 6..128 }
When signing up, user is required to specify his password (which is what I expect). When updating settings, though, if I leave password blank it raises an error to the user that says password must be at least 6 characters.
How can I work around this without having to change the way Devise works or having to implement a custom controller of some sort ?
Maybe this will look obvious for some, but it took me a while getting this together. After a few hours of trying different solutions and workarounds and looking all over the place, I dove deeper in Rails validations and found a few constructs that, when put together, make this really easy.
All I had to do was setup a validation for the create action and one for the update action and allow blanks on update.
validates :password, length: { in: 6..128 }, on: :create
validates :password, length: { in: 6..128 }, on: :update, allow_blank: true
With that, I'm getting the behaviour I want and it's only two short lines of code.
Additional note :
At first, I had tried this way :
validates :password, length: { in: 6..128 }, on: :create
This is wrong because it would skip the validation entirely on updates. Users would then be able to set short/long (or blank?) passwords when updating settings.
I tried Amal Kumar S solution, but unfortunately it didn't help me to solve the same issue, so here is modified version of solution tested in real project.
Here is the code from devise/models/validatable.rb module
protected
# Checks whether a password is needed or not. For validations only.
# Passwords are always required if it's a new record, or if the password
# or confirmation are being set somewhere.
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
The validation fails when you leave blank password and password confirmation fields while updating user info, because this condition is always false:
!password.nil? || !password_confirmation.nil?
Password and password_confirmation fields equals blank string '' which is never equals to nil. So you can fix that by overriding password_required? method in your user model and change nil? check to blank? check.
protected
def password_required?
!persisted? || !password.blank? || !password_confirmation.blank?
end
Think that it's easiest way to fix that and it's doesn't ruin original logic. Maybe it's devise's bug.
Add this code to your user model.
private
def password_required?
new_record? ? super : false
end
Devise has its own method to achieve that, update_without_password, It updates record attributes without asking for the current password. Never allows a change to the current password.
Note. If you are using this method, you should probably override this method to protect other attributes you would not like to be updated without a password.
Example:
def update_without_password(params={})
params.delete(:email)
super(params)
end
It is all in the Devise Documentation. http://rdoc.info/gems/devise/index

Devise .skip_confirmation! is not saving

I have added a custom method to my (Devise) User controller:
def confirm
#user = User.find(params[:id])
#user.skip_confirmation!
respond_to
if #user.save
//display success message
else
// display error message
end
end
end
When I try to confirm a user via that, the save fails and I have two errors in #user.errors: password and password_confirm
Even in the rails console, if I try
#user.skip_confirmation!
#save
I get a rollback and the save fails.
Any ideas would be appreciated..
Additional Info
The problem causing this is the
validates_presence_of :password, :password_confirmation
in my User model. Even though a password and password confirmation was entered by the user on signup, those get hashed together to make the encrypted_password. For example, when I find a user in the console, the user has an encrypted_password, but no password or password_confirmation.
I have tried this
validates_presence_of :password, :password_confirmation, :on => :create
However it doesn't force the user to enter both a password and a confirm on the Change Password screen.
Any idea on how to get around this?
I think you're misunderstanding what skip_confirmation! does.
Devise's confirmable module will send a new user an e-mail with a link back to your app. Until they click that link, they'll be "unconfirmed" and unable to sign in. What skip_confirmation! does is skip this workflow -- the user will be immediately confirmed, and can log in without first having to go through the e-mail confirmation flow.
You're getting the error you're getting because of validations on the user model. In particular, the user you're trying to save doesn't appear to have an existing password, so it's requiring a password and matching password_confirmation attribute.
I suspect that there's a better way to accomplish whatever your purpose is. Why isn't the normal confirmation workflow sufficient? What's this extra controller action trying to accomplish?
I think skip_confirmation! actually does the saving.
What you want to check is probably if #user.persisted?

Resources