How to search a record in RoR? - ruby-on-rails

I know that I can use Query language to find the record I want. I am doing a login page, I want to find the record which match the user name and password, but I don't want to loop all the elements to find out the user I want (<% #users.each do |user| %>), wt should I do in RoR, except typing SQL.

perhaps:
User.first(:conditions => {:login => 'ted', :password => 'secret'})
# returns nil for no match and first match for a good record
# make sure there is a unique index on login
For authentication I would strongly recommend authlogic (railscast)

You can use dynamic finders to find user by user_name and password:
#user = User.find_by_user_name_and_password('scott', 'tiger')

While the other answers provided by Sam and Chandra are technically correct, both solutions implies that passwords are stored in plain text--which is a very bad idea. If somebody who shouldn't gets access to your database, they'll have a full set of usernames (and potentially email addresses), combined with all of their passwords.
Instead, consider using an algorithm to make sure your password is encrypted in the database, such as bcrypt. You'll need the bcrypt-rub gem to use it.
You should also consider leaving out the password from the query altogether. This is good practice as it provides an extra level of security; SQL injections become more difficult to perform. If users have unique usernames, just fetching the username should return the same object, after which you can check if the password is correct:
#user = User.find_by_username(params[:username])
if #user.password == params[:password]
# do something
else
# do something else
end
Ideally, you should both use bcrypt and leave out the password from the query. How to do this is described in the bcrypt-ruby readme on GitHub (the link I provided).

Related

Should I accept a crypted password with AuthLogic?

Basically as the question asks.
The AuthLogic documentation for verify_password_method states:
The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
I'd like to support this, because it allows me to maintain our system's current masquerading behaviour without storing plain-text passwords like we have been in the past. However, it seems to me like allowing a simple string comparison to the crypted_password in the database is just as bad as storing the regular plain-text passwords.
Am I missing something here, or is there some other way to accept a crypted password in AuthLogic?
For reference, this is how I was going to write the valid_password? method:
def valid_password?(password, check_from_database = nil)
if password == self.crypted_password
true
else
super(password, check_from_database)
end
end
Ok, turns out that there's a much easier way to do this (although it seems horribly undocumented, and didn't turn up with a Google search of how to achieve this).
Authlogic::Session::Base.new(#user, true)
That line allows session creation without checking credentials. Obviously you should be careful with this since it assumes that the user has already identified themselves correctly - for my usage, since there is a check to ensure the current user is the admin user, it's safe.

Rails 3 + Devise: How do I change other user's passwords from an admin role?

I've developed a Rails 3 application with Devise for registration and login control. I want to be able to modify any user's password to one I provide.
The solution I've come up with (I haven't had the chance to test it yet) is to make a fake new registration with the password I choose, copy the password from the table record to the user's record in question, and then delete the fake record I generated in the DB. It's not the most elegant thing to do, but it is all I've got. I wait for better suggestions.
I might be misunderstanding the question but it should be as simple as;
#user = User.find(<some id>)
#user.update_attributes(:password => 'anewpassword', :password_confirmation => 'anewpassword')
then their password will be 'anewpassword'

Encrypting Passwords to match stored encrypted passwords.

I started working on a project which had a lot of code already in place. It is a Ruby on Rail application that uses Devise for user authentication. One of the requirements of the application is that when a user changes their password, they are not allowed to use the same password as the last three passwords they previously used. To acomplish this, there is a table that contains a history of passwords for a given user. These passwords are copies of the encrypted passwords that existed prior to any password change on the user.
Here is where the problem comes in. We have a password change form that collects the new password for a given user. I need to be able to take the new password and encrypt it so that I can match the encrypted value of the new password against encrypted values of the old passwords in history.
Technical Stuff
Rails version 3.0.9
Devise version 1.3.4
Using standard BCrypt with Devise. bcrypt_ruby version 2.1.4
To do this we are overrriding the reset_password method supported by Devise. This allows us to introduce our own method, has_repeated_password in the user controller.
The version of has_repeated_password I started with is below:
def has_repeated_password?
return false if self.new_record? || self.version == 1
histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)
histories.detect do |history|
history.encrypted_password == self.class.encryptor_class.digest(self.password, self.class.stretches, history.password_salt, self.class.pepper)
end
end
The problem here is that the encryptor class is never defined, causing an error every time this routine runs. Even through there are many examples that claim this works, I cannot get it to work when Devise is using the default encryption.
A second attempt at this is the following code:
def has_repeated_password?<br>
return false if self.new_record? || self.version == 1
histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)
histories.detect do |history|
pwd = self.password_digest(self.password)
history.encrypted_password == pwd
end
end
In this case, I never get a password that matches any of the stored passwords, even though I have verified that the password in the database is what I expect.
I have been trying to dig through the Devise code to see what I can find there. I know that the autentication has to do this somehow when it matches passwords collected from users against the stored password.
Any help would be appreciated.
I think I found a solution to my own problem. The key sticking point of this was that I was trying to get an encrypted password that was not part of the user model (any more) tied to Devise. This solution does assume that Devise will be using Bcrypt as the standard encryption tool (can't remember which version of Devise made the move). Bcrypt/Devise actually buries the salt for the password in the encrypted password. If you have the salt and the pepper, you can get the same password to generate the same encrypted value.
So here is the updated code for the routine refernced above:
def has_repeated_password?
return false if self.new_record? || self.version == 1
histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)
histories.detect do |history|
bcrypt = ::BCrypt::Password.new(history.encrypted_password)
password = ::BCrypt::Engine.hash_secret("#{self.password}#{self.class.pepper}", bcrypt.salt)
password == history.encrypted_password
end
end
The key here is that the Bcyrpt object has to be created with an existing encrypted password using the same salt that generated the original password. That is accomplished by giving it my stored historical encrypted password (history.encrypted_password). One of the other key elements is that both the history passwords and the proposed new password use the same pepper, which is managed by Devise. So by using the Engne.has_secret call with the intended new password, it can be compared with the history password.
I had to move the bcrypt code into here because all the password methods supported by Devise assume that you want to act on the user password of the current user object.

Validate password on change of certain fields in RoR

I am building a RoR 3 app, a community. It has a User model and some fields.
So when a user is updating a certain field, like his/her birthday, I want to validate that the User typed in the password that is the same in the database. This way I know that it is the right user trying to change the birthday.
So I ask you how i can create such a validator.
Also I would like to be able to specify an array of which fields the user has to validate the password to change.
This is actually pretty easy to do once you are familiar with the Rails framework.
models/User.rb
class User < ActiveRecord::Base
validate :correct_password?, :if => :check_password?
def check_password?
[birthday_changed?, other_field_changed?].any?
end
def correct_password?
# without knowing more about how you store the password
# this probably won't work with your code directly
errors.add_to_base("Must provide password") unless password?
errors.add_to_base("Incorrect password") unless password == User.find_by_id(id).password
end
end
Even though building user authentication and authorization is not hard - I would advise to use something like "AuthLogic" or "Devise" gems/plugins which will most likely cover 90% of the functionality that you need. You alsways can customize/add new functionality if needed.
Such plugins will do most of the grunt work for you: generate MVC, create database, do proper security checks, even email password recovery and such.

LDAP through Ruby or Rails

I've been attempting to hook a Rails application up to ActiveDirectory. I'll be synchronizing data about users between AD and a database, currently MySQL (but may turn into SQL Server or PostgreSQL).
I've checked out activedirectory-ruby, and it looks really buggy (for a 1.0 release!?). It wraps Net::LDAP, so I tried using that instead, but it's really close to the actual syntax of LDAP, and I enjoyed the abstraction of ActiveDirectory-Ruby because of its ActiveRecord-like syntax.
Is there an elegant ORM-type tool for a directory server? Better yet, if there were some kind of scaffolding tool for LDAP (CRUD for users, groups, organizational units, and so on). Then I could quickly integrate that with my existing authentication code though Authlogic, and keep all of the data synchronized.
Here is sample code I use with the net-ldap gem to verify user logins from the ActiveDirectory server at my work:
require 'net/ldap' # gem install net-ldap
def name_for_login( email, password )
email = email[/\A\w+/].downcase # Throw out the domain, if it was there
email << "#mycompany.com" # I only check people in my company
ldap = Net::LDAP.new(
host: 'ldap.mycompany.com', # Thankfully this is a standard name
auth: { method: :simple, email: email, password:password }
)
if ldap.bind
# Yay, the login credentials were valid!
# Get the user's full name and return it
ldap.search(
base: "OU=Users,OU=Accounts,DC=mycompany,DC=com",
filter: Net::LDAP::Filter.eq( "mail", email ),
attributes: %w[ displayName ],
return_result:true
).first.displayName.first
end
end
The first.displayName.first code at the end looks a little goofy, and so might benefit from some explanation:
Net::LDAP#search always returns an array of results, even if you end up matching only one entry. The first call to first finds the first (and presumably only) entry that matched the email address.
The Net::LDAP::Entry returned by the search conveniently lets you access attributes via method name, so some_entry.displayName is the same as some_entry['displayName'].
Every attribute in a Net::LDAP::Entry is always an array of values, even when only one value is present. Although it might be silly to have a user with multiple "displayName" values, LDAP's generic nature means that it's possible. The final first invocation turns the array-of-one-string into just the string for the user's full name.
Have you tried looking at these:
http://saush.wordpress.com/2006/07/18/rubyrails-user-authentication-with-microsoft-active-directory/
http://xaop.com/blog/2008/06/17/simple-windows-active-directory-ldap-authentication-with-rails/
This is more anecdotal than a real answer...
I had a similar experience using Samba and OpenLDAP server. I couldn't find a library to really do what I wanted so I rolled my own helper classes.
I used ldapbrowser to see what fields Samba filled in when I created a user the "official" way and and basically duplicated that.
The only tricky/non-standard LDAP thing was the crazy password encryption we have:
userPass:
"{MD5}" + Base64.encode64(Digest::MD5.digest(pass))
sambaNTPassword:
OpenSSL::Digest::MD4.hexdigest(Iconv.iconv("UCS-2", "UTF-8", pass).join).upcase
For the def authenticate(user, pass) function I try to get LDAP to bind to the domain using their credentials, if I catch an exception then the login failed, otherwise let them in.
Sorry, cannot comment yet... perhaps someone can relocate this appropriately.
#Phrogz's solution works well, but bind_simple (inside bind) raises an Net::LDAP::LdapError exception due to auth[:username] not being set as shown here:
https://github.com/ruby-ldap/ruby-net-ldap/blob/master/lib/net/ldap.rb
The corrected replaces:
auth: { method: :simple, email: email, password:password }
with:
auth: { method: :simple, username: email, password:password }
I began using ruby-activedirectory, and even extended it/fixed a few things, hosting judy-activedirectory in Github.
Doing the next iteration, I've discovered ActiveLdap has a much better code base, and I'm seriously contemplating switching to it. Does anyone have personal experience with this?
Have you checked out thoughtbot's ldap-activerecord-gateway? It might be something for you to consider...
http://github.com/thoughtbot/ldap-activerecord-gateway/tree/master

Resources