Authlogic Multiple Password Validation - ruby-on-rails

I'm using Authlogic to manage my user sessions. I'm using the LDAP add-on, so I have the following in my users model
acts_as_authentic do |c|
c.validate_password_field = false
end
The problem is that recently I found out that there will be some users inside the application that won't be part of the LDAP (and can't be added!). So I would need to validate SOME passwords against the database and the others against the LDAP.
The users whose password will be validated against the database will have an specific attribute that will tell me that that password will be validated in my database.
How can I manage that? Is it possible that the validate_password_field receives a "variable"? That way I could create some method that will return true/false depending on where the password validation will be done?
Thanks!
Nicolás Hock Isaza

You should be able to do this:
acts_as_authentic do |u|
u.validate_password_field = true
authentic_options = {:unless => Proc.new{|c| c.ldap}}
u.merge_validates_confirmation_of_password_field_options(authentic_options)
u.merge_validates_length_of_password_confirmation_field_options(authentic_options)
u.merge_validates_length_of_password_field_options(authentic_options)
end
If you were writing the validation yourself (not using authlogic) you would want to do something like this in the validation:
validates_presence_of :password, :unless => Proc.new{|u| u.ldap }
Since authlogic provides the 3 helper methods to add options to the end of the validates methods, you can use this to turn off validations when using LDAP.

You should be able to do an unless in your validation.
acts_as_authentic do |c|
c.validate_password_field = false if c.ldap
end
Or even (as your model field is a boolean) :
acts_as_authentic do |c|
c.validate_password_field = c.ldap
end

Related

Validates uniqueness but only when 'Admin' is selected as username

I want to avoid that somebody can create an user with the username 'Admin'. I use this username as Host to control some functions of my application. I know there is a expression to validate the uniqueness of an model record:
validates_uniqueness_of
But i only want that the username 'Admin' cant be created twice. I hope somebody has an idea! Thanks
You can use conditionals in your validation to do the following:
class Model
validates :username, uniqueness: true, if: :admin?
def admin?
username.downcase == 'admin'
end
end
With that said, they'd take away my developer card if I didn't discourage you from doing this.
Basing an admin account solely on whether the username is admin is setting yourself up for your security being compromised. Have a look at something like ActiveAdmin for managing your administrator accounts so that they're at the very least separated from your user accounts.
Or just validates_uniqueness_of :username, if: -> {username == 'admin'} in your model.

has_secure_password authenticate inside validation on password update

I'm using has_secure_password in a User model. I have implemented a way for users to change their password outside of the model, but to keep things DRY, I'm trying to move the validations needed from the controller to the model.
The User model looks something like this:
class User
include Mongoid::Document
include ActiveModel::SecurePassword
has_secure_password
field: :password_digest, type: String
attr_accessible :password, :password_confirmation, :current_password
end
Users change their passwords by submitting the following:
user[current_password] - Currently stored password
user[password] - New password
user[password_confirmation] - New password confirmation
I'm using update_attributes(params[:user]) on the User model for the current user. My problem is that calling update_attributes updates the password_digest before using validations, so the following code won't work:
def password_validation_required?
password_digest.blank? || !password.blank? || !password_confirmation.blank?
end
validate(on: :update, if: :password_validation_required?) do
unless authenticate(current_password)
add(:current_password, 'invalid password')
end
end
authenticate is authenticating based on the new password_digest generated from user[password]. Is there an elegant way to access the old password_digest value for authentication? One idea I had was to re-query the user to gain access to another authenticate method that will authenticate against the old password_digest value. The problem is that it's not a clean solution.
I think this one's a bit cleaner than #Parazuce's:
validate :validates_current_password
private
def validates_current_password
return if password_digest_was.nil? || !password_digest_changed?
unless BCrypt::Password.new(password_digest_was) == current_password
errors.add(:current_password, "is incorrect")
end
end
The password_digest field has ActiveModel::Dirty methods associated with it, so I decided to go with:
validate(on: :update, if: :password_validation_required?) do
unless BCrypt::Password.new(password_digest_was) == current_password
errors.add(:current_password, "is incorrect")
end
end
This prevents the need to override password= with additional logic which could introduce bugs in the future if other features used password=.

How to skip has_secure_password validations

In my app, only admins can create new User records. The user is emailed an activation link where they set their password.
I'd like to use the has_secure_passord method (railscast):
class User < ActiveRecord::Base
has_secure_password
...
end
Works great, but it automatically validates presence of password digest...so when the admin creates the record the validations fail. I there a way to skip just the automatically added password_digest validation without skipping the others I've added?
Since 4.X versions of rails, has_secure_password takes an option :validations. If you set it to false, it will not run validations.
The 3.X version of the gem does not support this parameter. However you can backport the activemodel/lib/active_model/secure_password.rb from latest 4.0.XRC code which supports this argument.
So with that, your code will look like this:
class User < ActiveRecord::Base
has_secure_password :validations => false
...
end
I decided to do my own custom authentication. The following solution will validate passwords but only when they are being set. This allows admins to create users without adding a password.
class User < ActiveRecord::Base
include BCrypt
attr_accessor :password, :password_confirmation
validates :password, length: (6..32), confirmation: true, if: :setting_password?
def password=(password)
#password = password
self.password_hash = Password.create(password)
end
def authenticate(password)
password.present? && password_hash.present? && Password.new(password_hash) == password
end
private
def setting_password?
password || password_confirmation
end
end
If someone posts an answer that allows me to still use the has_secure_password method, I'll accept it instead.
There's no way to skip the validation, but it would be easy enough to write your own version of the method that allows you to pass an argument to determine whether or not to validate presence of the password_digest field.
Just extend ActiveModel the same way they do in the SecurePassword module (via ActiveSupport::Concern) and add your own secure password method.
i.e.
module ActiveModel
module MySecurePassword
extend ActiveSupport::Concern
module ClassMethods
def my_has_secure_password(validate_password_digest=true)
# you custom logic
end
end
end
end
Piggybacking off of Journeyer and Tybro's answers I wanted to see if you can add a private method that returns true or false to the validations hash. It worked and I used it for a different use case but in this context it would look something like this.
class User < ActiveRecord::Base
has_secure_password validations: !:setting_password
#If setting_password is true then this will be the same as
validations: false
.....
private
def setting_password?
#Some logic that determines if a user is setting a password and resolves
to true or false
password || password_confirmation
end
...
end

Rails Password Validate only in certain cases

In my rails 3 application, I have a user sign up form. I have it require and validate the password for signing up, however, if a user connects using omniauth, I cannot retrieve their password which brings me to my question. How can I make it so the password field does not get validated in certain cases?
I am aware that I can do user.save(:validate => false), but that takes off all the validations.
P.S. i watched railscasts episode 235 & 236, however Ryan Bates does it with Devise, and I am doing it with my own login system.
You can write your own validation for password with a check if the user is using oauth.
Check this method: validate(*args, &block)
you can add conditions to the validation in the model:
validates_presence_of :password, :unless => :account_from_omniauth?
assuming you also define the method
def account_from_omniauth?
true if //...your conditions
false
end

How can I skip email validation with Authlogic in Rails?

I use Authlogic for authentication in my Rails project.
It provide default validation for email and login field.
But, I want to allow email to be null for join because my service is for mobile and it is difficult to insert email field in mobile device.
How can I skip email validation with Authlogic in Rails?
class User < ActiveRecord::Base
acts_as_authentic do |c|
c.validate_email_field = false
end
end
I was digging for this one for awhile - extending on what jaehyun said - say you want to skip email validation in Authlogic on a conditional basis
acts_as_authentic do |c|
c.merge_validates_length_of_email_field_options({:unless => Proc.new { |user| user.has_no_email? }})
c.merge_validates_format_of_email_field_options({:unless => Proc.new { |user| user.has_no_email? }})
end

Resources