Authenticable module: generating salt and encrypting password
This commit is contained in:
parent
673fda9725
commit
955c3383c2
|
@ -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.
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
require 'devise/authenticable'
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue