crankharder-authlogic 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG.rdoc +345 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +245 -0
  5. data/Rakefile +42 -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 +56 -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 +56 -0
  39. data/lib/authlogic/session/base.rb +37 -0
  40. data/lib/authlogic/session/brute_force_protection.rb +96 -0
  41. data/lib/authlogic/session/callbacks.rb +88 -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 +95 -0
  49. data/lib/authlogic/session/magic_states.rb +59 -0
  50. data/lib/authlogic/session/params.rb +101 -0
  51. data/lib/authlogic/session/password.rb +240 -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 +62 -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,130 @@
1
+ module Authlogic
2
+ module Session
3
+ # Handles all authentication that deals with cookies, such as persisting, saving, and destroying.
4
+ module Cookies
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ extend Config
8
+ include InstanceMethods
9
+ persist :persist_by_cookie
10
+ after_save :save_cookie
11
+ after_destroy :destroy_cookie
12
+ end
13
+ end
14
+
15
+ # Configuration for the cookie feature set.
16
+ module Config
17
+ # The name of the cookie or the key in the cookies hash. Be sure and use a unique name. If you have multiple sessions and they use the same cookie it will cause problems.
18
+ # Also, if a id is set it will be inserted into the beginning of the string. Exmaple:
19
+ #
20
+ # session = UserSession.new
21
+ # session.cookie_key => "user_credentials"
22
+ #
23
+ # session = UserSession.new(:super_high_secret)
24
+ # session.cookie_key => "super_high_secret_user_credentials"
25
+ #
26
+ # * <tt>Default:</tt> "#{klass_name.underscore}_credentials"
27
+ # * <tt>Accepts:</tt> String
28
+ def cookie_key(value = nil)
29
+ rw_config(:cookie_key, value, "#{klass_name.underscore}_credentials")
30
+ end
31
+ alias_method :cookie_key=, :cookie_key
32
+
33
+ # If sessions should be remembered by default or not.
34
+ #
35
+ # * <tt>Default:</tt> false
36
+ # * <tt>Accepts:</tt> Boolean
37
+ def remember_me(value = nil)
38
+ rw_config(:remember_me, value, false)
39
+ end
40
+ alias_method :remember_me=, :remember_me
41
+
42
+ # The length of time until the cookie expires.
43
+ #
44
+ # * <tt>Default:</tt> 3.months
45
+ # * <tt>Accepts:</tt> Integer, length of time in seconds, such as 60 or 3.months
46
+ def remember_me_for(value = :_read)
47
+ rw_config(:remember_me_for, value, 3.months, :_read)
48
+ end
49
+ alias_method :remember_me_for=, :remember_me_for
50
+ end
51
+
52
+ # The methods available for an Authlogic::Session::Base object that make up the cookie feature set.
53
+ module InstanceMethods
54
+ # Allows you to set the remember_me option when passing credentials.
55
+ def credentials=(value)
56
+ super
57
+ values = value.is_a?(Array) ? value : [value]
58
+ case values.first
59
+ when Hash
60
+ self.remember_me = values.first.with_indifferent_access[:remember_me] if values.first.with_indifferent_access.key?(:remember_me)
61
+ else
62
+ r = values.find { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) }
63
+ self.remember_me = r if !r.nil?
64
+ end
65
+ end
66
+
67
+ # Is the cookie going to expire after the session is over, or will it stick around?
68
+ def remember_me
69
+ return @remember_me if defined?(@remember_me)
70
+ @remember_me = self.class.remember_me
71
+ end
72
+
73
+ # Accepts a boolean as a flag to remember the session or not. Basically to expire the cookie at the end of the session or keep it for "remember_me_until".
74
+ def remember_me=(value)
75
+ @remember_me = value
76
+ end
77
+
78
+ # See remember_me
79
+ def remember_me?
80
+ remember_me == true || remember_me == "true" || remember_me == "1"
81
+ end
82
+
83
+ # How long to remember the user if remember_me is true. This is based on the class level configuration: remember_me_for
84
+ def remember_me_for
85
+ return unless remember_me?
86
+ self.class.remember_me_for
87
+ end
88
+
89
+ # When to expire the cookie. See remember_me_for configuration option to change this.
90
+ def remember_me_until
91
+ return unless remember_me?
92
+ remember_me_for.from_now
93
+ end
94
+
95
+ private
96
+ def cookie_key
97
+ build_key(self.class.cookie_key)
98
+ end
99
+
100
+ def cookie_credentials
101
+ controller.cookies[cookie_key] && controller.cookies[cookie_key].split("::")
102
+ end
103
+
104
+ # Tries to validate the session from information in the cookie
105
+ def persist_by_cookie
106
+ persistence_token, record_id = cookie_credentials
107
+ if !persistence_token.nil?
108
+ record = record_id.nil? ? search_for_record("find_by_persistence_token", persistence_token) : search_for_record("find_by_#{klass.primary_key}", record_id)
109
+ self.unauthorized_record = record if record && record.persistence_token == persistence_token
110
+ valid?
111
+ else
112
+ false
113
+ end
114
+ end
115
+
116
+ def save_cookie
117
+ controller.cookies[cookie_key] = {
118
+ :value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}",
119
+ :expires => remember_me_until,
120
+ :domain => controller.cookie_domain
121
+ }
122
+ end
123
+
124
+ def destroy_cookie
125
+ controller.cookies.delete cookie_key, :domain => controller.cookie_domain
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,93 @@
1
+ module Authlogic
2
+ module Session
3
+ # Provides methods to create and destroy objects. Basically controls their "existence".
4
+ module Existence
5
+ class SessionInvalidError < ::StandardError # :nodoc:
6
+ def initialize(session)
7
+ super("Your session is invalid and has the following errors: #{session.errors.full_messages.to_sentence}")
8
+ end
9
+ end
10
+
11
+ def self.included(klass)
12
+ klass.class_eval do
13
+ extend ClassMethods
14
+ include InstanceMethods
15
+ attr_accessor :new_session, :record
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+ # A convenince method. The same as:
21
+ #
22
+ # session = UserSession.new(*args)
23
+ # session.save
24
+ #
25
+ # Instead you can do:
26
+ #
27
+ # UserSession.create(*args)
28
+ def create(*args, &block)
29
+ session = new(*args)
30
+ session.save(&block)
31
+ session
32
+ end
33
+
34
+ # Same as create but calls create!, which raises an exception when validation fails.
35
+ def create!(*args)
36
+ session = new(*args)
37
+ session.save!
38
+ session
39
+ end
40
+ end
41
+
42
+ module InstanceMethods
43
+ # Clears all errors and the associated record, you should call this terminate a session, thus requring
44
+ # the user to authenticate again if it is needed.
45
+ def destroy
46
+ before_destroy
47
+ save_record
48
+ errors.clear
49
+ @record = nil
50
+ after_destroy
51
+ true
52
+ end
53
+
54
+ # Returns true if the session is new, meaning no action has been taken on it and a successful save
55
+ # has not taken place.
56
+ def new_session?
57
+ new_session != false
58
+ end
59
+
60
+ # After you have specified all of the details for your session you can try to save it. This will
61
+ # run validation checks and find the associated record, if all validation passes. If validation
62
+ # does not pass, the save will fail and the erorrs will be stored in the errors object.
63
+ def save(&block)
64
+ result = nil
65
+ if valid?
66
+ self.record = attempted_record
67
+
68
+ before_save
69
+ new_session? ? before_create : before_update
70
+ new_session? ? after_create : after_update
71
+ after_save
72
+
73
+ save_record
74
+ self.new_session = false
75
+ result = true
76
+ else
77
+ result = false
78
+ end
79
+
80
+ yield result if block_given?
81
+ result
82
+ end
83
+
84
+ # Same as save but raises an exception of validation errors when validation fails
85
+ def save!
86
+ result = save
87
+ raise SessionInvalidError.new(self) unless result
88
+ result
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,63 @@
1
+ module Authlogic
2
+ module Session
3
+ # Sort of like an interface, it sets the foundation for the class, such as the required methods. This also allows
4
+ # other modules to overwrite methods and call super on them. It's also a place to put "utility" methods used
5
+ # throughout Authlogic.
6
+ module Foundation
7
+ def self.included(klass)
8
+ klass.class_eval do
9
+ extend ClassMethods
10
+ include InstanceMethods
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ private
16
+ def rw_config(key, value, default_value = nil, read_value = nil)
17
+ if value == read_value
18
+ return read_inheritable_attribute(key) if inheritable_attributes.include?(key)
19
+ write_inheritable_attribute(key, default_value)
20
+ else
21
+ write_inheritable_attribute(key, value)
22
+ end
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ def initialize(*args)
28
+ self.credentials = args
29
+ end
30
+
31
+ # The credentials you passed to create your session. See credentials= for more info.
32
+ def credentials
33
+ []
34
+ end
35
+
36
+ # Set your credentials before you save your session. You can pass a hash of credentials:
37
+ #
38
+ # session.credentials = {:login => "my login", :password => "my password", :remember_me => true}
39
+ #
40
+ # or you can pass an array of objects:
41
+ #
42
+ # session.credentails = [my_user_object, true]
43
+ #
44
+ # and if you need to set an id, just pass it last. This value need be the last item in the array you pass, since the id is something that
45
+ # you control yourself, it should never be set from a hash or a form. Examples:
46
+ #
47
+ # session.credentials = [{:login => "my login", :password => "my password", :remember_me => true}, :my_id]
48
+ # session.credentials = [my_user_object, true, :my_id]
49
+ def credentials=(values)
50
+ end
51
+
52
+ def inspect
53
+ "#<#{self.class.name}: #{credentials.blank? ? "no credentials provided" : credentials.inspect}>"
54
+ end
55
+
56
+ private
57
+ def build_key(last_part)
58
+ last_part
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,58 @@
1
+ module Authlogic
2
+ module Session
3
+ # Handles all authentication that deals with basic HTTP auth. Which is authentication built into the HTTP protocol:
4
+ #
5
+ # http://username:password@whatever.com
6
+ #
7
+ # Also, if you are not comfortable letting users pass their raw username and password you can always use the single
8
+ # access token. See Authlogic::Session::Params for more info.
9
+ module HttpAuth
10
+ def self.included(klass)
11
+ klass.class_eval do
12
+ extend Config
13
+ include InstanceMethods
14
+ persist :persist_by_http_auth, :if => :persist_by_http_auth?
15
+ end
16
+ end
17
+
18
+ # Configuration for the HTTP basic auth feature of Authlogic.
19
+ module Config
20
+ # Do you want to allow your users to log in via HTTP basic auth?
21
+ #
22
+ # I recommend keeping this enabled. The only time I feel this should be disabled is if you are not comfortable
23
+ # having your users provide their raw username and password. Whatever the reason, you can disable it here.
24
+ #
25
+ # * <tt>Default:</tt> true
26
+ # * <tt>Accepts:</tt> Boolean
27
+ def allow_http_basic_auth(value = nil)
28
+ rw_config(:allow_http_basic_auth, value, true)
29
+ end
30
+ alias_method :allow_http_basic_auth=, :allow_http_basic_auth
31
+ end
32
+
33
+ # Instance methods for the HTTP basic auth feature of authlogic.
34
+ module InstanceMethods
35
+ private
36
+ def persist_by_http_auth?
37
+ allow_http_basic_auth? && login_field && password_field
38
+ end
39
+
40
+ def persist_by_http_auth
41
+ controller.authenticate_with_http_basic do |login, password|
42
+ if !login.blank? && !password.blank?
43
+ send("#{login_field}=", login)
44
+ send("#{password_field}=", password)
45
+ return valid?
46
+ end
47
+ end
48
+
49
+ false
50
+ end
51
+
52
+ def allow_http_basic_auth?
53
+ self.class.allow_http_basic_auth == true
54
+ end
55
+ end
56
+ end
57
+ end
58
+ 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