deviseOne 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +38 -0
- data/.yardopts +9 -0
- data/CHANGELOG.md +1117 -0
- data/CONTRIBUTING.md +14 -0
- data/Gemfile +29 -0
- data/Gemfile.lock +199 -0
- data/MIT-LICENSE +20 -0
- data/README.md +529 -0
- data/Rakefile +35 -0
- data/app/controllers/devise/confirmations_controller.rb +47 -0
- data/app/controllers/devise/omniauth_callbacks_controller.rb +30 -0
- data/app/controllers/devise/passwords_controller.rb +71 -0
- data/app/controllers/devise/registrations_controller.rb +143 -0
- data/app/controllers/devise/sessions_controller.rb +166 -0
- data/app/controllers/devise/unlocks_controller.rb +46 -0
- data/app/controllers/devise_controller.rb +193 -0
- data/app/helpers/devise_helper.rb +25 -0
- data/app/mailers/devise/mailer.rb +20 -0
- data/app/views/devise/confirmations/new.html.erb +16 -0
- data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/app/views/devise/passwords/edit.html.erb +25 -0
- data/app/views/devise/passwords/new.html.erb +16 -0
- data/app/views/devise/registrations/edit.html.erb +39 -0
- data/app/views/devise/registrations/new.html.erb +29 -0
- data/app/views/devise/sessions/new.html.erb +27 -0
- data/app/views/devise/shared/_links.html.erb +21 -0
- data/app/views/devise/unlocks/new.html.erb +16 -0
- data/config/locales/en.yml +70 -0
- data/devise.gemspec +33 -0
- data/devise.png +0 -0
- data/gemfiles/Gemfile.rails-3.2-stable +29 -0
- data/gemfiles/Gemfile.rails-3.2-stable.lock +169 -0
- data/gemfiles/Gemfile.rails-4.0-stable +29 -0
- data/gemfiles/Gemfile.rails-4.0-stable.lock +165 -0
- data/gemfiles/Gemfile.rails-4.1-stable +29 -0
- data/gemfiles/Gemfile.rails-4.1-stable.lock +170 -0
- data/lib/devise.rb +499 -0
- data/lib/devise/controllers/helpers.rb +284 -0
- data/lib/devise/controllers/rememberable.rb +47 -0
- data/lib/devise/controllers/scoped_views.rb +17 -0
- data/lib/devise/controllers/sign_in_out.rb +102 -0
- data/lib/devise/controllers/store_location.rb +58 -0
- data/lib/devise/controllers/url_helpers.rb +69 -0
- data/lib/devise/delegator.rb +16 -0
- data/lib/devise/failure_app.rb +212 -0
- data/lib/devise/hooks/activatable.rb +10 -0
- data/lib/devise/hooks/csrf_cleaner.rb +7 -0
- data/lib/devise/hooks/forgetable.rb +9 -0
- data/lib/devise/hooks/lockable.rb +7 -0
- data/lib/devise/hooks/proxy.rb +21 -0
- data/lib/devise/hooks/rememberable.rb +7 -0
- data/lib/devise/hooks/timeoutable.rb +35 -0
- data/lib/devise/hooks/trackable.rb +9 -0
- data/lib/devise/mailers/helpers.rb +90 -0
- data/lib/devise/mapping.rb +175 -0
- data/lib/devise/models.rb +119 -0
- data/lib/devise/models/authenticatable.rb +290 -0
- data/lib/devise/models/confirmable.rb +305 -0
- data/lib/devise/models/database_authenticatable.rb +164 -0
- data/lib/devise/models/lockable.rb +196 -0
- data/lib/devise/models/omniauthable.rb +27 -0
- data/lib/devise/models/recoverable.rb +157 -0
- data/lib/devise/models/registerable.rb +25 -0
- data/lib/devise/models/rememberable.rb +142 -0
- data/lib/devise/models/timeoutable.rb +49 -0
- data/lib/devise/models/trackable.rb +38 -0
- data/lib/devise/models/validatable.rb +66 -0
- data/lib/devise/modules.rb +28 -0
- data/lib/devise/omniauth.rb +28 -0
- data/lib/devise/omniauth/config.rb +45 -0
- data/lib/devise/omniauth/url_helpers.rb +18 -0
- data/lib/devise/orm/active_record.rb +3 -0
- data/lib/devise/orm/mongoid.rb +3 -0
- data/lib/devise/parameter_filter.rb +40 -0
- data/lib/devise/parameter_sanitizer.rb +99 -0
- data/lib/devise/rails.rb +56 -0
- data/lib/devise/rails/routes.rb +495 -0
- data/lib/devise/rails/warden_compat.rb +22 -0
- data/lib/devise/strategies/authenticatable.rb +173 -0
- data/lib/devise/strategies/base.rb +20 -0
- data/lib/devise/strategies/database_authenticatable.rb +24 -0
- data/lib/devise/strategies/rememberable.rb +59 -0
- data/lib/devise/test_helpers.rb +132 -0
- data/lib/devise/time_inflector.rb +14 -0
- data/lib/devise/token_generator.rb +70 -0
- data/lib/devise/version.rb +3 -0
- data/lib/generators/active_record/devise_generator.rb +91 -0
- data/lib/generators/active_record/templates/migration.rb +18 -0
- data/lib/generators/active_record/templates/migration_existing.rb +25 -0
- data/lib/generators/devise/controllers_generator.rb +44 -0
- data/lib/generators/devise/devise_generator.rb +26 -0
- data/lib/generators/devise/install_generator.rb +29 -0
- data/lib/generators/devise/orm_helpers.rb +51 -0
- data/lib/generators/devise/views_generator.rb +135 -0
- data/lib/generators/mongoid/devise_generator.rb +55 -0
- data/lib/generators/templates/README +35 -0
- data/lib/generators/templates/controllers/README +14 -0
- data/lib/generators/templates/controllers/confirmations_controller.rb +28 -0
- data/lib/generators/templates/controllers/omniauth_callbacks_controller.rb +28 -0
- data/lib/generators/templates/controllers/passwords_controller.rb +32 -0
- data/lib/generators/templates/controllers/registrations_controller.rb +60 -0
- data/lib/generators/templates/controllers/sessions_controller.rb +25 -0
- data/lib/generators/templates/controllers/unlocks_controller.rb +28 -0
- data/lib/generators/templates/devise.rb +263 -0
- data/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
- data/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
- data/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
- data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +16 -0
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +19 -0
- data/lib/generators/templates/simple_form_for/passwords/new.html.erb +15 -0
- data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +27 -0
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +17 -0
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +15 -0
- data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +16 -0
- data/script/cached-bundle +49 -0
- data/script/s3-put +71 -0
- data/test/controllers/custom_registrations_controller_test.rb +35 -0
- data/test/controllers/custom_strategy_test.rb +62 -0
- data/test/controllers/helpers_test.rb +316 -0
- data/test/controllers/internal_helpers_test.rb +129 -0
- data/test/controllers/load_hooks_controller_test.rb +19 -0
- data/test/controllers/passwords_controller_test.rb +31 -0
- data/test/controllers/sessions_controller_test.rb +102 -0
- data/test/controllers/url_helpers_test.rb +65 -0
- data/test/delegator_test.rb +19 -0
- data/test/devise_test.rb +107 -0
- data/test/failure_app_test.rb +275 -0
- data/test/generators/active_record_generator_test.rb +109 -0
- data/test/generators/controllers_generator_test.rb +48 -0
- data/test/generators/devise_generator_test.rb +39 -0
- data/test/generators/install_generator_test.rb +13 -0
- data/test/generators/mongoid_generator_test.rb +23 -0
- data/test/generators/views_generator_test.rb +96 -0
- data/test/helpers/devise_helper_test.rb +49 -0
- data/test/integration/authenticatable_test.rb +731 -0
- data/test/integration/confirmable_test.rb +324 -0
- data/test/integration/database_authenticatable_test.rb +94 -0
- data/test/integration/http_authenticatable_test.rb +105 -0
- data/test/integration/lockable_test.rb +239 -0
- data/test/integration/omniauthable_test.rb +133 -0
- data/test/integration/recoverable_test.rb +334 -0
- data/test/integration/registerable_test.rb +361 -0
- data/test/integration/rememberable_test.rb +176 -0
- data/test/integration/timeoutable_test.rb +189 -0
- data/test/integration/trackable_test.rb +92 -0
- data/test/mailers/confirmation_instructions_test.rb +115 -0
- data/test/mailers/reset_password_instructions_test.rb +96 -0
- data/test/mailers/unlock_instructions_test.rb +91 -0
- data/test/mapping_test.rb +128 -0
- data/test/models/authenticatable_test.rb +23 -0
- data/test/models/confirmable_test.rb +461 -0
- data/test/models/database_authenticatable_test.rb +249 -0
- data/test/models/lockable_test.rb +328 -0
- data/test/models/omniauthable_test.rb +7 -0
- data/test/models/recoverable_test.rb +205 -0
- data/test/models/registerable_test.rb +7 -0
- data/test/models/rememberable_test.rb +198 -0
- data/test/models/serializable_test.rb +49 -0
- data/test/models/timeoutable_test.rb +51 -0
- data/test/models/trackable_test.rb +41 -0
- data/test/models/validatable_test.rb +127 -0
- data/test/models_test.rb +144 -0
- data/test/omniauth/config_test.rb +57 -0
- data/test/omniauth/url_helpers_test.rb +54 -0
- data/test/orm/active_record.rb +10 -0
- data/test/orm/mongoid.rb +13 -0
- data/test/parameter_sanitizer_test.rb +81 -0
- data/test/rails_app/Rakefile +6 -0
- data/test/rails_app/app/active_record/admin.rb +6 -0
- data/test/rails_app/app/active_record/shim.rb +2 -0
- data/test/rails_app/app/active_record/user.rb +6 -0
- data/test/rails_app/app/active_record/user_on_engine.rb +7 -0
- data/test/rails_app/app/active_record/user_on_main_app.rb +7 -0
- data/test/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
- data/test/rails_app/app/controllers/admins_controller.rb +11 -0
- data/test/rails_app/app/controllers/application_controller.rb +12 -0
- data/test/rails_app/app/controllers/application_with_fake_engine.rb +30 -0
- data/test/rails_app/app/controllers/custom/registrations_controller.rb +21 -0
- data/test/rails_app/app/controllers/home_controller.rb +25 -0
- data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
- data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
- data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +14 -0
- data/test/rails_app/app/controllers/users_controller.rb +31 -0
- data/test/rails_app/app/helpers/application_helper.rb +3 -0
- data/test/rails_app/app/mailers/users/from_proc_mailer.rb +3 -0
- data/test/rails_app/app/mailers/users/mailer.rb +3 -0
- data/test/rails_app/app/mailers/users/reply_to_mailer.rb +4 -0
- data/test/rails_app/app/mongoid/admin.rb +29 -0
- data/test/rails_app/app/mongoid/shim.rb +23 -0
- data/test/rails_app/app/mongoid/user.rb +39 -0
- data/test/rails_app/app/mongoid/user_on_engine.rb +39 -0
- data/test/rails_app/app/mongoid/user_on_main_app.rb +39 -0
- data/test/rails_app/app/views/admins/index.html.erb +1 -0
- data/test/rails_app/app/views/admins/sessions/new.html.erb +2 -0
- data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -0
- data/test/rails_app/app/views/home/index.html.erb +1 -0
- data/test/rails_app/app/views/home/join.html.erb +1 -0
- data/test/rails_app/app/views/home/private.html.erb +1 -0
- data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -0
- data/test/rails_app/app/views/layouts/application.html.erb +24 -0
- data/test/rails_app/app/views/users/edit_form.html.erb +1 -0
- data/test/rails_app/app/views/users/index.html.erb +1 -0
- data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +1 -0
- data/test/rails_app/app/views/users/sessions/new.html.erb +1 -0
- data/test/rails_app/bin/bundle +3 -0
- data/test/rails_app/bin/rails +4 -0
- data/test/rails_app/bin/rake +4 -0
- data/test/rails_app/config.ru +4 -0
- data/test/rails_app/config/application.rb +40 -0
- data/test/rails_app/config/boot.rb +14 -0
- data/test/rails_app/config/database.yml +18 -0
- data/test/rails_app/config/environment.rb +5 -0
- data/test/rails_app/config/environments/development.rb +30 -0
- data/test/rails_app/config/environments/production.rb +80 -0
- data/test/rails_app/config/environments/test.rb +36 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_app/config/initializers/devise.rb +180 -0
- data/test/rails_app/config/initializers/inflections.rb +2 -0
- data/test/rails_app/config/initializers/secret_token.rb +8 -0
- data/test/rails_app/config/initializers/session_store.rb +1 -0
- data/test/rails_app/config/routes.rb +122 -0
- data/test/rails_app/db/migrate/20100401102949_create_tables.rb +71 -0
- data/test/rails_app/db/schema.rb +55 -0
- data/test/rails_app/lib/shared_admin.rb +17 -0
- data/test/rails_app/lib/shared_user.rb +29 -0
- data/test/rails_app/lib/shared_user_without_omniauth.rb +13 -0
- data/test/rails_app/public/404.html +26 -0
- data/test/rails_app/public/422.html +26 -0
- data/test/rails_app/public/500.html +26 -0
- data/test/rails_app/public/favicon.ico +0 -0
- data/test/routes_test.rb +264 -0
- data/test/support/action_controller/record_identifier.rb +10 -0
- data/test/support/assertions.rb +39 -0
- data/test/support/helpers.rb +73 -0
- data/test/support/integration.rb +92 -0
- data/test/support/locale/en.yml +8 -0
- data/test/support/mongoid.yml +6 -0
- data/test/support/webrat/integrations/rails.rb +24 -0
- data/test/test_helper.rb +34 -0
- data/test/test_helpers_test.rb +163 -0
- data/test/test_models.rb +33 -0
- metadata +531 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
module Devise
|
|
2
|
+
module Models
|
|
3
|
+
# Confirmable is responsible to verify if an account is already confirmed to
|
|
4
|
+
# sign in, and to send emails with confirmation instructions.
|
|
5
|
+
# Confirmation instructions are sent to the user email after creating a
|
|
6
|
+
# record and when manually requested by a new confirmation instruction request.
|
|
7
|
+
#
|
|
8
|
+
# == Options
|
|
9
|
+
#
|
|
10
|
+
# Confirmable adds the following options to +devise+:
|
|
11
|
+
#
|
|
12
|
+
# * +allow_unconfirmed_access_for+: the time you want to allow the user to access their account
|
|
13
|
+
# before confirming it. After this period, the user access is denied. You can
|
|
14
|
+
# use this to let your user access some features of your application without
|
|
15
|
+
# confirming the account, but blocking it after a certain period (ie 7 days).
|
|
16
|
+
# By default allow_unconfirmed_access_for is zero, it means users always have to confirm to sign in.
|
|
17
|
+
# * +reconfirmable+: requires any email changes to be confirmed (exactly the same way as
|
|
18
|
+
# initial account confirmation) to be applied. Requires additional unconfirmed_email
|
|
19
|
+
# db field to be setup (t.reconfirmable in migrations). Until confirmed new email is
|
|
20
|
+
# stored in unconfirmed email column, and copied to email column on successful
|
|
21
|
+
# confirmation.
|
|
22
|
+
# * +confirm_within+: the time before a sent confirmation token becomes invalid.
|
|
23
|
+
# You can use this to force the user to confirm within a set period of time.
|
|
24
|
+
#
|
|
25
|
+
# == Examples
|
|
26
|
+
#
|
|
27
|
+
# User.find(1).confirm! # returns true unless it's already confirmed
|
|
28
|
+
# User.find(1).confirmed? # true/false
|
|
29
|
+
# User.find(1).send_confirmation_instructions # manually send instructions
|
|
30
|
+
#
|
|
31
|
+
module Confirmable
|
|
32
|
+
extend ActiveSupport::Concern
|
|
33
|
+
include ActionView::Helpers::DateHelper
|
|
34
|
+
|
|
35
|
+
included do
|
|
36
|
+
before_create :generate_confirmation_token, if: :confirmation_required?
|
|
37
|
+
after_create :send_on_create_confirmation_instructions, if: :send_confirmation_notification?
|
|
38
|
+
before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, if: :postpone_email_change?
|
|
39
|
+
after_update :send_reconfirmation_instructions, if: :reconfirmation_required?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(*args, &block)
|
|
43
|
+
@bypass_confirmation_postpone = false
|
|
44
|
+
@reconfirmation_required = false
|
|
45
|
+
@skip_confirmation_notification = false
|
|
46
|
+
@raw_confirmation_token = nil
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.required_fields(klass)
|
|
51
|
+
required_methods = [:confirmation_token, :confirmed_at, :confirmation_sent_at]
|
|
52
|
+
required_methods << :unconfirmed_email if klass.reconfirmable
|
|
53
|
+
required_methods
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
|
57
|
+
# is already confirmed, add an error to email field. If the user is invalid
|
|
58
|
+
# add errors
|
|
59
|
+
def confirm!(args={})
|
|
60
|
+
pending_any_confirmation do
|
|
61
|
+
if confirmation_period_expired?
|
|
62
|
+
self.errors.add(:email, :confirmation_period_expired,
|
|
63
|
+
period: Devise::TimeInflector.time_ago_in_words(self.class.confirm_within.ago))
|
|
64
|
+
return false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
self.confirmation_token = nil
|
|
68
|
+
self.confirmed_at = Time.now.utc
|
|
69
|
+
|
|
70
|
+
saved = if self.class.reconfirmable && unconfirmed_email.present?
|
|
71
|
+
skip_reconfirmation!
|
|
72
|
+
self.email = unconfirmed_email
|
|
73
|
+
self.unconfirmed_email = nil
|
|
74
|
+
|
|
75
|
+
# We need to validate in such cases to enforce e-mail uniqueness
|
|
76
|
+
save(validate: true)
|
|
77
|
+
else
|
|
78
|
+
save(validate: args[:ensure_valid] == true)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
after_confirmation if saved
|
|
82
|
+
saved
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Verifies whether a user is confirmed or not
|
|
87
|
+
def confirmed?
|
|
88
|
+
!!confirmed_at
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def pending_reconfirmation?
|
|
92
|
+
self.class.reconfirmable && unconfirmed_email.present?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Send confirmation instructions by email
|
|
96
|
+
def send_confirmation_instructions
|
|
97
|
+
unless @raw_confirmation_token
|
|
98
|
+
generate_confirmation_token!
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
opts = pending_reconfirmation? ? { to: unconfirmed_email } : { }
|
|
102
|
+
send_devise_notification(:confirmation_instructions, @raw_confirmation_token, opts)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def send_reconfirmation_instructions
|
|
106
|
+
@reconfirmation_required = false
|
|
107
|
+
|
|
108
|
+
unless @skip_confirmation_notification
|
|
109
|
+
send_confirmation_instructions
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Resend confirmation token.
|
|
114
|
+
# Regenerates the token if the period is expired.
|
|
115
|
+
def resend_confirmation_instructions
|
|
116
|
+
pending_any_confirmation do
|
|
117
|
+
send_confirmation_instructions
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Overwrites active_for_authentication? for confirmation
|
|
122
|
+
# by verifying whether a user is active to sign in or not. If the user
|
|
123
|
+
# is already confirmed, it should never be blocked. Otherwise we need to
|
|
124
|
+
# calculate if the confirm time has not expired for this user.
|
|
125
|
+
def active_for_authentication?
|
|
126
|
+
super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# The message to be shown if the account is inactive.
|
|
130
|
+
def inactive_message
|
|
131
|
+
!confirmed? ? :unconfirmed : super
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# If you don't want confirmation to be sent on create, neither a code
|
|
135
|
+
# to be generated, call skip_confirmation!
|
|
136
|
+
def skip_confirmation!
|
|
137
|
+
self.confirmed_at = Time.now.utc
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Skips sending the confirmation/reconfirmation notification email after_create/after_update. Unlike
|
|
141
|
+
# #skip_confirmation!, record still requires confirmation.
|
|
142
|
+
def skip_confirmation_notification!
|
|
143
|
+
@skip_confirmation_notification = true
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# If you don't want reconfirmation to be sent, neither a code
|
|
147
|
+
# to be generated, call skip_reconfirmation!
|
|
148
|
+
def skip_reconfirmation!
|
|
149
|
+
@bypass_confirmation_postpone = true
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
protected
|
|
153
|
+
|
|
154
|
+
# A callback method used to deliver confirmation
|
|
155
|
+
# instructions on creation. This can be overridden
|
|
156
|
+
# in models to map to a nice sign up e-mail.
|
|
157
|
+
def send_on_create_confirmation_instructions
|
|
158
|
+
send_confirmation_instructions
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Callback to overwrite if confirmation is required or not.
|
|
162
|
+
def confirmation_required?
|
|
163
|
+
!confirmed?
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Checks if the confirmation for the user is within the limit time.
|
|
167
|
+
# We do this by calculating if the difference between today and the
|
|
168
|
+
# confirmation sent date does not exceed the confirm in time configured.
|
|
169
|
+
# Confirm_within is a model configuration, must always be an integer value.
|
|
170
|
+
#
|
|
171
|
+
# Example:
|
|
172
|
+
#
|
|
173
|
+
# # allow_unconfirmed_access_for = 1.day and confirmation_sent_at = today
|
|
174
|
+
# confirmation_period_valid? # returns true
|
|
175
|
+
#
|
|
176
|
+
# # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 4.days.ago
|
|
177
|
+
# confirmation_period_valid? # returns true
|
|
178
|
+
#
|
|
179
|
+
# # allow_unconfirmed_access_for = 5.days and confirmation_sent_at = 5.days.ago
|
|
180
|
+
# confirmation_period_valid? # returns false
|
|
181
|
+
#
|
|
182
|
+
# # allow_unconfirmed_access_for = 0.days
|
|
183
|
+
# confirmation_period_valid? # will always return false
|
|
184
|
+
#
|
|
185
|
+
# # allow_unconfirmed_access_for = nil
|
|
186
|
+
# confirmation_period_valid? # will always return true
|
|
187
|
+
#
|
|
188
|
+
def confirmation_period_valid?
|
|
189
|
+
self.class.allow_unconfirmed_access_for.nil? || (confirmation_sent_at && confirmation_sent_at.utc >= self.class.allow_unconfirmed_access_for.ago)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Checks if the user confirmation happens before the token becomes invalid
|
|
193
|
+
# Examples:
|
|
194
|
+
#
|
|
195
|
+
# # confirm_within = 3.days and confirmation_sent_at = 2.days.ago
|
|
196
|
+
# confirmation_period_expired? # returns false
|
|
197
|
+
#
|
|
198
|
+
# # confirm_within = 3.days and confirmation_sent_at = 4.days.ago
|
|
199
|
+
# confirmation_period_expired? # returns true
|
|
200
|
+
#
|
|
201
|
+
# # confirm_within = nil
|
|
202
|
+
# confirmation_period_expired? # will always return false
|
|
203
|
+
#
|
|
204
|
+
def confirmation_period_expired?
|
|
205
|
+
self.class.confirm_within && (Time.now > self.confirmation_sent_at + self.class.confirm_within )
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Checks whether the record requires any confirmation.
|
|
209
|
+
def pending_any_confirmation
|
|
210
|
+
if (!confirmed? || pending_reconfirmation?)
|
|
211
|
+
yield
|
|
212
|
+
else
|
|
213
|
+
self.errors.add(:email, :already_confirmed)
|
|
214
|
+
false
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Generates a new random token for confirmation, and stores
|
|
219
|
+
# the time this token is being generated
|
|
220
|
+
def generate_confirmation_token
|
|
221
|
+
raw, enc = Devise.token_generator.generate(self.class, :confirmation_token)
|
|
222
|
+
@raw_confirmation_token = raw
|
|
223
|
+
self.confirmation_token = enc
|
|
224
|
+
self.confirmation_sent_at = Time.now.utc
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def generate_confirmation_token!
|
|
228
|
+
generate_confirmation_token && save(validate: false)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
|
|
232
|
+
@reconfirmation_required = true
|
|
233
|
+
self.unconfirmed_email = self.email
|
|
234
|
+
self.email = self.email_was
|
|
235
|
+
generate_confirmation_token
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def postpone_email_change?
|
|
239
|
+
postpone = self.class.reconfirmable && email_changed? && !@bypass_confirmation_postpone && self.email.present?
|
|
240
|
+
@bypass_confirmation_postpone = false
|
|
241
|
+
postpone
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def reconfirmation_required?
|
|
245
|
+
self.class.reconfirmable && @reconfirmation_required && self.email.present?
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def send_confirmation_notification?
|
|
249
|
+
confirmation_required? && !@skip_confirmation_notification && self.email.present?
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# A callback initiated after successfully confirming. This can be
|
|
253
|
+
# used to insert your own logic that is only run after the user successfully
|
|
254
|
+
# confirms.
|
|
255
|
+
#
|
|
256
|
+
# Example:
|
|
257
|
+
#
|
|
258
|
+
# def after_confirmation
|
|
259
|
+
# self.update_attribute(:invite_code, nil)
|
|
260
|
+
# end
|
|
261
|
+
#
|
|
262
|
+
def after_confirmation
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
module ClassMethods
|
|
266
|
+
# Attempt to find a user by its email. If a record is found, send new
|
|
267
|
+
# confirmation instructions to it. If not, try searching for a user by unconfirmed_email
|
|
268
|
+
# field. If no user is found, returns a new user with an email not found error.
|
|
269
|
+
# Options must contain the user email
|
|
270
|
+
def send_confirmation_instructions(attributes={})
|
|
271
|
+
confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
|
|
272
|
+
unless confirmable.try(:persisted?)
|
|
273
|
+
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
|
|
274
|
+
end
|
|
275
|
+
confirmable.resend_confirmation_instructions if confirmable.persisted?
|
|
276
|
+
confirmable
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Find a user by its confirmation token and try to confirm it.
|
|
280
|
+
# If no user is found, returns a new user with an error.
|
|
281
|
+
# If the user is already confirmed, create an error for the user
|
|
282
|
+
# Options must have the confirmation_token
|
|
283
|
+
def confirm_by_token(confirmation_token)
|
|
284
|
+
original_token = confirmation_token
|
|
285
|
+
confirmation_token = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
|
|
286
|
+
|
|
287
|
+
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
|
|
288
|
+
confirmable.confirm! if confirmable.persisted?
|
|
289
|
+
confirmable.confirmation_token = original_token
|
|
290
|
+
confirmable
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Find a record for confirmation by unconfirmed email field
|
|
294
|
+
def find_by_unconfirmed_email_with_errors(attributes = {})
|
|
295
|
+
unconfirmed_required_attributes = confirmation_keys.map { |k| k == :email ? :unconfirmed_email : k }
|
|
296
|
+
unconfirmed_attributes = attributes.symbolize_keys
|
|
297
|
+
unconfirmed_attributes[:unconfirmed_email] = unconfirmed_attributes.delete(:email)
|
|
298
|
+
find_or_initialize_with_errors(unconfirmed_required_attributes, unconfirmed_attributes, :not_found)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
Devise::Models.config(self, :allow_unconfirmed_access_for, :confirmation_keys, :reconfirmable, :confirm_within)
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
require 'devise/strategies/database_authenticatable'
|
|
2
|
+
require 'bcrypt'
|
|
3
|
+
|
|
4
|
+
module Devise
|
|
5
|
+
# Digests the password using bcrypt.
|
|
6
|
+
def self.bcrypt(klass, password)
|
|
7
|
+
::BCrypt::Password.create("#{password}#{klass.pepper}", cost: klass.stretches).to_s
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Models
|
|
11
|
+
# Authenticatable Module, responsible for encrypting password and validating
|
|
12
|
+
# authenticity of a user while signing in.
|
|
13
|
+
#
|
|
14
|
+
# == Options
|
|
15
|
+
#
|
|
16
|
+
# DatabaseAuthenticable adds the following options to devise_for:
|
|
17
|
+
#
|
|
18
|
+
# * +pepper+: a random string used to provide a more secure hash. Use
|
|
19
|
+
# `rake secret` to generate new keys.
|
|
20
|
+
#
|
|
21
|
+
# * +stretches+: the cost given to bcrypt.
|
|
22
|
+
#
|
|
23
|
+
# == Examples
|
|
24
|
+
#
|
|
25
|
+
# User.find(1).valid_password?('password123') # returns true/false
|
|
26
|
+
#
|
|
27
|
+
module DatabaseAuthenticatable
|
|
28
|
+
extend ActiveSupport::Concern
|
|
29
|
+
|
|
30
|
+
included do
|
|
31
|
+
attr_reader :password, :current_password
|
|
32
|
+
attr_accessor :password_confirmation
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.required_fields(klass)
|
|
36
|
+
[:encrypted_password] + klass.authentication_keys
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Generates password encryption based on the given value.
|
|
40
|
+
def password=(new_password)
|
|
41
|
+
@password = new_password
|
|
42
|
+
self.encrypted_password = password_digest(@password) if @password.present?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Verifies whether a password (ie from sign in) is the user password.
|
|
46
|
+
def valid_password?(password)
|
|
47
|
+
return false if encrypted_password.blank?
|
|
48
|
+
bcrypt = ::BCrypt::Password.new(encrypted_password)
|
|
49
|
+
password = ::BCrypt::Engine.hash_secret("#{password}#{self.class.pepper}", bcrypt.salt)
|
|
50
|
+
Devise.secure_compare(password, encrypted_password)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Set password and password confirmation to nil
|
|
54
|
+
def clean_up_passwords
|
|
55
|
+
self.password = self.password_confirmation = nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Update record attributes when :current_password matches, otherwise
|
|
59
|
+
# returns error on :current_password.
|
|
60
|
+
#
|
|
61
|
+
# This method also rejects the password field if it is blank (allowing
|
|
62
|
+
# users to change relevant information like the e-mail without changing
|
|
63
|
+
# their password). In case the password field is rejected, the confirmation
|
|
64
|
+
# is also rejected as long as it is also blank.
|
|
65
|
+
def update_with_password(params, *options)
|
|
66
|
+
current_password = params.delete(:current_password)
|
|
67
|
+
|
|
68
|
+
if params[:password].blank?
|
|
69
|
+
params.delete(:password)
|
|
70
|
+
params.delete(:password_confirmation) if params[:password_confirmation].blank?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
result = if valid_password?(current_password)
|
|
74
|
+
update_attributes(params, *options)
|
|
75
|
+
else
|
|
76
|
+
self.assign_attributes(params, *options)
|
|
77
|
+
self.valid?
|
|
78
|
+
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
clean_up_passwords
|
|
83
|
+
result
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Updates record attributes without asking for the current password.
|
|
87
|
+
# Never allows a change to the current password. If you are using this
|
|
88
|
+
# method, you should probably override this method to protect other
|
|
89
|
+
# attributes you would not like to be updated without a password.
|
|
90
|
+
#
|
|
91
|
+
# Example:
|
|
92
|
+
#
|
|
93
|
+
# def update_without_password(params, *options)
|
|
94
|
+
# params.delete(:email)
|
|
95
|
+
# super(params)
|
|
96
|
+
# end
|
|
97
|
+
#
|
|
98
|
+
def update_without_password(params, *options)
|
|
99
|
+
params.delete(:password)
|
|
100
|
+
params.delete(:password_confirmation)
|
|
101
|
+
|
|
102
|
+
result = update_attributes(params, *options)
|
|
103
|
+
clean_up_passwords
|
|
104
|
+
result
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Destroy record when :current_password matches, otherwise returns
|
|
108
|
+
# error on :current_password. It also automatically rejects
|
|
109
|
+
# :current_password if it is blank.
|
|
110
|
+
def destroy_with_password(current_password)
|
|
111
|
+
result = if valid_password?(current_password)
|
|
112
|
+
destroy
|
|
113
|
+
else
|
|
114
|
+
self.valid?
|
|
115
|
+
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
|
|
116
|
+
false
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
result
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# A callback initiated after successfully authenticating. This can be
|
|
123
|
+
# used to insert your own logic that is only run after the user successfully
|
|
124
|
+
# authenticates.
|
|
125
|
+
#
|
|
126
|
+
# Example:
|
|
127
|
+
#
|
|
128
|
+
# def after_database_authentication
|
|
129
|
+
# self.update_attribute(:invite_code, nil)
|
|
130
|
+
# end
|
|
131
|
+
#
|
|
132
|
+
def after_database_authentication
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# A reliable way to expose the salt regardless of the implementation.
|
|
136
|
+
def authenticatable_salt
|
|
137
|
+
encrypted_password[0,29] if encrypted_password
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
protected
|
|
141
|
+
|
|
142
|
+
# Digests the password using bcrypt. Custom encryption should override
|
|
143
|
+
# this method to apply their own algorithm.
|
|
144
|
+
#
|
|
145
|
+
# See https://github.com/plataformatec/devise-encryptable for examples
|
|
146
|
+
# of other encryption engines.
|
|
147
|
+
def password_digest(password)
|
|
148
|
+
Devise.bcrypt(self.class, password)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
module ClassMethods
|
|
152
|
+
Devise::Models.config(self, :pepper, :stretches)
|
|
153
|
+
|
|
154
|
+
# We assume this method already gets the sanitized values from the
|
|
155
|
+
# DatabaseAuthenticatable strategy. If you are using this method on
|
|
156
|
+
# your own, be sure to sanitize the conditions hash to only include
|
|
157
|
+
# the proper fields.
|
|
158
|
+
def find_for_database_authentication(conditions)
|
|
159
|
+
find_for_authentication(conditions)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|