authlogic 1.4.3 → 2.0.0

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

Potentially problematic release.


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

Files changed (131) hide show
  1. data/CHANGELOG.rdoc +19 -0
  2. data/Manifest.txt +111 -0
  3. data/README.rdoc +116 -389
  4. data/Rakefile +14 -7
  5. data/lib/authlogic.rb +33 -35
  6. data/lib/authlogic/acts_as_authentic/base.rb +91 -0
  7. data/lib/authlogic/acts_as_authentic/email.rb +77 -0
  8. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +54 -0
  9. data/lib/authlogic/acts_as_authentic/login.rb +65 -0
  10. data/lib/authlogic/acts_as_authentic/magic_columns.rb +24 -0
  11. data/lib/authlogic/acts_as_authentic/password.rb +215 -0
  12. data/lib/authlogic/acts_as_authentic/perishable_token.rb +100 -0
  13. data/lib/authlogic/acts_as_authentic/persistence_token.rb +66 -0
  14. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +60 -0
  15. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +127 -0
  16. data/lib/authlogic/acts_as_authentic/single_access_token.rb +58 -0
  17. data/lib/authlogic/acts_as_authentic/validations_scope.rb +32 -0
  18. data/lib/authlogic/{session/authenticates_many_association.rb → authenticates_many/association.rb} +10 -6
  19. data/lib/authlogic/authenticates_many/base.rb +55 -0
  20. data/lib/authlogic/controller_adapters/abstract_adapter.rb +2 -3
  21. data/lib/authlogic/controller_adapters/merb_adapter.rb +0 -4
  22. data/lib/authlogic/controller_adapters/rails_adapter.rb +0 -4
  23. data/lib/authlogic/crypto_providers/aes256.rb +0 -2
  24. data/lib/authlogic/crypto_providers/bcrypt.rb +0 -2
  25. data/lib/authlogic/crypto_providers/md5.rb +34 -0
  26. data/lib/authlogic/crypto_providers/sha1.rb +0 -2
  27. data/lib/authlogic/crypto_providers/sha512.rb +1 -3
  28. data/lib/authlogic/i18n.rb +1 -4
  29. data/lib/authlogic/random.rb +33 -0
  30. data/lib/authlogic/session/activation.rb +56 -0
  31. data/lib/authlogic/session/active_record_trickery.rb +15 -7
  32. data/lib/authlogic/session/base.rb +31 -456
  33. data/lib/authlogic/session/brute_force_protection.rb +50 -27
  34. data/lib/authlogic/session/callbacks.rb +24 -15
  35. data/lib/authlogic/session/cookies.rb +108 -22
  36. data/lib/authlogic/session/existence.rb +89 -0
  37. data/lib/authlogic/session/foundation.rb +63 -0
  38. data/lib/authlogic/session/http_auth.rb +23 -0
  39. data/lib/authlogic/session/id.rb +41 -0
  40. data/lib/authlogic/session/klass.rb +75 -0
  41. data/lib/authlogic/session/magic_columns.rb +75 -0
  42. data/lib/authlogic/session/magic_states.rb +58 -0
  43. data/lib/authlogic/session/params.rb +82 -19
  44. data/lib/authlogic/session/password.rb +156 -0
  45. data/lib/authlogic/session/{perishability.rb → perishable_token.rb} +4 -4
  46. data/lib/authlogic/session/persistence.rb +70 -0
  47. data/lib/authlogic/session/priority_record.rb +34 -0
  48. data/lib/authlogic/session/scopes.rb +57 -53
  49. data/lib/authlogic/session/session.rb +46 -31
  50. data/lib/authlogic/session/timeout.rb +65 -31
  51. data/lib/authlogic/session/unauthorized_record.rb +50 -0
  52. data/lib/authlogic/session/validation.rb +76 -0
  53. data/lib/authlogic/testing/test_unit_helpers.rb +3 -3
  54. data/lib/authlogic/version.rb +3 -3
  55. data/test/acts_as_authentic_test/base_test.rb +12 -0
  56. data/test/acts_as_authentic_test/email_test.rb +79 -0
  57. data/test/acts_as_authentic_test/logged_in_status_test.rb +36 -0
  58. data/test/acts_as_authentic_test/login_test.rb +79 -0
  59. data/test/acts_as_authentic_test/magic_columns_test.rb +27 -0
  60. data/test/acts_as_authentic_test/password_test.rb +212 -0
  61. data/test/acts_as_authentic_test/perishable_token_test.rb +56 -0
  62. data/test/acts_as_authentic_test/persistence_token_test.rb +55 -0
  63. data/test/acts_as_authentic_test/session_maintenance_test.rb +68 -0
  64. data/test/acts_as_authentic_test/single_access_test.rb +39 -0
  65. data/test/authenticates_many_test.rb +16 -0
  66. data/test/{crypto_provider_tests → crypto_provider_test}/aes256_test.rb +1 -1
  67. data/test/{crypto_provider_tests → crypto_provider_test}/bcrypt_test.rb +1 -1
  68. data/test/{crypto_provider_tests → crypto_provider_test}/sha1_test.rb +1 -1
  69. data/test/{crypto_provider_tests → crypto_provider_test}/sha512_test.rb +1 -1
  70. data/test/fixtures/employees.yml +4 -4
  71. data/test/fixtures/users.yml +6 -6
  72. data/test/libs/company.rb +6 -0
  73. data/test/libs/employee.rb +7 -0
  74. data/test/libs/employee_session.rb +2 -0
  75. data/test/libs/project.rb +3 -0
  76. data/test/libs/user_session.rb +2 -0
  77. data/test/random_test.rb +49 -0
  78. data/test/session_test/activation_test.rb +43 -0
  79. data/test/session_test/active_record_trickery_test.rb +26 -0
  80. data/test/session_test/brute_force_protection_test.rb +76 -0
  81. data/test/session_test/callbacks_test.rb +6 -0
  82. data/test/session_test/cookies_test.rb +107 -0
  83. data/test/session_test/credentials_test.rb +0 -0
  84. data/test/session_test/existence_test.rb +64 -0
  85. data/test/session_test/http_auth_test.rb +16 -0
  86. data/test/session_test/id_test.rb +17 -0
  87. data/test/session_test/klass_test.rb +35 -0
  88. data/test/session_test/magic_columns_test.rb +59 -0
  89. data/test/session_test/magic_states_test.rb +60 -0
  90. data/test/session_test/params_test.rb +53 -0
  91. data/test/session_test/password_test.rb +84 -0
  92. data/test/{session_tests → session_test}/perishability_test.rb +1 -1
  93. data/test/session_test/persistence_test.rb +21 -0
  94. data/test/{session_tests → session_test}/scopes_test.rb +2 -3
  95. data/test/session_test/session_test.rb +59 -0
  96. data/test/session_test/timeout_test.rb +43 -0
  97. data/test/session_test/unauthorized_record_test.rb +13 -0
  98. data/test/session_test/validation_test.rb +23 -0
  99. data/test/test_helper.rb +14 -29
  100. metadata +120 -112
  101. data/Manifest +0 -76
  102. data/authlogic.gemspec +0 -38
  103. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/base.rb +0 -22
  104. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb +0 -238
  105. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb +0 -155
  106. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb +0 -51
  107. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/perishability.rb +0 -71
  108. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb +0 -94
  109. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb +0 -87
  110. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb +0 -61
  111. data/lib/authlogic/orm_adapters/active_record_adapter/authenticates_many.rb +0 -58
  112. data/lib/authlogic/session/config.rb +0 -421
  113. data/lib/authlogic/session/errors.rb +0 -18
  114. data/lib/authlogic/session/record_info.rb +0 -24
  115. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb +0 -154
  116. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb +0 -157
  117. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb +0 -24
  118. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/perishability_test.rb +0 -41
  119. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb +0 -54
  120. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb +0 -62
  121. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/single_access_test.rb +0 -41
  122. data/test/orm_adapters_tests/active_record_adapter_tests/authenticates_many_test.rb +0 -32
  123. data/test/session_tests/active_record_trickery_test.rb +0 -14
  124. data/test/session_tests/authenticates_many_association_test.rb +0 -28
  125. data/test/session_tests/base_test.rb +0 -307
  126. data/test/session_tests/brute_force_protection_test.rb +0 -53
  127. data/test/session_tests/config_test.rb +0 -184
  128. data/test/session_tests/cookies_test.rb +0 -32
  129. data/test/session_tests/params_test.rb +0 -32
  130. data/test/session_tests/session_test.rb +0 -45
  131. data/test/session_tests/timeout_test.rb +0 -71
@@ -0,0 +1,23 @@
1
+ module Authlogic
2
+ module Session
3
+ # Handles all authentication that deals with basic HTTP auth.
4
+ module HttpAuth
5
+ def self.included(klass)
6
+ klass.persist :persist_by_http_auth
7
+ end
8
+
9
+ private
10
+ def persist_by_http_auth
11
+ controller.authenticate_with_http_basic do |login, password|
12
+ if !login.blank? && !password.blank?
13
+ send("#{login_field}=", login)
14
+ send("#{password_field}=", password)
15
+ return valid?
16
+ end
17
+ end
18
+
19
+ false
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ module Authlogic
2
+ module Session
3
+ # Allows you to separate sessions with an id, ultimately letting you create multiple sessions for the same user.
4
+ module Id
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ attr_writer :id
8
+ end
9
+ end
10
+
11
+ # Setting the id if it is passed in the credentials.
12
+ def credentials=(value)
13
+ super
14
+ values = value.is_a?(Array) ? value : [value]
15
+ self.id = values.last if values.last.is_a?(Symbol)
16
+ end
17
+
18
+ # Allows you to set a unique identifier for your session, so that you can have more than 1 session at a time.
19
+ # A good example when this might be needed is when you want to have a normal user session and a "secure" user session.
20
+ # The secure user session would be created only when they want to modify their billing information, or other sensitive
21
+ # information. Similar to me.com. This requires 2 user sessions. Just use an id for the "secure" session and you should be good.
22
+ #
23
+ # You can set the id during initialization (see initialize for more information), or as an attribute:
24
+ #
25
+ # session.id = :my_id
26
+ #
27
+ # Just be sure and set your id before you save your session.
28
+ #
29
+ # Lastly, to retrieve your session with the id check out the find class method.
30
+ def id
31
+ @id
32
+ end
33
+
34
+ private
35
+ # Used for things like cookie_key, session_key, etc.
36
+ def build_key(last_part)
37
+ [id, super].compact.join("_")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,75 @@
1
+ module Authlogic
2
+ module Session
3
+ # Handles authenticating via a traditional username and password.
4
+ module Klass
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ extend Config
8
+ include InstanceMethods
9
+
10
+ class << self
11
+ attr_accessor :configured_klass_methods
12
+ end
13
+ end
14
+ end
15
+
16
+ module Config
17
+ # Lets you change which model to use for authentication.
18
+ #
19
+ # * <tt>Default:</tt> inferred from the class name. UserSession would automatically try User
20
+ # * <tt>Accepts:</tt> an ActiveRecord class
21
+ def authenticate_with(klass)
22
+ @klass_name = klass.name
23
+ @klass = klass
24
+ end
25
+ alias_method :authenticate_with=, :authenticate_with
26
+
27
+ # The name of the class that this session is authenticating with. For example, the UserSession class will
28
+ # authenticate with the User class unless you specify otherwise in your configuration. See authenticate_with
29
+ # for information on how to change this value.
30
+ def klass
31
+ @klass ||=
32
+ if klass_name
33
+ klass_name.constantize
34
+ else
35
+ nil
36
+ end
37
+ end
38
+
39
+ # Same as klass, just returns a string instead of the actual constant.
40
+ def klass_name
41
+ @klass_name ||=
42
+ if guessed_name = name.scan(/(.*)Session/)[0]
43
+ @klass_name = guessed_name[0]
44
+ end
45
+ end
46
+ end
47
+
48
+ module InstanceMethods
49
+ # Creating an alias method for the "record" method based on the klass name, so that we can do:
50
+ #
51
+ # session.user
52
+ #
53
+ # instead of:
54
+ #
55
+ # session.record
56
+ def initialize(*args)
57
+ if !self.class.configured_klass_methods
58
+ self.class.send(:alias_method, klass_name.demodulize.underscore.to_sym, :record)
59
+ self.class.configured_klass_methods = true
60
+ end
61
+ super
62
+ end
63
+
64
+ private
65
+ def klass
66
+ self.class.klass
67
+ end
68
+
69
+ def klass_name
70
+ self.class.klass_name
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,75 @@
1
+ module Authlogic
2
+ module Session
3
+ # Just like ActiveRecord has "magic" columns, such as: created_at and updated_at. Authlogic has its own "magic" columns too:
4
+ #
5
+ # Column name Description
6
+ # login_count Increased every time an explicit login is made. This will *NOT* increase if logging in by a session, cookie, or basic http auth
7
+ # failed_login_count This increases for each consecutive failed login. See Authlogic::Session::BruteForceProtection and the consecutive_failed_logins_limit config option for more details.
8
+ # last_request_at Updates every time the user logs in, either by explicitly logging in, or logging in by cookie, session, or http auth
9
+ # current_login_at Updates with the current time when an explicit login is made.
10
+ # last_login_at Updates with the value of current_login_at before it is reset.
11
+ # current_login_ip Updates with the request remote_ip when an explicit login is made.
12
+ # last_login_ip Updates with the value of current_login_ip before it is reset.
13
+ module MagicColumns
14
+ def self.included(klass)
15
+ klass.class_eval do
16
+ extend Config
17
+ include InstanceMethods
18
+ after_persisting :set_last_request_at
19
+ validate :increase_failed_login_count
20
+ before_save :update_info
21
+ end
22
+ end
23
+
24
+ # Configuration for the magic columns feature.
25
+ module Config
26
+ # Every time a session is found the last_request_at field for that record is updatd with the current time, if that field exists. If you want to limit how frequent that field is updated specify the threshold
27
+ # here. For example, if your user is making a request every 5 seconds, and you feel this is too frequent, and feel a minute is a good threashold. Set this to 1.minute. Once a minute has passed in between
28
+ # requests the field will be updated.
29
+ #
30
+ # * <tt>Default:</tt> 0
31
+ # * <tt>Accepts:</tt> integer representing time in seconds
32
+ def last_request_at_threshold(value = nil)
33
+ config(:last_request_at_threshold, value, 0)
34
+ end
35
+ alias_method :last_request_at_threshold=, :last_request_at_threshold
36
+ end
37
+
38
+ # The methods available for an Authlogic::Session::Base object that make up the magic columns feature.
39
+ module InstanceMethods
40
+ private
41
+ def increase_failed_login_count
42
+ if errors.on(password_field) && attempted_record.respond_to?(:failed_login_count)
43
+ attempted_record.failed_login_count ||= 0
44
+ attempted_record.failed_login_count += 1
45
+ end
46
+ end
47
+
48
+ def update_info
49
+ record.login_count = (record.login_count.blank? ? 1 : record.login_count + 1) if record.respond_to?(:login_count)
50
+ record.failed_login_count = 0 if record.respond_to?(:failed_login_count)
51
+
52
+ if record.respond_to?(:current_login_at)
53
+ record.last_login_at = record.current_login_at if record.respond_to?(:last_login_at)
54
+ record.current_login_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
55
+ end
56
+
57
+ if record.respond_to?(:current_login_ip)
58
+ record.last_login_ip = record.current_login_ip if record.respond_to?(:last_login_ip)
59
+ record.current_login_ip = controller.request.remote_ip
60
+ end
61
+ end
62
+
63
+ def set_last_request_at
64
+ if record && record.class.column_names.include?("last_request_at") && (record.last_request_at.blank? || last_request_at_threshold.to_i.seconds.ago >= record.last_request_at)
65
+ record.last_request_at = klass.default_timezone == :utc ? Time.now.utc : Time.now
66
+ end
67
+ end
68
+
69
+ def last_request_at_threshold
70
+ self.class.last_request_at_threshold
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,58 @@
1
+ module Authlogic
2
+ module Session
3
+ # Authlogic tries to check the state of the record before creating the session. If your record responds to the following methods and any of them return false, validation will fail:
4
+ #
5
+ # Method name Description
6
+ # active? Is the record marked as active?
7
+ # approved? Has the record been approved?
8
+ # confirmed? Has the record been conirmed?
9
+ #
10
+ # Authlogic does nothing to define these methods for you, its up to you to define what they mean. If your object responds to these methods Authlogic will use them, otherwise they are ignored.
11
+ #
12
+ # What's neat about this is that these are checked upon any type of login. When logging in explicitly, by cookie, session, or basic http auth. So if you mark a user inactive in the middle of their session they wont be logged back in next time they refresh the page. Giving you complete control.
13
+ #
14
+ # Need Authlogic to check your own "state"? No problem, check out the hooks section below. Add in a before_validation to do your own checking. The sky is the limit.
15
+ module MagicStates
16
+ def self.included(klass)
17
+ klass.class_eval do
18
+ extend Config
19
+ include InstanceMethods
20
+ validate :validate_magic_states
21
+ end
22
+ end
23
+
24
+ # Configuration for the magic states feature.
25
+ module Config
26
+ # Set this to true if you want to disable the checking of active?, approved?, and confirmed? on your record. This is more or less of a
27
+ # convenience feature, since 99% of the time if those methods exist and return false you will not want the user logging in. You could
28
+ # easily accomplish this same thing with a before_validation method or other callbacks.
29
+ #
30
+ # * <tt>Default:</tt> false
31
+ # * <tt>Accepts:</tt> Boolean
32
+ def disable_magic_states(value = nil)
33
+ config(:disable_magic_states, value, false)
34
+ end
35
+ alias_method :disable_magic_states=, :disable_magic_states
36
+ end
37
+
38
+ # The methods available for an Authlogic::Session::Base object that make up the magic states feature.
39
+ module InstanceMethods
40
+ private
41
+ def disable_magic_states?
42
+ self.class.disable_magic_states == true
43
+ end
44
+
45
+ def validate_magic_states
46
+ return true if disable_magic_states? || attempted_record.nil?
47
+ [:active, :approved, :confirmed].each do |required_status|
48
+ if attempted_record.respond_to?("#{required_status}?") && !attempted_record.send("#{required_status}?")
49
+ errors.add_to_base(I18n.t("error_messages.not_#{required_status}", :default => "Your account is not #{required_status}"))
50
+ return false
51
+ end
52
+ end
53
+ true
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,32 +1,95 @@
1
1
  module Authlogic
2
2
  module Session
3
- # = Params
3
+ # This module is responsible for authenticating the user via params, which ultimately allows the user to log in using a URL like the following:
4
4
  #
5
- # Tries to log the user in via params. Think about cookies and sessions. They are just hashes in your controller, so are params. People never
6
- # look at params as an authentication option, but it can be useful for logging into private feeds, etc. Logging in a user is as simple as:
5
+ # https://www.domain.com?user_credentials=4LiXF7FiGUppIPubBPey
7
6
  #
8
- # https://www.domain.com?user_credentials=[insert single access token here]
7
+ # Notice the token in the URL, this is a single access token. A single access token is used for single access only, it is not persisted. Meaning the user
8
+ # provides it, Authlogic grants them access, and that's it. If they want access again they need to provide the token again. Authlogic will
9
+ # *NEVER* try to persist the session after authenticating through this method.
9
10
  #
10
- # Wait, what is a single access token? It is all explained in the README. Checkout the "Tokens" section in the README, there is section about
11
- # single access tokens. For security reasons, this type of authentication is ONLY available via single access tokens, you can NOT pass your persistence token.
12
- # Which means you must have a single_access_token field in your database.
11
+ # For added security, this token is *ONLY* allowed for RSS and ATOM requests. You can change this with the configuration. You can also define if
12
+ # it is allowed dynamically by defining a single_access_allowed? method in your controller. For example:
13
+ #
14
+ # class UsersController < ApplicationController
15
+ # private
16
+ # def single_access_allowed?
17
+ # action_name == "index"
18
+ # end
19
+ #
20
+ # Also, by default, this token is permanent. Meaning if the user changes their password, this token will remain the same. It will only change
21
+ # when it is explicitly reset.
22
+ #
23
+ # You can modify all of this behavior with the Config sub module.
13
24
  module Params
14
- # Tries to validate the session from information in the params token
15
- def valid_params?
16
- if params_credentials && single_access_token_field && (single_access_allowed_request_types.include?(controller.request_content_type) || single_access_allowed_request_types.include?(:all) || controller.single_access_allowed?)
17
- self.unauthorized_record = search_for_record("find_by_#{single_access_token_field}", params_credentials)
18
- self.persisting = false
19
- return true if valid?
20
- self.persisting = true
21
- else
22
- false
25
+ def self.included(klass)
26
+ klass.class_eval do
27
+ extend Config
28
+ include InstanceMethods
29
+ attr_accessor :single_access
30
+ persist :persist_by_params
23
31
  end
24
32
  end
25
33
 
26
- private
27
- def params_credentials
28
- controller.params[params_key]
34
+ # Configuration for the params / single access feature.
35
+ module Config
36
+ # Works exactly like cookie_key, but for params. So a user can login via params just like a cookie or a session. Your URL would look like:
37
+ #
38
+ # http://www.domain.com?user_credentials=my_single_access_key
39
+ #
40
+ # You can change the "user_credentials" key above with this configuration option. Keep in mind, just like cookie_key, if you supply an id
41
+ # the id will be appended to the front. Check out cookie_key for more details. Also checkout the "Single Access / Private Feeds Access" section in the README.
42
+ #
43
+ # * <tt>Default:</tt> cookie_key
44
+ # * <tt>Accepts:</tt> String
45
+ def params_key(value = nil)
46
+ config(:params_key, value, cookie_key)
47
+ end
48
+ alias_method :params_key=, :params_key
49
+
50
+ # Authentication is allowed via a single access token, but maybe this is something you don't want for your application as a whole. Maybe this is something you only want for specific request types.
51
+ # Specify a list of allowed request types and single access authentication will only be allowed for the ones you specify. Checkout the "Single Access / Private Feeds Access" section in the README.
52
+ #
53
+ # * <tt>Default:</tt> "application/rss+xml", "application/atom+xml"
54
+ # * <tt>Accepts:</tt> String of request type, or :all to allow single access authentication for any and all request types
55
+ def single_access_allowed_request_types(value = nil)
56
+ config(:single_access_allowed_request_types, value, ["application/rss+xml", "application/atom+xml"])
29
57
  end
58
+ alias_method :single_access_allowed_request_types=, :single_access_allowed_request_types
59
+ end
60
+
61
+ # The methods available for an Authlogic::Session::Base object that make up the params / single access feature.
62
+ module InstanceMethods
63
+ private
64
+ def persist_by_params
65
+ return false if !params_enabled?
66
+ self.unauthorized_record = search_for_record("find_by_single_access_token", params_credentials)
67
+ self.single_access = valid?
68
+ end
69
+
70
+ def params_enabled?
71
+ params_credentials && klass.column_names.include?("single_access_token") &&
72
+ (single_access_allowed_request_types.include?(controller.request_content_type) ||
73
+ single_access_allowed_request_types.include?(:all) ||
74
+ controller.single_access_allowed?)
75
+ end
76
+
77
+ def params_key
78
+ build_key(self.class.params_key)
79
+ end
80
+
81
+ def single_access?
82
+ single_access == true
83
+ end
84
+
85
+ def single_access_allowed_request_types
86
+ self.class.single_access_allowed_request_types
87
+ end
88
+
89
+ def params_credentials
90
+ controller.params[params_key]
91
+ end
92
+ end
30
93
  end
31
94
  end
32
95
  end
@@ -0,0 +1,156 @@
1
+ module Authlogic
2
+ module Session
3
+ # Handles authenticating via a traditional username and password.
4
+ module Password
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ extend Config
8
+ include InstanceMethods
9
+ validate :validate_by_password, :if => :authenticating_with_password?
10
+
11
+ class << self
12
+ attr_accessor :configured_password_methods
13
+ end
14
+ end
15
+ end
16
+
17
+ # Password configuration
18
+ module Config
19
+ # Authlogic tries to validate the credentials passed to it. One part of validation is actually finding the user and making sure it exists. What method it uses the do this is up to you.
20
+ #
21
+ # Let's say you have a UserSession that is authenticating a User. By default UserSession will call User.find_by_login(login). You can change what method UserSession calls by specifying it here. Then
22
+ # in your User model you can make that method do anything you want, giving you complete control of how users are found by the UserSession.
23
+ #
24
+ # Let's take an example: You want to allow users to login by username or email. Set this to the name of the class method that does this in the User model. Let's call it "find_by_username_or_email"
25
+ #
26
+ # class User < ActiveRecord::Base
27
+ # def self.find_by_username_or_email(login)
28
+ # find_by_username(login) || find_by_email(login)
29
+ # end
30
+ # end
31
+ #
32
+ # * <tt>Default:</tt> "find_by_#{login_field}"
33
+ # * <tt>Accepts:</tt> Symbol or String
34
+ def find_by_login_method(value = nil)
35
+ config(:find_by_login_method, value, "find_by_#{login_field}")
36
+ end
37
+ alias_method :find_by_login_method=, :find_by_login_method
38
+
39
+ # The name of the method you want Authlogic to create for storing the login / username. Keep in mind this is just for your
40
+ # Authlogic::Session, if you want it can be something completely different than the field in your model. So if you wanted people to
41
+ # login with a field called "login" and then find users by email this is compeltely doable. See the find_by_login_method configuration
42
+ # option for more details.
43
+ #
44
+ # * <tt>Default:</tt> Uses the configuration option in your model: User.login_field
45
+ # * <tt>Accepts:</tt> Symbol or String
46
+ def login_field(value = nil)
47
+ config(:login_field, value, klass.login_field || klass.email_field)
48
+ end
49
+ alias_method :login_field=, :login_field
50
+
51
+ # Works exactly like login_field, but for the password instead.
52
+ #
53
+ # * <tt>Default:</tt> :password
54
+ # * <tt>Accepts:</tt> Symbol or String
55
+ def password_field(value = nil)
56
+ config(:password_field, value, :password)
57
+ end
58
+ alias_method :password_field=, :password_field
59
+
60
+ # The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
61
+ #
62
+ # * <tt>Default:</tt> "valid_#{password_field}?"
63
+ # * <tt>Accepts:</tt> Symbol or String
64
+ def verify_password_method(value = nil)
65
+ config(:verify_password_method, value, "valid_#{password_field}?")
66
+ end
67
+ alias_method :verify_password_method=, :verify_password_method
68
+ end
69
+
70
+ # Password related instance methods
71
+ module InstanceMethods
72
+ def initialize(*args)
73
+ if !self.class.configured_password_methods
74
+ self.class.send(:attr_writer, login_field) if !respond_to?("#{login_field}=")
75
+ self.class.send(:attr_reader, login_field) if !respond_to?(login_field)
76
+ self.class.send(:attr_writer, password_field) if !respond_to?("#{password_field}=")
77
+ self.class.send(:define_method, password_field) {} if !respond_to?(password_field)
78
+
79
+ self.class.class_eval <<-"end_eval", __FILE__, __LINE__
80
+ private
81
+ # The password should not be accessible publicly. This way forms using form_for don't fill the password with the attempted password. The prevent this we just create this method that is private.
82
+ def protected_#{password_field}
83
+ @#{password_field}
84
+ end
85
+ end_eval
86
+
87
+ self.class.configured_password_methods = true
88
+ end
89
+
90
+ super
91
+ end
92
+
93
+ def credentials
94
+ if authenticating_with_password?
95
+ details = {}
96
+ details[login_field.to_sym] = send(login_field)
97
+ details[password_field.to_sym] = "<protected>"
98
+ details
99
+ else
100
+ super
101
+ end
102
+ end
103
+
104
+ def credentials=(value)
105
+ super
106
+ values = value.is_a?(Array) ? value : [value]
107
+ if values.first.is_a?(Hash)
108
+ values.first.with_indifferent_access.slice(login_field, password_field).each do |field, value|
109
+ next if value.blank?
110
+ send("#{field}=", value)
111
+ end
112
+ end
113
+ end
114
+
115
+ private
116
+ def authenticating_with_password?
117
+ !send(login_field).nil? || !send("protected_#{password_field}").nil?
118
+ end
119
+
120
+ def validate_by_password
121
+ errors.add(login_field, I18n.t('error_messages.login_blank', :default => "can not be blank")) if send(login_field).blank?
122
+ errors.add(password_field, I18n.t('error_messages.password_blank', :default => "can not be blank")) if send("protected_#{password_field}").blank?
123
+ return if errors.count > 0
124
+
125
+ self.attempted_record = search_for_record(find_by_login_method, send(login_field))
126
+
127
+ if attempted_record.blank?
128
+ errors.add(login_field, I18n.t('error_messages.login_not_found', :default => "does not exist"))
129
+ return
130
+ end
131
+
132
+ if !attempted_record.send(verify_password_method, send("protected_#{password_field}"))
133
+ errors.add(password_field, I18n.t('error_messages.password_invalid', :default => "is not valid"))
134
+ return
135
+ end
136
+ end
137
+
138
+ def find_by_login_method
139
+ self.class.find_by_login_method
140
+ end
141
+
142
+ def login_field
143
+ self.class.login_field
144
+ end
145
+
146
+ def password_field
147
+ self.class.password_field
148
+ end
149
+
150
+ def verify_password_method
151
+ self.class.verify_password_method
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end