Empact-authlogic 2.1.5 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
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