Validates uniqueness but only when 'Admin' is selected as username - ruby-on-rails

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.

Related

Devise: Unable to limit model validation to specific def's

So I'm working on the registration aspect of the site currently. I have a main sign up which is just full name, email and password. (aka new.html.erb)
After you fill in that information I direct you to a new site (setup.html.erb) and ask for more info like city, country etc.
On that you also have the edit profile account.
I am trying to make my app more secure and adding restrictions and presence etc in the model. However how can I limit them.
Currently if I do
validates :email, presence: true,
and I go to a form that doesn't even contain the email for nor permits it I get an error up that I need to add an email.
Also how do I fix this: I make presence true, I input require in html5. But still if I go to my source code and just remove the form and push submit it saves and I can bypass adding info.
Currently if I do validates :email, presence: true,
and I go to a form that doesn't even contain the email for nor permits it I get an error up that I need to add an email.
Fix:
what you need is a conditional validation. If we look at rail guides it says
Sometimes it will make sense to validate an object only when a given predicate is satisfied. You can do that by using the :if and :unless options, which can take a symbol, a string, a Proc or an Array.
So in your model you could do something like:
class Order < ActiveRecord::Base
validates :email, presence: true, if: :need_to_validate?
def need_to_validate?
#your condition to check whether you want email validation or not
end
end
Update:
You can use params[:action] and params[:controller] smartly to check in which action and controller(hence which view) you currently are in so your method would be:
def need_to_validate?
params[:action] == your_view_action && params[:controller] == your_controller_name #your condition to check whether you want email validation or not
end

Rails Conditional Validations

I am working on a project and need some help on where to begin. I have three pages
Update User
Create User
Admin User Password Change (like a Hard Reset Password for but only the admin can reset the user's password)
Change Password
On Create User first name, last name, username, password, and password confirmation are mandatory.
On Update User just first name, last name and username are mandatory.
On Admin User Password Change and Change Password, just password and password confirmation are mandatory.
How would you go about doing this? I don't think this is possible through models using validates_presence_of with an if because there are too many scenarios. Any help or guidance would be appreciated. Also, I am pretty new to Rails if you can't already tell.
You can pass conditionals to your validations:
validates :password, :confirmation => true, :presence => true
validates :first_name, :last_name, :username, :presence => true
validate :admin_user_password_change?
Of course you'd have to define what the admin_user_password_change? method would be to determine if it is an admin user changing a password.
UPDATE
The admin_user_password_change? method might be something like:
def admin_user_password_change?
unless self.admin? && self.password.present? && self.password_confirmation.present?
self.errors.add(:admin_password_change, "password and password_confirmation are required.")
end
end
As for How would it communicate with the controller?, it wouldn't directly. But if any of the conditions in the method are false (e.g. self.admin? && self.password.present? && self.password_confirmation.present?), an error will be added to the instance of User and the instance won't save in the controller.
Setting some fields to new values doesn't unset other fields; just because you're only updating some fields in one action doesn't mean the other fields will be unset, so long as they start in a consistent state.
Just add your validations. It will work fine.
You can tell to your validation work only on certain cenarios only using:
The create:
validates :first_name, :last_name, :username, presence: true, on: :create
The update:
validates :password, presence: true, on: :update
Take a look at on.
For validation based on context take a look at Context Validations

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=.

Unique usernames in Devise for Rails

I followed Devise's instructions for using usernames in addition to emails for logins. Works great except people can signup with duplicate usernames. If someone tries to signup with a duplicate email address, they are presented with this error message on the signup page:
1 error prohibited this user from being saved:
Email has already been taken
I want something very similar for usernames. How do I implement this?
You should use uniqueness validator. Add following into user.rb:
validates_uniqueness_of :username
more about this validator
Note that you probably want to make usernames unique, but also case-insensitive, so you don't end up with both username and Username:
class User < ApplicationRecord
validates :username, presence: true, uniqueness: { case_sensitive: false }
# Rest of user model
end
From the Devise wiki.

Authlogic Multiple Password Validation

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

Resources