devise 0.1.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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +220 -0
- data/Rakefile +45 -0
- data/TODO +37 -0
- data/app/controllers/confirmations_controller.rb +32 -0
- data/app/controllers/passwords_controller.rb +38 -0
- data/app/controllers/sessions_controller.rb +35 -0
- data/app/models/notifier.rb +47 -0
- data/app/views/confirmations/new.html.erb +16 -0
- data/app/views/notifier/confirmation_instructions.html.erb +5 -0
- data/app/views/notifier/reset_password_instructions.html.erb +8 -0
- data/app/views/passwords/edit.html.erb +20 -0
- data/app/views/passwords/new.html.erb +16 -0
- data/app/views/sessions/new.html.erb +23 -0
- data/config/locales/en.yml +16 -0
- data/init.rb +2 -0
- data/lib/devise.rb +48 -0
- data/lib/devise/active_record.rb +86 -0
- data/lib/devise/controllers/filters.rb +109 -0
- data/lib/devise/controllers/helpers.rb +91 -0
- data/lib/devise/controllers/url_helpers.rb +47 -0
- data/lib/devise/hooks/rememberable.rb +24 -0
- data/lib/devise/mapping.rb +95 -0
- data/lib/devise/migrations.rb +50 -0
- data/lib/devise/models/authenticable.rb +98 -0
- data/lib/devise/models/confirmable.rb +125 -0
- data/lib/devise/models/recoverable.rb +88 -0
- data/lib/devise/models/rememberable.rb +71 -0
- data/lib/devise/models/validatable.rb +36 -0
- data/lib/devise/routes.rb +95 -0
- data/lib/devise/strategies/authenticable.rb +45 -0
- data/lib/devise/strategies/base.rb +24 -0
- data/lib/devise/strategies/rememberable.rb +33 -0
- data/lib/devise/version.rb +3 -0
- data/lib/devise/warden.rb +64 -0
- data/test/active_record_test.rb +96 -0
- data/test/controllers/filters_test.rb +97 -0
- data/test/controllers/helpers_test.rb +40 -0
- data/test/controllers/url_helpers_test.rb +47 -0
- data/test/integration/authenticable_test.rb +191 -0
- data/test/integration/confirmable_test.rb +60 -0
- data/test/integration/recoverable_test.rb +131 -0
- data/test/integration/rememberable_test.rb +56 -0
- data/test/mailers/confirmation_instructions_test.rb +59 -0
- data/test/mailers/reset_password_instructions_test.rb +62 -0
- data/test/mapping_test.rb +71 -0
- data/test/models/authenticable_test.rb +138 -0
- data/test/models/confirmable_test.rb +206 -0
- data/test/models/recoverable_test.rb +145 -0
- data/test/models/rememberable_test.rb +68 -0
- data/test/models/validatable_test.rb +99 -0
- data/test/rails_app/app/controllers/admins_controller.rb +6 -0
- data/test/rails_app/app/controllers/application_controller.rb +10 -0
- data/test/rails_app/app/controllers/home_controller.rb +4 -0
- data/test/rails_app/app/controllers/users_controller.rb +7 -0
- data/test/rails_app/app/helpers/application_helper.rb +3 -0
- data/test/rails_app/app/models/account.rb +3 -0
- data/test/rails_app/app/models/admin.rb +3 -0
- data/test/rails_app/app/models/organizer.rb +3 -0
- data/test/rails_app/app/models/user.rb +3 -0
- data/test/rails_app/config/boot.rb +110 -0
- data/test/rails_app/config/environment.rb +41 -0
- data/test/rails_app/config/environments/development.rb +17 -0
- data/test/rails_app/config/environments/production.rb +28 -0
- data/test/rails_app/config/environments/test.rb +28 -0
- data/test/rails_app/config/initializers/new_rails_defaults.rb +21 -0
- data/test/rails_app/config/initializers/session_store.rb +15 -0
- data/test/rails_app/config/routes.rb +18 -0
- data/test/routes_test.rb +75 -0
- data/test/support/assertions_helper.rb +22 -0
- data/test/support/integration_tests_helper.rb +66 -0
- data/test/support/model_tests_helper.rb +40 -0
- data/test/test_helper.rb +39 -0
- metadata +136 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Devise
|
2
|
+
module Controllers
|
3
|
+
# Create url helpers to be used with resource/scope configuration. Acts as
|
4
|
+
# proxies to the generated routes created by devise.
|
5
|
+
# Resource param can be a string or symbol, a class, or an instance object.
|
6
|
+
# Example using a :user resource:
|
7
|
+
#
|
8
|
+
# new_session_path(:user) => new_user_session_path
|
9
|
+
# session_path(:user) => user_session_path
|
10
|
+
# destroy_session_path(:user) => destroy_user_session_path
|
11
|
+
#
|
12
|
+
# new_password_path(:user) => new_user_password_path
|
13
|
+
# password_path(:user) => user_password_path
|
14
|
+
# edit_password_path(:user) => edit_user_password_path
|
15
|
+
#
|
16
|
+
# new_confirmation_path(:user) => new_user_confirmation_path
|
17
|
+
# confirmation_path(:user) => user_confirmation_path
|
18
|
+
module UrlHelpers
|
19
|
+
|
20
|
+
[:session, :password, :confirmation].each do |module_name|
|
21
|
+
[:path, :url].each do |path_or_url|
|
22
|
+
actions = [ nil, :new_ ]
|
23
|
+
actions << :edit_ if module_name == :password
|
24
|
+
actions << :destroy_ if module_name == :session
|
25
|
+
|
26
|
+
actions.each do |action|
|
27
|
+
class_eval <<-URL_HELPERS
|
28
|
+
def #{action}#{module_name}_#{path_or_url}(resource, *args)
|
29
|
+
resource = case resource
|
30
|
+
when Symbol, String
|
31
|
+
resource
|
32
|
+
when Class
|
33
|
+
resource.name.underscore
|
34
|
+
else
|
35
|
+
resource.class.name.underscore
|
36
|
+
end
|
37
|
+
|
38
|
+
send("#{action}\#{resource}_#{module_name}_#{path_or_url}", *args)
|
39
|
+
end
|
40
|
+
URL_HELPERS
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# After authenticate hook to verify if the user in the given scope asked to be
|
2
|
+
# remembered while he does not sign out. Generates a new remember token for
|
3
|
+
# that specific user and adds a cookie with this user info to sign in this user
|
4
|
+
# automatically without asking for credentials. Refer to rememberable strategy
|
5
|
+
# for more info.
|
6
|
+
Warden::Manager.after_authentication do |record, auth, options|
|
7
|
+
scope = options[:scope]
|
8
|
+
remember_me = auth.params[scope].try(:fetch, :remember_me, nil)
|
9
|
+
|
10
|
+
if Devise::TRUE_VALUES.include?(remember_me) && record.respond_to?(:remember_me!)
|
11
|
+
record.remember_me!
|
12
|
+
auth.cookies['remember_token'] = record.class.serialize_into_cookie(record)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Before logout hook to forget the user in the given scope, only if rememberable
|
17
|
+
# is activated for this scope. Also clear remember token to ensure the user
|
18
|
+
# won't be remembered again.
|
19
|
+
Warden::Manager.before_logout do |record, auth, scope|
|
20
|
+
if record.respond_to?(:forget_me!)
|
21
|
+
record.forget_me!
|
22
|
+
auth.cookies['remember_token'] = nil
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Devise
|
2
|
+
# Responsible for handling devise mappings and routes configuration. Each
|
3
|
+
# resource configured by devise_for in routes is actually creating a mapping
|
4
|
+
# object. You can refer to devise_for in routes for usage options.
|
5
|
+
#
|
6
|
+
# The required value in devise_for is actually not used internally, but it's
|
7
|
+
# inflected to find all other values.
|
8
|
+
#
|
9
|
+
# map.devise_for :users
|
10
|
+
# mapping = Devise.mappings[:user]
|
11
|
+
#
|
12
|
+
# mapping.name #=> :user
|
13
|
+
# # is the scope used in controllers and warden, given in the route as :singular.
|
14
|
+
#
|
15
|
+
# mapping.as #=> "users"
|
16
|
+
# # how the mapping should be search in the path, given in the route as :as.
|
17
|
+
#
|
18
|
+
# mapping.to #=> User
|
19
|
+
# # is the class to be loaded from routes, given in the route as :class_name.
|
20
|
+
#
|
21
|
+
# mapping.for #=> [:authenticable]
|
22
|
+
# # is the modules included in the class
|
23
|
+
#
|
24
|
+
class Mapping #:nodoc:
|
25
|
+
attr_reader :name, :as, :path_names
|
26
|
+
|
27
|
+
def initialize(name, options)
|
28
|
+
@as = (options[:as] || name).to_sym
|
29
|
+
@klass = (options[:class_name] || name.to_s.classify).to_s
|
30
|
+
@name = (options[:singular] || name.to_s.singularize).to_sym
|
31
|
+
@path_names = options[:path_names] || {}
|
32
|
+
setup_path_names
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return modules for the mapping.
|
36
|
+
def for
|
37
|
+
@for ||= to.devise_modules
|
38
|
+
end
|
39
|
+
|
40
|
+
# Reload mapped class each time when cache_classes is false.
|
41
|
+
def to
|
42
|
+
return @to if @to
|
43
|
+
klass = @klass.constantize
|
44
|
+
@to = klass if Rails.configuration.cache_classes
|
45
|
+
klass
|
46
|
+
end
|
47
|
+
|
48
|
+
# Check if the respective controller has a module in the mapping class.
|
49
|
+
def allows?(controller)
|
50
|
+
self.for.include?(CONTROLLERS[controller.to_sym])
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create magic predicates for verifying what module is activated by this map.
|
54
|
+
# Example:
|
55
|
+
#
|
56
|
+
# def confirmable?
|
57
|
+
# self.for.include?(:confirmable)
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
ALL.each do |m|
|
61
|
+
class_eval <<-METHOD, __FILE__, __LINE__
|
62
|
+
def #{m}?
|
63
|
+
self.for.include?(:#{m})
|
64
|
+
end
|
65
|
+
METHOD
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Configure default path names, allowing the user overwrite defaults by
|
71
|
+
# passing a hash in :path_names.
|
72
|
+
def setup_path_names
|
73
|
+
[:sign_in, :sign_out, :password, :confirmation].each do |path_name|
|
74
|
+
@path_names[path_name] ||= path_name.to_s
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
mattr_accessor :mappings
|
80
|
+
self.mappings = {}
|
81
|
+
|
82
|
+
# Loop through all mappings looking for a map that matches with the requested
|
83
|
+
# path (ie /users/sign_in). The important part here is the key :users. If no
|
84
|
+
# map is found just returns nil.
|
85
|
+
def self.find_mapping_by_path(path)
|
86
|
+
route = path.split("/")[1]
|
87
|
+
return nil unless route
|
88
|
+
|
89
|
+
route = route.to_sym
|
90
|
+
mappings.each do |key, map|
|
91
|
+
return map if map.as == route.to_sym
|
92
|
+
end
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Devise
|
2
|
+
# Helpers to migration:
|
3
|
+
#
|
4
|
+
# create_table :accounts do |t|
|
5
|
+
# t.authenticable
|
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 authenticable
|
23
|
+
string :email, :limit => 100, :null => false
|
24
|
+
string :encrypted_password, :limit => 40, :null => false
|
25
|
+
string :password_salt, :limit => 20, :null => false
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates confirmation_token, confirmed_at and confirmation_sent_at.
|
29
|
+
#
|
30
|
+
def confirmable
|
31
|
+
string :confirmation_token, :limit => 20
|
32
|
+
datetime :confirmed_at
|
33
|
+
datetime :confirmation_sent_at
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates reset_password_token.
|
37
|
+
#
|
38
|
+
def recoverable
|
39
|
+
string :reset_password_token, :limit => 20
|
40
|
+
end
|
41
|
+
|
42
|
+
# Creates remember_token and remember_expires_at.
|
43
|
+
#
|
44
|
+
def rememberable
|
45
|
+
string :remember_token, :limit => 20
|
46
|
+
datetime :remember_expires_at
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module Devise
|
4
|
+
module Models
|
5
|
+
|
6
|
+
# Authenticable Module, responsible for encrypting password and validating
|
7
|
+
# authenticity of a user while signing in.
|
8
|
+
#
|
9
|
+
# Configuration:
|
10
|
+
#
|
11
|
+
# You can overwrite configuration values by setting in globally in Devise,
|
12
|
+
# using devise method or overwriting the respective instance method.
|
13
|
+
#
|
14
|
+
# pepper: encryption key used for creating encrypted password. Each time
|
15
|
+
# password changes, it's gonna be encrypted again, and this key
|
16
|
+
# is added to the password and salt to create a secure hash.
|
17
|
+
# Always use `rake secret' to generate a new key.
|
18
|
+
#
|
19
|
+
# stretches: defines how many times the password will be encrypted.
|
20
|
+
#
|
21
|
+
# Examples:
|
22
|
+
#
|
23
|
+
# User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
|
24
|
+
# User.find(1).valid_password?('password123') # returns true/false
|
25
|
+
#
|
26
|
+
module Authenticable
|
27
|
+
Devise.model_config(self, :pepper)
|
28
|
+
Devise.model_config(self, :stretches, 10)
|
29
|
+
|
30
|
+
def self.included(base)
|
31
|
+
base.class_eval do
|
32
|
+
extend ClassMethods
|
33
|
+
|
34
|
+
attr_reader :password
|
35
|
+
attr_accessor :password_confirmation
|
36
|
+
attr_accessible :email, :password, :password_confirmation
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Regenerates password salt and encrypted password each time password is
|
41
|
+
# setted.
|
42
|
+
def password=(new_password)
|
43
|
+
@password = new_password
|
44
|
+
self.password_salt = friendly_token
|
45
|
+
self.encrypted_password = password_digest(@password)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Verifies whether an incoming_password (ie from login) is the user
|
49
|
+
# password.
|
50
|
+
def valid_password?(incoming_password)
|
51
|
+
password_digest(incoming_password) == encrypted_password
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
# Gererates a default password digest based on salt, pepper and the
|
57
|
+
# incoming password.
|
58
|
+
def password_digest(password_to_digest)
|
59
|
+
digest = pepper
|
60
|
+
stretches.times { digest = secure_digest(password_salt, digest, password_to_digest, pepper) }
|
61
|
+
digest
|
62
|
+
end
|
63
|
+
|
64
|
+
# Generate a SHA1 digest joining args. Generated token is something like
|
65
|
+
#
|
66
|
+
# --arg1--arg2--arg3--argN--
|
67
|
+
def secure_digest(*tokens)
|
68
|
+
::Digest::SHA1.hexdigest('--' << tokens.flatten.join('--') << '--')
|
69
|
+
end
|
70
|
+
|
71
|
+
# Generate a friendly string randomically to be used as token.
|
72
|
+
def friendly_token
|
73
|
+
ActiveSupport::SecureRandom.base64(15).tr('+/=', '-_ ').strip.delete("\n")
|
74
|
+
end
|
75
|
+
|
76
|
+
module ClassMethods
|
77
|
+
|
78
|
+
# Authenticate a user based on email and password. Returns the
|
79
|
+
# authenticated user if it's valid or nil.
|
80
|
+
# Attributes are :email and :password
|
81
|
+
def authenticate(attributes={})
|
82
|
+
authenticable = find_by_email(attributes[:email])
|
83
|
+
authenticable if authenticable.try(:valid_password?, attributes[:password])
|
84
|
+
end
|
85
|
+
|
86
|
+
# Attempt to find a user by it's email. If not user is found, returns a
|
87
|
+
# new user with an email not found error.
|
88
|
+
def find_or_initialize_with_error_by_email(email)
|
89
|
+
perishable = find_or_initialize_by_email(email)
|
90
|
+
if perishable.new_record?
|
91
|
+
perishable.errors.add(:email, :not_found, :default => 'not found')
|
92
|
+
end
|
93
|
+
perishable
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
|
4
|
+
# Confirmable is responsible to verify if an account is already confirmed to
|
5
|
+
# sign in, and to send emails with confirmation instructions.
|
6
|
+
# Confirmation instructions are sent to the user email after creating a
|
7
|
+
# record, after updating it's email and also when manually requested by
|
8
|
+
# a new confirmation instruction request.
|
9
|
+
# Whenever the user update it's email, his account is automatically unconfirmed,
|
10
|
+
# it means it won't be able to sign in again without confirming the account
|
11
|
+
# again through the email that was sent.
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# User.find(1).confirm! # returns true unless it's already confirmed
|
15
|
+
# User.find(1).confirmed? # true/false
|
16
|
+
# User.find(1).send_confirmation_instructions # manually send instructions
|
17
|
+
# User.find(1).reset_confirmation! # reset confirmation status and send instructions
|
18
|
+
module Confirmable
|
19
|
+
|
20
|
+
def self.included(base)
|
21
|
+
base.class_eval do
|
22
|
+
extend ClassMethods
|
23
|
+
|
24
|
+
before_save :reset_confirmation, :if => :email_changed?
|
25
|
+
after_save :send_confirmation_instructions, :if => :email_changed?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
30
|
+
# is already confirmed, add en error to email field
|
31
|
+
def confirm!
|
32
|
+
unless_confirmed do
|
33
|
+
clear_confirmation_token
|
34
|
+
update_attribute(:confirmed_at, Time.now)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Verifies whether a user is confirmed or not
|
39
|
+
def confirmed?
|
40
|
+
!new_record? && confirmed_at?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Send confirmation instructions by email
|
44
|
+
def send_confirmation_instructions
|
45
|
+
::Notifier.deliver_confirmation_instructions(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Remove confirmation date and send confirmation instructions, to ensure
|
49
|
+
# after sending these instructions the user won't be able to sign in without
|
50
|
+
# confirming it's account
|
51
|
+
def reset_confirmation!
|
52
|
+
unless_confirmed do
|
53
|
+
reset_confirmation
|
54
|
+
save(false)
|
55
|
+
send_confirmation_instructions
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
|
61
|
+
# Remove confirmation date from the user, ensuring after a user update
|
62
|
+
# it's email, it won't be able to sign in without confirming it.
|
63
|
+
def reset_confirmation
|
64
|
+
generate_confirmation_token
|
65
|
+
self.confirmed_at = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Checks whether the record is confirmed or not, yielding to the block
|
69
|
+
# if it's already confirmed, otherwise adds an error to email.
|
70
|
+
def unless_confirmed
|
71
|
+
unless confirmed?
|
72
|
+
yield
|
73
|
+
else
|
74
|
+
errors.add(:email, :already_confirmed, :default => 'already confirmed')
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Generates a new random token for confirmation, and stores the time
|
80
|
+
# this token is being generated
|
81
|
+
def generate_confirmation_token
|
82
|
+
self.confirmation_token = friendly_token
|
83
|
+
self.confirmation_sent_at = Time.now.utc
|
84
|
+
end
|
85
|
+
|
86
|
+
# Resets the confirmation token with and save the record without
|
87
|
+
# validating.
|
88
|
+
def generate_confirmation_token!
|
89
|
+
generate_confirmation_token && save(false)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Removes confirmation token
|
93
|
+
def clear_confirmation_token
|
94
|
+
self.confirmation_token = nil
|
95
|
+
end
|
96
|
+
|
97
|
+
module ClassMethods
|
98
|
+
|
99
|
+
# Attempt to find a user by it's email. If a record is found, send new
|
100
|
+
# confirmation instructions to it. If not user is found, returns a new user
|
101
|
+
# with an email not found error.
|
102
|
+
# Options must contain the user email
|
103
|
+
def send_confirmation_instructions(attributes={})
|
104
|
+
confirmable = find_or_initialize_with_error_by_email(attributes[:email])
|
105
|
+
confirmable.reset_confirmation! unless confirmable.new_record?
|
106
|
+
confirmable
|
107
|
+
end
|
108
|
+
|
109
|
+
# Find a user by it's confirmation token and try to confirm it.
|
110
|
+
# If no user is found, returns a new user with an error.
|
111
|
+
# If the user is already confirmed, create an error for the user
|
112
|
+
# Options must have the confirmation_token
|
113
|
+
def confirm!(attributes={})
|
114
|
+
confirmable = find_or_initialize_by_confirmation_token(attributes[:confirmation_token])
|
115
|
+
if confirmable.new_record?
|
116
|
+
confirmable.errors.add(:confirmation_token, :invalid)
|
117
|
+
else
|
118
|
+
confirmable.confirm!
|
119
|
+
end
|
120
|
+
confirmable
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Devise
|
2
|
+
module Models
|
3
|
+
|
4
|
+
# Recoverable takes care of reseting the user password and send reset instructions
|
5
|
+
# Examples:
|
6
|
+
#
|
7
|
+
# # resets the user password and save the record, true if valid passwords are given, otherwise false
|
8
|
+
# User.find(1).reset_password!('password123', 'password123')
|
9
|
+
# # only resets the user password, without saving the record
|
10
|
+
# user = User.find(1)
|
11
|
+
# user.reset_password('password123', 'password123')
|
12
|
+
# # creates a new token and send it with instructions about how to reset the password
|
13
|
+
# User.find(1).send_reset_password_instructions
|
14
|
+
module Recoverable
|
15
|
+
def self.included(base)
|
16
|
+
base.class_eval do
|
17
|
+
extend ClassMethods
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Update password
|
22
|
+
def reset_password(new_password, new_password_confirmation)
|
23
|
+
self.password = new_password
|
24
|
+
self.password_confirmation = new_password_confirmation
|
25
|
+
end
|
26
|
+
|
27
|
+
# Update password saving the record and clearing token. Returns true if
|
28
|
+
# the passwords are valid and the record was saved, false otherwise.
|
29
|
+
def reset_password!(new_password, new_password_confirmation)
|
30
|
+
reset_password(new_password, new_password_confirmation)
|
31
|
+
clear_reset_password_token if valid?
|
32
|
+
save
|
33
|
+
end
|
34
|
+
|
35
|
+
# Resets reset password token and send reset password instructions by email
|
36
|
+
def send_reset_password_instructions
|
37
|
+
generate_reset_password_token!
|
38
|
+
::Notifier.deliver_reset_password_instructions(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# Generates a new random token for reset password
|
44
|
+
def generate_reset_password_token
|
45
|
+
self.reset_password_token = friendly_token
|
46
|
+
end
|
47
|
+
|
48
|
+
# Resets the reset password token with and save the record without
|
49
|
+
# validating
|
50
|
+
def generate_reset_password_token!
|
51
|
+
generate_reset_password_token && save(false)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Removes reset_password token
|
55
|
+
def clear_reset_password_token
|
56
|
+
self.reset_password_token = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
module ClassMethods
|
60
|
+
|
61
|
+
# Attempt to find a user by it's email. If a record is found, send new
|
62
|
+
# password instructions to it. If not user is found, returns a new user
|
63
|
+
# with an email not found error.
|
64
|
+
# Attributes must contain the user email
|
65
|
+
def send_reset_password_instructions(attributes={})
|
66
|
+
recoverable = find_or_initialize_with_error_by_email(attributes[:email])
|
67
|
+
recoverable.send_reset_password_instructions unless recoverable.new_record?
|
68
|
+
recoverable
|
69
|
+
end
|
70
|
+
|
71
|
+
# Attempt to find a user by it's reset_password_token to reset it's
|
72
|
+
# password. If a user is found, reset it's password and automatically
|
73
|
+
# try saving the record. If not user is found, returns a new user
|
74
|
+
# containing an error in reset_password_token attribute.
|
75
|
+
# Attributes must contain reset_password_token, password and confirmation
|
76
|
+
def reset_password!(attributes={})
|
77
|
+
recoverable = find_or_initialize_by_reset_password_token(attributes[:reset_password_token])
|
78
|
+
if recoverable.new_record?
|
79
|
+
recoverable.errors.add(:reset_password_token, :invalid)
|
80
|
+
else
|
81
|
+
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
|
82
|
+
end
|
83
|
+
recoverable
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|