Authenticable module: generating salt and encrypting password

This commit is contained in:
Carlos A. da Silva 2009-09-17 09:24:33 -03:00
parent 673fda9725
commit 955c3383c2
7 changed files with 248 additions and 0 deletions

20
MIT-LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2009 [name of plugin creator]
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

23
Rakefile Normal file
View File

@ -0,0 +1,23 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
desc 'Default: run unit tests.'
task :default => :test
desc 'Test the devise plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
desc 'Generate documentation for the devise plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'Devise'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end

2
init.rb Normal file
View File

@ -0,0 +1,2 @@
require 'devise'

2
lib/devise.rb Normal file
View File

@ -0,0 +1,2 @@
require 'devise/authenticable'

View File

@ -0,0 +1,55 @@
module Devise
module Authenticable
require 'digest/sha1'
# Password digest config
# Auth key for encrypting password
SECURE_AUTH_SITE_KEY = '23c64df433d9b08e464db5c05d1e6202dd2823f0'
# Times digest will be applied to crypted password
SECURE_AUTH_DIGEST_STRETCHES = 10
def self.included(base)
base.class_eval do
#attr_accessor :password, :password_confirmation
attr_reader :password
attr_accessor :password_confirmation
attr_accessible :email, :password, :password_confirmation
end
end
# Defines the new password, generating a salt and encrypting it.
#
def password=(new_password)
if new_password != @password
@password = new_password
if @password.present?
generate_salt
encrypt_password
end
end
end
private
# Generate password salt using SHA1 based on password and Time.now
#
def generate_salt
self.password_salt = secure_digest(Time.now.utc, password) if password_salt.blank?
end
# Encrypt password using SHA1 based on salt, password and SECURE_AUTH_SITE_KEY
#
def encrypt_password
self.encrypted_password = secure_digest(password_salt, SECURE_AUTH_SITE_KEY, password)
end
# Generate a SHA1 digest joining args. Generated token is something like
#
# --arg1--arg2--arg3--argN--
#
def secure_digest(*tokens)
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
end
end
end

108
test/authenticable_test.rb Normal file
View File

@ -0,0 +1,108 @@
require 'test_helper'
require 'digest/sha1'
class AuthenticableTest < ActiveSupport::TestCase
def valid_attributes(attributes={})
{ :email => 'test@email.com',
:password => '12345',
:password_confirmation => '12345' }.update(attributes)
end
def new_user(attributes={})
User.new(valid_attributes(attributes))
end
def create_user(attributes={})
User.create!(valid_attributes(attributes))
end
def field_accessible?(field)
new_user(field => 'test').send(field) == 'test'
end
test 'should respond to password and password confirmation' do
user = new_user
assert user.respond_to?(:password)
assert user.respond_to?(:password_confirmation)
end
test 'should have email acessible' do
assert field_accessible?(:email)
end
test 'should have password acessible' do
assert field_accessible?(:password)
end
test 'should have password confirmation accessible' do
assert field_accessible?(:password)
end
test 'should not have password salt accessible' do
assert_not field_accessible?(:password_salt)
end
test 'should not have encrypted password accessible' do
assert_not field_accessible?(:encrypted_password)
end
test 'should generate password salt after set the password' do
assert_present new_user.password_salt
assert_present create_user.password_salt
end
test 'should not generate salt while setting password to nil or blank string' do
assert_nil new_user(:password => nil).password_salt
assert_nil new_user(:password => '').password_salt
end
test 'should not change password salt when updating' do
user = create_user
salt = user.password_salt
user.expects(:password_salt=).never
user.save!
assert_equal salt, user.password_salt
end
test 'should generate a sha1 hash for password salt' do
now = Time.now
Time.stubs(:now).returns(now)
expected_salt = ::Digest::SHA1.hexdigest("--#{now.utc}--#{12345}--")
user = create_user
assert_equal expected_salt, user.password_salt
end
test 'should generate encrypted password after setting a password' do
assert_present new_user.encrypted_password
assert_present create_user.encrypted_password
end
test 'should not generate encrypted password while setting password to nil or blank string' do
assert_nil new_user(:password => nil).encrypted_password
assert_nil new_user(:password => '').encrypted_password
end
test 'should not encrypt password if it didn\'t change' do
user = create_user
encrypted_password = user.encrypted_password
user.expects(:encrypted_password=).never
user.password = '12345'
assert_equal encrypted_password, user.encrypted_password
end
test 'should encrypt password again if password has changed' do
user = create_user
encrypted_password = user.encrypted_password
user.password = 'new_password'
assert_not_equal encrypted_password, user.encrypted_password
end
test 'should encrypt password using a sha1 hash' do
digest_key = Devise::Authenticable::SECURE_AUTH_SITE_KEY = 'digest_key'
user = create_user
expected_password = ::Digest::SHA1.hexdigest("--#{user.password_salt}--#{digest_key}--#{12345}--")
assert_equal expected_password, user.encrypted_password
end
end

38
test/test_helper.rb Normal file
View File

@ -0,0 +1,38 @@
require 'test/unit'
require 'rubygems'
require 'active_support'
require 'active_support/test_case'
require 'active_record'
require 'mocha'
require File.join(File.dirname(__FILE__), '..', 'lib', 'devise')
ActiveRecord::Base.logger = Logger.new(nil)
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
ActiveRecord::Schema.define(:version => 1) do
create_table :users do |t|
t.string :email, :null => false
t.string :encrypted_password, :null => false
t.string :password_salt, :null => false
end
end
class User < ::ActiveRecord::Base
include ::Devise::Authenticable
end
class ActiveSupport::TestCase
def assert_not(assertion)
assert !assertion
end
def assert_blank(assertion)
assert assertion.blank?
end
def assert_not_blank(assertion)
assert !assertion.blank?
end
alias :assert_present :assert_not_blank
end