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,3 +1,14 @@
1
+ == 1.0.0 released 2008-11-05
2
+
3
+ * Checked for blank login counts, if a default wasnt set in the migrations.
4
+ * Added check for database table in acts_as_authentic to avoid errors in initial setup.
5
+ * Completely rewrote tests to be more conventional and thorough tests, removed test_app.
6
+ * Modified how validations work so that a validate method was added as well as callbacks for that method.
7
+ * Extracted scope support into its own module to help organize code better.
8
+ * Added in salt for encryption, just like hashes and removed :crypto_provider_type option for acts_as_authentic.
9
+ * Added merb adapters.
10
+ * Improved documentation throughout.
11
+
1
12
  == 0.10.4 released 2008-10-31
2
13
 
3
14
  * Changed configuration to use inheritable attributes
data/Manifest CHANGED
@@ -4,12 +4,14 @@ lib/authlogic/active_record/acts_as_authentic.rb
4
4
  lib/authlogic/active_record/authenticates_many.rb
5
5
  lib/authlogic/active_record/scoped_session.rb
6
6
  lib/authlogic/controller_adapters/abstract_adapter.rb
7
+ lib/authlogic/controller_adapters/merb_adapter.rb
7
8
  lib/authlogic/controller_adapters/rails_adapter.rb
8
9
  lib/authlogic/session/active_record_trickery.rb
9
10
  lib/authlogic/session/base.rb
10
11
  lib/authlogic/session/callbacks.rb
11
12
  lib/authlogic/session/config.rb
12
13
  lib/authlogic/session/errors.rb
14
+ lib/authlogic/session/scopes.rb
13
15
  lib/authlogic/sha512_crypto_provider.rb
14
16
  lib/authlogic/version.rb
15
17
  lib/authlogic.rb
@@ -17,84 +19,19 @@ Manifest
17
19
  MIT-LICENSE
18
20
  Rakefile
19
21
  README.rdoc
20
- test_app/app/controllers/application.rb
21
- test_app/app/controllers/companies_controller.rb
22
- test_app/app/controllers/user_sessions_controller.rb
23
- test_app/app/controllers/users_controller.rb
24
- test_app/app/helpers/application_helper.rb
25
- test_app/app/helpers/companies_helper.rb
26
- test_app/app/helpers/user_sessions_helper.rb
27
- test_app/app/helpers/users_helper.rb
28
- test_app/app/models/company.rb
29
- test_app/app/models/project.rb
30
- test_app/app/models/user.rb
31
- test_app/app/models/user_session.rb
32
- test_app/app/views/layouts/application.html.erb
33
- test_app/app/views/user_sessions/new.html.erb
34
- test_app/app/views/users/_form.erb
35
- test_app/app/views/users/edit.html.erb
36
- test_app/app/views/users/new.html.erb
37
- test_app/app/views/users/show.html.erb
38
- test_app/config/boot.rb
39
- test_app/config/database.yml
40
- test_app/config/environment.rb
41
- test_app/config/environments/development.rb
42
- test_app/config/environments/production.rb
43
- test_app/config/environments/test.rb
44
- test_app/config/initializers/inflections.rb
45
- test_app/config/initializers/mime_types.rb
46
- test_app/config/initializers/new_rails_defaults.rb
47
- test_app/config/routes.rb
48
- test_app/db/development.sqlite3
49
- test_app/db/migrate/20081023040052_create_users.rb
50
- test_app/db/migrate/20081103003828_create_companies.rb
51
- test_app/db/migrate/20081103003834_create_projects.rb
52
- test_app/db/schema.rb
53
- test_app/db/test.sqlite3
54
- test_app/doc/README_FOR_APP
55
- test_app/public/404.html
56
- test_app/public/422.html
57
- test_app/public/500.html
58
- test_app/public/dispatch.cgi
59
- test_app/public/dispatch.fcgi
60
- test_app/public/dispatch.rb
61
- test_app/public/favicon.ico
62
- test_app/public/images/rails.png
63
- test_app/public/javascripts/application.js
64
- test_app/public/javascripts/controls.js
65
- test_app/public/javascripts/dragdrop.js
66
- test_app/public/javascripts/effects.js
67
- test_app/public/javascripts/prototype.js
68
- test_app/public/robots.txt
69
- test_app/public/stylesheets/scaffold.css
70
- test_app/Rakefile
71
- test_app/README
72
- test_app/script/about
73
- test_app/script/console
74
- test_app/script/dbconsole
75
- test_app/script/destroy
76
- test_app/script/generate
77
- test_app/script/performance/benchmarker
78
- test_app/script/performance/profiler
79
- test_app/script/performance/request
80
- test_app/script/plugin
81
- test_app/script/process/inspector
82
- test_app/script/process/reaper
83
- test_app/script/process/spawner
84
- test_app/script/runner
85
- test_app/script/server
86
- test_app/test/fixtures/companies.yml
87
- test_app/test/fixtures/projects.yml
88
- test_app/test/fixtures/users.yml
89
- test_app/test/functional/companies_controller_test.rb
90
- test_app/test/functional/user_sessions_controller_test.rb
91
- test_app/test/functional/users_controller_test.rb
92
- test_app/test/integration/company_user_session_stories_test.rb
93
- test_app/test/integration/user_sesion_stories_test.rb
94
- test_app/test/integration/user_session_config_test.rb
95
- test_app/test/integration/user_session_test.rb
96
- test_app/test/test_helper.rb
97
- test_app/test/unit/account_test.rb
98
- test_app/test/unit/company_test.rb
99
- test_app/test/unit/project_test.rb
100
- test_app/test/unit/user_test.rb
22
+ test/active_record_acts_as_authentic_test.rb
23
+ test/active_record_authenticates_many_test.rb
24
+ test/fixtures/companies.yml
25
+ test/fixtures/employees.yml
26
+ test/fixtures/projects.yml
27
+ test/fixtures/users.yml
28
+ test/test_helper.rb
29
+ test/user_session_active_record_trickery_test.rb
30
+ test/user_session_base_test.rb
31
+ test/user_session_config_test.rb
32
+ test/user_session_scopes_test.rb
33
+ test_libs/aes128_crypto_provider.rb
34
+ test_libs/mock_controller.rb
35
+ test_libs/mock_cookie_jar.rb
36
+ test_libs/mock_request.rb
37
+ test_libs/ordered_hash.rb
@@ -1,12 +1,12 @@
1
1
  = Authlogic
2
2
 
3
- Authlogic is "rails authentication done right". Put simply, its the Chuck Norris of authentication solutions.
3
+ Authlogic is a framework agnostic object based authentication solution that handles all of the non sense for you. It's as easy as ActiveRecord is with a database. Put simply, its the Chuck Norris of authentication solutions for rails, merb, etc.
4
4
 
5
- The last thing we need is another authentication solution for rails, right? That's what I thought until I tried out some of the current solutions. None of them felt right. They were either too complicated, bloated, littered my application with tons of code, or were just confusing. This is not the simple / elegant rails we all fell in love with. We need a "rails like" authentication solution. Authlogic is my attempt to satisfy that need...
5
+ The last thing we need is another authentication solution, right? That's what I thought until I tried out some of the current solutions in both rails and merb. None of them felt right. They were either too complicated, bloated, littered my application with tons of code, or were just confusing. This is not the simple / elegant ruby we all fell in love with. We need a "ruby like" authentication solution. Authlogic is my attempt to satisfy that need...
6
6
 
7
- 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?
7
+ Let's take a rails application, 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?
8
8
 
9
- 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 in rails.
9
+ 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.
10
10
 
11
11
  What if creating a user session could be as simple as...
12
12
 
@@ -35,8 +35,8 @@ What if your user sessions controller could look just like your other controller
35
35
 
36
36
  Look familiar? If you didn't know any better, you would think UserSession was an ActiveRecord model. I think that's pretty cool, because it fits nicely into the RESTful development pattern, a style we all know and love. What about the view...
37
37
 
38
- <%= error_messages_for "user_session" %>
39
38
  <% form_for @user_session do |f| %>
39
+ <%= f.error_messages %>
40
40
  <%= f.label :login %><br />
41
41
  <%= f.text_field :login %><br />
42
42
  <br />
@@ -63,8 +63,8 @@ Authlogic makes this a reality. This is just the tip of the ice berg. Keep readi
63
63
  == Helpful links
64
64
 
65
65
  * <b>Documentation:</b> http://authlogic.rubyforge.org
66
- * <b>Authlogic setup tutorial:</b> coming soon...
67
- * <b>Live example of the setup tutorial above (with source):</b> coming soon....
66
+ * <b>Authlogic setup tutorial:</b> http://www.binarylogic.com/2008/11/3/tutorial-authlogic-basic-setup
67
+ * <b>Live example of the setup tutorial above (with source):</b> http://authlogic_example.binarylogic.com
68
68
  * <b>Bugs / feature suggestions:</b> http://binarylogic.lighthouseapp.com/projects/18752-authlogic
69
69
 
70
70
  == Install and use
@@ -79,7 +79,39 @@ Or as a plugin
79
79
 
80
80
  script/plugin install git://github.com/binarylogic/authlogic.git
81
81
 
82
- In an effort to keep this readme a reference instead of a step by step guide, I moved the setup tutorial to my blog. See "helpful links" above. If this is your first time using Authlogic checkout the tutorial.
82
+ === Create your session
83
+
84
+ Lets assume you are setting up a session for your User model.
85
+
86
+ Create your user_session.rb file:
87
+
88
+ # app/models/user_session.rb
89
+ class UserSession < Authgasm::Session::Base
90
+ # configuration here, just like ActiveRecord, or in an initializer
91
+ # See Authgasm::Session::Config::ClassMethods for more details
92
+ end
93
+
94
+ === Ensure proper database fields
95
+
96
+ The user model needs to have the following columns. The names of these columns can be changed with configuration. Better yet, Authgasm tries to guess these names by checking for the existence of common names. See Authgasm::Session::Config::ClassMethods for more details, but chances are you won't have to specify any configuration for your field names, even if they aren't the same names as below.
97
+
98
+ t.string :login, :null => false
99
+ t.string :crypted_password, :null => false
100
+ t.string :password_salt, :null => false # not needed if you are encrypting your pw instead of using a hash algorithm
101
+ t.string :remember_token, :null => false
102
+ t.integer :login_count # This is optional, it is a "magic" column, just like "created_at". See below for a list of all magic columns.
103
+
104
+ === Set up your model
105
+
106
+ Make sure you have a model that you will be authenticating with. For this example let's say you have a User model:
107
+
108
+ class User < ActiveRecord::Base
109
+ acts_as_authentic # for options see documentation: Authgasm::ActsAsAuthentic::ClassMethods
110
+ end
111
+
112
+ The options for acts_as_authentic are based on the UserSession configuration. So if you specified configuration for your UserSession model you should not have to specify any options for acts_as_authentic, unless you want them to be different.
113
+
114
+ Done! Now go use it just like you would with any other ActiveRecord model. Either glance at the code at the beginning of this readme or check out the tutorial (see above in "helpful links") for a more detailed walk through.
83
115
 
84
116
  == Magic Columns
85
117
 
@@ -104,7 +136,7 @@ Authlogic tries to check the state of the record before creating the session. If
104
136
 
105
137
  What's neat about this is that these are checked upon any type of login. When logging in explicitly, by cookie, session, or basic http auth. So if you mark a user inactive in the middle of their session they wont be logged back in next time they refresh the page. Giving you complete control.
106
138
 
107
- Need Authlogic to check your own "state"? No problem, check out the hooks section below. Add in a before_validation or after_validation to do your own checking.
139
+ Need Authlogic to check your own "state"? No problem, check out the hooks section below. Add in a before_validation to do your own checking. The sky is the limit.
108
140
 
109
141
  == Hooks / Callbacks
110
142
 
@@ -172,7 +204,7 @@ This scopes your login field validation, so that users are allowed to have the s
172
204
 
173
205
  === Scoping your session
174
206
 
175
- When you session tries to validate it searches for a record. You want to scope that search. No problem...
207
+ When the session tries to validate it searches for a record. You want to scope that search. No problem...
176
208
 
177
209
  The goal of Authlogic was to not try and introduce anything new. As a result I came up with:
178
210
 
@@ -189,9 +221,9 @@ This works just like ActiveRecord, so it should come natural. Here is how you ge
189
221
 
190
222
  === Scoping cookies
191
223
 
192
- What's neat about cookies is that if you use sub domains they automatically scope their self. Meaning if you create a cookie in whatever.yourdomain.com it will not exist in another.yourdomain.com. So if you have the example used above, where you can access accounts via a sub domain, you don't have to do anything.
224
+ What's neat about cookies is that if you use sub domains they automatically scope their self. Meaning if you create a cookie in whatever.yourdomain.com it will not exist in another.yourdomain.com. So if you are using subdomains to scope your users, you don't have to do anything.
193
225
 
194
- But what if you don't want to separate your cookies by subdomains? You can accomplish this by doing:
226
+ But what if you *don't* want to separate your cookies by subdomains? You can accomplish this by doing:
195
227
 
196
228
  ActionController::Base.session_options[:session_domain] = ‘.mydomain.com’
197
229
 
@@ -205,7 +237,7 @@ Now let's look at this from the other angle. What if you are *NOT* using subdoma
205
237
 
206
238
  Done, Authlogic will give each cookie a unique name depending on the account.
207
239
 
208
- With the above information you should be able to scope your sessions any way you want. Just mix and match the tools above to accomplish what you want. Also check out the documentation on Authlogic::ActiveRecord::AuthenticatesMany.
240
+ With the above information you should be able to scope your sessions any way you want. Just mix and match the tools above to accomplish this. Also check out the documentation on Authlogic::ActiveRecord::AuthenticatesMany.
209
241
 
210
242
  == Errors
211
243
 
@@ -250,13 +282,17 @@ Obviously there is a little more to it than this, but hopefully this clarifies a
250
282
 
251
283
  When things come together like this I think its a sign that you are doing something right. Put that in your pipe and smoke it!
252
284
 
253
- == What about [insert framework here]?
285
+ == Framework agnostic (Rails, Merb, etc.)
286
+
287
+ I designed Authlogic to be framework agnostic, meaning it doesn't care what framework you use it in. Right out of the box it supports rails and merb. I have not had the opportunity to use other frameworks, but the only thing stopping Authlogic from being used in other frameworks is a simple adapter. Check out controller_adapters/rails_adapter, or controller_adapters/merb_adapter.
288
+
289
+ Since pretty much all of the frameworks in ruby follow the Rack conventions, the code should be very similar across adapters. You're saying "but Ben, why not just hook into Rack and avoid the need for controller adapters all together?". It's not that simple, because rails doesn't inherit from the Rack::Request class, plus there are small differences between how rack is implemented in each framework. Authlogic has to hook into your controller with a before_filter anyways, so it can "activate" itself. Why not just use the controller object?
254
290
 
255
- As of now, authlogic supports rails right out of the box. But I designed authlogic to be framework agnostic. The only thing stopping Authlogic from being implemented in merb, or any other framework, is a simple adapter. I have not had the opportunity to use Authlogic in anything other than rails. If you want to use this in merb or any other framework take a look at authlogic/controller/rails_adapter.rb.
291
+ The point in all of this rambling is that implementing Authlogic is as simple as creating an adapter. I created both the rails and merb adapters in under 10 minutes. If you have an adapter you created and would like to add please let me know and I will add it into the source.
256
292
 
257
293
  == How it works
258
294
 
259
- Interested in how all of this all works? Basically a before_filter is automatically set in your controller which lets Authlogic know about the current controller object. This "activates" Authlogic and allows Authlogic to set sessions, cookies, login via basic http auth, etc. If you are using rails in a multiple thread environment, don't worry. I kept that in mind and made this thread safe.
295
+ Interested in how all of this all works? Basically a before_filter is automatically set in your controller which lets Authlogic know about the current controller object. This "activates" Authlogic and allows Authlogic to set sessions, cookies, login via basic http auth, etc. If you are using your framework in a multiple thread environment, don't worry. I kept that in mind and made this thread safe.
260
296
 
261
297
  From there it is pretty simple. When you try to create a new session the record is authenticated and then all of the session / cookie magic is done for you. The sky is the limit.
262
298
 
@@ -268,7 +304,7 @@ I don't necessarily think the current solutions are "wrong", nor am I saying Aut
268
304
 
269
305
  === Generators are messy
270
306
 
271
- Generators have their place, and it is not to add authentication to a rails app. It doesn't make sense. Generators are meant to be a starting point for repetitive tasks that have no sustainable pattern. Take controllers, the set up is the same thing over and over, but they eventually evolve to a point where there is no clear cut pattern. Trying to extract a pattern out into a library would be extremely hard, messy, and overly complicated. As a result, generators make sense here.
307
+ Generators have their place, and it is not to add authentication to an app. It doesn't make sense. Generators are meant to be a starting point for repetitive tasks that have no sustainable pattern. Take controllers, the set up is the same thing over and over, but they eventually evolve to a point where there is no clear cut pattern. Trying to extract a pattern out into a library would be extremely hard, messy, and overly complicated. As a result, generators make sense here.
272
308
 
273
309
  Authentication is a one time set up process for your app. It's the same thing over and over and the pattern never really changes. The only time it changes is to conform with newer / stricter security techniques. This is exactly why generators should not be an authentication solution. Generators add code to your application, once code crosses that line, you are responsible for maintaining it. You get to make sure it stays up with the latest and greatest security techniques. And when the plugin you used releases some major update, you can't just re-run the generator, you get to sift through the code to see what changed. You don't really have a choice either, because you can't ignore security updates.
274
310
 
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ Echoe.new 'authlogic' do |p|
8
8
  p.author = "Ben Johnson of Binary Logic"
9
9
  p.email = 'bjohnson@binarylogic.com'
10
10
  p.project = 'authlogic'
11
- p.summary = "Rails authentication done right"
11
+ p.summary = "Framework agnostic object based authentication solution that handles all of the non sense for you. It's as easy as ActiveRecord is with a database."
12
12
  p.url = "http://github.com/binarylogic/authlogic"
13
13
  p.dependencies = %w(activesupport activerecord)
14
14
  p.include_rakefile = true
@@ -1,21 +1,22 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{authlogic}
3
- s.version = "0.10.4"
3
+ s.version = "1.0.0"
4
4
 
5
5
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
6
6
  s.authors = ["Ben Johnson of Binary Logic"]
7
- s.date = %q{2008-11-03}
8
- s.description = %q{Rails authentication done right}
7
+ s.date = %q{2008-11-05}
8
+ s.description = %q{Framework agnostic object based authentication solution that handles all of the non sense for you. It's as easy as ActiveRecord is with a database.}
9
9
  s.email = %q{bjohnson@binarylogic.com}
10
- s.extra_rdoc_files = ["CHANGELOG.rdoc", "lib/authlogic/active_record/acts_as_authentic.rb", "lib/authlogic/active_record/authenticates_many.rb", "lib/authlogic/active_record/scoped_session.rb", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/sha512_crypto_provider.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "README.rdoc"]
11
- s.files = ["CHANGELOG.rdoc", "init.rb", "lib/authlogic/active_record/acts_as_authentic.rb", "lib/authlogic/active_record/authenticates_many.rb", "lib/authlogic/active_record/scoped_session.rb", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/sha512_crypto_provider.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "Manifest", "MIT-LICENSE", "Rakefile", "README.rdoc", "test_app/app/controllers/application.rb", "test_app/app/controllers/companies_controller.rb", "test_app/app/controllers/user_sessions_controller.rb", "test_app/app/controllers/users_controller.rb", "test_app/app/helpers/application_helper.rb", "test_app/app/helpers/companies_helper.rb", "test_app/app/helpers/user_sessions_helper.rb", "test_app/app/helpers/users_helper.rb", "test_app/app/models/company.rb", "test_app/app/models/project.rb", "test_app/app/models/user.rb", "test_app/app/models/user_session.rb", "test_app/app/views/layouts/application.html.erb", "test_app/app/views/user_sessions/new.html.erb", "test_app/app/views/users/_form.erb", "test_app/app/views/users/edit.html.erb", "test_app/app/views/users/new.html.erb", "test_app/app/views/users/show.html.erb", "test_app/config/boot.rb", "test_app/config/database.yml", "test_app/config/environment.rb", "test_app/config/environments/development.rb", "test_app/config/environments/production.rb", "test_app/config/environments/test.rb", "test_app/config/initializers/inflections.rb", "test_app/config/initializers/mime_types.rb", "test_app/config/initializers/new_rails_defaults.rb", "test_app/config/routes.rb", "test_app/db/development.sqlite3", "test_app/db/migrate/20081023040052_create_users.rb", "test_app/db/migrate/20081103003828_create_companies.rb", "test_app/db/migrate/20081103003834_create_projects.rb", "test_app/db/schema.rb", "test_app/db/test.sqlite3", "test_app/doc/README_FOR_APP", "test_app/public/404.html", "test_app/public/422.html", "test_app/public/500.html", "test_app/public/dispatch.cgi", "test_app/public/dispatch.fcgi", "test_app/public/dispatch.rb", "test_app/public/favicon.ico", "test_app/public/images/rails.png", "test_app/public/javascripts/application.js", "test_app/public/javascripts/controls.js", "test_app/public/javascripts/dragdrop.js", "test_app/public/javascripts/effects.js", "test_app/public/javascripts/prototype.js", "test_app/public/robots.txt", "test_app/public/stylesheets/scaffold.css", "test_app/Rakefile", "test_app/README", "test_app/script/about", "test_app/script/console", "test_app/script/dbconsole", "test_app/script/destroy", "test_app/script/generate", "test_app/script/performance/benchmarker", "test_app/script/performance/profiler", "test_app/script/performance/request", "test_app/script/plugin", "test_app/script/process/inspector", "test_app/script/process/reaper", "test_app/script/process/spawner", "test_app/script/runner", "test_app/script/server", "test_app/test/fixtures/companies.yml", "test_app/test/fixtures/projects.yml", "test_app/test/fixtures/users.yml", "test_app/test/functional/companies_controller_test.rb", "test_app/test/functional/user_sessions_controller_test.rb", "test_app/test/functional/users_controller_test.rb", "test_app/test/integration/company_user_session_stories_test.rb", "test_app/test/integration/user_sesion_stories_test.rb", "test_app/test/integration/user_session_config_test.rb", "test_app/test/integration/user_session_test.rb", "test_app/test/test_helper.rb", "test_app/test/unit/account_test.rb", "test_app/test/unit/company_test.rb", "test_app/test/unit/project_test.rb", "test_app/test/unit/user_test.rb", "authlogic.gemspec"]
10
+ s.extra_rdoc_files = ["CHANGELOG.rdoc", "lib/authlogic/active_record/acts_as_authentic.rb", "lib/authlogic/active_record/authenticates_many.rb", "lib/authlogic/active_record/scoped_session.rb", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/merb_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/session/scopes.rb", "lib/authlogic/sha512_crypto_provider.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "README.rdoc"]
11
+ s.files = ["CHANGELOG.rdoc", "init.rb", "lib/authlogic/active_record/acts_as_authentic.rb", "lib/authlogic/active_record/authenticates_many.rb", "lib/authlogic/active_record/scoped_session.rb", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/merb_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/session/scopes.rb", "lib/authlogic/sha512_crypto_provider.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "Manifest", "MIT-LICENSE", "Rakefile", "README.rdoc", "test/active_record_acts_as_authentic_test.rb", "test/active_record_authenticates_many_test.rb", "test/fixtures/companies.yml", "test/fixtures/employees.yml", "test/fixtures/projects.yml", "test/fixtures/users.yml", "test/test_helper.rb", "test/user_session_active_record_trickery_test.rb", "test/user_session_base_test.rb", "test/user_session_config_test.rb", "test/user_session_scopes_test.rb", "test_libs/aes128_crypto_provider.rb", "test_libs/mock_controller.rb", "test_libs/mock_cookie_jar.rb", "test_libs/mock_request.rb", "test_libs/ordered_hash.rb", "authlogic.gemspec"]
12
12
  s.has_rdoc = true
13
13
  s.homepage = %q{http://github.com/binarylogic/authlogic}
14
14
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Authlogic", "--main", "README.rdoc"]
15
15
  s.require_paths = ["lib"]
16
16
  s.rubyforge_project = %q{authlogic}
17
17
  s.rubygems_version = %q{1.2.0}
18
- s.summary = %q{Rails authentication done right}
18
+ s.summary = %q{Framework agnostic object based authentication solution that handles all of the non sense for you. It's as easy as ActiveRecord is with a database.}
19
+ s.test_files = ["test/active_record_acts_as_authentic_test.rb", "test/active_record_authenticates_many_test.rb", "test/test_helper.rb", "test/user_session_active_record_trickery_test.rb", "test/user_session_base_test.rb", "test/user_session_config_test.rb", "test/user_session_scopes_test.rb"]
19
20
 
20
21
  if s.respond_to? :specification_version then
21
22
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -1,7 +1,10 @@
1
+ require "active_support"
2
+
1
3
  require File.dirname(__FILE__) + "/authlogic/version"
2
4
 
3
5
  require File.dirname(__FILE__) + "/authlogic/controller_adapters/abstract_adapter"
4
6
  require File.dirname(__FILE__) + "/authlogic/controller_adapters/rails_adapter" if defined?(Rails)
7
+ require File.dirname(__FILE__) + "/authlogic/controller_adapters/merb_adapter" if defined?(Merb)
5
8
 
6
9
  require File.dirname(__FILE__) + "/authlogic/sha512_crypto_provider"
7
10
 
@@ -13,6 +16,7 @@ require File.dirname(__FILE__) + "/authlogic/session/active_record_trickery"
13
16
  require File.dirname(__FILE__) + "/authlogic/session/callbacks"
14
17
  require File.dirname(__FILE__) + "/authlogic/session/config"
15
18
  require File.dirname(__FILE__) + "/authlogic/session/errors"
19
+ require File.dirname(__FILE__) + "/authlogic/session/scopes"
16
20
  require File.dirname(__FILE__) + "/authlogic/session/base"
17
21
 
18
22
  module Authlogic
@@ -20,6 +24,7 @@ module Authlogic
20
24
  class Base
21
25
  include ActiveRecordTrickery
22
26
  include Callbacks
27
+ include Scopes
23
28
  end
24
29
  end
25
30
  end
@@ -1,15 +1,9 @@
1
1
  module Authlogic
2
2
  module ActiveRecord # :nodoc:
3
3
  # = Acts As Authentic
4
- # Provides and "acts_as" method to include in your models to help with authentication. See method below.
4
+ # Provides the acts_as_authentic method to include in your models to help with authentication. See method below.
5
5
  module ActsAsAuthentic
6
- # Call this method in your model to add in basic authentication madness that your authlogic session expects.
7
- #
8
- # <b>Please keep in mind</b> that based on your configuration the method names could change. For example, if you pass the option:
9
- #
10
- # :password_field => :pass
11
- #
12
- # The method will not be password=, it will be pass=. Same with valid_password?, it will be valid_pass?, etc.
6
+ # Call this method in your model to add in basic authentication madness that your authlogic session expects.
13
7
  #
14
8
  # === Methods
15
9
  # For example purposes lets assume you have a User model.
@@ -32,21 +26,71 @@ module Authlogic
32
26
  # user.forget! Changes their remember token, making their cookie and session invalid. A way to log the user out withouth changing their password.
33
27
  #
34
28
  # === Options
35
- # * <tt>session_class:</tt> default: "#{name}Session", the related session class. Used so that you don't have to repeat yourself here. A lot of the configuration will be based off of the configuration values of this class.
36
- # * <tt>crypto_provider:</tt> default: Authlogic::Sha512CryptoProvider, class that provides Sha512 encryption. What ultimately encrypts your password.
37
- # * <tt>crypto_provider_type:</tt> default: options[:crypto_provider].respond_to?(:decrypt) ? :encryption : :hash. You can explicitly set this if you wish. Since encryptions and hashes are handled different this is the flag Authlogic uses.
38
- # * <tt>login_field:</tt> default: options[:session_class].login_field, the name of the field used for logging in
39
- # * <tt>login_field_type:</tt> default: options[:login_field] == :email ? :email : :login, tells authlogic how to validation the field, what regex to use, etc.
40
- # * <tt>password_field:</tt> default: options[:session_class].password_field, the name of the field to set the password, *NOT* the field the encrypted password is stored
41
- # * <tt>crypted_password_field:</tt> default: depends on which columns are present, checks: crypted_password, encrypted_password, password_hash, pw_hash, if none are present defaults to crypted_password. This is the name of column that your encrypted password is stored.
42
- # * <tt>password_salt_field:</tt> default: depends on which columns are present, checks: password_salt, pw_salt, salt, if none are present defaults to password_salt. This is the name of the field your salt is stored, only relevant for a hash crypto provider.
43
- # * <tt>remember_token_field:</tt> default: options[:session_class].remember_token_field, the name of the field your remember token is stored. What the cookie stores so the session can be "remembered"
44
- # * <tt>scope:</tt> default: nil, if all of your users belong to an account you might want to scope everything to the account. Just pass :account_id
45
- # * <tt>logged_in_timeout:</tt> default: 10.minutes, this allows you to specify a time the determines if a user is logged in or out. Useful if you want to count how many users are currently logged in.
46
- # * <tt>session_ids:</tt> default: [nil], the sessions that we want to automatically reset when a user is created or updated so you don't have to worry about this. Set to [] to disable. Should be an array of ids. See Authlogic::Session::Base#initialize for information on ids. The order is important. The first id should be your main session, the session they need to log into first. This is generally nil, meaning so explicitly set id.
29
+ #
30
+ # * <tt>session_class:</tt> default: "#{name}Session",
31
+ # This is the related session class. A lot of the configuration will be based off of the configuration values of this class.
32
+ #
33
+ # * <tt>crypto_provider:</tt> default: Authlogic::Sha512CryptoProvider,
34
+ # This is the class that provides your encryption. By default Authlogic provides its own crypto provider that uses Sha512 encrypton.
35
+ #
36
+ # * <tt>login_field:</tt> default: options[:session_class].login_field,
37
+ # The name of the field used for logging in, this is guess based on what columns are in your db. Only specify if you aren't using:
38
+ # login, username, or email
39
+ #
40
+ # * <tt>login_field_type:</tt> default: options[:login_field] == :email ? :email : :login,
41
+ # Tells authlogic how to validation the field, what regex to use, etc. If the field name is email it will automatically use email,
42
+ # otherwise it uses login.
43
+ #
44
+ # * <tt>login_field_regex:</tt> default: if email then typical email regex, otherwise typical login regex.
45
+ # This is used in validates_format_of for the login_field.
46
+ #
47
+ # * <tt>login_field_regex_message:</tt> the message to use when the validates_format_of for the login field fails.
48
+ #
49
+ # * <tt>password_field:</tt> default: options[:session_class].password_field,
50
+ # This is the name of the field to set the password, *NOT* the field the encrypted password is stored.
51
+ #
52
+ # * <tt>crypted_password_field:</tt> default: depends on which columns are present,
53
+ # The name of the database field where your encrypted password is stored. If the name of the field is different from any of the following
54
+ # you need to specify it with this option: crypted_password, encrypted_password, password_hash, pw_hash
55
+ #
56
+ # * <tt>password_salt_field:</tt> default: depends on which columns are present,
57
+ # This is the name of the field in your database that stores your password salt. If the name of the field is different from any of the
58
+ # following then you need to specify it with this option: password_salt, pw_salt, salt
59
+ #
60
+ # * <tt>remember_token_field:</tt> default: options[:session_class].remember_token_field,
61
+ # This is the name of the field your remember_token is stored. The remember token is a unique token that is stored in the users cookie and
62
+ # session. This way you have complete control of when session expire and you don't have to change passwords to expire sessions. This also
63
+ # ensures that stale sessions can not be persisted. By stale, I mean sessions that are logged in using an outdated password. If the name
64
+ # of the field is anything other than the following you need to specify it with this option: remember_token, remember_key, cookie_token,
65
+ # cookie_key
66
+ #
67
+ # * <tt>scope:</tt> default: nil,
68
+ # This scopes validations. If all of your users belong to an account you might want to scope everything to the account. Just pass :account_id
69
+ #
70
+ # * <tt>logged_in_timeout:</tt> default: 10.minutes,
71
+ # This is really just a nifty feature to tell if a user is logged in or not. It's based on activity. So if the user in inactive longer than
72
+ # the value you pass here they are assumed "logged out".
73
+ #
74
+ # * <tt>session_ids:</tt> default: [nil],
75
+ # The sessions that we want to automatically reset when a user is created or updated so you don't have to worry about this. Set to [] to disable.
76
+ # Should be an array of ids. See the Authlogic::Session documentation for information on ids. The order is important.
77
+ # The first id should be your main session, the session they need to log into first. This is generally nil. When you don't specify an id
78
+ # in your session you are really just inexplicitly saying you want to use the id of nil.
47
79
  def acts_as_authentic(options = {})
80
+ # If we don't have a database, skip all of this, solves initial setup errors
81
+ begin
82
+ column_names
83
+ rescue Exception
84
+ return
85
+ end
86
+
48
87
  # Setup default options
49
- options[:session_class] ||= "#{name}Session".constantize
88
+ begin
89
+ options[:session_class] ||= "#{name}Session".constantize
90
+ rescue NameError
91
+ raise NameError.new("You must create a #{name}Session class before a model can act_as_authentic. If that is not the name of the class pass the class constant via the :session_class option.")
92
+ end
93
+
50
94
  options[:crypto_provider] ||= Sha512CryptoProvider
51
95
  options[:crypto_provider_type] ||= options[:crypto_provider].respond_to?(:decrypt) ? :encryption : :hash
52
96
  options[:login_field] ||= options[:session_class].login_field
@@ -74,11 +118,14 @@ module Authlogic
74
118
  email_name_regex = '[\w\.%\+\-]+'
75
119
  domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
76
120
  domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'
77
- email_regex = /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
78
- validates_format_of options[:login_field], :with => email_regex, :message => "should look like an email address."
121
+ options[:login_field_regex] ||= /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
122
+ options[:login_field_regex_message] ||= "should look like an email address."
123
+ validates_format_of options[:login_field], :with => options[:login_field_regex], :message => options[:login_field_regex_message]
79
124
  else
80
125
  validates_length_of options[:login_field], :within => 2..100
81
- validates_format_of options[:login_field], :with => /\A\w[\w\.\-_@]+\z/, :message => "use only letters, numbers, and .-_@ please."
126
+ options[:login_field_regex] ||= /\A\w[\w\.\-_@ ]+\z/
127
+ options[:login_field_regex_message] ||= "use only letters, numbers, spaces, and .-_@ please."
128
+ validates_format_of options[:login_field], :with => options[:login_field_regex], :message => options[:login_field_regex_message]
82
129
  end
83
130
 
84
131
  validates_uniqueness_of options[:login_field], :scope => options[:scope]
@@ -88,7 +135,7 @@ module Authlogic
88
135
 
89
136
  if column_names.include?("last_request_at")
90
137
  named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", options[:logged_in_timeout].ago]} }
91
- named_scope :logged_out, lambda { {:conditions => ["last_request_at <= ?", options[:logged_in_timeout].ago]} }
138
+ named_scope :logged_out, lambda { {:conditions => ["last_request_at is NULL or last_request_at <= ?", options[:logged_in_timeout].ago]} }
92
139
  end
93
140
 
94
141
  before_save :get_session_information, :if => :update_sessions?
@@ -101,7 +148,8 @@ module Authlogic
101
148
  # Class methods
102
149
  class_eval <<-"end_eval", __FILE__, __LINE__
103
150
  def self.unique_token
104
- crypto_provider.encrypt(Time.now.to_s + (1..10).collect{ rand.to_s }.join)
151
+ # Force using the Sha512 because all that we are doing is creating a unique token, a hash is perfect for this
152
+ Authlogic::Sha512CryptoProvider.encrypt(Time.now.to_s + (1..10).collect{ rand.to_s }.join)
105
153
  end
106
154
 
107
155
  def self.crypto_provider
@@ -129,39 +177,23 @@ module Authlogic
129
177
  end_eval
130
178
  end
131
179
 
132
- case options[:crypto_provider_type]
133
- when :hash
134
- class_eval <<-"end_eval", __FILE__, __LINE__
135
- def #{options[:password_field]}=(pass)
136
- return if pass.blank?
137
- self.tried_to_set_#{options[:password_field]} = true
138
- @#{options[:password_field]} = pass
139
- self.#{options[:remember_token_field]} = self.class.unique_token
140
- self.#{options[:password_salt_field]} = self.class.unique_token
141
- self.#{options[:crypted_password_field]} = crypto_provider.encrypt(@#{options[:password_field]} + #{options[:password_salt_field]})
142
- end
143
-
144
- def valid_#{options[:password_field]}?(attempted_password)
145
- return false if attempted_password.blank?
146
- attempted_password == #{options[:crypted_password_field]} || #{options[:crypted_password_field]} == crypto_provider.encrypt(attempted_password + #{options[:password_salt_field]})
147
- end
148
- end_eval
149
- when :encryption
150
- class_eval <<-"end_eval", __FILE__, __LINE__
151
- def #{options[:password_field]}=(pass)
152
- return if pass.blank?
153
- self.tried_to_set_#{options[:password_field]} = true
154
- @#{options[:password_field]} = pass
155
- self.#{options[:remember_token_field]} = self.class.unique_token
156
- self.#{options[:crypted_password_field]} = crypto_provider.encrypt(@#{options[:password_field]})
157
- end
180
+ class_eval <<-"end_eval", __FILE__, __LINE__
181
+ def #{options[:password_field]}=(pass)
182
+ return if pass.blank?
183
+ self.tried_to_set_#{options[:password_field]} = true
184
+ @#{options[:password_field]} = pass
185
+ self.#{options[:remember_token_field]} = self.class.unique_token
186
+ self.#{options[:password_salt_field]} = self.class.unique_token
187
+ self.#{options[:crypted_password_field]} = crypto_provider.encrypt(@#{options[:password_field]} + #{options[:password_salt_field]})
188
+ end
158
189
 
159
- def valid_#{options[:password_field]}?(attemtped_password)
160
- return false if attempted_password.blank?
161
- attempted_password == #{options[:crypted_password_field]} || #{options[:crypted_password_field]} = crypto_provider.decrypt(attempted_password)
162
- end
163
- end_eval
164
- end
190
+ def valid_#{options[:password_field]}?(attempted_password)
191
+ return false if attempted_password.blank? || #{options[:crypted_password_field]}.blank? || #{options[:password_salt_field]}.blank?
192
+ attempted_password == #{options[:crypted_password_field]} ||
193
+ (crypto_provider.respond_to?(:decrypt) && crypto_provider.decrypt(#{options[:crypted_password_field]}) == attempted_password + #{options[:password_salt_field]}) ||
194
+ (!crypto_provider.respond_to?(:decrypt) && crypto_provider.encrypt(attempted_password + #{options[:password_salt_field]}) == #{options[:crypted_password_field]})
195
+ end
196
+ end_eval
165
197
 
166
198
  class_eval <<-"end_eval", __FILE__, __LINE__
167
199
  def #{options[:password_field]}; end