authlogic 0.10.4 → 1.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 (111) hide show
  1. data/CHANGELOG.rdoc +11 -0
  2. data/Manifest +18 -81
  3. data/README.rdoc +53 -17
  4. data/Rakefile +1 -1
  5. data/authlogic.gemspec +7 -6
  6. data/lib/authlogic.rb +5 -0
  7. data/lib/authlogic/active_record/acts_as_authentic.rb +90 -58
  8. data/lib/authlogic/active_record/authenticates_many.rb +37 -0
  9. data/lib/authlogic/controller_adapters/abstract_adapter.rb +5 -7
  10. data/lib/authlogic/controller_adapters/merb_adapter.rb +55 -0
  11. data/lib/authlogic/controller_adapters/rails_adapter.rb +21 -15
  12. data/lib/authlogic/session/base.rb +64 -116
  13. data/lib/authlogic/session/callbacks.rb +29 -13
  14. data/lib/authlogic/session/config.rb +5 -1
  15. data/lib/authlogic/session/scopes.rb +101 -0
  16. data/lib/authlogic/version.rb +3 -3
  17. data/test/active_record_acts_as_authentic_test.rb +213 -0
  18. data/test/active_record_authenticates_many_test.rb +28 -0
  19. data/{test_app/test → test}/fixtures/companies.yml +0 -2
  20. data/test/fixtures/employees.yml +17 -0
  21. data/{test_app/test → test}/fixtures/projects.yml +1 -2
  22. data/{test_app/test → test}/fixtures/users.yml +3 -5
  23. data/test/test_helper.rb +142 -0
  24. data/test/user_session_active_record_trickery_test.rb +12 -0
  25. data/test/user_session_base_test.rb +316 -0
  26. data/test/user_session_config_test.rb +144 -0
  27. data/test/user_session_scopes_test.rb +19 -0
  28. data/test_libs/aes128_crypto_provider.rb +17 -0
  29. data/test_libs/mock_controller.rb +19 -0
  30. data/test_libs/mock_cookie_jar.rb +6 -0
  31. data/test_libs/mock_request.rb +5 -0
  32. data/test_libs/ordered_hash.rb +9 -0
  33. metadata +32 -87
  34. data/test_app/README +0 -256
  35. data/test_app/Rakefile +0 -10
  36. data/test_app/app/controllers/application.rb +0 -72
  37. data/test_app/app/controllers/companies_controller.rb +0 -2
  38. data/test_app/app/controllers/user_sessions_controller.rb +0 -25
  39. data/test_app/app/controllers/users_controller.rb +0 -61
  40. data/test_app/app/helpers/application_helper.rb +0 -3
  41. data/test_app/app/helpers/companies_helper.rb +0 -2
  42. data/test_app/app/helpers/user_sessions_helper.rb +0 -2
  43. data/test_app/app/helpers/users_helper.rb +0 -2
  44. data/test_app/app/models/company.rb +0 -4
  45. data/test_app/app/models/project.rb +0 -3
  46. data/test_app/app/models/user.rb +0 -5
  47. data/test_app/app/models/user_session.rb +0 -3
  48. data/test_app/app/views/layouts/application.html.erb +0 -27
  49. data/test_app/app/views/user_sessions/new.html.erb +0 -15
  50. data/test_app/app/views/users/_form.erb +0 -15
  51. data/test_app/app/views/users/edit.html.erb +0 -8
  52. data/test_app/app/views/users/new.html.erb +0 -8
  53. data/test_app/app/views/users/show.html.erb +0 -29
  54. data/test_app/config/boot.rb +0 -109
  55. data/test_app/config/database.yml +0 -19
  56. data/test_app/config/environment.rb +0 -69
  57. data/test_app/config/environments/development.rb +0 -17
  58. data/test_app/config/environments/production.rb +0 -22
  59. data/test_app/config/environments/test.rb +0 -22
  60. data/test_app/config/initializers/inflections.rb +0 -10
  61. data/test_app/config/initializers/mime_types.rb +0 -5
  62. data/test_app/config/initializers/new_rails_defaults.rb +0 -17
  63. data/test_app/config/routes.rb +0 -11
  64. data/test_app/db/development.sqlite3 +0 -0
  65. data/test_app/db/migrate/20081023040052_create_users.rb +0 -20
  66. data/test_app/db/migrate/20081103003828_create_companies.rb +0 -14
  67. data/test_app/db/migrate/20081103003834_create_projects.rb +0 -18
  68. data/test_app/db/schema.rb +0 -46
  69. data/test_app/db/test.sqlite3 +0 -0
  70. data/test_app/doc/README_FOR_APP +0 -2
  71. data/test_app/public/404.html +0 -30
  72. data/test_app/public/422.html +0 -30
  73. data/test_app/public/500.html +0 -30
  74. data/test_app/public/dispatch.cgi +0 -10
  75. data/test_app/public/dispatch.fcgi +0 -24
  76. data/test_app/public/dispatch.rb +0 -10
  77. data/test_app/public/favicon.ico +0 -0
  78. data/test_app/public/images/rails.png +0 -0
  79. data/test_app/public/javascripts/application.js +0 -2
  80. data/test_app/public/javascripts/controls.js +0 -963
  81. data/test_app/public/javascripts/dragdrop.js +0 -972
  82. data/test_app/public/javascripts/effects.js +0 -1120
  83. data/test_app/public/javascripts/prototype.js +0 -4225
  84. data/test_app/public/robots.txt +0 -5
  85. data/test_app/public/stylesheets/scaffold.css +0 -62
  86. data/test_app/script/about +0 -4
  87. data/test_app/script/console +0 -3
  88. data/test_app/script/dbconsole +0 -3
  89. data/test_app/script/destroy +0 -3
  90. data/test_app/script/generate +0 -3
  91. data/test_app/script/performance/benchmarker +0 -3
  92. data/test_app/script/performance/profiler +0 -3
  93. data/test_app/script/performance/request +0 -3
  94. data/test_app/script/plugin +0 -3
  95. data/test_app/script/process/inspector +0 -3
  96. data/test_app/script/process/reaper +0 -3
  97. data/test_app/script/process/spawner +0 -3
  98. data/test_app/script/runner +0 -3
  99. data/test_app/script/server +0 -3
  100. data/test_app/test/functional/companies_controller_test.rb +0 -8
  101. data/test_app/test/functional/user_sessions_controller_test.rb +0 -36
  102. data/test_app/test/functional/users_controller_test.rb +0 -8
  103. data/test_app/test/integration/company_user_session_stories_test.rb +0 -46
  104. data/test_app/test/integration/user_sesion_stories_test.rb +0 -105
  105. data/test_app/test/integration/user_session_config_test.rb +0 -24
  106. data/test_app/test/integration/user_session_test.rb +0 -161
  107. data/test_app/test/test_helper.rb +0 -81
  108. data/test_app/test/unit/account_test.rb +0 -8
  109. data/test_app/test/unit/company_test.rb +0 -8
  110. data/test_app/test/unit/project_test.rb +0 -8
  111. data/test_app/test/unit/user_test.rb +0 -80
@@ -1,6 +1,43 @@
1
1
  module Authlogic
2
2
  module ActiveRecord
3
+ # = Authenticates Many
4
+ #
5
+ # This allows you to scope your authentication. For example, let's say all users belong to an account, you want to make sure only users
6
+ # that belong to that account can actually login into that account. Simple, just do:
7
+ #
8
+ # class Account < ActiveRecord::Base
9
+ # authenticates_many :user_sessions
10
+ # end
11
+ #
12
+ # Now you can scope sessions just like everything else in ActiveRecord:
13
+ #
14
+ # @account.user_sessions.new(*args)
15
+ # @account.user_sessions.create(*args)
16
+ # @account.user_sessions.find(*args)
17
+ # # ... etc
18
+ #
19
+ # For more information on scopes check out the scopes section in the README.
3
20
  module AuthenticatesMany
21
+ # Allows you set essentially set up a relationship with your sessions. See module definition above for more details.
22
+ #
23
+ # === Options
24
+ #
25
+ # * <tt>session_class:</tt> default: "#{name}Session",
26
+ # This is the related session class.
27
+ #
28
+ # * <tt>relationship_name:</tt> default: options[:session_class].klass_name.underscore.pluralize,
29
+ # 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
30
+ # 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
31
+ # could have all kinds of custom options. So instead of repeating yourself we essentially use the scope that the relationship creates.
32
+ #
33
+ # * <tt>find_options:</tt> default: nil,
34
+ # By default the find options are created from the relationship you specify with :relationship_name. But if you want to override this and
35
+ # manually specify find_options you can do it here. Specify options just as you would in ActiveRecord::Base.find.
36
+ #
37
+ # * <tt>scope_cookies:</tt> default: false
38
+ # By the nature of cookies they scope theirself if you are using subdomains to access accounts. If you aren't using subdomains you need to have
39
+ # separate cookies for each account, assuming a user is logging into mroe than one account. Authlogic can take care of this for you by
40
+ # prefixing the name of the cookie and sessin with the model id. You just need to tell Authlogic to do this by passing this option.
4
41
  def authenticates_many(name, options = {})
5
42
  options[:session_class] ||= name.to_s.classify.constantize
6
43
  options[:relationship_name] ||= options[:session_class].klass_name.underscore.pluralize
@@ -3,22 +3,20 @@ module Authlogic
3
3
  # = Abstract Adapter
4
4
  # 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
5
  class AbstractAdapter
6
- attr_accessor :controller
7
-
8
- def initialize(controller)
9
- self.controller = controller
10
- end
11
-
12
- def authenticate_with_http_basic(*args, &block)
6
+ def authenticate_with_http_basic(&block)
7
+ black.call(nil, nil)
13
8
  end
14
9
 
15
10
  def cookies
11
+ {}
16
12
  end
17
13
 
18
14
  def request
15
+ nil
19
16
  end
20
17
 
21
18
  def session
19
+ {}
22
20
  end
23
21
  end
24
22
  end
@@ -0,0 +1,55 @@
1
+ module Authlogic
2
+ module ControllerAdapters
3
+ # = Merb Adapter
4
+ # Adapts authlogic to work with merb. The point is to close the gap between what authlogic expects and what the merb controller object
5
+ # provides. Similar to how ActiveRecord has an adapter for MySQL, PostgreSQL, SQLite, etc.
6
+ class MerbAdapter < AbstractAdapter
7
+ attr_accessor :controller
8
+
9
+ def initialize(controller)
10
+ self.controller = controller
11
+ end
12
+
13
+ def authenticate_with_http_basic(&block)
14
+ @auth = Rack::Auth::Basic::Request.new(controller.request.env)
15
+ if @auth.provided? and @auth.basic?
16
+ black.call(*@auth.credentials)
17
+ else
18
+ false
19
+ end
20
+ end
21
+
22
+ def cookies
23
+ controller.cookies
24
+ end
25
+
26
+ def request
27
+ controller.request
28
+ end
29
+
30
+ def session
31
+ controller.session
32
+ end
33
+
34
+ # = Merb Implementation
35
+ # Lets Authlogic know about the controller object, AKA "activates" authlogic.
36
+ module MerbImplementation
37
+ def self.included(klass) # :nodoc:
38
+ klass.before :activate_authlogic
39
+ end
40
+
41
+ private
42
+ def activate_authlogic
43
+ Authlogic::Session::Base.controller = MerbAdapter.new(self)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # make sure we're running inside Merb
51
+ if defined?(Merb::Plugins)
52
+ Merb::BootLoader.before_app_loads do
53
+ Merb::Controller.send(:include, Authlogic::ControllerAdapters::MerbAdapter::MerbImplementation)
54
+ end
55
+ end
@@ -4,8 +4,14 @@ module Authlogic
4
4
  # Adapts authlogic to work with rails. The point is to close the gap between what authlogic expects and what the rails controller object
5
5
  # provides. Similar to how ActiveRecord has an adapter for MySQL, PostgreSQL, SQLite, etc.
6
6
  class RailsAdapter < AbstractAdapter
7
- def authenticate_with_http_basic(*args, &block)
8
- controller.authenticate_with_http_basic(*args, &block)
7
+ attr_accessor :controller
8
+
9
+ def initialize(controller)
10
+ self.controller = controller
11
+ end
12
+
13
+ def authenticate_with_http_basic(&block)
14
+ controller.authenticate_with_http_basic(&block)
9
15
  end
10
16
 
11
17
  def cookies
@@ -19,21 +25,21 @@ module Authlogic
19
25
  def session
20
26
  controller.session
21
27
  end
22
- end
23
-
24
- # = Rails Implementation
25
- # Lets Authlogic know about the controller object, AKA "activates" authlogic.
26
- module RailsImplementation
27
- def self.included(klass) # :nodoc:
28
- klass.prepend_before_filter :set_controller
29
- end
30
-
31
- private
32
- def set_controller
33
- Authlogic::Session::Base.controller = RailsAdapter.new(self)
28
+
29
+ # = Rails Implementation
30
+ # Lets Authlogic know about the controller object, AKA "activates" authlogic.
31
+ module RailsImplementation
32
+ def self.included(klass) # :nodoc:
33
+ klass.prepend_before_filter :activate_authlogic
34
34
  end
35
+
36
+ private
37
+ def activate_authlogic
38
+ Authlogic::Session::Base.controller = RailsAdapter.new(self)
39
+ end
40
+ end
35
41
  end
36
42
  end
37
43
  end
38
44
 
39
- ActionController::Base.send(:include, Authlogic::ControllerAdapters::RailsImplementation)
45
+ ActionController::Base.send(:include, Authlogic::ControllerAdapters::RailsAdapter::RailsImplementation)
@@ -22,6 +22,10 @@ module Authlogic
22
22
  controllers[Thread.current]
23
23
  end
24
24
 
25
+ def reset_controllers!
26
+ @@controllers = {}
27
+ end
28
+
25
29
  # A convenince method. The same as:
26
30
  #
27
31
  # session = UserSession.new
@@ -70,77 +74,15 @@ module Authlogic
70
74
  end
71
75
  end
72
76
 
73
- # The current scope set, should be used in the block passed to with_scope.
74
- def scope
75
- scopes[Thread.current]
76
- end
77
-
78
- # Authentication can be scoped, but scoping authentication can get a little tricky. Checkout the section "Scoping" in the readme for more details.
79
- #
80
- # What with_scopes focuses on is scoping the query when finding the object and the name of the cookies.
81
- #
82
- # with_scope accepts a hash with any of the following options:
83
- #
84
- # * <tt>find_options:</tt> any options you can pass into ActiveRecord::Base.find. This is used when trying to find the record.
85
- # * <tt>id:</tt> see the id method above
86
- #
87
- # So you use it just like an ActiveRecord scope, essentially:
88
- #
89
- # UserSession.with_scope(:find_options => {:conditions => "account_id = 2"}, :id => "account_2") do
90
- # UserSession.find
91
- # end
92
- #
93
- # Eseentially what the above does is scope the searching of the object with the sql you provided. So instead of:
94
- #
95
- # User.find(:first, :conditions => "login = 'ben'")
96
- #
97
- # it would be:
98
- #
99
- # User.find(:first, :conditions => "login = 'ben' and account_id = 2")
100
- #
101
- # 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:
102
- #
103
- # account_2_user_credentials
104
- #
105
- # instead of:
106
- #
107
- # user_credentials
108
- #
109
- # What is also nifty about scoping with an :id is that it merges your id's. So if you do:
110
- #
111
- # UserSession.with_scope(:find_options => {:conditions => "account_id = 2"}, :id => "account_2") do
112
- # session = UserSession.new
113
- # session.id = :secure
114
- # end
115
- #
116
- # The name of your cookies will be:
117
- #
118
- # secure_account_2_user_credentials
119
- def with_scope(options = {}, &block)
120
- raise ArgumentError.new("You must provide a block") unless block_given?
121
- self.scope = options
122
- result = yield
123
- self.scope = nil
124
- result
125
- end
126
-
127
77
  private
128
78
  def controllers
129
79
  @@controllers ||= {}
130
80
  end
131
-
132
- def scope=(value)
133
- scopes[Thread.current] = value
134
- end
135
-
136
- def scopes
137
- @scopes ||= {}
138
- end
139
81
  end
140
82
 
141
83
  attr_accessor :login_with, :new_session
142
84
  attr_reader :record, :unauthorized_record
143
- attr_writer :id, :scope
85
+ attr_writer :id
144
86
 
145
87
  # You can initialize a session by doing any of the following:
146
88
  #
@@ -163,22 +105,18 @@ module Authlogic
163
105
 
164
106
  create_configurable_methods!
165
107
 
166
- self.scope = self.class.scope
167
108
  self.id = args.pop if args.last.is_a?(Symbol)
168
109
 
169
- case args.size
170
- when 1
171
- credentials_or_record = args.first
172
- case credentials_or_record
173
- when Hash
174
- self.credentials = credentials_or_record
175
- else
176
- self.unauthorized_record = credentials_or_record
177
- end
178
- else
110
+ case args.first
111
+ when Hash
112
+ self.credentials = args.first
113
+ when String
179
114
  send("#{login_field}=", args[0]) if args.size > 0
180
115
  send("#{password_field}=", args[1]) if args.size > 1
181
116
  self.remember_me = args[2] if args.size > 2
117
+ else
118
+ self.unauthorized_record = args.first
119
+ self.remember_me = args[1] if args.size > 1
182
120
  end
183
121
  end
184
122
 
@@ -294,11 +232,6 @@ module Authlogic
294
232
  remember_me_for.from_now
295
233
  end
296
234
 
297
- # See the class level with_scope method on information on scopes. with_scope essentialls sets this scope with the options passed and unsets it after the block executes.
298
- def scope
299
- @scope ||= {}
300
- end
301
-
302
235
  # Creates / updates a new user session for you. It does all of the magic:
303
236
  #
304
237
  # 1. validates
@@ -313,7 +246,7 @@ module Authlogic
313
246
  :expires => remember_me_until
314
247
  }
315
248
 
316
- record.login_count = record.login_count + 1 if record.respond_to?(:login_count)
249
+ record.login_count = (record.login_count.blank? ? 1 : record.login_count + 1) if record.respond_to?(:login_count)
317
250
 
318
251
  if record.respond_to?(:current_login_at)
319
252
  record.last_login_at = record.current_login_at if record.respond_to?(:last_login_at)
@@ -350,11 +283,12 @@ module Authlogic
350
283
  # you will not have a record.
351
284
  def valid?
352
285
  errors.clear
353
- temp_record = validate_credentials
354
- if errors.empty?
355
- @record = temp_record
356
- return true
286
+ if valid_credentials?
287
+ validate
288
+ return true if errors.empty?
357
289
  end
290
+
291
+ self.record = nil
358
292
  false
359
293
  end
360
294
 
@@ -362,11 +296,12 @@ module Authlogic
362
296
  def valid_http_auth?
363
297
  controller.authenticate_with_http_basic do |login, password|
364
298
  if !login.blank? && !password.blank?
365
- send("#{login_method}=", login)
366
- send("#{password_method}=", password)
299
+ send("#{login_field}=", login)
300
+ send("#{password_field}=", password)
367
301
  result = valid?
368
302
  if result
369
303
  update_session!
304
+ self.new_session = false
370
305
  return result
371
306
  end
372
307
  end
@@ -404,6 +339,10 @@ module Authlogic
404
339
  false
405
340
  end
406
341
 
342
+ # Overwite this method to add your own validation, or use callbacks: before_validation, after_validation
343
+ def validate
344
+ end
345
+
407
346
  private
408
347
  def controller
409
348
  self.class.controller
@@ -439,12 +378,6 @@ module Authlogic
439
378
  end_eval
440
379
  end
441
380
 
442
- def search_for_record(method, value)
443
- klass.send(:with_scope, :find => (scope[:find_options] || {})) do
444
- klass.send(method, value)
445
- end
446
- end
447
-
448
381
  def klass
449
382
  self.class.klass
450
383
  end
@@ -453,6 +386,18 @@ module Authlogic
453
386
  self.class.klass_name
454
387
  end
455
388
 
389
+ def record=(value)
390
+ @record = value
391
+ end
392
+
393
+ def search_for_record(method, value)
394
+ begin
395
+ klass.send(method, value)
396
+ rescue Exception
397
+ raise method.inspect + " " + value.inspect
398
+ end
399
+ end
400
+
456
401
  def session_credentials
457
402
  controller.session[session_key]
458
403
  end
@@ -461,49 +406,52 @@ module Authlogic
461
406
  controller.session[session_key] = record && record.send(remember_token_field)
462
407
  end
463
408
 
464
- def validate_credentials
465
- temp_record = unauthorized_record
466
-
409
+ def valid_credentials?
410
+ unchecked_record = nil
411
+
467
412
  case login_with
468
413
  when :credentials
469
414
  errors.add(login_field, "can not be blank") if send(login_field).blank?
470
415
  errors.add(password_field, "can not be blank") if send("protected_#{password_field}").blank?
471
- return if errors.count > 0
472
-
473
- temp_record = search_for_record(find_by_login_method, send(login_field))
474
-
475
- if temp_record.blank?
416
+ return false if errors.count > 0
417
+
418
+ unchecked_record = search_for_record(find_by_login_method, send(login_field))
419
+
420
+ if unchecked_record.blank?
476
421
  errors.add(login_field, "was not found")
477
- return
422
+ return false
478
423
  end
479
-
480
- unless temp_record.send(verify_password_method, send("protected_#{password_field}"))
424
+
425
+ unless unchecked_record.send(verify_password_method, send("protected_#{password_field}"))
481
426
  errors.add(password_field, "is invalid")
482
- return
427
+ return false
483
428
  end
484
429
  when :unauthorized_record
485
- if temp_record.blank?
486
- errors.add_to_base("You can not log in with a blank record.")
487
- return
430
+ unchecked_record = unauthorized_record
431
+
432
+ if unchecked_record.blank?
433
+ errors.add_to_base("The record could not be found and did not match the requirements.")
434
+ return false
488
435
  end
489
-
490
- if temp_record.new_record?
491
- errors.add_to_base("You can not login with a new record.") if temp_record.new_record?
492
- return
436
+
437
+ if unchecked_record.new_record?
438
+ errors.add_to_base("You can not login with a new record.")
439
+ return false
493
440
  end
494
441
  else
495
442
  errors.add_to_base("You must provide some form of credentials before logging in.")
496
- return
443
+ return false
497
444
  end
498
-
445
+
499
446
  [:active, :approved, :confirmed].each do |required_status|
500
- if temp_record.respond_to?("#{required_status}?") && !temp_record.send("#{required_status}?")
447
+ if unchecked_record.respond_to?("#{required_status}?") && !unchecked_record.send("#{required_status}?")
501
448
  errors.add_to_base("Your account has not been marked as #{required_status}")
502
- return
449
+ return false
503
450
  end
504
451
  end
505
452
 
506
- temp_record
453
+ self.record = unchecked_record
454
+ true
507
455
  end
508
456
  end
509
457
  end