authlogic 1.3.7 → 1.3.8

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.

@@ -1,3 +1,10 @@
1
+ == 1.3.8 released 2008-11-30
2
+
3
+ * Only change persistence token if the password is not blank
4
+ * Normalize the last_request_at_threshold so that you can pass an integer or a date/time range.
5
+ * Fixed bug where password length validations were not being run because the password value was not blank. It should be run if it is a new record, the password has changed, or the password is blank.
6
+ * Added disable_magic_states option for sessions, to turn off the automatic checking of "magic states" such as active?, confirmed?, and approved?.
7
+
1
8
  == 1.3.7 released 2008-11-30
2
9
 
3
10
  * Added session generator: script/generate session UserSession
@@ -6,7 +6,7 @@ So what is Authlogic, and why would I create a solution to a problem that alread
6
6
 
7
7
  Let's take a rails application...
8
8
 
9
- Wouldn't it be nice to keep your app up to date with the latest and greatest security techniques with a simple update of a plugin?
9
+ Wouldn't it be nice to keep your app up to date with the latest and greatest security techniques with a simple update of a gem?
10
10
 
11
11
  What if you could have authentication up and running in minutes without having to run a generator? All because it's simple, like everything else.
12
12
 
@@ -100,6 +100,10 @@ Lets assume you are setting up a session for your User model.
100
100
 
101
101
  Create your user_session.rb file:
102
102
 
103
+ $ script/generate session user_session
104
+
105
+ This will create a file that looks similar to:
106
+
103
107
  # app/models/user_session.rb
104
108
  class UserSession < Authlogic::Session::Base
105
109
  # configuration here, just like ActiveRecord, or in an initializer
@@ -416,9 +420,9 @@ This is one of my favorite features that I think is pretty cool. It's things lik
416
420
 
417
421
  Just to clear up any confusion, Authlogic stores both the record id and the persistence token in the session. Why? So stale sessions can not be persisted. It stores the id so it can quickly find the record, and the persistence token to ensure no sessions are stale. The persistence token changes with the password, if someone is logged in and their password is changed, they should be logged out, unless they made the change. That being said, the person making the change needs their session to be updated with the new persistence token, so they stay logged in, which is what this section is all about.
418
422
 
419
- That being said...What if a user changes their password? You have to re-log them in with the new password, recreate the session, etc, pain in the ass. Or what if a user creates a new user account? You have to do the same thing. Here's an even better one: what if a user is in the admin area and changes his own password? There might even be another place passwords can change. It shouldn't matter, your code should be written in a way where you don't have to remember to do this.
423
+ So what if a user changes their password? You have to re-log them in with the new password, recreate the session, etc. This is messy and leads to redundant code. Or what if a user creates a new user account? You have to do the same thing. Here's an even better one: what if a user is in the admin area and changes their own password? There might even be another place passwords can change. It shouldn't matter, your code should be written in a way where you don't have to remember to do this.
420
424
 
421
- Instead of updating sessions all over the place, doesn't it make sense to do this at a lower level? Like the User model? You're saying "but Ben, models can't mess around with sessions and cookies". True...but Authlogic can, and you can access Authlogic just like a model. I know in most situations it's not good practice to do this but I view this in the same class as sweepers, and feel like it actually is good practice here. User sessions are directly tied to users, they should be connected on the model level.
425
+ Instead of updating sessions all over the place, doesn't it make sense to do this at a lower level? Like the User model? You're saying "but Ben, models can't mess around with sessions and cookies". True...but Authlogic can, and you can access Authlogic just like a model. I know in most situations it's not good practice to do this, but I view this in the same class as sweepers, and feel like it actually is good practice here. User sessions are directly tied to users, they should be connected on the model level.
422
426
 
423
427
  Fear not, because the acts_as_authentic method you call in your model takes care of this for you, by adding an after_save callback to automatically keep the session up to date. You don't have to worry about it anymore. Don't even think about it. Let your UsersController deal with users, not users *AND* sessions. *ANYTIME* the user changes his password in *ANY* way, his session will be updated.
424
428
 
@@ -452,7 +456,7 @@ You get the following methods:
452
456
  set_cookie_for(record_object)
453
457
  set_http_auth_for(username, password)
454
458
 
455
- In your test, before you execute a request, just call one of those methods and it will set the proper values so that it will seems as if that record is logged in.
459
+ In your test, before you execute a request, just call one of those methods and it will set the proper values so that it will seem as if that record is logged in.
456
460
 
457
461
  You can also checkout the authlogic_example application (see helpful links above), the tests there use this.
458
462
 
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ begin
11
11
  p.project = 'authlogic'
12
12
  p.summary = "A clean, simple, and unobtrusive ruby authentication solution."
13
13
  p.url = "http://github.com/binarylogic/authlogic"
14
- p.dependencies = %w(activesupport)
14
+ p.dependencies = %w(activesupport echoe)
15
15
  end
16
16
  rescue LoadError => boom
17
17
  puts "You are missing a dependency required for meta-operations on this gem."
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{authlogic}
5
- s.version = "1.3.7"
5
+ s.version = "1.3.8"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Ben Johnson of Binary Logic"]
9
- s.date = %q{2008-12-01}
9
+ s.date = %q{2008-12-24}
10
10
  s.description = %q{A clean, simple, and unobtrusive ruby authentication solution.}
11
11
  s.email = %q{bjohnson@binarylogic.com}
12
12
  s.extra_rdoc_files = ["CHANGELOG.rdoc", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/merb_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/crypto_providers/aes256.rb", "lib/authlogic/crypto_providers/bcrypt.rb", "lib/authlogic/crypto_providers/sha1.rb", "lib/authlogic/crypto_providers/sha512.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/perishability.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb", "lib/authlogic/orm_adapters/active_record_adapter/authenticates_many.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/authenticates_many_association.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/cookies.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/session/params.rb", "lib/authlogic/session/perishability.rb", "lib/authlogic/session/scopes.rb", "lib/authlogic/session/session.rb", "lib/authlogic/testing/test_unit_helpers.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "README.rdoc"]
@@ -26,13 +26,16 @@ Gem::Specification.new do |s|
26
26
 
27
27
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
28
  s.add_runtime_dependency(%q<activesupport>, [">= 0"])
29
+ s.add_runtime_dependency(%q<echoe>, [">= 0"])
29
30
  s.add_development_dependency(%q<echoe>, [">= 0"])
30
31
  else
31
32
  s.add_dependency(%q<activesupport>, [">= 0"])
32
33
  s.add_dependency(%q<echoe>, [">= 0"])
34
+ s.add_dependency(%q<echoe>, [">= 0"])
33
35
  end
34
36
  else
35
37
  s.add_dependency(%q<activesupport>, [">= 0"])
36
38
  s.add_dependency(%q<echoe>, [">= 0"])
39
+ s.add_dependency(%q<echoe>, [">= 0"])
37
40
  end
38
41
  end
@@ -2,7 +2,7 @@ module Authlogic
2
2
  module ControllerAdapters # :nodoc:
3
3
  # = Abstract Adapter
4
4
  #
5
- # Allows you to use Authlogic in any framework you want, not just rails. See tha RailsAdapter for an example of how to adapter Authlogic to work with your framework.
5
+ # Allows you to use Authlogic in any framework you want, not just rails. See the RailsAdapter or MerbAdapter for an example of how to adapt Authlogic to work with your framework.
6
6
  class AbstractAdapter
7
7
  attr_accessor :controller
8
8
 
@@ -7,7 +7,7 @@ module Authlogic
7
7
  class MerbAdapter < AbstractAdapter
8
8
  # = Merb Implementation
9
9
  #
10
- # Lets Authlogic know about the controller object, AKA "activates" authlogic.
10
+ # Lets Authlogic know about the controller object via a before filter, AKA "activates" authlogic.
11
11
  module MerbImplementation
12
12
  def self.included(klass) # :nodoc:
13
13
  klass.before :activate_authlogic
@@ -19,7 +19,7 @@ module Authlogic
19
19
 
20
20
  # = Rails Implementation
21
21
  #
22
- # Lets Authlogic know about the controller object, AKA "activates" authlogic.
22
+ # Lets Authlogic know about the controller object via a before filter, AKA "activates" authlogic.
23
23
  module RailsImplementation
24
24
  def self.included(klass) # :nodoc:
25
25
  klass.prepend_before_filter :activate_authlogic
@@ -5,7 +5,7 @@ module Authlogic
5
5
  # = Sha1
6
6
  #
7
7
  # This class was made for the users transitioning from restful_authentication. I highly discourage using this crypto provider as it inferior to your other options.
8
- # Please use the Sha512 crypto provider or the BCrypt provider.
8
+ # Please use any other provider offered by Authlogic.
9
9
  class Sha1
10
10
  class << self
11
11
  def join_token
@@ -10,7 +10,7 @@ module Authlogic
10
10
  #
11
11
  # class MyAwesomeEncryptionMethod
12
12
  # def self.encrypt(*tokens)
13
- # # the tokens passed wil be an array of objects, what type of object is irrelevant
13
+ # # the tokens passed will be an array of objects, what type of object is irrelevant,
14
14
  # # just do what you need to do with them and return a single encrypted string.
15
15
  # # for example, you will most likely join all of the objects into a single string and then encrypt that string
16
16
  # end
@@ -127,9 +127,12 @@ module Authlogic
127
127
  # * <tt>password_field_validates_length_of_options</tt> - default: {:minimum => 4},
128
128
  # These options are applied to the validates_length_of call for the :password_field
129
129
  #
130
- # * <tt>login_field_validates_confirmation_of_options</tt> - default: {},
130
+ # * <tt>password_field_validates_confirmation_of_options</tt> - default: {},
131
131
  # These options are applied to the validates_confirmation_of call for the :password_field
132
132
  #
133
+ # * <tt>password_confirmation_field_validates_presence_of_options</tt> - default: {},
134
+ # These options are applied to the validates_presence_of call for the :password_confirmation_field.
135
+ #
133
136
  # * <tt>email_field_validation_options</tt> - default: {},
134
137
  # The same as :validation_options but these are only applied to validations that pertain to the :email_field
135
138
  #
@@ -198,6 +201,8 @@ module Authlogic
198
201
  end
199
202
  end
200
203
 
204
+ options[:password_confirmation_field_validates_presence_of_options] ||= {}
205
+
201
206
  if options[:scope]
202
207
  options[:login_field_validates_uniqueness_of_options][:scope] ||= options[:scope]
203
208
  options[:email_field_validates_uniqueness_of_options][:scope] ||= options[:scope]
@@ -42,7 +42,7 @@ module Authlogic
42
42
  if options[:validate_password_field]
43
43
  validates_length_of options[:password_field], {:minimum => 4}.merge(options[:password_field_validates_length_of_options].merge(:if => "validate_#{options[:password_field]}?".to_sym))
44
44
  validates_confirmation_of options[:password_field], options[:password_field_validates_confirmation_of_options].merge(:if => "#{options[:password_salt_field]}_changed?".to_sym)
45
- validates_presence_of "#{options[:password_field]}_confirmation", :if => "#{options[:password_salt_field]}_changed?".to_sym
45
+ validates_presence_of "#{options[:password_field]}_confirmation", options[:password_confirmation_field_validates_presence_of_options].merge(:if => "#{options[:password_salt_field]}_changed?".to_sym)
46
46
  end
47
47
 
48
48
  if options[:validate_email_field] && options[:email_field]
@@ -116,12 +116,12 @@ module Authlogic
116
116
  def validate_#{options[:password_field]}?
117
117
  case #{options[:password_field_validates_length_of_options][:if].inspect}
118
118
  when String
119
- return false unless eval('#{options[:password_field_validates_length_of_options][:if]}')
119
+ return false if !eval('#{options[:password_field_validates_length_of_options][:if]}')
120
120
  when Symbol
121
- return false unless send(#{options[:password_field_validates_length_of_options][:if].inspect})
121
+ return false if !send(#{options[:password_field_validates_length_of_options][:if].inspect})
122
122
  end
123
123
 
124
- #{options[:crypted_password_field]}.blank?
124
+ new_record? || #{options[:password_salt_field]}_changed? || #{options[:crypted_password_field]}.blank?
125
125
  end
126
126
 
127
127
  private
@@ -49,7 +49,7 @@ module Authlogic
49
49
  end
50
50
 
51
51
  def #{options[:password_field]}_with_persistence=(value)
52
- reset_#{options[:persistence_token_field]}
52
+ reset_#{options[:persistence_token_field]} unless value.blank?
53
53
  self.#{options[:password_field]}_without_persistence = value
54
54
  end
55
55
  alias_method_chain :#{options[:password_field]}=, :persistence
@@ -2,8 +2,8 @@ module Authlogic
2
2
  module Session
3
3
  # = ActiveRecord Trickery
4
4
  #
5
- # Authlogic looks like ActiveRecord, sounds like ActiveRecord, but its not ActiveRecord. That's the goal here. This is useful for the various rails helper methods such as form_for, error_messages_for, etc.
6
- # These helpers exptect various methods to be present. This adds in those methods into Authlogic.
5
+ # Authlogic looks like ActiveRecord, sounds like ActiveRecord, but its not ActiveRecord. That's the goal here. This is useful for the various rails helper methods such as form_for, error_messages_for, or any
6
+ # method that expects an ActiveRecord object. The point is to disguise the object as an ActiveRecord object so we have no problems.
7
7
  module ActiveRecordTrickery
8
8
  def self.included(klass) # :nodoc:
9
9
  klass.extend ClassMethods
@@ -191,7 +191,7 @@ module Authlogic
191
191
  if send("valid_#{find_method}?")
192
192
  self.new_session = false
193
193
 
194
- if record.class.column_names.include?("last_request_at") && (record.last_request_at.blank? || last_request_at_threshold.ago >= record.last_request_at)
194
+ if 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)
195
195
  record.last_request_at = Time.now
196
196
  record.save_without_session_maintenance(false)
197
197
  end
@@ -357,7 +357,7 @@ module Authlogic
357
357
  return if respond_to?(login_field) # already created these methods
358
358
 
359
359
  self.class.class_eval <<-"end_eval", __FILE__, __LINE__
360
- alias_method :#{klass_name.underscore.split("/").last}, :record
360
+ alias_method :#{klass_name.demodulize.underscore}, :record
361
361
 
362
362
  attr_reader :#{login_field}
363
363
 
@@ -439,12 +439,14 @@ module Authlogic
439
439
  end
440
440
 
441
441
  def valid_record?
442
+ return true if disable_magic_states?
442
443
  [:active, :approved, :confirmed].each do |required_status|
443
444
  if record.respond_to?("#{required_status}?") && !record.send("#{required_status}?")
444
445
  errors.add_to_base(send("not_#{required_status}_message"))
445
446
  return false
446
447
  end
447
448
  end
449
+ true
448
450
  end
449
451
  end
450
452
  end
@@ -65,6 +65,21 @@ module Authlogic
65
65
  end
66
66
  alias_method :cookie_key=, :cookie_key
67
67
 
68
+ # 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
69
+ # convenience feature, since 99% of the time if those methods exist and return false you will not want the user logging in. You could
70
+ # easily accomplish this same thing with a before_validation method or other callbacks.
71
+ #
72
+ # * <tt>Default:</tt> false
73
+ # * <tt>Accepts:</tt> Boolean
74
+ def disable_magic_states(value = nil)
75
+ if value.nil?
76
+ read_inheritable_attribute(:disable_magic_states)
77
+ else
78
+ write_inheritable_attribute(:disable_magic_states, value)
79
+ end
80
+ end
81
+ alias_method :disable_magic_states=, :disable_magic_states
82
+
68
83
  # 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.
69
84
  #
70
85
  # 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
@@ -333,6 +348,10 @@ module Authlogic
333
348
  build_key(self.class.cookie_key)
334
349
  end
335
350
 
351
+ def disable_magic_states?
352
+ self.class.disable_magic_states == true
353
+ end
354
+
336
355
  def find_by_login_method
337
356
  self.class.find_by_login_method
338
357
  end
@@ -1,8 +1,12 @@
1
1
  module Authlogic
2
- module Testing
2
+ module Testing # :nodoc:
3
3
  # = Test Unit Helpers
4
4
  #
5
- # Provides useful methods for testing in Test::Unit, lets you log records in, etc.
5
+ # Provides useful methods for testing in Test::Unit, lets you log records in, etc. Just include this in your test_helper filter:
6
+ #
7
+ # require "authlogic/testing/test_unit_helpers"
8
+ #
9
+ # Then you will have the methods below to use in your tests.
6
10
  module TestUnitHelpers
7
11
  private
8
12
  def session_class(record) # :nodoc:
@@ -44,7 +44,7 @@ module Authlogic # :nodoc:
44
44
 
45
45
  MAJOR = 1
46
46
  MINOR = 3
47
- TINY = 7
47
+ TINY = 8
48
48
 
49
49
  # The current version as a Version instance
50
50
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -45,7 +45,8 @@ module ORMAdaptersTests
45
45
  :validate_email_field => true,
46
46
  :validation_options => {},
47
47
  :login_field_validation_options => {},
48
- :transition_from_crypto_provider => []
48
+ :transition_from_crypto_provider => [],
49
+ :password_confirmation_field_validates_presence_of_options => {}
49
50
  }
50
51
  assert_equal default_config, User.acts_as_authentic_config
51
52
  end
@@ -39,6 +39,15 @@ module ORMAdaptersTests
39
39
  assert !UserSession.find
40
40
  assert UserSession.find(:ziggity_zack)
41
41
  end
42
+
43
+ def test_password
44
+ ben = users(:ben)
45
+ old_persistence_token = ben.persistence_token
46
+ ben.password = ""
47
+ assert_equal old_persistence_token, ben.persistence_token
48
+ ben.password = "newpass"
49
+ assert_not_equal old_persistence_token, ben.persistence_token
50
+ end
42
51
  end
43
52
  end
44
53
  end
@@ -274,6 +274,39 @@ module SessionTests
274
274
  assert session.errors.empty?
275
275
  end
276
276
 
277
+ def test_valid_record
278
+ session = UserSession.new
279
+ ben = users(:ben)
280
+ session.send(:record=, ben)
281
+ assert session.send :valid_record?
282
+ assert session.errors.empty?
283
+
284
+ ben.update_attribute(:active, false)
285
+ assert !session.send(:valid_record?)
286
+ assert session.errors.on_base.size > 0
287
+
288
+ ben.active = true
289
+ ben.approved = false
290
+ ben.save
291
+ assert !session.send(:valid_record?)
292
+ assert session.errors.on_base.size > 0
293
+
294
+ ben.approved = true
295
+ ben.confirmed = false
296
+ ben.save
297
+ assert !session.send(:valid_record?)
298
+ assert session.errors.on_base.size > 0
299
+
300
+ ben.approved = false
301
+ ben.confirmed = false
302
+ ben.active = false
303
+
304
+ UserSession.disable_magic_states = true
305
+ session = UserSession.new
306
+ session.send(:record=, ben)
307
+ assert session.send :valid_record?
308
+ end
309
+
277
310
  def test_valid_http_auth
278
311
  ben = users(:ben)
279
312
  session = UserSession.new
@@ -23,6 +23,18 @@ module SessionTests
23
23
  session = UserSession.new
24
24
  assert_equal "user_credentials", session.cookie_key
25
25
  end
26
+
27
+ def test_disable_magic_states
28
+ UserSession.disable_magic_states = true
29
+ assert_equal true, UserSession.disable_magic_states
30
+ session = UserSession.new
31
+ assert_equal true, session.disable_magic_states?
32
+
33
+ UserSession.disable_magic_states false
34
+ assert_equal false, UserSession.disable_magic_states
35
+ session = UserSession.new
36
+ assert_equal false, session.disable_magic_states?
37
+ end
26
38
 
27
39
  def test_find_by_login_method
28
40
  UserSession.find_by_login_method = "my_login_method"
@@ -50,6 +50,9 @@ ActiveRecord::Schema.define(:version => 1) do
50
50
  t.datetime :last_login_at
51
51
  t.string :current_login_ip
52
52
  t.string :last_login_ip
53
+ t.boolean :active, :default => true
54
+ t.boolean :approved, :default => true
55
+ t.boolean :confirmed, :default => true
53
56
  end
54
57
 
55
58
  create_table :employees do |t|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authlogic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.7
4
+ version: 1.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Johnson of Binary Logic
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-01 00:00:00 -05:00
12
+ date: 2008-12-24 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,6 +22,16 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: "0"
24
24
  version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: echoe
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
25
35
  - !ruby/object:Gem::Dependency
26
36
  name: echoe
27
37
  type: :development