Empact-authlogic 2.1.5 → 3.0.3

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 (70) hide show
  1. data/Empact-authlogic.gemspec +190 -187
  2. data/Gemfile +10 -0
  3. data/Gemfile.lock +49 -0
  4. data/LICENSE +1 -1
  5. data/README.rdoc +14 -10
  6. data/Rakefile +5 -5
  7. data/VERSION.yml +3 -3
  8. data/lib/authlogic/acts_as_authentic/base.rb +14 -12
  9. data/lib/authlogic/acts_as_authentic/email.rb +12 -12
  10. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +8 -9
  11. data/lib/authlogic/acts_as_authentic/login.rb +19 -18
  12. data/lib/authlogic/acts_as_authentic/password.rb +3 -3
  13. data/lib/authlogic/acts_as_authentic/perishable_token.rb +15 -15
  14. data/lib/authlogic/acts_as_authentic/persistence_token.rb +1 -1
  15. data/lib/authlogic/authenticates_many/base.rb +3 -4
  16. data/lib/authlogic/controller_adapters/rails_adapter.rb +1 -1
  17. data/lib/authlogic/random.rb +1 -1
  18. data/lib/authlogic/session/active_record_trickery.rb +8 -0
  19. data/lib/authlogic/session/callbacks.rb +2 -2
  20. data/lib/authlogic/session/cookies.rb +54 -2
  21. data/lib/authlogic/session/foundation.rb +17 -3
  22. data/lib/authlogic/session/http_auth.rb +43 -2
  23. data/lib/authlogic/session/scopes.rb +9 -9
  24. data/lib/authlogic/test_case/mock_controller.rb +12 -2
  25. data/lib/generators/authlogic/USAGE +8 -0
  26. data/lib/generators/authlogic/session_generator.rb +14 -0
  27. data/lib/generators/authlogic/templates/session.rb +2 -0
  28. data/test/acts_as_authentic_test/base_test.rb +1 -1
  29. data/test/acts_as_authentic_test/email_test.rb +29 -21
  30. data/test/acts_as_authentic_test/logged_in_status_test.rb +1 -1
  31. data/test/acts_as_authentic_test/login_test.rb +1 -1
  32. data/test/acts_as_authentic_test/magic_columns_test.rb +1 -1
  33. data/test/acts_as_authentic_test/password_test.rb +1 -1
  34. data/test/acts_as_authentic_test/perishable_token_test.rb +1 -1
  35. data/test/acts_as_authentic_test/persistence_token_test.rb +1 -1
  36. data/test/acts_as_authentic_test/restful_authentication_test.rb +1 -1
  37. data/test/acts_as_authentic_test/session_maintenance_test.rb +1 -1
  38. data/test/acts_as_authentic_test/single_access_test.rb +1 -1
  39. data/test/authenticates_many_test.rb +1 -1
  40. data/test/crypto_provider_test/aes256_test.rb +1 -1
  41. data/test/crypto_provider_test/bcrypt_test.rb +1 -1
  42. data/test/crypto_provider_test/sha1_test.rb +1 -1
  43. data/test/crypto_provider_test/sha256_test.rb +1 -1
  44. data/test/crypto_provider_test/sha512_test.rb +1 -1
  45. data/test/i18n_test.rb +1 -1
  46. data/test/random_test.rb +1 -8
  47. data/test/session_test/activation_test.rb +1 -1
  48. data/test/session_test/active_record_trickery_test.rb +12 -2
  49. data/test/session_test/brute_force_protection_test.rb +1 -1
  50. data/test/session_test/callbacks_test.rb +1 -1
  51. data/test/session_test/cookies_test.rb +26 -2
  52. data/test/session_test/existence_test.rb +1 -1
  53. data/test/session_test/http_auth_test.rb +31 -3
  54. data/test/session_test/id_test.rb +1 -1
  55. data/test/session_test/klass_test.rb +1 -1
  56. data/test/session_test/magic_columns_test.rb +1 -1
  57. data/test/session_test/magic_states_test.rb +1 -1
  58. data/test/session_test/params_test.rb +1 -1
  59. data/test/session_test/password_test.rb +1 -1
  60. data/test/session_test/perishability_test.rb +1 -1
  61. data/test/session_test/persistence_test.rb +1 -1
  62. data/test/session_test/scopes_test.rb +12 -12
  63. data/test/session_test/session_test.rb +1 -1
  64. data/test/session_test/timeout_test.rb +1 -1
  65. data/test/session_test/unauthorized_record_test.rb +1 -1
  66. data/test/session_test/validation_test.rb +1 -1
  67. data/test/test_helper.rb +27 -41
  68. metadata +50 -55
  69. data/.gitignore +0 -9
  70. data/CHANGELOG.rdoc +0 -353
@@ -11,7 +11,7 @@ module Authlogic
11
11
  add_acts_as_authentic_module(Methods)
12
12
  end
13
13
  end
14
-
14
+
15
15
  # Change how the perishable token works.
16
16
  module Config
17
17
  # When using the find_using_perishable_token method the token can expire. If the token is expired, no
@@ -23,7 +23,7 @@ module Authlogic
23
23
  rw_config(:perishable_token_valid_for, (!value.nil? && value.to_i) || value, 10.minutes.to_i)
24
24
  end
25
25
  alias_method :perishable_token_valid_for=, :perishable_token_valid_for
26
-
26
+
27
27
  # Authlogic tries to expire and change the perishable token as much as possible, without comprising
28
28
  # it's purpose. This is for security reasons. If you want to manage it yourself, you can stop
29
29
  # Authlogic from getting your in way by setting this to true.
@@ -35,21 +35,21 @@ module Authlogic
35
35
  end
36
36
  alias_method :disable_perishable_token_maintenance=, :disable_perishable_token_maintenance
37
37
  end
38
-
38
+
39
39
  # All methods relating to the perishable token.
40
40
  module Methods
41
41
  def self.included(klass)
42
42
  return if !klass.column_names.include?("perishable_token")
43
-
43
+
44
44
  klass.class_eval do
45
45
  extend ClassMethods
46
46
  include InstanceMethods
47
-
47
+
48
48
  validates_uniqueness_of :perishable_token, :if => :perishable_token_changed?
49
49
  before_save :reset_perishable_token, :unless => :disable_perishable_token_maintenance?
50
50
  end
51
51
  end
52
-
52
+
53
53
  # Class level methods for the perishable token
54
54
  module ClassMethods
55
55
  # Use this methdo to find a record with a perishable token. This method does 2 things for you:
@@ -63,37 +63,37 @@ module Authlogic
63
63
  def find_using_perishable_token(token, age = self.perishable_token_valid_for)
64
64
  return if token.blank?
65
65
  age = age.to_i
66
-
66
+
67
67
  conditions_sql = "perishable_token = ?"
68
68
  conditions_subs = [token]
69
-
69
+
70
70
  if column_names.include?("updated_at") && age > 0
71
71
  conditions_sql += " and updated_at > ?"
72
72
  conditions_subs << age.seconds.ago
73
73
  end
74
-
75
- find(:first, :conditions => [conditions_sql, *conditions_subs])
74
+
75
+ where(conditions_sql, *conditions_subs).first
76
76
  end
77
-
77
+
78
78
  # This method will raise ActiveRecord::NotFound if no record is found.
79
79
  def find_using_perishable_token!(token, age = perishable_token_valid_for)
80
80
  find_using_perishable_token(token, age) || raise(ActiveRecord::RecordNotFound)
81
81
  end
82
82
  end
83
-
83
+
84
84
  # Instance level methods for the perishable token.
85
85
  module InstanceMethods
86
86
  # Resets the perishable token to a random friendly token.
87
87
  def reset_perishable_token
88
88
  self.perishable_token = Random.friendly_token
89
89
  end
90
-
90
+
91
91
  # Same as reset_perishable_token, but then saves the record afterwards.
92
92
  def reset_perishable_token!
93
93
  reset_perishable_token
94
- save_without_session_maintenance(false)
94
+ save_without_session_maintenance(:validate => false)
95
95
  end
96
-
96
+
97
97
  # A convenience method based on the disable_perishable_token_maintenance configuration option.
98
98
  def disable_perishable_token_maintenance?
99
99
  self.class.disable_perishable_token_maintenance == true
@@ -53,7 +53,7 @@ module Authlogic
53
53
  # Same as reset_persistence_token, but then saves the record.
54
54
  def reset_persistence_token!
55
55
  reset_persistence_token
56
- save_without_session_maintenance(false)
56
+ save_without_session_maintenance(:validate => false)
57
57
  end
58
58
  alias_method :forget!, :reset_persistence_token!
59
59
 
@@ -23,7 +23,7 @@ module Authlogic
23
23
  #
24
24
  # * <tt>session_class:</tt> default: "#{name}Session",
25
25
  # This is the related session class.
26
- #
26
+ #
27
27
  # * <tt>relationship_name:</tt> default: options[:session_class].klass_name.underscore.pluralize,
28
28
  # This is the name of the relationship you want to use to scope everything. For example an Account has many Users. There should be a relationship
29
29
  # called :users that you defined with a has_many. The reason we use the relationship is so you don't have to repeat yourself. The relatonship
@@ -42,14 +42,13 @@ module Authlogic
42
42
  options[:relationship_name] ||= options[:session_class].klass_name.underscore.pluralize
43
43
  class_eval <<-"end_eval", __FILE__, __LINE__
44
44
  def #{name}
45
- find_options = #{options[:find_options].inspect} || #{options[:relationship_name]}.scope(:find)
46
- find_options.delete_if { |key, value| ![:conditions, :include, :joins].include?(key.to_sym) || value.nil? }
45
+ find_options = #{options[:find_options].inspect} || #{options[:relationship_name]}.scoped
47
46
  @#{name} ||= Authlogic::AuthenticatesMany::Association.new(#{options[:session_class]}, find_options, #{options[:scope_cookies] ? "self.class.model_name.underscore + '_' + self.send(self.class.primary_key).to_s" : "nil"})
48
47
  end
49
48
  end_eval
50
49
  end
51
50
  end
52
-
51
+
53
52
  ::ActiveRecord::Base.extend(Base) if defined?(::ActiveRecord)
54
53
  end
55
54
  end
@@ -15,7 +15,7 @@ module Authlogic
15
15
 
16
16
  def cookie_domain
17
17
  @cookie_domain_key ||= Rails::VERSION::STRING >= '2.3' ? :domain : :session_domain
18
- ActionController::Base.session_options[@cookie_domain_key]
18
+ controller.request.session_options[@cookie_domain_key]
19
19
  end
20
20
 
21
21
  def request_content_type
@@ -13,7 +13,7 @@ module Authlogic
13
13
 
14
14
  def friendly_token
15
15
  # use base64url as defined by RFC4648
16
- SecureRandom.base64(15).tr('+/=', '-_ ').strip.delete("\n")
16
+ SecureRandom.base64(15).tr('+/=', '').strip.delete("\n")
17
17
  end
18
18
  else
19
19
  def hex_token
@@ -46,6 +46,14 @@ module Authlogic
46
46
  ::ActiveSupport::ModelName.new(self.to_s)
47
47
  end
48
48
  end
49
+
50
+ def i18n_scope
51
+ I18n.scope
52
+ end
53
+
54
+ def lookup_ancestors
55
+ ancestors.select { |x| x.respond_to?(:model_name) }
56
+ end
49
57
  end
50
58
 
51
59
  module InstanceMethods
@@ -66,7 +66,7 @@ module Authlogic
66
66
  base.define_callbacks *METHODS
67
67
 
68
68
  # If Rails 3, support the new callback syntax
69
- if base.singleton_class.method_defined?(:set_callback)
69
+ if base.send(base.respond_to?(:singleton_class) ? :singleton_class : :metaclass).method_defined?(:set_callback)
70
70
  METHODS.each do |method|
71
71
  base.class_eval <<-"end_eval", __FILE__, __LINE__
72
72
  def self.#{method}(*methods, &block)
@@ -92,7 +92,7 @@ module Authlogic
92
92
 
93
93
  def save_record(alternate_record = nil)
94
94
  r = alternate_record || record
95
- r.save_without_session_maintenance(false) if r && r.changed? && !r.readonly?
95
+ r.save_without_session_maintenance(:validate => false) if r && r.changed? && !r.readonly?
96
96
  end
97
97
  end
98
98
  end
@@ -47,6 +47,24 @@ module Authlogic
47
47
  rw_config(:remember_me_for, value, 3.months, :_read)
48
48
  end
49
49
  alias_method :remember_me_for=, :remember_me_for
50
+
51
+ # Should the cookie be set as secure? If true, the cookie will only be sent over SSL connections
52
+ #
53
+ # * <tt>Default:</tt> false
54
+ # * <tt>Accepts:</tt> Boolean
55
+ def secure(value = nil)
56
+ rw_config(:secure, value, false)
57
+ end
58
+ alias_method :secure=, :secure
59
+
60
+ # Should the cookie be set as httponly? If true, the cookie will not be accessable from javascript
61
+ #
62
+ # * <tt>Default:</tt> false
63
+ # * <tt>Accepts:</tt> Boolean
64
+ def httponly(value = nil)
65
+ rw_config(:httponly, value, false)
66
+ end
67
+ alias_method :httponly=, :httponly
50
68
  end
51
69
 
52
70
  # The methods available for an Authlogic::Session::Base object that make up the cookie feature set.
@@ -91,7 +109,39 @@ module Authlogic
91
109
  return unless remember_me?
92
110
  remember_me_for.from_now
93
111
  end
94
-
112
+
113
+ # If the cookie should be marked as secure (SSL only)
114
+ def secure
115
+ return @secure if defined?(@secure)
116
+ @secure = self.class.secure
117
+ end
118
+
119
+ # Accepts a boolean as to whether the cookie should be marked as secure. If true the cookie will only ever be sent over an SSL connection.
120
+ def secure=(value)
121
+ @secure = value
122
+ end
123
+
124
+ # See secure
125
+ def secure?
126
+ secure == true || secure == "true" || secure == "1"
127
+ end
128
+
129
+ # If the cookie should be marked as httponly (not accessable via javascript)
130
+ def httponly
131
+ return @httponly if defined?(@httponly)
132
+ @httponly = self.class.httponly
133
+ end
134
+
135
+ # Accepts a boolean as to whether the cookie should be marked as httponly. If true, the cookie will not be accessable from javascript
136
+ def httponly=(value)
137
+ @httponly = value
138
+ end
139
+
140
+ # See httponly
141
+ def httponly?
142
+ httponly == true || httponly == "true" || httponly == "1"
143
+ end
144
+
95
145
  private
96
146
  def cookie_key
97
147
  build_key(self.class.cookie_key)
@@ -117,6 +167,8 @@ module Authlogic
117
167
  controller.cookies[cookie_key] = {
118
168
  :value => "#{record.persistence_token}::#{record.send(record.class.primary_key)}",
119
169
  :expires => remember_me_until,
170
+ :secure => secure,
171
+ :httponly => httponly,
120
172
  :domain => controller.cookie_domain
121
173
  }
122
174
  end
@@ -127,4 +179,4 @@ module Authlogic
127
179
  end
128
180
  end
129
181
  end
130
- end
182
+ end
@@ -6,6 +6,9 @@ module Authlogic
6
6
  module Foundation
7
7
  def self.included(klass)
8
8
  klass.class_eval do
9
+ class_attribute :acts_as_authentic_config
10
+ self.acts_as_authentic_config ||= {}
11
+
9
12
  extend ClassMethods
10
13
  include InstanceMethods
11
14
  end
@@ -15,10 +18,13 @@ module Authlogic
15
18
  private
16
19
  def rw_config(key, value, default_value = nil, read_value = nil)
17
20
  if value == read_value
18
- return read_inheritable_attribute(key) if inheritable_attributes.include?(key)
19
- write_inheritable_attribute(key, default_value)
21
+ return acts_as_authentic_config[key] if acts_as_authentic_config.include?(key)
22
+ rw_config(key, default_value)
20
23
  else
21
- write_inheritable_attribute(key, value)
24
+ config = acts_as_authentic_config.clone
25
+ config[key] = value
26
+ self.acts_as_authentic_config = config
27
+ value
22
28
  end
23
29
  end
24
30
  end
@@ -53,6 +59,14 @@ module Authlogic
53
59
  "#<#{self.class.name}: #{credentials.blank? ? "no credentials provided" : credentials.inspect}>"
54
60
  end
55
61
 
62
+ def persisted?
63
+ !(new_record? || destroyed?)
64
+ end
65
+
66
+ def to_key
67
+ new_record? ? nil : [ self.send(self.class.primary_key) ]
68
+ end
69
+
56
70
  private
57
71
  def build_key(last_part)
58
72
  last_part
@@ -28,6 +28,41 @@ module Authlogic
28
28
  rw_config(:allow_http_basic_auth, value, true)
29
29
  end
30
30
  alias_method :allow_http_basic_auth=, :allow_http_basic_auth
31
+
32
+ # Whether or not to request HTTP authentication
33
+ #
34
+ # If set to true and no HTTP authentication credentials are sent with
35
+ # the request, the Rails controller method
36
+ # authenticate_or_request_with_http_basic will be used and a '401
37
+ # Authorization Required' header will be sent with the response. In
38
+ # most cases, this will cause the classic HTTP authentication popup to
39
+ # appear in the users browser.
40
+ #
41
+ # If set to false, the Rails controller method
42
+ # authenticate_with_http_basic is used and no 401 header is sent.
43
+ #
44
+ # Note: This parameter has no effect unless allow_http_basic_auth is
45
+ # true
46
+ #
47
+ # * <tt>Default:</tt> false
48
+ # * <tt>Accepts:</tt> Boolean
49
+ def request_http_basic_auth(value = nil)
50
+ rw_config(:request_http_basic_auth, value, false)
51
+ end
52
+ alias_method :request_http_basic_auth=, :request_http_basic_auth
53
+
54
+ # HTTP authentication realm
55
+ #
56
+ # Sets the HTTP authentication realm.
57
+ #
58
+ # Note: This option has no effect unless request_http_basic_auth is true
59
+ #
60
+ # * <tt>Default:</tt> 'Application'
61
+ # * <tt>Accepts:</tt> String
62
+ def http_basic_auth_realm(value = nil)
63
+ rw_config(:http_basic_auth_realm, value, 'Application')
64
+ end
65
+ alias_method :http_basic_auth_realm=, :http_basic_auth_realm
31
66
  end
32
67
 
33
68
  # Instance methods for the HTTP basic auth feature of authlogic.
@@ -38,13 +73,19 @@ module Authlogic
38
73
  end
39
74
 
40
75
  def persist_by_http_auth
41
- controller.authenticate_with_http_basic do |login, password|
76
+ login_proc = Proc.new do |login, password|
42
77
  if !login.blank? && !password.blank?
43
78
  send("#{login_field}=", login)
44
79
  send("#{password_field}=", password)
45
80
  return valid?
46
81
  end
47
82
  end
83
+
84
+ if self.class.request_http_basic_auth
85
+ controller.authenticate_or_request_with_http_basic(self.class.http_basic_auth_realm, &login_proc)
86
+ else
87
+ controller.authenticate_with_http_basic(&login_proc)
88
+ end
48
89
 
49
90
  false
50
91
  end
@@ -55,4 +96,4 @@ module Authlogic
55
96
  end
56
97
  end
57
98
  end
58
- end
99
+ end
@@ -12,14 +12,14 @@ module Authlogic
12
12
  attr_writer :scope
13
13
  end
14
14
  end
15
-
15
+
16
16
  # = Scopes
17
17
  module ClassMethods
18
18
  # The current scope set, should be used in the block passed to with_scope.
19
19
  def scope
20
20
  Thread.current[:authlogic_scope]
21
21
  end
22
-
22
+
23
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
24
  # ActiveRecord::Base#with_scopes. It accepts a hash with any of the following options:
25
25
  #
@@ -34,11 +34,11 @@ module Authlogic
34
34
  #
35
35
  # Eseentially what the above does is scope the searching of the object with the sql you provided. So instead of:
36
36
  #
37
- # User.find(:first, :conditions => "login = 'ben'")
37
+ # User.where("login = 'ben'").first
38
38
  #
39
39
  # it would be:
40
40
  #
41
- # User.find(:first, :conditions => "login = 'ben' and account_id = 2")
41
+ # User.where("login = 'ben' and account_id = 2").first
42
42
  #
43
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
44
  #
@@ -65,31 +65,31 @@ module Authlogic
65
65
  self.scope = nil
66
66
  result
67
67
  end
68
-
68
+
69
69
  private
70
70
  def scope=(value)
71
71
  Thread.current[:authlogic_scope] = value
72
72
  end
73
73
  end
74
-
74
+
75
75
  module InstanceMethods
76
76
  # Setting the scope if it exists upon instantiation.
77
77
  def initialize(*args)
78
78
  self.scope = self.class.scope
79
79
  super
80
80
  end
81
-
81
+
82
82
  # The scope of the current object
83
83
  def scope
84
84
  @scope ||= {}
85
85
  end
86
-
86
+
87
87
  private
88
88
  # Used for things like cookie_key, session_key, etc.
89
89
  def build_key(last_part)
90
90
  [scope[:id], super].compact.join("_")
91
91
  end
92
-
92
+
93
93
  def search_for_record(*args)
94
94
  klass.send(:with_scope, :find => (scope[:find_options] || {})) do
95
95
  klass.send(*args)
@@ -3,7 +3,7 @@ module Authlogic
3
3
  # Basically acts like a controller but doesn't do anything. Authlogic can interact with this, do it's thing and then you
4
4
  # can look at the controller object to see if anything changed.
5
5
  class MockController < ControllerAdapters::AbstractAdapter
6
- attr_accessor :http_user, :http_password
6
+ attr_accessor :http_user, :http_password, :realm
7
7
  attr_writer :request_content_type
8
8
 
9
9
  def initialize
@@ -13,6 +13,12 @@ module Authlogic
13
13
  yield http_user, http_password
14
14
  end
15
15
 
16
+ def authenticate_or_request_with_http_basic(realm = 'DefaultRealm', &block)
17
+ self.realm = realm
18
+ @http_auth_requested = true
19
+ yield http_user, http_password
20
+ end
21
+
16
22
  def cookies
17
23
  @cookies ||= MockCookieJar.new
18
24
  end
@@ -40,6 +46,10 @@ module Authlogic
40
46
  def session
41
47
  @session ||= {}
42
48
  end
49
+
50
+ def http_auth_requested?
51
+ @http_auth_requested ||= false
52
+ end
43
53
  end
44
54
  end
45
- end
55
+ end