clearance 1.8.0 → 1.13.0

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

Potentially problematic release.


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

Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +24 -6
  4. data/.yardopts +6 -0
  5. data/Appraisals +12 -4
  6. data/CONTRIBUTING.md +4 -1
  7. data/Gemfile +2 -2
  8. data/Gemfile.lock +87 -85
  9. data/NEWS.md +672 -311
  10. data/README.md +185 -330
  11. data/app/controllers/clearance/passwords_controller.rb +25 -19
  12. data/app/controllers/clearance/sessions_controller.rb +17 -4
  13. data/app/controllers/clearance/users_controller.rb +10 -4
  14. data/app/mailers/clearance_mailer.rb +2 -3
  15. data/app/views/clearance_mailer/change_password.html.erb +6 -3
  16. data/app/views/clearance_mailer/change_password.text.erb +5 -0
  17. data/app/views/layouts/application.html.erb +2 -2
  18. data/app/views/passwords/create.html.erb +1 -1
  19. data/app/views/passwords/edit.html.erb +2 -2
  20. data/app/views/passwords/new.html.erb +2 -2
  21. data/app/views/sessions/_form.html.erb +2 -2
  22. data/app/views/sessions/new.html.erb +1 -1
  23. data/app/views/users/new.html.erb +2 -2
  24. data/bin/setup +6 -2
  25. data/config/locales/clearance.en.yml +6 -0
  26. data/db/migrate/20110111224543_create_clearance_users.rb +1 -1
  27. data/gemfiles/{rails3.2.gemfile → rails32.gemfile} +1 -1
  28. data/gemfiles/{rails4.0.gemfile → rails40.gemfile} +2 -2
  29. data/gemfiles/{rails4.1.gemfile → rails41.gemfile} +2 -2
  30. data/gemfiles/{rails4.2.gemfile → rails42.gemfile} +2 -2
  31. data/gemfiles/rails50.gemfile +19 -0
  32. data/lib/clearance/authentication.rb +52 -1
  33. data/lib/clearance/authorization.rb +47 -4
  34. data/lib/clearance/back_door.rb +1 -0
  35. data/lib/clearance/configuration.rb +127 -15
  36. data/lib/clearance/constraints/signed_in.rb +21 -0
  37. data/lib/clearance/constraints/signed_out.rb +12 -0
  38. data/lib/clearance/constraints.rb +12 -0
  39. data/lib/clearance/controller.rb +13 -0
  40. data/lib/clearance/default_sign_in_guard.rb +17 -0
  41. data/lib/clearance/engine.rb +24 -4
  42. data/lib/clearance/password_strategies/bcrypt.rb +9 -2
  43. data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +19 -0
  44. data/lib/clearance/password_strategies/blowfish.rb +17 -0
  45. data/lib/clearance/password_strategies/sha1.rb +17 -0
  46. data/lib/clearance/password_strategies.rb +13 -0
  47. data/lib/clearance/rack_session.rb +13 -0
  48. data/lib/clearance/rspec.rb +15 -4
  49. data/lib/clearance/session.rb +46 -1
  50. data/lib/clearance/session_status.rb +7 -0
  51. data/lib/clearance/sign_in_guard.rb +65 -0
  52. data/lib/clearance/test_unit.rb +3 -3
  53. data/lib/clearance/testing/controller_helpers.rb +44 -0
  54. data/lib/clearance/testing/deny_access_matcher.rb +35 -1
  55. data/lib/clearance/testing/helpers.rb +9 -25
  56. data/lib/clearance/testing/view_helpers.rb +32 -0
  57. data/lib/clearance/token.rb +7 -0
  58. data/lib/clearance/user.rb +159 -0
  59. data/lib/clearance/version.rb +1 -1
  60. data/lib/clearance.rb +2 -0
  61. data/lib/generators/clearance/install/install_generator.rb +13 -3
  62. data/lib/generators/clearance/install/templates/db/migrate/add_clearance_to_users.rb +3 -3
  63. data/lib/generators/clearance/install/templates/db/migrate/create_users.rb +2 -2
  64. data/lib/generators/clearance/routes/routes_generator.rb +23 -0
  65. data/lib/generators/clearance/routes/templates/routes.rb +7 -7
  66. data/lib/generators/clearance/specs/templates/features/clearance/user_signs_out_spec.rb.tt +1 -1
  67. data/lib/generators/clearance/specs/templates/features/clearance/visitor_resets_password_spec.rb.tt +12 -2
  68. data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_in_spec.rb.tt +1 -1
  69. data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_up_spec.rb.tt +1 -1
  70. data/lib/generators/clearance/specs/templates/features/clearance/visitor_updates_password_spec.rb.tt +1 -1
  71. data/spec/acceptance/clearance_installation_spec.rb +4 -1
  72. data/spec/app_templates/config/initializers/clearance.rb +2 -0
  73. data/spec/app_templates/testapp/app/controllers/home_controller.rb +5 -1
  74. data/spec/app_templates/testapp/config/initializers/action_mailer.rb +1 -3
  75. data/spec/clearance/controller_spec.rb +11 -0
  76. data/spec/clearance/rack_session_spec.rb +5 -5
  77. data/spec/clearance/testing/{helpers_spec.rb → controller_helpers_spec.rb} +12 -12
  78. data/spec/clearance/testing/view_helpers_spec.rb +37 -0
  79. data/spec/configuration_spec.rb +24 -0
  80. data/spec/controllers/apis_controller_spec.rb +6 -2
  81. data/spec/controllers/forgeries_controller_spec.rb +6 -1
  82. data/spec/controllers/passwords_controller_spec.rb +1 -11
  83. data/spec/controllers/permissions_controller_spec.rb +13 -3
  84. data/spec/controllers/sessions_controller_spec.rb +4 -4
  85. data/spec/dummy/app/controllers/application_controller.rb +5 -1
  86. data/spec/dummy/application.rb +4 -0
  87. data/spec/generators/clearance/install/install_generator_spec.rb +15 -3
  88. data/spec/generators/clearance/routes/routes_generator_spec.rb +5 -1
  89. data/spec/generators/clearance/views/views_generator_spec.rb +11 -10
  90. data/spec/helpers/helper_helpers_spec.rb +10 -0
  91. data/spec/mailers/clearance_mailer_spec.rb +13 -19
  92. data/spec/password_strategies/bcrypt_migration_from_sha1_spec.rb +6 -0
  93. data/spec/password_strategies/blowfish_spec.rb +6 -0
  94. data/spec/password_strategies/sha1_spec.rb +6 -0
  95. data/spec/spec_helper.rb +7 -0
  96. data/spec/support/generator_spec_helpers.rb +4 -0
  97. data/spec/support/http_method_shim.rb +23 -0
  98. data/spec/user_spec.rb +9 -0
  99. data/spec/views/view_helpers_spec.rb +10 -0
  100. metadata +19 -8
@@ -1,13 +1,19 @@
1
1
  require 'clearance/default_sign_in_guard'
2
2
 
3
3
  module Clearance
4
+ # Represents a clearance session, ultimately persisted in
5
+ # `request.env[:clearance]` by {RackSession}.
4
6
  class Session
7
+ # @param env The current rack environment
5
8
  def initialize(env)
6
9
  @env = env
7
10
  @current_user = nil
8
11
  @cookies = nil
9
12
  end
10
13
 
14
+ # Called by {RackSession} to add the Clearance session cookie to a response.
15
+ #
16
+ # @return [void]
11
17
  def add_cookie_to_headers(headers)
12
18
  if cookie_value[:value].present?
13
19
  Rack::Utils.set_cookie_header!(
@@ -18,6 +24,9 @@ module Clearance
18
24
  end
19
25
  end
20
26
 
27
+ # The current user represented by this session.
28
+ #
29
+ # @return [User, nil]
21
30
  def current_user
22
31
  if remember_token.present?
23
32
  @current_user ||= user_from_remember_token(remember_token)
@@ -26,6 +35,20 @@ module Clearance
26
35
  @current_user
27
36
  end
28
37
 
38
+ # Sign the provided user in, if approved by the configured sign in guards.
39
+ # If the sign in guard stack returns {SuccessStatus}, the {#current_user}
40
+ # will be set and then remember token cookie will be set to the user's
41
+ # remember token. If the stack returns {FailureStatus}, {#current_user} will
42
+ # be nil.
43
+ #
44
+ # In either event, the resulting status will be yielded to a provided block,
45
+ # if provided. See {SessionsController#create} for an example of how this
46
+ # can be used.
47
+ #
48
+ # @param [User] user
49
+ # @yieldparam [SuccessStatus,FailureStatus] status Result of the sign in
50
+ # operation.
51
+ # @return [void]
29
52
  def sign_in(user, &block)
30
53
  @current_user = user
31
54
  status = run_sign_in_stack
@@ -41,6 +64,13 @@ module Clearance
41
64
  end
42
65
  end
43
66
 
67
+ # Invalidates the users remember token and removes the remember token cookie
68
+ # from the store. The invalidation of the remember token causes any other
69
+ # sessions that are signed in from other locations to also be invalidated on
70
+ # their next request. This is because all Clearance sessions for a given
71
+ # user share a remember token.
72
+ #
73
+ # @return [void]
44
74
  def sign_out
45
75
  if signed_in?
46
76
  current_user.reset_remember_token!
@@ -50,24 +80,33 @@ module Clearance
50
80
  cookies.delete remember_token_cookie
51
81
  end
52
82
 
83
+ # True if {#current_user} is set.
84
+ #
85
+ # @return [Boolean]
53
86
  def signed_in?
54
87
  current_user.present?
55
88
  end
56
89
 
90
+ # True if {#current_user} is not set
91
+ #
92
+ # @return [Boolean]
57
93
  def signed_out?
58
94
  ! signed_in?
59
95
  end
60
96
 
61
97
  private
62
98
 
99
+ # @api private
63
100
  def cookies
64
- @cookies ||= @env['action_dispatch.cookies'] || Rack::Request.new(@env).cookies
101
+ @cookies ||= ActionDispatch::Request.new(@env).cookie_jar
65
102
  end
66
103
 
104
+ # @api private
67
105
  def remember_token
68
106
  cookies[remember_token_cookie]
69
107
  end
70
108
 
109
+ # @api private
71
110
  def remember_token_expires
72
111
  if expires_configuration.arity == 1
73
112
  expires_configuration.call(cookies)
@@ -80,23 +119,28 @@ module Clearance
80
119
  end
81
120
  end
82
121
 
122
+ # @api private
83
123
  def remember_token_cookie
84
124
  Clearance.configuration.cookie_name.freeze
85
125
  end
86
126
 
127
+ # @api private
87
128
  def expires_configuration
88
129
  Clearance.configuration.cookie_expiration
89
130
  end
90
131
 
132
+ # @api private
91
133
  def user_from_remember_token(token)
92
134
  Clearance.configuration.user_model.where(remember_token: token).first
93
135
  end
94
136
 
137
+ # @api private
95
138
  def run_sign_in_stack
96
139
  @stack ||= initialize_sign_in_guard_stack
97
140
  @stack.call
98
141
  end
99
142
 
143
+ # @api private
100
144
  def initialize_sign_in_guard_stack
101
145
  default_guard = DefaultSignInGuard.new(self)
102
146
  guards = Clearance.configuration.sign_in_guards
@@ -106,6 +150,7 @@ module Clearance
106
150
  end
107
151
  end
108
152
 
153
+ # @api private
109
154
  def cookie_value
110
155
  value = {
111
156
  expires: remember_token_expires,
@@ -1,17 +1,24 @@
1
1
  module Clearance
2
+ # Indicates a user was successfully signed in, passing all {SignInGuard}s.
2
3
  class SuccessStatus
4
+ # Is true, indicating that the sign in was successful.
3
5
  def success?
4
6
  true
5
7
  end
6
8
  end
7
9
 
10
+ # Indicates a failure in the {SignInGuard} stack which prevented successful
11
+ # sign in.
8
12
  class FailureStatus
13
+ # The reason the sign in failed.
9
14
  attr_reader :failure_message
10
15
 
16
+ # @param [String] failure_message The reason the sign in failed.
11
17
  def initialize(failure_message)
12
18
  @failure_message = failure_message
13
19
  end
14
20
 
21
+ # Is false, indicating that the sign in was unsuccessful.
15
22
  def success?
16
23
  false
17
24
  end
@@ -1,20 +1,83 @@
1
1
  require 'clearance/session_status'
2
2
 
3
3
  module Clearance
4
+ # The base class for {DefaultSignInGuard} and all custom sign in guards.
5
+ #
6
+ # Sign in guards provide you with fine-grained control over the process of
7
+ # signing in a user. Each guard is run in order and can do one of the
8
+ # following:
9
+ #
10
+ # * Fail the sign in process
11
+ # * Call the next guard in the stack
12
+ # * Short circuit all remaining guards, declaring sign in successfull.
13
+ #
14
+ # Sign In Guards could be used, for instance, to require that a user confirm
15
+ # their email address before being allowed to sign in.
16
+ #
17
+ # # in config/initializers/clearance.rb
18
+ # Clearance.configure do |config|
19
+ # config.sign_in_guards = [ConfirmationGuard]
20
+ # end
21
+ #
22
+ # # in lib/guards/confirmation_guard.rb
23
+ # class ConfirmationGuard < Clearance::SignInGuard
24
+ # def call
25
+ # if signed_in? && current_user.email_confirmed?
26
+ # next_guard
27
+ # else
28
+ # failure("You must confirm your email address.")
29
+ # end
30
+ # end
31
+ # end
32
+ #
33
+ # Calling `success` or `failure` in any guard short circuits all of the
34
+ # remaining guards in the stack. In most cases, you will want to either call
35
+ # `failure` or `next_guard`. The {DefaultSignInGuard} will always be the final
36
+ # guard called and will handle calling `success` if appropriate.
37
+ #
38
+ # The stack is designed such that calling `call` will eventually return
39
+ # {SuccessStatus} or {FailureStatus}, thus halting the chain.
4
40
  class SignInGuard
41
+ # Creates an instance of a sign in guard.
42
+ #
43
+ # This is called by {Session} automatically using the array of guards
44
+ # configured in {Configuration#sign_in_guards} and the {DefaultSignInGuard}.
45
+ # There is no reason for users of Clearance to concern themselves with the
46
+ # initialization of each guard or the stack as a whole.
47
+ #
48
+ # @param [Session] session The current clearance session
49
+ # @param [[SignInGuard]] stack The sign in guards that come after this
50
+ # guard in the stack
5
51
  def initialize(session, stack = [])
6
52
  @session = session
7
53
  @stack = stack
8
54
  end
9
55
 
56
+ # Indicates the entire sign in operation is successful and that no further
57
+ # guards should be run.
58
+ #
59
+ # In most cases your guards will want to delegate this responsibility to the
60
+ # {DefaultSignInGuard}, allowing the entire stack to execute. In that case,
61
+ # your custom guard would likely want to call `next_guard` instead.
62
+ #
63
+ # @return [SuccessStatus]
10
64
  def success
11
65
  SuccessStatus.new
12
66
  end
13
67
 
68
+ # Indicates this guard failed, and the entire sign in process should fail as
69
+ # a result.
70
+ #
71
+ # @param [String] message The reason the guard failed.
72
+ # @return [FailureStatus]
14
73
  def failure(message)
15
74
  FailureStatus.new(message)
16
75
  end
17
76
 
77
+ # Passes off responsibility for determining success or failure to the next
78
+ # guard in the stack.
79
+ #
80
+ # @return [SuccessStatus, FailureStatus]
18
81
  def next_guard
19
82
  stack.call
20
83
  end
@@ -23,10 +86,12 @@ module Clearance
23
86
 
24
87
  attr_reader :stack, :session
25
88
 
89
+ # True if there is a currently a user stored in the clearance environment.
26
90
  def signed_in?
27
91
  session.signed_in?
28
92
  end
29
93
 
94
+ # The user currently stored in the clearance environment.
30
95
  def current_user
31
96
  session.current_user
32
97
  end
@@ -1,8 +1,8 @@
1
- require 'clearance/testing/deny_access_matcher'
2
- require 'clearance/testing/helpers'
1
+ require "clearance/testing/deny_access_matcher"
2
+ require "clearance/testing/controller_helpers"
3
3
 
4
4
  ActionController::TestCase.extend Clearance::Testing::Matchers
5
5
 
6
6
  class ActionController::TestCase
7
- include Clearance::Testing::Helpers
7
+ include Clearance::Testing::ControllerHelpers
8
8
  end
@@ -0,0 +1,44 @@
1
+ module Clearance
2
+ module Testing
3
+ # Provides helpers to your controller specs.
4
+ # These are typically used in tests by requiring `clearance/rspec` or
5
+ # `clearance/test_unit` as appropriate in your `rails_helper.rb` or
6
+ # `test_helper.rb` files.
7
+ module ControllerHelpers
8
+ # @api private
9
+ def setup_controller_request_and_response
10
+ super
11
+ @request.env[:clearance] = Clearance::Session.new(@request.env)
12
+ end
13
+
14
+ # Signs in a user that is created using FactoryGirl.
15
+ # The factory name is derrived from your `user_class` Clearance
16
+ # configuration.
17
+ #
18
+ # @raise [RuntimeError] if FactoryGirl is not defined.
19
+ def sign_in
20
+ unless defined?(FactoryGirl)
21
+ raise("Clearance's `sign_in` helper requires factory_girl")
22
+ end
23
+
24
+ factory = Clearance.configuration.user_model.to_s.underscore.to_sym
25
+ sign_in_as FactoryGirl.create(factory)
26
+ end
27
+
28
+ # Signs in the provided user.
29
+ #
30
+ # @return user
31
+ def sign_in_as(user)
32
+ @request.env[:clearance].sign_in(user)
33
+ user
34
+ end
35
+
36
+ # Signs out a user that may be signed in.
37
+ #
38
+ # @return [void]
39
+ def sign_out
40
+ @request.env[:clearance].sign_out
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,10 +1,40 @@
1
1
  module Clearance
2
2
  module Testing
3
+ # Provides matchers to be used in your controller specs.
4
+ # These are typically exposed to your controller specs by
5
+ # requiring `clearance/rspec` or `clearance/test_unit` as
6
+ # appropriate in your `rails_helper.rb` or `test_helper.rb`
7
+ # files.
3
8
  module Matchers
9
+ # The `deny_access` matcher is used to assert that a
10
+ # request is denied access by clearance.
11
+ # @option opts [String] :flash The expected flash notice message. Defaults
12
+ # to nil, which means the flash will not be checked.
13
+ # @option opts [String] :redirect The expected redirect url. Defaults to
14
+ # `'/'` if signed in or the `sign_in_url` if signed out.
15
+ #
16
+ # class PostsController < ActionController::Base
17
+ # before_action :require_login
18
+ #
19
+ # def index
20
+ # @posts = Post.all
21
+ # end
22
+ # end
23
+ #
24
+ # describe PostsController do
25
+ # describe "#index" do
26
+ # it "denies access to users not signed in" do
27
+ # get :index
28
+ #
29
+ # expect(controller).to deny_access
30
+ # end
31
+ # end
32
+ # end
4
33
  def deny_access(opts = {})
5
34
  DenyAccessMatcher.new(self, opts)
6
35
  end
7
36
 
37
+ # @api private
8
38
  class DenyAccessMatcher
9
39
  attr_reader :failure_message, :failure_message_when_negated
10
40
 
@@ -37,13 +67,17 @@ module Clearance
37
67
  private
38
68
 
39
69
  def denied_access_url
40
- if @controller.signed_in?
70
+ if clearance_session.signed_in?
41
71
  '/'
42
72
  else
43
73
  @controller.sign_in_url
44
74
  end
45
75
  end
46
76
 
77
+ def clearance_session
78
+ @controller.request.env[:clearance]
79
+ end
80
+
47
81
  def flash_notice
48
82
  @controller.flash[:notice]
49
83
  end
@@ -1,31 +1,15 @@
1
+ require "clerance/testing/controller_helpers"
2
+
1
3
  module Clearance
2
4
  module Testing
5
+ # @deprecated Use Clearance::Testing::ControllerHelpers
3
6
  module Helpers
4
- def setup_controller_request_and_response
5
- super
6
- @request.env[:clearance] = Clearance::Session.new(@request.env)
7
- end
8
-
9
- def sign_in
10
- unless defined?(FactoryGirl)
11
- raise(
12
- RuntimeError,
13
- "Clearance's `sign_in` helper requires factory_girl"
14
- )
15
- end
16
-
17
- factory = Clearance.configuration.user_model.to_s.underscore.to_sym
18
- sign_in_as FactoryGirl.create(factory)
19
- end
20
-
21
- def sign_in_as(user)
22
- @controller.sign_in user
23
- user
24
- end
25
-
26
- def sign_out
27
- @controller.sign_out
28
- end
7
+ warn(
8
+ "#{Kernel.caller.first} [DEPRECATION] Clearance::Testing::Helpers is "\
9
+ "deprecated and has been replaced with " \
10
+ "Clearance::Testing::ControllerHelpers. Require " \
11
+ "clearance/testing/controller_helpers instead."
12
+ )
29
13
  end
30
14
  end
31
15
  end
@@ -0,0 +1,32 @@
1
+ module Clearance
2
+ module Testing
3
+ # Provides helpers to your view and helper specs.
4
+ # Using these helpers makes `current_user`, `signed_in?` and `signed_out?`
5
+ # behave properly in view and helper specs.
6
+ module ViewHelpers
7
+ # Sets current_user on the view under test to a new instance of your user
8
+ # model.
9
+ def sign_in
10
+ view.current_user = Clearance.configuration.user_model.new
11
+ end
12
+
13
+ # Sets current_user on the view under test to the supplied user.
14
+ def sign_in_as(user)
15
+ view.current_user = user
16
+ end
17
+
18
+ # @api private
19
+ module CurrentUser
20
+ attr_accessor :current_user
21
+
22
+ def signed_in?
23
+ current_user.present?
24
+ end
25
+
26
+ def signed_out?
27
+ !signed_in?
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,12 @@
1
1
  module Clearance
2
+ # Random token used for password reset and remember tokens.
3
+ # Clearance tokens are also public API and are inteded to be used anywhere you
4
+ # need a random token to correspond to a given user (e.g. you added an email
5
+ # confirmation token).
2
6
  class Token
7
+ # Generate a new random, 20 byte hex token.
8
+ #
9
+ # @return [String]
3
10
  def self.new
4
11
  SecureRandom.hex(20).encode('UTF-8')
5
12
  end
@@ -3,6 +3,102 @@ require 'email_validator'
3
3
  require 'clearance/token'
4
4
 
5
5
  module Clearance
6
+ # Required to be included in your configued user class, which is `User` by
7
+ # default, but can be changed with {Configuration#user_model=}.
8
+ #
9
+ # class User
10
+ # include Clearance::User
11
+ #
12
+ # # ...
13
+ # end
14
+ #
15
+ # This will also include methods exposed by your password strategy, which can
16
+ # be configured with {Configuration#password_strategy=}. By default, this is
17
+ # {PasswordStrategies::BCrypt}.
18
+ #
19
+ # ## Validations
20
+ #
21
+ # These validations are added to the class that the {User} module is mixed
22
+ # into.
23
+ #
24
+ # * If {#email_optional?} is false, {#email} is validated for presence,
25
+ # uniqueness and email format (using the `email_validator` gem in strict
26
+ # mode).
27
+ # * If {#skip_password_validation?} is false, {#password} is validated
28
+ # for presence.
29
+ #
30
+ # ## Callbacks
31
+ #
32
+ # * {#normalize_email} will be called on `before_validation`
33
+ # * {#generate_remember_token} will be called on `before_create`
34
+ #
35
+ # @!attribute email
36
+ # @return [String] The user's email.
37
+ #
38
+ # @!attribute encrypted_password
39
+ # @return [String] The user's encrypted password.
40
+ #
41
+ # @!attribute remember_token
42
+ # @return [String] The value used to identify this user in their {Session}
43
+ # cookie.
44
+ #
45
+ # @!attribute confirmation_token
46
+ # @return [String] The value used to identify this user in the password
47
+ # reset link.
48
+ #
49
+ # @!attribute password_changing
50
+ # @return [String] Transient (non-persisted) attribute that is set to
51
+ # `true` when {#update_password} is called. This value is read by
52
+ # {#skip_password_validation?} to determine if password validations need
53
+ # to be run.
54
+ #
55
+ # @!attribute [r] password
56
+ # @return [String] Transient (non-persisted) attribute that is set when
57
+ # updating a user's password. Only the {#encrypted_password} is persisted.
58
+ #
59
+ # @!method password=
60
+ # Sets the user's encrypted_password by using the configured Password
61
+ # Strategy's `password=` method. By default, this will be
62
+ # {PasswordStrategies::BCrypt#password=}, but can be changed with
63
+ # {Configuration#password_strategy}.
64
+ #
65
+ # @see PasswordStrategies
66
+ # @return [void]
67
+ #
68
+ # @!method authenticated?
69
+ # Check's the provided password against the user's encrypted password using
70
+ # the configured password strategy. By default, this will be
71
+ # {PasswordStrategies::BCrypt#authenticated?}, but can be changed with
72
+ # {Configuration#password_strategy}.
73
+ #
74
+ # @see PasswordStrategies
75
+ # @param [String] password
76
+ # The password to check.
77
+ # @return [Boolean]
78
+ # True if the password provided is correct for the user.
79
+ #
80
+ # @!method self.authenticate
81
+ # Finds the user with the given email and authenticates them with the
82
+ # provided password. If the email corresponds to a user and the provided
83
+ # password is correct for that user, this method will return that user.
84
+ # Otherwise it will return nil.
85
+ #
86
+ # @return [User, nil]
87
+ #
88
+ # @!method self.find_by_normalized_email
89
+ # Finds the user with the given email. The email with be normalized via
90
+ # {#normalize_email}.
91
+ #
92
+ # @return [User, nil]
93
+ #
94
+ # @!method self.normalize_email
95
+ # Normalizes the provided email by downcasing and removing all spaces.
96
+ # This is used by {find_by_normalized_email} and is also called when
97
+ # validating a user to ensure only normalized emails are stored in the
98
+ # database.
99
+ #
100
+ # @return [String]
101
+ #
6
102
  module User
7
103
  extend ActiveSupport::Concern
8
104
 
@@ -15,6 +111,7 @@ module Clearance
15
111
  include password_strategy
16
112
  end
17
113
 
114
+ # @api private
18
115
  module ClassMethods
19
116
  def authenticate(email, password)
20
117
  if user = find_by_normalized_email(email)
@@ -39,6 +136,7 @@ module Clearance
39
136
  end
40
137
  end
41
138
 
139
+ # @api private
42
140
  module Validations
43
141
  extend ActiveSupport::Concern
44
142
 
@@ -53,6 +151,7 @@ module Clearance
53
151
  end
54
152
  end
55
153
 
154
+ # @api private
56
155
  module Callbacks
57
156
  extend ActiveSupport::Concern
58
157
 
@@ -62,16 +161,47 @@ module Clearance
62
161
  end
63
162
  end
64
163
 
164
+ # Generates a {#confirmation_token} for the user, which allows them to reset
165
+ # their password via an email link.
166
+ #
167
+ # Calling `forgot_password!` will cause the user model to be saved without
168
+ # validations. Any other changes you made to this user instance will also
169
+ # be persisted, without validation. It is inteded to be called on an
170
+ # instance with no changes (`dirty? == false`).
171
+ #
172
+ # @return [Boolean] Was the save successful?
65
173
  def forgot_password!
66
174
  generate_confirmation_token
67
175
  save validate: false
68
176
  end
69
177
 
178
+ # Generates a new {#remember_token} for the user, which will have the effect
179
+ # of signing all of the user's current sessions out. This is called
180
+ # internally by {Session#sign_out}.
181
+ #
182
+ # Calling `reset_remember_token!` will cause the user model to be saved
183
+ # without validations. Any other changes you made to this user instance will
184
+ # also be persisted, without validation. It is inteded to be called on an
185
+ # instance with no changes (`dirty? == false`).
186
+ #
187
+ # @return [Boolean] Was the save successful?
70
188
  def reset_remember_token!
71
189
  generate_remember_token
72
190
  save validate: false
73
191
  end
74
192
 
193
+ # Sets the user's password to the new value, using the `password=` method on
194
+ # the configured password strategy. By default, this is
195
+ # {PasswordStrategies::BCrypt#password=}.
196
+ #
197
+ # This also has the side-effect of blanking the {#confirmation_token} and
198
+ # rotating the `#remember_token`.
199
+ #
200
+ # Validations will be run as part of this update. If the user instance is
201
+ # not valid, the password change will not be persisted, and this method will
202
+ # return `false`.
203
+ #
204
+ # @return [Boolean] Was the save successful?
75
205
  def update_password(new_password)
76
206
  self.password_changing = true
77
207
  self.password = new_password
@@ -86,28 +216,57 @@ module Clearance
86
216
 
87
217
  private
88
218
 
219
+ # Sets the email on this instance to the value returned by
220
+ # {.normalize_email}
221
+ #
222
+ # @return [String]
89
223
  def normalize_email
90
224
  self.email = self.class.normalize_email(email)
91
225
  end
92
226
 
227
+ # Always false. Override this method in your user model to allow for other
228
+ # forms of user authentication (username, Facebook, etc).
229
+ #
230
+ # @return [false]
93
231
  def email_optional?
94
232
  false
95
233
  end
96
234
 
235
+ # Always false. Override this method in your user model to allow for other
236
+ # forms of user authentication (username, Facebook, etc).
237
+ #
238
+ # @return [false]
97
239
  def password_optional?
98
240
  false
99
241
  end
100
242
 
243
+ # True if {#password_optional?} is true or if the user already has an
244
+ # {#encrypted_password} that is not changing.
245
+ #
246
+ # @return [Boolean]
101
247
  def skip_password_validation?
102
248
  password_optional? || (encrypted_password.present? && !password_changing)
103
249
  end
104
250
 
251
+ # Sets the {#confirmation_token} on the instance to a new value generated by
252
+ # {Token.new}. The change is not automatically persisted. If you would like
253
+ # to generate and save in a single method call, use {#forgot_password!}.
254
+ #
255
+ # @return [String] The new confirmation token
105
256
  def generate_confirmation_token
106
257
  self.confirmation_token = Clearance::Token.new
107
258
  end
108
259
 
260
+ # Sets the {#remember_token} on the instance to a new value generated by
261
+ # {Token.new}. The change is not automatically persisted. If you would like
262
+ # to generate and sace in a single method call, use
263
+ # {#reset_remember_token!}.
264
+ #
265
+ # @return [String] The new remember token
109
266
  def generate_remember_token
110
267
  self.remember_token = Clearance::Token.new
111
268
  end
269
+
270
+ private_constant :Callbacks, :ClassMethods, :Validations
112
271
  end
113
272
  end
@@ -1,3 +1,3 @@
1
1
  module Clearance
2
- VERSION = "1.8.0"
2
+ VERSION = "1.13.0".freeze
3
3
  end