devise 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise might be problematic. Click here for more details.

Files changed (37) hide show
  1. data/CHANGELOG.rdoc +10 -0
  2. data/README.rdoc +17 -1
  3. data/TODO +1 -3
  4. data/app/controllers/confirmations_controller.rb +1 -1
  5. data/app/controllers/passwords_controller.rb +1 -1
  6. data/generators/devise/templates/migration.rb +1 -1
  7. data/generators/devise_install/templates/devise.rb +10 -0
  8. data/lib/devise.rb +33 -4
  9. data/lib/devise/encryptors/authlogic_sha512.rb +28 -0
  10. data/lib/devise/encryptors/clearance_sha1.rb +26 -0
  11. data/lib/devise/encryptors/restful_authentication_sha1.rb +29 -0
  12. data/lib/devise/encryptors/sha1.rb +34 -0
  13. data/lib/devise/encryptors/sha512.rb +34 -0
  14. data/lib/devise/hooks/confirmable.rb +2 -2
  15. data/lib/devise/hooks/rememberable.rb +8 -6
  16. data/lib/devise/mapping.rb +7 -8
  17. data/lib/devise/middlewares/rememberable.rb +35 -0
  18. data/lib/devise/models.rb +3 -0
  19. data/lib/devise/models/authenticatable.rb +6 -16
  20. data/lib/devise/models/rememberable.rb +1 -1
  21. data/lib/devise/orm/active_record.rb +37 -0
  22. data/lib/devise/orm/mongo_mapper.rb +23 -0
  23. data/lib/devise/rails.rb +4 -4
  24. data/lib/devise/rails/routes.rb +8 -2
  25. data/lib/devise/rails/warden_compat.rb +7 -4
  26. data/lib/devise/schema.rb +43 -0
  27. data/lib/devise/version.rb +1 -1
  28. data/test/devise_test.rb +1 -1
  29. data/test/encryptors_test.rb +28 -0
  30. data/test/integration/rememberable_test.rb +1 -3
  31. data/test/mapping_test.rb +8 -0
  32. data/test/models/authenticatable_test.rb +18 -6
  33. data/test/rails_app/config/routes.rb +3 -1
  34. data/test/routes_test.rb +5 -2
  35. metadata +12 -4
  36. data/lib/devise/migrations.rb +0 -51
  37. data/lib/devise/strategies/rememberable.rb +0 -35
@@ -1,3 +1,13 @@
1
+ == 0.5.0
2
+
3
+ * bug fix
4
+ * Fixed a bug where remember me module was not working properly
5
+
6
+ * enhancements
7
+ * Moved encryption strategy into the Encryptors module to allow several algorithms (by github.com/mhfs)
8
+ * Implemented encryptors for Clearance, Authlogic and Restful-Authentication (by github.com/mhfs)
9
+ * Added support for MongoMapper (by github.com/shingara)
10
+
1
11
  == 0.4.3
2
12
 
3
13
  * bug fix
@@ -27,7 +27,7 @@ All gems are on gemcutter, so you need to add gemcutter to your sources if you h
27
27
 
28
28
  sudo gem sources -a http://gemcutter.org/
29
29
 
30
- Install warden gem if you don't have it installed (requires 0.5.1 or higher):
30
+ Install warden gem if you don't have it installed (requires 0.5.2 or higher):
31
31
 
32
32
  sudo gem install warden
33
33
 
@@ -228,10 +228,26 @@ Devise mailer uses the same pattern to create subject messages:
228
228
 
229
229
  Take a look at our locale file to check all available messages.
230
230
 
231
+ == Migrating from other solutions
232
+
233
+ Devise implements encryption strategies for Clearance, Authlogic and Restful-Authentication. To make use of it set the desired encryptor in the encryptor initializer config option. You might also need to rename your encrypted password and salt columns to match Devises's one (encrypted_password and password_salt).
234
+
235
+ == Other ORMs
236
+
237
+ Devise was made to work from scratch with ActiveRecord. However it currently supports MongoMapper as well.
238
+ To use it, just set Devise.orm or configure it in the initialization file (which is created with devise_install).
239
+
231
240
  == TODO
232
241
 
233
242
  Please refer to TODO file.
234
243
 
244
+ == Contributors
245
+
246
+ * José Valim (http://github.com/josevalim)
247
+ * Carlos Antônio da Silva (http://github.com/carlosantoniodasilva)
248
+ * Marcelo Silveira (http://github.com/mhfs)
249
+ * Cyril Mougel (http://github.com/shingara)
250
+
235
251
  == Bugs and Feedback
236
252
 
237
253
  If you discover any bugs or want to drop a line, feel free to create an issue on
data/TODO CHANGED
@@ -1,8 +1,6 @@
1
+ * Allow authentication keys to be configured, so things like username and subdomain can be used
1
2
  * Devise::Timeoutable
2
3
  * Devise::TestHelper
3
4
  * Use request_ip in session cookies
4
5
  * Devise::BruteForceProtection
5
6
  * Devise::MagicColumns
6
- * Improve Generators to pass modules as arguments
7
- * Different cryptography providers
8
- * Devise::Invitable
@@ -18,7 +18,7 @@ class ConfirmationsController < ApplicationController
18
18
  end
19
19
  end
20
20
 
21
- # GET /resource/confirmation?perishable_token=abcdef
21
+ # GET /resource/confirmation?confirmation_token=abcdef
22
22
  def show
23
23
  self.resource = resource_class.confirm!(:confirmation_token => params[:confirmation_token])
24
24
 
@@ -20,7 +20,7 @@ class PasswordsController < ApplicationController
20
20
  end
21
21
  end
22
22
 
23
- # GET /resource/password/edit?perishable_token=abcdef
23
+ # GET /resource/password/edit?reset_password_token=abcdef
24
24
  def edit
25
25
  self.resource = resource_class.new
26
26
  resource.reset_password_token = params[:reset_password_token]
@@ -1,7 +1,7 @@
1
1
  class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table(:<%= table_name %>) do |t|
4
- t.authenticatable
4
+ t.authenticatable :encryptor => :sha1
5
5
  t.confirmable
6
6
  t.recoverable
7
7
  t.rememberable
@@ -8,6 +8,13 @@ Devise.setup do |config|
8
8
  # Configure how many times you want the password is reencrypted. Default is 10.
9
9
  # config.stretches = 10
10
10
 
11
+ # Define which will be the encryption algorithm. Supported algorithms are :sha1
12
+ # (default) and :sha512. Devise also supports encryptors from others authentication
13
+ # frameworks as :clearance_sha1, :authlogic_sha512 (then you should set stretches
14
+ # above to 20 for default behavior) and :restful_authentication_sha1 (then you
15
+ # should set stretches to 10, and copy REST_AUTH_SITE_KEY to pepper)
16
+ # config.encryptor = :sha1
17
+
11
18
  # The time you want give to your user to confirm his account. During this time
12
19
  # he will be able to access your application without confirming. Default is nil.
13
20
  # config.confirm_within = 2.days
@@ -18,6 +25,9 @@ Devise.setup do |config|
18
25
  # Configure the e-mail address which will be shown in DeviseMailer.
19
26
  # config.mailer_sender = "foo.bar@yourapp.com"
20
27
 
28
+ # Configure the ORM. Supports :active_record and :mongo_mapper
29
+ # config.orm = :active_record
30
+
21
31
  # If you want to use other strategies, that are not (yet) supported by Devise,
22
32
  # you can configure them inside the config.warden block. The example below
23
33
  # allows you to setup OAuth, using http://github.com/roman/warden_oauth
@@ -8,7 +8,7 @@ module Devise
8
8
  :confirmations => :confirmable
9
9
  }.freeze
10
10
 
11
- STRATEGIES = [:rememberable, :authenticatable].freeze
11
+ STRATEGIES = [:authenticatable].freeze
12
12
  TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze
13
13
 
14
14
  # Maps the messages types that comes from warden to a flash type.
@@ -18,26 +18,50 @@ module Devise
18
18
  :unconfirmed => :failure
19
19
  }
20
20
 
21
+ # Declare encryptors length which are used in migrations.
22
+ ENCRYPTORS_LENGTH = {
23
+ :sha1 => 40,
24
+ :sha512 => 128,
25
+ :clearance_sha1 => 40,
26
+ :restful_authentication_sha1 => 40,
27
+ :authlogic_sha512 => 128
28
+ }
29
+
21
30
  # Used to encrypt password. Please generate one with rake secret
22
31
  mattr_accessor :pepper
23
32
  @@pepper = nil
24
-
33
+
25
34
  # The number of times to encrypt password.
26
35
  mattr_accessor :stretches
27
36
  @@stretches = 10
28
-
37
+
29
38
  # Time interval where the remember me token is valid.
30
39
  mattr_accessor :remember_for
31
40
  @@remember_for = 2.weeks
32
-
41
+
33
42
  # Time interval you can access your account before confirming your account.
34
43
  mattr_accessor :confirm_within
35
44
  @@confirm_within = 0.days
36
45
 
46
+ # Used to define the password encryption algorithm.
47
+ def self.encryptor=(value)
48
+ @@encryptor = if value.is_a?(Symbol)
49
+ ::Devise::Encryptors.const_get(value.to_s.classify)
50
+ else
51
+ value
52
+ end
53
+ end
54
+ mattr_reader :encryptor
55
+ @@encryptor = ::Devise::Encryptors::Sha1
56
+
37
57
  # Store scopes mappings.
38
58
  mattr_accessor :mappings
39
59
  @@mappings = {}
40
60
 
61
+ # Stores the chosen ORM.
62
+ mattr_accessor :orm
63
+ @@orm = :active_record
64
+
41
65
  class << self
42
66
  # Default way to setup Devise. Run script/generate devise_install to create
43
67
  # a fresh initializer with all configuration values.
@@ -86,6 +110,11 @@ module Devise
86
110
  # If the user provided a warden hook, call it now.
87
111
  @warden_config.try :call, manager
88
112
  end
113
+
114
+ # The class of the configured ORM
115
+ def orm_class
116
+ Devise::Orm.const_get(@@orm.to_s.camelize.to_sym)
117
+ end
89
118
  end
90
119
  end
91
120
 
@@ -0,0 +1,28 @@
1
+ require "digest/sha2"
2
+
3
+ module Devise
4
+ # Implements a way of adding different encryptions.
5
+ # The class should implement a self.digest method that taks the following params:
6
+ # - password
7
+ # - stretches: the number of times the encryption will be applied
8
+ # - salt: the password salt as defined by devise
9
+ # - pepper: Devise config option
10
+ #
11
+ module Encryptors
12
+ # = AuthlogicSha512
13
+ # Simulates Authlogic's default encryption mechanism.
14
+ # Warning: it uses Devise's stretches configuration to port Authlogic's one. Should be set to 20 in the initializer to silumate
15
+ # the default behavior.
16
+ class AuthlogicSha512
17
+
18
+ # Gererates a default password digest based on salt, pepper and the
19
+ # incoming password.
20
+ def self.digest(password, stretches, salt, pepper)
21
+ digest = [password, salt].flatten.join('')
22
+ stretches.times { digest = Digest::SHA512.hexdigest(digest) }
23
+ digest
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ require "digest/sha1"
2
+
3
+ module Devise
4
+ # Implements a way of adding different encryptions.
5
+ # The class should implement a self.digest method that taks the following params:
6
+ # - password
7
+ # - stretches: the number of times the encryption will be applied
8
+ # - salt: the password salt as defined by devise
9
+ # - pepper: Devise config option
10
+ #
11
+ module Encryptors
12
+ # = ClearanceSha1
13
+ # Simulates Clearance's default encryption mechanism.
14
+ # Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
15
+ # Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES
16
+ class ClearanceSha1
17
+
18
+ # Gererates a default password digest based on salt, pepper and the
19
+ # incoming password.
20
+ def self.digest(password, stretches, salt, pepper)
21
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ require "digest/sha1"
2
+
3
+ module Devise
4
+ # Implements a way of adding different encryptions.
5
+ # The class should implement a self.digest method that taks the following params:
6
+ # - password
7
+ # - stretches: the number of times the encryption will be applied
8
+ # - salt: the password salt as defined by devise
9
+ # - pepper: Devise config option
10
+ #
11
+ module Encryptors
12
+ # = RestfulAuthenticationSha1
13
+ # Simulates Restful Authentication's default encryption mechanism.
14
+ # Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
15
+ # Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES. Should be set to 10 in
16
+ # the initializer to silumate the default behavior.
17
+ class RestfulAuthenticationSha1
18
+
19
+ # Gererates a default password digest based on salt, pepper and the
20
+ # incoming password.
21
+ def self.digest(password, stretches, salt, pepper)
22
+ digest = pepper
23
+ stretches.times { digest = Digest::SHA1.hexdigest([digest, salt, password, pepper].flatten.join('--')) }
24
+ digest
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ require "digest/sha1"
2
+
3
+ module Devise
4
+ # Implements a way of adding different encryptions.
5
+ # The class should implement a self.digest method that taks the following params:
6
+ # - password
7
+ # - stretches: the number of times the encryption will be applied
8
+ # - salt: the password salt as defined by devise
9
+ # - pepper: Devise config option
10
+ #
11
+ module Encryptors
12
+ # = Sha1
13
+ # Uses the Sha1 hash algorithm to encrypt passwords.
14
+ class Sha1
15
+
16
+ # Gererates a default password digest based on stretches, salt, pepper and the
17
+ # incoming password.
18
+ def self.digest(password, stretches, salt, pepper)
19
+ digest = pepper
20
+ stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
21
+ digest
22
+ end
23
+
24
+ private
25
+
26
+ # Generate a SHA1 digest joining args. Generated token is something like
27
+ # --arg1--arg2--arg3--argN--
28
+ def self.secure_digest(*tokens)
29
+ ::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require "digest/sha2"
2
+
3
+ module Devise
4
+ # Implements a way of adding different encryptions.
5
+ # The class should implement a self.digest method that taks the following params:
6
+ # - password
7
+ # - stretches: the number of times the encryption will be applied
8
+ # - salt: the password salt as defined by devise
9
+ # - pepper: Devise config option
10
+ #
11
+ module Encryptors
12
+ # = Sha512
13
+ # Uses the Sha512 hash algorithm to encrypt passwords.
14
+ class Sha512
15
+
16
+ # Gererates a default password digest based on salt, pepper and the
17
+ # incoming password.
18
+ def self.digest(password, stretches, salt, pepper)
19
+ digest = pepper
20
+ stretches.times { digest = self.secure_digest(salt, digest, password, pepper) }
21
+ digest
22
+ end
23
+
24
+ private
25
+
26
+ # Generate a Sha512 digest joining args. Generated token is something like
27
+ # --arg1--arg2--arg3--argN--
28
+ def self.secure_digest(*tokens)
29
+ ::Digest::SHA512.hexdigest('--' << tokens.flatten.join('--') << '--')
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -2,10 +2,10 @@
2
2
  # This is done by checking the time frame the user is able to sign in without
3
3
  # confirming it's account. If the user has not confirmed it's account during
4
4
  # this time frame, he/she will not able to sign in anymore.
5
- Warden::Manager.after_set_user do |record, auth, options|
5
+ Warden::Manager.after_set_user do |record, warden, options|
6
6
  if record && record.respond_to?(:active?) && !record.active?
7
7
  scope = options[:scope]
8
- auth.logout(scope)
8
+ warden.logout(scope)
9
9
  throw :warden, :scope => scope, :params => { :unconfirmed => true }
10
10
  end
11
11
  end
@@ -3,15 +3,17 @@
3
3
  # that specific user and adds a cookie with this user info to sign in this user
4
4
  # automatically without asking for credentials. Refer to rememberable strategy
5
5
  # for more info.
6
- Warden::Manager.after_authentication do |record, auth, options|
6
+ Warden::Manager.after_authentication do |record, warden, options|
7
7
  scope = options[:scope]
8
- remember_me = auth.params[scope].try(:fetch, :remember_me, nil)
8
+ remember_me = warden.params[scope].try(:fetch, :remember_me, nil)
9
9
 
10
10
  if Devise::TRUE_VALUES.include?(remember_me) && record.respond_to?(:remember_me!)
11
11
  record.remember_me!
12
- auth.cookies['remember_token'] = {
12
+
13
+ warden.response.set_cookie "remember_#{scope}_token", {
13
14
  :value => record.class.serialize_into_cookie(record),
14
- :expires => record.remember_expires_at
15
+ :expires => record.remember_expires_at,
16
+ :path => "/"
15
17
  }
16
18
  end
17
19
  end
@@ -19,9 +21,9 @@ end
19
21
  # Before logout hook to forget the user in the given scope, only if rememberable
20
22
  # is activated for this scope. Also clear remember token to ensure the user
21
23
  # won't be remembered again.
22
- Warden::Manager.before_logout do |record, auth, scope|
24
+ Warden::Manager.before_logout do |record, warden, scope|
23
25
  if record.respond_to?(:forget_me!)
24
26
  record.forget_me!
25
- auth.cookies.delete('remember_token')
27
+ warden.response.delete_cookie "remember_#{scope}_token"
26
28
  end
27
29
  end
@@ -22,7 +22,7 @@ module Devise
22
22
  # # is the modules included in the class
23
23
  #
24
24
  class Mapping #:nodoc:
25
- attr_reader :name, :as, :path_names, :path_prefix
25
+ attr_reader :name, :as, :path_names, :path_prefix, :route_options
26
26
 
27
27
  # Loop through all mappings looking for a map that matches with the requested
28
28
  # path (ie /users/sign_in). If a path prefix is given, it's taken into account.
@@ -40,14 +40,13 @@ module Devise
40
40
  end
41
41
 
42
42
  def initialize(name, options) #:nodoc:
43
- options.assert_valid_keys(:class_name, :as, :path_names, :singular, :path_prefix)
44
-
45
- @as = (options[:as] || name).to_sym
46
- @klass = (options[:class_name] || name.to_s.classify).to_s
47
- @name = (options[:singular] || name.to_s.singularize).to_sym
48
- @path_names = options[:path_names] || {}
49
- @path_prefix = options[:path_prefix] || ""
43
+ @as = (options.delete(:as) || name).to_sym
44
+ @klass = (options.delete(:class_name) || name.to_s.classify).to_s
45
+ @name = (options.delete(:singular) || name.to_s.singularize).to_sym
46
+ @path_names = options.delete(:path_names) || {}
47
+ @path_prefix = options.delete(:path_prefix) || ""
50
48
  @path_prefix << "/" unless @path_prefix[-1] == ?/
49
+ @route_options = options || {}
51
50
 
52
51
  setup_path_names
53
52
  end
@@ -0,0 +1,35 @@
1
+ module Devise
2
+ module Middlewares
3
+ class Rememberable
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ auth = env['warden']
10
+ scopes = select_cookies(auth.request)
11
+ scopes.each do |scope, token|
12
+ mapping = Devise.mappings[scope]
13
+ next unless mapping && mapping.for.include?(:rememberable)
14
+ user = mapping.to.serialize_from_cookie(token)
15
+ auth.set_user(user, :scope => scope) if user
16
+ end
17
+
18
+ @app.call(env)
19
+ end
20
+
21
+ protected
22
+
23
+ def select_cookies(request)
24
+ scopes = {}
25
+ matching = /remember_(#{Devise.mappings.keys.join("|")})_token/
26
+ request.cookies.each do |key, value|
27
+ if key.to_s =~ matching
28
+ scopes[$1.to_sym] = value
29
+ end
30
+ end
31
+ scopes
32
+ end
33
+ end
34
+ end
35
+ end
@@ -92,6 +92,9 @@ module Devise
92
92
 
93
93
  # Convert new keys to methods which overwrites Devise defaults
94
94
  options.each { |key, value| send(:"#{key}=", value) }
95
+
96
+ # Call specific hooks for each ORM
97
+ Devise.orm_class.included_modules_hook(self, devise_modules)
95
98
  end
96
99
 
97
100
  # Stores all modules included inside the model, so we are able to verify
@@ -1,4 +1,3 @@
1
- require 'digest/sha1'
2
1
  require 'devise/strategies/authenticatable'
3
2
 
4
3
  module Devise
@@ -49,22 +48,12 @@ module Devise
49
48
  end
50
49
 
51
50
  protected
52
-
53
- # Gererates a default password digest based on salt, pepper and the
54
- # incoming password.
55
- def password_digest(password_to_digest)
56
- digest = pepper
57
- stretches.times { digest = secure_digest(password_salt, digest, password_to_digest, pepper) }
58
- digest
59
- end
60
-
61
- # Generate a SHA1 digest joining args. Generated token is something like
62
- #
63
- # --arg1--arg2--arg3--argN--
64
- def secure_digest(*tokens)
65
- ::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
51
+
52
+ # Digests the password using the configured encryptor
53
+ def password_digest(password)
54
+ encryptor.digest(password, stretches, password_salt, pepper)
66
55
  end
67
-
56
+
68
57
  # Generate a friendly string randomically to be used as token.
69
58
  def friendly_token
70
59
  ActiveSupport::SecureRandom.base64(15).tr('+/=', '-_ ').strip.delete("\n")
@@ -92,6 +81,7 @@ module Devise
92
81
 
93
82
  Devise::Models.config(self, :pepper)
94
83
  Devise::Models.config(self, :stretches)
84
+ Devise::Models.config(self, :encryptor)
95
85
  end
96
86
  end
97
87
  end
@@ -1,6 +1,6 @@
1
1
  require 'digest/sha1'
2
2
  require 'devise/hooks/rememberable'
3
- require 'devise/strategies/rememberable'
3
+ require 'devise/middlewares/rememberable'
4
4
 
5
5
  module Devise
6
6
  module Models
@@ -0,0 +1,37 @@
1
+ module Devise
2
+ module Orm
3
+ # This module contains some helpers and handle schema (migrations):
4
+ #
5
+ # create_table :accounts do |t|
6
+ # t.authenticatable
7
+ # t.confirmable
8
+ # t.recoverable
9
+ # t.rememberable
10
+ # t.timestamps
11
+ # end
12
+ #
13
+ # However this method does not add indexes. If you need them, here is the declaration:
14
+ #
15
+ # add_index "accounts", ["email"], :name => "email", :unique => true
16
+ # add_index "accounts", ["confirmation_token"], :name => "confirmation_token", :unique => true
17
+ # add_index "accounts", ["reset_password_token"], :name => "reset_password_token", :unique => true
18
+ #
19
+ module ActiveRecord
20
+ # Required ORM hook. By default, do nothing on ActiveRecord.
21
+ def self.included_modules_hook(klass, modules)
22
+ end
23
+
24
+ include Devise::Schema
25
+
26
+ # Tell how to apply schema methods.
27
+ def apply_schema(name, type, options={})
28
+ column name, type.to_s.downcase.to_sym, options
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ if defined?(ActiveRecord)
35
+ ActiveRecord::Base.extend Devise::Models
36
+ ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Devise::Orm::ActiveRecord
37
+ end
@@ -0,0 +1,23 @@
1
+ module Devise
2
+ module Orm
3
+ module MongoMapper
4
+ # Include attributes modules and set the proper ones.
5
+ def self.included_modules_hook(klass, modules)
6
+ klass.send :extend, self
7
+
8
+ modules.each do |mod|
9
+ klass.send(mod)
10
+ end
11
+ end
12
+
13
+ include Devise::Schema
14
+
15
+ # Tell how to apply schema methods.
16
+ def apply_schema(name, type, options={})
17
+ key name, type, options
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ MongoMapper::Document::ClassMethods.send(:include, Devise::Models)
@@ -2,10 +2,7 @@ require 'devise/rails/routes'
2
2
  require 'devise/rails/warden_compat'
3
3
 
4
4
  Rails.configuration.after_initialize do
5
- if defined?(ActiveRecord)
6
- ActiveRecord::Base.extend Devise::Models
7
- ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Devise::Migrations
8
- end
5
+ require "devise/orm/#{Devise.orm}"
9
6
 
10
7
  # Adds Warden Manager to Rails middleware stack, configuring default devise
11
8
  # strategy and also the failure app.
@@ -13,5 +10,8 @@ Rails.configuration.after_initialize do
13
10
  Devise.configure_warden_manager(manager)
14
11
  end
15
12
 
13
+ # If using a rememberable module, include the middleware that log users.
14
+ Rails.configuration.middleware.use Devise::Middlewares::Rememberable
15
+
16
16
  I18n.load_path.unshift File.expand_path(File.join(File.dirname(__FILE__), 'locales', 'en.yml'))
17
17
  end
@@ -64,6 +64,10 @@ module ActionController::Routing
64
64
  #
65
65
  # map.devise_for :users, :path_prefix => "/:locale"
66
66
  #
67
+ # Any other options will be passed to route definition. If you need conditions for your routes, just map:
68
+ #
69
+ # map.devise_for :users, :conditions => { :subdomain => /.+/ }
70
+ #
67
71
  # If you are using a dynamic prefix, like :locale above, you need to configure default_url_options through Devise. You can do that in config/initializers/devise.rb or setting a Devise.default_url_options:
68
72
  #
69
73
  # Devise.default_url_options do
@@ -75,10 +79,12 @@ module ActionController::Routing
75
79
 
76
80
  resources.map!(&:to_sym)
77
81
  resources.each do |resource|
78
- mapping = Devise::Mapping.new(resource, options)
82
+ mapping = Devise::Mapping.new(resource, options.dup)
79
83
  Devise.mappings[mapping.name] = mapping
80
84
 
81
- with_options(:path_prefix => mapping.raw_path, :name_prefix => "#{mapping.name}_") do |routes|
85
+ route_options = mapping.route_options.merge(:path_prefix => mapping.raw_path, :name_prefix => "#{mapping.name}_")
86
+
87
+ with_options(route_options) do |routes|
82
88
  mapping.for.each do |strategy|
83
89
  send(strategy, routes, mapping) if self.respond_to?(strategy, true)
84
90
  end
@@ -1,6 +1,5 @@
1
1
  # Taken from RailsWarden, thanks to Hassox. http://github.com/hassox/rails_warden
2
2
  module Warden::Mixins::Common
3
- # Gets the rails request object by default if it's available
4
3
  def request
5
4
  return @request if @request
6
5
  if env['action_controller.rescue.request']
@@ -19,8 +18,12 @@ module Warden::Mixins::Common
19
18
  raw_session.clear
20
19
  end
21
20
 
22
- # Proxy to request cookies
23
- def cookies
24
- request.cookies
21
+ def response
22
+ return @response if @response
23
+ if env['action_controller.rescue.response']
24
+ @response = env['action_controller.rescue.response']
25
+ else
26
+ Rack::Response.new(env)
27
+ end
25
28
  end
26
29
  end
@@ -0,0 +1,43 @@
1
+ module Devise
2
+ # Holds devise schema information. To use it, just include its methods
3
+ # and overwrite the apply_schema method.
4
+ module Schema
5
+
6
+ # Creates email, encrypted_password and password_salt.
7
+ #
8
+ # == Options
9
+ # * :null when true, allow columns to be null
10
+ # * :encryptor The encryptor going to be used, necessary for setting the proper encrypter password length
11
+ def authenticatable(options={})
12
+ null = options[:null] || false
13
+ encryptor = options[:encryptor] || :sha1
14
+
15
+ apply_schema :email, String, :null => null, :limit => 100
16
+ apply_schema :encrypted_password, String, :null => null, :limit => Devise::ENCRYPTORS_LENGTH[encryptor]
17
+ apply_schema :password_salt, String, :null => null, :limit => 20
18
+ end
19
+
20
+ # Creates confirmation_token, confirmed_at and confirmation_sent_at.
21
+ def confirmable
22
+ apply_schema :confirmation_token, String, :limit => 20
23
+ apply_schema :confirmed_at, DateTime
24
+ apply_schema :confirmation_sent_at, DateTime
25
+ end
26
+
27
+ # Creates reset_password_token.
28
+ def recoverable
29
+ apply_schema :reset_password_token, String, :limit => 20
30
+ end
31
+
32
+ # Creates remember_token and remember_created_at.
33
+ def rememberable
34
+ apply_schema :remember_token, String, :limit => 20
35
+ apply_schema :remember_created_at, DateTime
36
+ end
37
+
38
+ # Overwrite with specific modification to create your own schema.
39
+ def apply_schema(name, tupe, options={})
40
+ raise NotImplementedError
41
+ end
42
+ end
43
+ end
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "0.4.3".freeze
2
+ VERSION = "0.5.0".freeze
3
3
  end
@@ -50,7 +50,7 @@ class DeviseTest < ActiveSupport::TestCase
50
50
  Devise.configure_warden_manager(manager)
51
51
 
52
52
  assert_equal Devise::Failure, manager.failure_app
53
- assert_equal [:rememberable, :authenticatable], manager.default_strategies
53
+ assert_equal [:authenticatable], manager.default_strategies
54
54
  assert manager.silence_missing_strategies
55
55
  end
56
56
 
@@ -0,0 +1,28 @@
1
+ class Encryptors < ActiveSupport::TestCase
2
+
3
+ test 'should match a password created by authlogic' do
4
+ authlogic = "b623c3bc9c775b0eb8edb218a382453396fec4146422853e66ecc4b6bc32d7162ee42074dcb5f180a770dc38b5df15812f09bbf497a4a1b95fe5e7d2b8eb7eb4"
5
+ encryptor = Devise::Encryptors::AuthlogicSha512.digest('123mudar', 20, 'usZK_z_EAaF61Gwkw-ed', '')
6
+ assert_equal authlogic, encryptor
7
+ end
8
+
9
+ test 'should match a password created by restful_authentication' do
10
+ restful_authentication = "93110f71309ce91366375ea44e2a6f5cc73fa8d4"
11
+ encryptor = Devise::Encryptors::RestfulAuthenticationSha1.digest('123mudar', 10, '48901d2b247a54088acb7f8ea3e695e50fe6791b', 'fee9a51ec0a28d11be380ca6dee6b4b760c1a3bf')
12
+ assert_equal restful_authentication, encryptor
13
+ end
14
+
15
+ test 'should match a password created by clearance' do
16
+ clearance = "0f40bbae18ddefd7066276c3ef209d40729b0378"
17
+ encryptor = Devise::Encryptors::ClearanceSha1.digest('123mudar', nil, '65c58472c207c829f28c68619d3e3aefed18ab3f', nil)
18
+ assert_equal clearance, encryptor
19
+ end
20
+
21
+ Devise::ENCRYPTORS_LENGTH.each do |key, value|
22
+ test "should have length #{value} for #{key.inspect}" do
23
+ swap Devise, :encryptor => key do
24
+ assert_equal value, Devise.encryptor.digest('a', 2, 'b', 'c').size
25
+ end
26
+ end
27
+ end
28
+ end
@@ -6,19 +6,17 @@ class RememberMeTest < ActionController::IntegrationTest
6
6
  Devise.remember_for = 1
7
7
  user = create_user
8
8
  user.remember_me!
9
- cookies['remember_token'] = User.serialize_into_cookie(user) + add_to_token
9
+ cookies['remember_user_token'] = User.serialize_into_cookie(user) + add_to_token
10
10
  user
11
11
  end
12
12
 
13
13
  test 'do not remember the user if he has not checked remember me option' do
14
14
  user = sign_in_as_user
15
-
16
15
  assert_nil user.reload.remember_token
17
16
  end
18
17
 
19
18
  test 'generate remember token after sign in' do
20
19
  user = sign_in_as_user :remember_me => true
21
-
22
20
  assert_not_nil user.reload.remember_token
23
21
  end
24
22
 
@@ -85,6 +85,14 @@ class MappingTest < ActiveSupport::TestCase
85
85
  end
86
86
  end
87
87
 
88
+ test 'should have default route options' do
89
+ assert_equal({}, Devise.mappings[:user].route_options)
90
+ end
91
+
92
+ test 'should allow passing route options to devise routes' do
93
+ assert_equal({ :requirements => { :extra => 'value' } }, Devise.mappings[:manager].route_options)
94
+ end
95
+
88
96
  test 'magic predicates' do
89
97
  mapping = Devise.mappings[:user]
90
98
  assert mapping.authenticatable?
@@ -3,10 +3,8 @@ require 'digest/sha1'
3
3
 
4
4
  class AuthenticatableTest < ActiveSupport::TestCase
5
5
 
6
- def encrypt_password(user, pepper=nil, stretches=1)
7
- user.class_eval { define_method(:stretches) { stretches } } if stretches
8
- user.password = '123456'
9
- ::Digest::SHA1.hexdigest("--#{user.password_salt}--#{pepper}--123456--#{pepper}--")
6
+ def encrypt_password(user, pepper=User.pepper, stretches=User.stretches, encryptor = ::Devise::Encryptors::Sha1)
7
+ encryptor.digest('123456', stretches, user.password_salt, pepper)
10
8
  end
11
9
 
12
10
  test 'should respond to password and password confirmation' do
@@ -59,7 +57,7 @@ class AuthenticatableTest < ActiveSupport::TestCase
59
57
  assert_not_equal encrypted_password, user.encrypted_password
60
58
  end
61
59
 
62
- test 'should encrypt password using a sha1 hash' do
60
+ test 'should fallback to Sha1 as default encryption' do
63
61
  user = new_user
64
62
  assert_equal encrypt_password(user), user.encrypted_password
65
63
  end
@@ -69,12 +67,15 @@ class AuthenticatableTest < ActiveSupport::TestCase
69
67
  Devise.pepper = ''
70
68
  user = new_user
71
69
  assert_equal encrypt_password(user), user.encrypted_password
70
+ assert_not_equal encrypt_password(user, 'another_pepper'), user.encrypted_password
72
71
  Devise.pepper = 'new_pepper'
73
72
  user = new_user
74
73
  assert_equal encrypt_password(user, 'new_pepper'), user.encrypted_password
74
+ assert_not_equal encrypt_password(user, 'another_pepper'), user.encrypted_password
75
75
  Devise.pepper = '123456'
76
76
  user = new_user
77
77
  assert_equal encrypt_password(user, '123456'), user.encrypted_password
78
+ assert_not_equal encrypt_password(user, 'another_pepper'), user.encrypted_password
78
79
  ensure
79
80
  Devise.pepper = nil
80
81
  end
@@ -85,11 +86,22 @@ class AuthenticatableTest < ActiveSupport::TestCase
85
86
  default_stretches = Devise.stretches
86
87
  Devise.stretches = 1
87
88
  user = new_user
88
- assert_equal encrypt_password(user, nil, nil), user.encrypted_password
89
+ assert_equal encrypt_password(user, nil, 1), user.encrypted_password
90
+ assert_not_equal encrypt_password(user, nil, 2), user.encrypted_password
89
91
  ensure
90
92
  Devise.stretches = default_stretches
91
93
  end
92
94
  end
95
+
96
+ test 'should respect encryptor configuration' do
97
+ begin
98
+ Devise.encryptor = ::Devise::Encryptors::Sha512
99
+ user = create_user
100
+ assert_equal user.encrypted_password, encrypt_password(user, User.pepper, User.stretches, ::Devise::Encryptors::Sha512)
101
+ ensure
102
+ Devise.encryptor = ::Devise::Encryptors::Sha1
103
+ end
104
+ end
93
105
 
94
106
  test 'should test for a valid password' do
95
107
  user = create_user
@@ -4,7 +4,9 @@ ActionController::Routing::Routes.draw do |map|
4
4
  map.devise_for :account, :path_names => {
5
5
  :sign_in => 'login', :sign_out => 'logout', :password => 'secret', :confirmation => 'verification'
6
6
  }
7
- map.devise_for :organizers, :singular => 'manager', :path_prefix => '/:locale'
7
+ map.devise_for :organizers, :singular => 'manager',
8
+ :path_prefix => '/:locale',
9
+ :requirements => { :extra => 'value' }
8
10
 
9
11
  map.resources :users, :only => :index
10
12
  map.resources :admins, :only => :index
@@ -69,11 +69,14 @@ class MapRoutingTest < ActionController::TestCase
69
69
  end
70
70
 
71
71
  test 'map organizer with custom singular name' do
72
- assert_recognizes({:controller => 'passwords', :action => 'new', :locale => "en"}, '/en/organizers/password/new')
72
+ assert_recognizes({:controller => 'passwords', :action => 'new', :locale => "en", :extra => 'value'}, '/en/organizers/password/new')
73
73
  end
74
74
 
75
75
  test 'map organizer with path prefix' do
76
- assert_recognizes({:controller => 'sessions', :action => 'new', :locale => "en"}, '/en/organizers/sign_in')
76
+ assert_recognizes({:controller => 'sessions', :action => 'new', :locale => "en", :extra => 'value'}, '/en/organizers/sign_in')
77
77
  end
78
78
 
79
+ test 'map organizer with additional route options' do
80
+ assert_recognizes({:controller => 'passwords', :action => 'new', :locale => "en", :extra => 'value'}, '/en/organizers/password/new')
81
+ end
79
82
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Jos\xC3\xA9 Valim"
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-11-10 00:00:00 -02:00
13
+ date: 2009-11-13 00:00:00 -02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -63,24 +63,31 @@ files:
63
63
  - lib/devise/controllers/filters.rb
64
64
  - lib/devise/controllers/helpers.rb
65
65
  - lib/devise/controllers/url_helpers.rb
66
+ - lib/devise/encryptors/authlogic_sha512.rb
67
+ - lib/devise/encryptors/clearance_sha1.rb
68
+ - lib/devise/encryptors/restful_authentication_sha1.rb
69
+ - lib/devise/encryptors/sha1.rb
70
+ - lib/devise/encryptors/sha512.rb
66
71
  - lib/devise/failure.rb
67
72
  - lib/devise/hooks/confirmable.rb
68
73
  - lib/devise/hooks/rememberable.rb
69
74
  - lib/devise/locales/en.yml
70
75
  - lib/devise/mapping.rb
71
- - lib/devise/migrations.rb
76
+ - lib/devise/middlewares/rememberable.rb
72
77
  - lib/devise/models.rb
73
78
  - lib/devise/models/authenticatable.rb
74
79
  - lib/devise/models/confirmable.rb
75
80
  - lib/devise/models/recoverable.rb
76
81
  - lib/devise/models/rememberable.rb
77
82
  - lib/devise/models/validatable.rb
83
+ - lib/devise/orm/active_record.rb
84
+ - lib/devise/orm/mongo_mapper.rb
78
85
  - lib/devise/rails.rb
79
86
  - lib/devise/rails/routes.rb
80
87
  - lib/devise/rails/warden_compat.rb
88
+ - lib/devise/schema.rb
81
89
  - lib/devise/strategies/authenticatable.rb
82
90
  - lib/devise/strategies/base.rb
83
- - lib/devise/strategies/rememberable.rb
84
91
  - lib/devise/version.rb
85
92
  - lib/devise/warden.rb
86
93
  has_rdoc: true
@@ -116,6 +123,7 @@ test_files:
116
123
  - test/controllers/helpers_test.rb
117
124
  - test/controllers/url_helpers_test.rb
118
125
  - test/devise_test.rb
126
+ - test/encryptors_test.rb
119
127
  - test/failure_test.rb
120
128
  - test/integration/authenticatable_test.rb
121
129
  - test/integration/confirmable_test.rb
@@ -1,51 +0,0 @@
1
- module Devise
2
- # Helpers to migration:
3
- #
4
- # create_table :accounts do |t|
5
- # t.authenticatable
6
- # t.confirmable
7
- # t.recoverable
8
- # t.rememberable
9
- # t.timestamps
10
- # end
11
- #
12
- # However this method does not add indexes. If you need them, here is the declaration:
13
- #
14
- # add_index "accounts", ["email"], :name => "email", :unique => true
15
- # add_index "accounts", ["confirmation_token"], :name => "confirmation_token", :unique => true
16
- # add_index "accounts", ["reset_password_token"], :name => "reset_password_token", :unique => true
17
- #
18
- module Migrations
19
-
20
- # Creates email, encrypted_password and password_salt.
21
- #
22
- def authenticatable(options={})
23
- null = options[:null] || false
24
- string :email, :limit => 100, :null => null
25
- string :encrypted_password, :limit => 40, :null => null
26
- string :password_salt, :limit => 20, :null => null
27
- end
28
-
29
- # Creates confirmation_token, confirmed_at and confirmation_sent_at.
30
- #
31
- def confirmable
32
- string :confirmation_token, :limit => 20
33
- datetime :confirmed_at
34
- datetime :confirmation_sent_at
35
- end
36
-
37
- # Creates reset_password_token.
38
- #
39
- def recoverable
40
- string :reset_password_token, :limit => 20
41
- end
42
-
43
- # Creates remember_token and remember_created_at.
44
- #
45
- def rememberable
46
- string :remember_token, :limit => 20
47
- datetime :remember_created_at
48
- end
49
-
50
- end
51
- end
@@ -1,35 +0,0 @@
1
- module Devise
2
- module Strategies
3
- # Remember the user through the remember token. This strategy is responsible
4
- # to verify whether there is a cookie with the remember token, and to
5
- # recreate the user from this cookie if it exists. Must be called *before*
6
- # authenticatable.
7
- class Rememberable < Devise::Strategies::Base
8
-
9
- # A valid strategy for rememberable needs a remember token in the cookies.
10
- def valid?
11
- super && remember_me_cookie.present?
12
- end
13
-
14
- # To authenticate a user we deserialize the cookie and attempt finding
15
- # the record in the database. If the attempt fails, we pass to another
16
- # strategy handle the authentication.
17
- def authenticate!
18
- if resource = mapping.to.serialize_from_cookie(remember_me_cookie)
19
- success!(resource)
20
- else
21
- pass
22
- end
23
- end
24
-
25
- private
26
-
27
- # Accessor for remember cookie
28
- def remember_me_cookie
29
- cookies['remember_token']
30
- end
31
- end
32
- end
33
- end
34
-
35
- Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable)