kschrader-authlogic 2.1.2

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.
Files changed (120) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG.rdoc +346 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +245 -0
  5. data/Rakefile +49 -0
  6. data/VERSION.yml +4 -0
  7. data/authlogic.gemspec +205 -0
  8. data/generators/session/session_generator.rb +9 -0
  9. data/generators/session/templates/session.rb +2 -0
  10. data/init.rb +1 -0
  11. data/lib/authlogic.rb +55 -0
  12. data/lib/authlogic/acts_as_authentic/base.rb +112 -0
  13. data/lib/authlogic/acts_as_authentic/email.rb +110 -0
  14. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +60 -0
  15. data/lib/authlogic/acts_as_authentic/login.rb +141 -0
  16. data/lib/authlogic/acts_as_authentic/magic_columns.rb +24 -0
  17. data/lib/authlogic/acts_as_authentic/password.rb +344 -0
  18. data/lib/authlogic/acts_as_authentic/perishable_token.rb +105 -0
  19. data/lib/authlogic/acts_as_authentic/persistence_token.rb +68 -0
  20. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +61 -0
  21. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +139 -0
  22. data/lib/authlogic/acts_as_authentic/single_access_token.rb +65 -0
  23. data/lib/authlogic/acts_as_authentic/validations_scope.rb +32 -0
  24. data/lib/authlogic/authenticates_many/association.rb +42 -0
  25. data/lib/authlogic/authenticates_many/base.rb +55 -0
  26. data/lib/authlogic/controller_adapters/abstract_adapter.rb +67 -0
  27. data/lib/authlogic/controller_adapters/merb_adapter.rb +30 -0
  28. data/lib/authlogic/controller_adapters/rails_adapter.rb +48 -0
  29. data/lib/authlogic/crypto_providers/aes256.rb +43 -0
  30. data/lib/authlogic/crypto_providers/bcrypt.rb +89 -0
  31. data/lib/authlogic/crypto_providers/md5.rb +34 -0
  32. data/lib/authlogic/crypto_providers/sha1.rb +35 -0
  33. data/lib/authlogic/crypto_providers/sha512.rb +50 -0
  34. data/lib/authlogic/i18n.rb +63 -0
  35. data/lib/authlogic/random.rb +33 -0
  36. data/lib/authlogic/regex.rb +25 -0
  37. data/lib/authlogic/session/activation.rb +58 -0
  38. data/lib/authlogic/session/active_record_trickery.rb +55 -0
  39. data/lib/authlogic/session/base.rb +37 -0
  40. data/lib/authlogic/session/brute_force_protection.rb +92 -0
  41. data/lib/authlogic/session/callbacks.rb +87 -0
  42. data/lib/authlogic/session/cookies.rb +130 -0
  43. data/lib/authlogic/session/existence.rb +93 -0
  44. data/lib/authlogic/session/foundation.rb +63 -0
  45. data/lib/authlogic/session/http_auth.rb +58 -0
  46. data/lib/authlogic/session/id.rb +41 -0
  47. data/lib/authlogic/session/klass.rb +75 -0
  48. data/lib/authlogic/session/magic_columns.rb +94 -0
  49. data/lib/authlogic/session/magic_states.rb +58 -0
  50. data/lib/authlogic/session/params.rb +100 -0
  51. data/lib/authlogic/session/password.rb +231 -0
  52. data/lib/authlogic/session/perishable_token.rb +18 -0
  53. data/lib/authlogic/session/persistence.rb +70 -0
  54. data/lib/authlogic/session/priority_record.rb +34 -0
  55. data/lib/authlogic/session/scopes.rb +101 -0
  56. data/lib/authlogic/session/session.rb +60 -0
  57. data/lib/authlogic/session/timeout.rb +82 -0
  58. data/lib/authlogic/session/unauthorized_record.rb +50 -0
  59. data/lib/authlogic/session/validation.rb +80 -0
  60. data/lib/authlogic/test_case.rb +114 -0
  61. data/lib/authlogic/test_case/mock_controller.rb +45 -0
  62. data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -0
  63. data/lib/authlogic/test_case/mock_logger.rb +10 -0
  64. data/lib/authlogic/test_case/mock_request.rb +19 -0
  65. data/lib/authlogic/test_case/rails_request_adapter.rb +30 -0
  66. data/rails/init.rb +1 -0
  67. data/shoulda_macros/authlogic.rb +13 -0
  68. data/test/acts_as_authentic_test/base_test.rb +18 -0
  69. data/test/acts_as_authentic_test/email_test.rb +97 -0
  70. data/test/acts_as_authentic_test/logged_in_status_test.rb +36 -0
  71. data/test/acts_as_authentic_test/login_test.rb +109 -0
  72. data/test/acts_as_authentic_test/magic_columns_test.rb +27 -0
  73. data/test/acts_as_authentic_test/password_test.rb +236 -0
  74. data/test/acts_as_authentic_test/perishable_token_test.rb +90 -0
  75. data/test/acts_as_authentic_test/persistence_token_test.rb +55 -0
  76. data/test/acts_as_authentic_test/restful_authentication_test.rb +40 -0
  77. data/test/acts_as_authentic_test/session_maintenance_test.rb +84 -0
  78. data/test/acts_as_authentic_test/single_access_test.rb +44 -0
  79. data/test/authenticates_many_test.rb +16 -0
  80. data/test/crypto_provider_test/aes256_test.rb +14 -0
  81. data/test/crypto_provider_test/bcrypt_test.rb +14 -0
  82. data/test/crypto_provider_test/sha1_test.rb +23 -0
  83. data/test/crypto_provider_test/sha512_test.rb +14 -0
  84. data/test/fixtures/companies.yml +5 -0
  85. data/test/fixtures/employees.yml +17 -0
  86. data/test/fixtures/projects.yml +3 -0
  87. data/test/fixtures/users.yml +24 -0
  88. data/test/libs/affiliate.rb +7 -0
  89. data/test/libs/company.rb +6 -0
  90. data/test/libs/employee.rb +7 -0
  91. data/test/libs/employee_session.rb +2 -0
  92. data/test/libs/ldaper.rb +3 -0
  93. data/test/libs/ordered_hash.rb +9 -0
  94. data/test/libs/project.rb +3 -0
  95. data/test/libs/user.rb +5 -0
  96. data/test/libs/user_session.rb +2 -0
  97. data/test/random_test.rb +49 -0
  98. data/test/session_test/activation_test.rb +43 -0
  99. data/test/session_test/active_record_trickery_test.rb +27 -0
  100. data/test/session_test/brute_force_protection_test.rb +101 -0
  101. data/test/session_test/callbacks_test.rb +6 -0
  102. data/test/session_test/cookies_test.rb +107 -0
  103. data/test/session_test/credentials_test.rb +0 -0
  104. data/test/session_test/existence_test.rb +64 -0
  105. data/test/session_test/http_auth_test.rb +28 -0
  106. data/test/session_test/id_test.rb +17 -0
  107. data/test/session_test/klass_test.rb +35 -0
  108. data/test/session_test/magic_columns_test.rb +62 -0
  109. data/test/session_test/magic_states_test.rb +60 -0
  110. data/test/session_test/params_test.rb +53 -0
  111. data/test/session_test/password_test.rb +106 -0
  112. data/test/session_test/perishability_test.rb +15 -0
  113. data/test/session_test/persistence_test.rb +21 -0
  114. data/test/session_test/scopes_test.rb +60 -0
  115. data/test/session_test/session_test.rb +59 -0
  116. data/test/session_test/timeout_test.rb +52 -0
  117. data/test/session_test/unauthorized_record_test.rb +13 -0
  118. data/test/session_test/validation_test.rb +23 -0
  119. data/test/test_helper.rb +174 -0
  120. metadata +229 -0
@@ -0,0 +1,18 @@
1
+ module Authlogic
2
+ module Session
3
+ # Maintains the perishable token, which is helpful for confirming records or authorizing records to reset their password. All that this
4
+ # module does is reset it after a session have been saved, just keep it changing. The more it changes, the tighter the security.
5
+ #
6
+ # See Authlogic::ActsAsAuthentic::PerishableToken for more information.
7
+ module PerishableToken
8
+ def self.included(klass)
9
+ klass.after_save :reset_perishable_token!
10
+ end
11
+
12
+ private
13
+ def reset_perishable_token!
14
+ record.reset_perishable_token if record.respond_to?(:reset_perishable_token) && !record.disable_perishable_token_maintenance?
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,70 @@
1
+ module Authlogic
2
+ module Session
3
+ # Responsible for allowing you to persist your sessions.
4
+ module Persistence
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ extend ClassMethods
8
+ include InstanceMethods
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ # This is how you persist a session. This finds the record for the current session using
14
+ # a variety of methods. It basically tries to "log in" the user without the user having
15
+ # to explicitly log in. Check out the other Authlogic::Session modules for more information.
16
+ #
17
+ # The best way to use this method is something like:
18
+ #
19
+ # helper_method :current_user_session, :current_user
20
+ #
21
+ # def current_user_session
22
+ # return @current_user_session if defined?(@current_user_session)
23
+ # @current_user_session = UserSession.find
24
+ # end
25
+ #
26
+ # def current_user
27
+ # return @current_user if defined?(@current_user)
28
+ # @current_user = current_user_session && current_user_session.user
29
+ # end
30
+ #
31
+ # Also, this method accepts a single parameter as the id, to find session that you marked with an id:
32
+ #
33
+ # UserSession.find(:secure)
34
+ #
35
+ # See the id method for more information on ids.
36
+ def find(id = nil, priority_record = nil)
37
+ session = new({:priority_record => priority_record}, id)
38
+ session.priority_record = priority_record
39
+ if session.persisting?
40
+ session
41
+ else
42
+ nil
43
+ end
44
+ end
45
+ end
46
+
47
+ module InstanceMethods
48
+ # Let's you know if the session is being persisted or not, meaning the user does not have to explicitly log in
49
+ # in order to be logged in. If the session has no associated record, it will try to find a record and persis
50
+ # the session. This is the method that the class level method find uses to ultimately persist the session.
51
+ def persisting?
52
+ return true if !record.nil?
53
+ self.attempted_record = nil
54
+ before_persisting
55
+ persist
56
+ ensure_authentication_attempted
57
+ if errors.empty? && !attempted_record.nil?
58
+ self.record = attempted_record
59
+ after_persisting
60
+ save_record
61
+ self.new_session = false
62
+ true
63
+ else
64
+ false
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,34 @@
1
+ module Authlogic
2
+ module Session
3
+ # The point of this module is to avoid the StaleObjectError raised when lock_version is implemented in ActiveRecord.
4
+ # We accomplish this by using a "priority record". Meaning this record is used if possible, it gets priority.
5
+ # This way we don't save a record behind the scenes thus making an object being used stale.
6
+ module PriorityRecord
7
+ def self.included(klass)
8
+ klass.class_eval do
9
+ attr_accessor :priority_record
10
+ end
11
+ end
12
+
13
+ # Setting priority record if it is passed. The only way it can be passed is through an array:
14
+ #
15
+ # session.credentials = [real_user_object, priority_user_object]
16
+ def credentials=(value)
17
+ super
18
+ values = value.is_a?(Array) ? value : [value]
19
+ self.priority_record = values[1] if values[1].class < ::ActiveRecord::Base
20
+ end
21
+
22
+ private
23
+ def attempted_record=(value)
24
+ value = priority_record if value == priority_record
25
+ super
26
+ end
27
+
28
+ def save_record(alternate_record = nil)
29
+ r = alternate_record || record
30
+ super if r != priority_record
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,101 @@
1
+ module Authlogic
2
+ module Session
3
+ # Authentication can be scoped, and it's easy, you just need to define how you want to scope everything. This should help you:
4
+ #
5
+ # 1. Want to scope by a parent object? Ex: An account has many users. Checkout Authlogic::AuthenticatesMany
6
+ # 2. Want to scope the validations in your model? Ex: 2 users can have the same login under different accounts. See Authlogic::ActsAsAuthentic::Scope
7
+ module Scopes # :nodoc:
8
+ def self.included(klass)
9
+ klass.class_eval do
10
+ extend ClassMethods
11
+ include InstanceMethods
12
+ attr_writer :scope
13
+ end
14
+ end
15
+
16
+ # = Scopes
17
+ module ClassMethods
18
+ # The current scope set, should be used in the block passed to with_scope.
19
+ def scope
20
+ Thread.current[:authlogic_scope]
21
+ end
22
+
23
+ # What with_scopes focuses on is scoping the query when finding the object and the name of the cookie / session. It works very similar to
24
+ # ActiveRecord::Base#with_scopes. It accepts a hash with any of the following options:
25
+ #
26
+ # * <tt>find_options:</tt> any options you can pass into ActiveRecord::Base.find. This is used when trying to find the record.
27
+ # * <tt>id:</tt> The id of the session, this gets merged with the real id. For information ids see the id method.
28
+ #
29
+ # Here is how you use it:
30
+ #
31
+ # UserSession.with_scope(:find_options => {:conditions => "account_id = 2"}, :id => "account_2") do
32
+ # UserSession.find
33
+ # end
34
+ #
35
+ # Eseentially what the above does is scope the searching of the object with the sql you provided. So instead of:
36
+ #
37
+ # User.find(:first, :conditions => "login = 'ben'")
38
+ #
39
+ # it would be:
40
+ #
41
+ # User.find(:first, :conditions => "login = 'ben' and account_id = 2")
42
+ #
43
+ # You will also notice the :id option. This works just like the id method. It scopes your cookies. So the name of your cookie will be:
44
+ #
45
+ # account_2_user_credentials
46
+ #
47
+ # instead of:
48
+ #
49
+ # user_credentials
50
+ #
51
+ # What is also nifty about scoping with an :id is that it merges your id's. So if you do:
52
+ #
53
+ # UserSession.with_scope(:find_options => {:conditions => "account_id = 2"}, :id => "account_2") do
54
+ # session = UserSession.new
55
+ # session.id = :secure
56
+ # end
57
+ #
58
+ # The name of your cookies will be:
59
+ #
60
+ # secure_account_2_user_credentials
61
+ def with_scope(options = {}, &block)
62
+ raise ArgumentError.new("You must provide a block") unless block_given?
63
+ self.scope = options
64
+ result = yield
65
+ self.scope = nil
66
+ result
67
+ end
68
+
69
+ private
70
+ def scope=(value)
71
+ Thread.current[:authlogic_scope] = value
72
+ end
73
+ end
74
+
75
+ module InstanceMethods
76
+ # Setting the scope if it exists upon instantiation.
77
+ def initialize(*args)
78
+ self.scope = self.class.scope
79
+ super
80
+ end
81
+
82
+ # The scope of the current object
83
+ def scope
84
+ @scope ||= {}
85
+ end
86
+
87
+ private
88
+ # Used for things like cookie_key, session_key, etc.
89
+ def build_key(last_part)
90
+ [scope[:id], super].compact.join("_")
91
+ end
92
+
93
+ def search_for_record(*args)
94
+ klass.send(:with_scope, :find => (scope[:find_options] || {})) do
95
+ klass.send(*args)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,60 @@
1
+ module Authlogic
2
+ module Session
3
+ # Handles all parts of authentication that deal with sessions. Such as persisting a session and saving / destroy a session.
4
+ module Session
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ extend Config
8
+ include InstanceMethods
9
+ persist :persist_by_session
10
+ after_save :update_session
11
+ after_destroy :update_session
12
+ after_persisting :update_session, :unless => :single_access?
13
+ end
14
+ end
15
+
16
+ # Configuration for the session feature.
17
+ module Config
18
+ # Works exactly like cookie_key, but for sessions. See cookie_key for more info.
19
+ #
20
+ # * <tt>Default:</tt> cookie_key
21
+ # * <tt>Accepts:</tt> Symbol or String
22
+ def session_key(value = nil)
23
+ rw_config(:session_key, value, cookie_key)
24
+ end
25
+ alias_method :session_key=, :session_key
26
+ end
27
+
28
+ # Instance methods for the session feature.
29
+ module InstanceMethods
30
+ private
31
+ # Tries to validate the session from information in the session
32
+ def persist_by_session
33
+ persistence_token, record_id = session_credentials
34
+ if !persistence_token.nil?
35
+ # Allow finding by persistence token, because when records are created the session is maintained in a before_save, when there is no id.
36
+ # This is done for performance reasons and to save on queries.
37
+ record = record_id.nil? ? search_for_record("find_by_persistence_token", persistence_token) : search_for_record("find_by_#{klass.primary_key}", record_id)
38
+ self.unauthorized_record = record if record && record.persistence_token == persistence_token
39
+ valid?
40
+ else
41
+ false
42
+ end
43
+ end
44
+
45
+ def session_credentials
46
+ [controller.session[session_key], controller.session["#{session_key}_#{klass.primary_key}"]].compact
47
+ end
48
+
49
+ def session_key
50
+ build_key(self.class.session_key)
51
+ end
52
+
53
+ def update_session
54
+ controller.session[session_key] = record && record.persistence_token
55
+ controller.session["#{session_key}_#{klass.primary_key}"] = record && record.send(record.class.primary_key)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,82 @@
1
+ module Authlogic
2
+ module Session
3
+ # Think about financial websites, if you are inactive for a certain period of time you will be asked to
4
+ # log back in on your next request. You can do this with Authlogic easily, there are 2 parts to this:
5
+ #
6
+ # 1. Define the timeout threshold:
7
+ #
8
+ # acts_as_authentic do |c|
9
+ # c.logged_in_timeout = 10.minutes # default is 10.minutes
10
+ # end
11
+ #
12
+ # 2. Enable logging out on timeouts
13
+ #
14
+ # class UserSession < Authlogic::Session::Base
15
+ # logout_on_timeout true # default if false
16
+ # end
17
+ #
18
+ # This will require a user to log back in if they are inactive for more than 10 minutes. In order for
19
+ # this feature to be used you must have a last_request_at datetime column in your table for whatever model
20
+ # you are authenticating with.
21
+ module Timeout
22
+ def self.included(klass)
23
+ klass.class_eval do
24
+ extend Config
25
+ include InstanceMethods
26
+ before_persisting :reset_stale_state
27
+ after_persisting :enforce_timeout
28
+ attr_accessor :stale_record
29
+ end
30
+ end
31
+
32
+ # Configuration for the timeout feature.
33
+ module Config
34
+ # With acts_as_authentic you get a :logged_in_timeout configuration option. If this is set, after this amount of time has passed the user
35
+ # will be marked as logged out. Obviously, since web based apps are on a per request basis, we have to define a time limit threshold that
36
+ # determines when we consider a user to be "logged out". Meaning, if they login and then leave the website, when do mark them as logged out?
37
+ # I recommend just using this as a fun feature on your website or reports, giving you a ballpark number of users logged in and active. This is
38
+ # not meant to be a dead accurate representation of a users logged in state, since there is really no real way to do this with web based apps.
39
+ # Think about a user that logs in and doesn't log out. There is no action that tells you that the user isn't technically still logged in and
40
+ # active.
41
+ #
42
+ # That being said, you can use that feature to require a new login if their session timesout. Similar to how financial sites work. Just set this option to
43
+ # true and if your record returns true for stale? then they will be required to log back in.
44
+ #
45
+ # Lastly, UserSession.find will still return a object is the session is stale, but you will not get a record. This allows you to determine if the
46
+ # user needs to log back in because their session went stale, or because they just aren't logged in. Just call current_user_session.stale? as your flag.
47
+ #
48
+ # * <tt>Default:</tt> false
49
+ # * <tt>Accepts:</tt> Boolean
50
+ def logout_on_timeout(value = nil)
51
+ rw_config(:logout_on_timeout, value, false)
52
+ end
53
+ alias_method :logout_on_timeout=, :logout_on_timeout
54
+ end
55
+
56
+ # Instance methods for the timeout feature.
57
+ module InstanceMethods
58
+ # Tells you if the record is stale or not. Meaning the record has timed out. This will only return true if you set logout_on_timeout to true in your configuration.
59
+ # Basically how a bank website works. If you aren't active over a certain period of time your session becomes stale and requires you to log back in.
60
+ def stale?
61
+ !stale_record.nil? || (logout_on_timeout? && record && record.logged_out?)
62
+ end
63
+
64
+ private
65
+ def reset_stale_state
66
+ self.stale_record = nil
67
+ end
68
+
69
+ def enforce_timeout
70
+ if stale?
71
+ self.stale_record = record
72
+ self.record = nil
73
+ end
74
+ end
75
+
76
+ def logout_on_timeout?
77
+ self.class.logout_on_timeout == true
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,50 @@
1
+ module Authlogic
2
+ module Session
3
+ # Allows you to create session with an object. Ex:
4
+ #
5
+ # UserSession.create(my_user_object)
6
+ #
7
+ # Be careful with this, because Authlogic is assuming that you have already confirmed that the
8
+ # user is who he says he is.
9
+ #
10
+ # For example, this is the method used to persist the session internally. Authlogic finds the user with
11
+ # the persistence token. At this point we know the user is who he says he is, so Authlogic just creates a
12
+ # session with the record. This is particularly useful for 3rd party authentication methods, such as
13
+ # OpenID. Let that method verify the identity, once it's verified, pass the object and create a session.
14
+ module UnauthorizedRecord
15
+ def self.included(klass)
16
+ klass.class_eval do
17
+ attr_accessor :unauthorized_record
18
+ validate :validate_by_unauthorized_record, :if => :authenticating_with_unauthorized_record?
19
+ end
20
+ end
21
+
22
+ # Returning meaningful credentials
23
+ def credentials
24
+ if authenticating_with_unauthorized_record?
25
+ details = {}
26
+ details[:unauthorized_record] = "<protected>"
27
+ details
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ # Setting the unauthorized record if it exists in the credentials passed.
34
+ def credentials=(value)
35
+ super
36
+ values = value.is_a?(Array) ? value : [value]
37
+ self.unauthorized_record = values.first if values.first.class < ::ActiveRecord::Base
38
+ end
39
+
40
+ private
41
+ def authenticating_with_unauthorized_record?
42
+ !unauthorized_record.nil?
43
+ end
44
+
45
+ def validate_by_unauthorized_record
46
+ self.attempted_record = unauthorized_record
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,80 @@
1
+ module Authlogic
2
+ module Session
3
+ # Responsible for session validation
4
+ module Validation
5
+ # The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class. Use it the same way:
6
+ #
7
+ # class UserSession
8
+ # validate :check_if_awesome
9
+ #
10
+ # private
11
+ # def check_if_awesome
12
+ # errors.add(:login, "must contain awesome") if login && !login.include?("awesome")
13
+ # errors.add(:base, "You must be awesome to log in") unless attempted_record.awesome?
14
+ # end
15
+ # end
16
+ class Errors < ::ActiveRecord::Errors
17
+ def [](key)
18
+ value = super
19
+ value.is_a?(Array) ? value : [value].compact
20
+ end
21
+ end
22
+
23
+ # You should use this as a place holder for any records that you find during validation. The main reason for this is to
24
+ # allow other modules to use it if needed. Take the failed_login_count feature, it needs this in order to increase
25
+ # the failed login count.
26
+ def attempted_record
27
+ @attempted_record
28
+ end
29
+
30
+ # See attempted_record
31
+ def attempted_record=(value)
32
+ @attempted_record = value
33
+ end
34
+
35
+ # The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses the exact same ActiveRecord errors class.
36
+ # Use it the same way:
37
+ #
38
+ # === Example
39
+ #
40
+ # class UserSession
41
+ # before_validation :check_if_awesome
42
+ #
43
+ # private
44
+ # def check_if_awesome
45
+ # errors.add(:login, "must contain awesome") if login && !login.include?("awesome")
46
+ # errors.add(:base, "You must be awesome to log in") unless attempted_record.awesome?
47
+ # end
48
+ # end
49
+ def errors
50
+ @errors ||= Errors.new(self)
51
+ end
52
+
53
+ # Determines if the information you provided for authentication is valid or not. If there is
54
+ # a problem with the information provided errors will be added to the errors object and this
55
+ # method will return false.
56
+ def valid?
57
+ errors.clear
58
+ self.attempted_record = nil
59
+
60
+ before_validation
61
+ new_session? ? before_validation_on_create : before_validation_on_update
62
+ validate
63
+ ensure_authentication_attempted
64
+
65
+ if errors.size == 0
66
+ new_session? ? after_validation_on_create : after_validation_on_update
67
+ after_validation
68
+ end
69
+
70
+ save_record(attempted_record)
71
+ errors.size == 0
72
+ end
73
+
74
+ private
75
+ def ensure_authentication_attempted
76
+ errors.add(:base, I18n.t('error_messages.no_authentication_details', :default => "You did not provide any details for authentication.")) if errors.empty? && attempted_record.nil?
77
+ end
78
+ end
79
+ end
80
+ end