kent-authlogic_rpx 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Kent Fenwick
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,535 @@
1
+ = Authlogic RPX
2
+
3
+ == Purpose
4
+
5
+ Authlogic RPX is an Authlogic extension library that provides support for authentication using the RPX multi-authentication service offered by JanRain. To use RPX, you must first register your application at {RPX}[http://rpxnow.com/]. A free "Basic" account is available, in addition to paid enhanced versions. All work with Authlogic_RPX.
6
+
7
+ Key features and capabilities:
8
+ * Auto-registration by default following RPX authentication (can be disabled if required)
9
+ * Can allow users to enable RPX authentication for their existing password-enabled accounts
10
+ * View helpers to assist with inserting login fragments in pages
11
+ * Can co-exist with standard password authentication
12
+
13
+
14
+ == Authlogic_RPX References
15
+
16
+ * <b>Authlogic_RPX gem repo:</b> [http://github.com/tardate/authlogic_rpx]
17
+ * <b>Authlogic_RPX issues and feedback:</b> [http://github.com/tardate/authlogic_rpx/issues]
18
+
19
+ The demonstration Rails application is where you can see Authlogic_RPX in action:
20
+
21
+ * <b>Live Demonstration Site:</b> [http://rails-authlogic-rpx-sample.heroku.com]
22
+ * <b>Demonstration site source repository:</b> [http://github.com/tardate/rails-authlogic-rpx-sample]
23
+
24
+ == Authlogic and RPX References
25
+
26
+ * <b>Authlogic documentation:</b> [http://rdoc.info/projects/binarylogic/authlogic]
27
+ * <b>Authlogic repo:</b> [http://github.com/binarylogic/authlogic]
28
+ * <b>RPX documentation:</b> [https://rpxnow.com/docs]
29
+ * <b>RPX_now gem repo:</b> [http://github.com/grosser/rpx_now]
30
+
31
+
32
+ == Installing Authlogic RPX gem
33
+
34
+ Three gems are required: authlogic, grosser-rpx_now, and authlogic_rpx. Install these as appropriate to your environment and preferences.
35
+
36
+ Currently tested versions:
37
+ * authlogic 2.1.2,2.1.1
38
+ * rpx_now 0.6.6
39
+ * authlogic_rpx 1.0.4
40
+
41
+
42
+ === 1. Direct gem installation
43
+
44
+ sudo gem install authlogic
45
+ sudo gem install rpx_now --source http://gemcutter.org
46
+ sudo gem install authlogic_rpx --source http://gemcutter.org
47
+
48
+
49
+ === 2. Using Rails config.gems
50
+
51
+ Include in config/environment.rb:
52
+
53
+ config.gem 'authlogic', :version => '>= 2.1.1'
54
+ config.gem 'rpx_now', :version => '>= 0.6.6', :source => 'http://gemcutter.org'
55
+ config.gem 'authlogic_rpx', :version => '>= 1.0.4', :source => 'http://gemcutter.org'
56
+
57
+ Then to install, run from the command line:
58
+
59
+ sudo rake gems:install
60
+
61
+
62
+ === 3. Using .gems file (e.g for heroku.com deployments)
63
+
64
+ Include in RAILS_ROOT/.gems:
65
+
66
+ authlogic --version '>= 2.1.1'
67
+ rpx_now --version '>= 0.6.6' --source gemcutter.org
68
+ authlogic_rpx --version '>= 1.0.4' --source gemcutter.org
69
+
70
+
71
+ == Using Authlogic RPX
72
+
73
+ <i>Note: in what follows, the user model is called User and the session controller takes the name UserSession (the authlogic convention). You are not restricted to these names - could be Member and MemberSession for example - but for simplicity, this documentation will stick to using the "User" convention.</i>
74
+
75
+ Using Authlogic RPX is very similar to using standard authlogic, with the addition of just a few configuration options. So if you already have a project setup with authlogic, adding RPX support will be trivial.
76
+
77
+ An important capability to be aware of is "auto registration". This means that when a user has logged in with RPX, if an account does not already exist in your application, it will be automatically created. That is, there is no separate/special "register" step for users to go through before just signing in. You can disable this if you need, but for most sites that use RPX as a primary authentication mechanism, this is probably what you want to happen.
78
+
79
+ The main steps for enabling Authlogic RPX:
80
+ * 1. Enable RPX for your user model
81
+ * 2. Add RPX configuration for the Authlogic session model
82
+ * 3. Add custom user profile mapping (optional)
83
+ * 4. Add application controller helpers: current_user, current_user_session
84
+ * 5. Setup the Authlogic session controller
85
+ * 6. Setup the Authlogic user controller
86
+ * 7. Use view helpers to provide login links
87
+ * 8. Allow users to "Add RPX" to existing accounts (optional)
88
+
89
+
90
+ === 1. Enable RPX for your user model
91
+
92
+ The user model requires an additional field called "rpx_identifier". Creat a migration to add this.
93
+ You may need to remove database constraints on other fields if they will be unused in the RPX case (e.g. crypted_password and password_salt to make password authentication optional).
94
+
95
+ If you are using auto-registration, you must also remove any database constraints for fields that will be automatically mapped (see notes in "3. Add custom user profile mapping during auto-registration")
96
+
97
+ class AddUsersRpxIdentifier < ActiveRecord::Migration
98
+ def self.up
99
+ add_column :users, :rpx_identifier, :string
100
+ add_index :users, :rpx_identifier
101
+
102
+ change_column :users, :crypted_password, :string, :default => nil, :null => true
103
+ change_column :users, :password_salt, :string, :default => nil, :null => true
104
+
105
+ end
106
+
107
+ def self.down
108
+ remove_column :users, :rpx_identifier
109
+
110
+ [:crypted_password, :password_salt].each do |field|
111
+ User.all(:conditions => "#{field} is NULL").each { |user| user.update_attribute(field, "") if user.send(field).nil? }
112
+ change_column :users, field, :string, :default => "", :null => false
113
+ end
114
+ end
115
+ end
116
+
117
+ The user model then needs to be tagged with "acts_as_authentic", and you must add rpx_identifier to the attr_accessible configuration (if you are using it)
118
+
119
+ class User < ActiveRecord::Base
120
+ acts_as_authentic do |c|
121
+ c.my_config_option = my_value # for available options see documentation in: Authlogic::ActsAsAuthentic
122
+ end # block optional
123
+ attr_accessible :username, :email, :password, :password_confirmation, :rpx_identifier
124
+ end
125
+
126
+ {See the source for the sample user.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/models/user.rb].
127
+
128
+
129
+ === 2. Add RPX configuration for the Authlogic session model
130
+
131
+ Authlogic provides a helper to create the session model:
132
+
133
+ script/generate session user_session
134
+
135
+ The minimum configuration required is to add your RPX_API_KEY:
136
+
137
+ class UserSession < Authlogic::Session::Base
138
+ rpx_key RPX_API_KEY
139
+ end
140
+
141
+ Get an API key by registering your application at {RPX}[http://rpxnow.com/]. A free "Basic" account is available, in addition to paid enhanced versions. All work with Authlogic_RPX.
142
+
143
+ You probably don't want to put your API key in directly. A recommended approach is to set the key as an environment variable, and then set it as a constant in config/environment.rb:
144
+
145
+ RPX_API_KEY = ENV['RPX_API_KEY']
146
+
147
+ Two additional RPX-specific session configuration options are available.
148
+ * auto_register: enable/disable user auto-registration (enabled by default)
149
+ * rpx_extended_info: enable/disable extended profile information in the RPX authentication (disabled by default)
150
+
151
+ For example, to disable auto-registration and enable extended info:
152
+
153
+ class UserSession < Authlogic::Session::Base
154
+ rpx_key RPX_API_KEY
155
+ auto_register false
156
+ rpx_extended_info
157
+ end
158
+
159
+ {See the source for the sample user_session.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/models/user_session.rb].
160
+
161
+ === 3. Add custom user profile mapping (optional)
162
+
163
+ Authlogic_rpx provides three hooks for mapping information from the RPX profile into your application's user model:
164
+
165
+ * map_rpx_data: user profile mapping during auto-registration
166
+ * map_rpx_data_each_login: user profile mapping during login
167
+ * map_added_rpx_data: user profile mapping when adding RPX to an existing account
168
+
169
+ See https://rpxnow.com/docs#profile_data for the definition of available attributes in the RPX profile.
170
+
171
+ === 3a. map_rpx_data: user profile mapping during auto-registration
172
+
173
+ When users auto-register, profile data from RPX is available to be inserted in the user's record on your site. By default, authlogic_rpx will map the username and email fields.
174
+
175
+ If you have other fields you want to map, you can provide your own implementation of the map_rpx_data method in the UserSession model. In that method, you will be updating the "self.attempted_record" object, with information from the "@rpx_data" object. See the {RPX documentation}[https://rpxnow.com/docs#profile_data] to find out about the set of information that is available.
176
+
177
+ class UserSession < Authlogic::Session::Base
178
+ rpx_key RPX_API_KEY
179
+ rpx_extended_info
180
+
181
+ private
182
+
183
+ # map_rpx_data maps additional fields from the RPX response into the user object
184
+ # override this in your session controller to change the field mapping
185
+ # see https://rpxnow.com/docs#profile_data for the definition of available attributes
186
+ #
187
+ def map_rpx_data
188
+ # map core profile data using authlogic indirect column names
189
+ self.attempted_record.send("#{klass.login_field}=", @rpx_data['profile']['preferredUsername'] ) if attempted_record.send(klass.login_field).blank?
190
+ self.attempted_record.send("#{klass.email_field}=", @rpx_data['profile']['email'] ) if attempted_record.send(klass.email_field).blank?
191
+
192
+ # map some other columns explicitly
193
+ self.attempted_record.fullname = @rpx_data['profile']['displayName'] if attempted_record.fullname.blank?
194
+
195
+ if rpx_extended_info?
196
+ # map some extended attributes
197
+ end
198
+ end
199
+
200
+ end
201
+
202
+ {See the source for the sample user_session.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/models/user_session.rb].
203
+
204
+ WARNING: if you are using auto-registration, any fields you map should NOT have constraints enforced at the database level.
205
+ Authlogic_rpx will optimistically attempt to save the user record during registration, and violating a database constraint will cause the authentication/registration to fail.
206
+
207
+ You can/should enforce any required validations at the model level e.g.
208
+
209
+ validates_uniqueness_of :username, :case_sensitive => false
210
+
211
+ This will allow the auto-registration to proceed, and the user can be given a chance to rectify the validation errors on your user profile page.
212
+
213
+ If it is not acceptable in your application to have user records created with potential validation errors in auto-populated fields, you will need to override map_rpx_data and implement whatever special handling makes sense in your case. For example:
214
+
215
+ * directly check for uniqueness and other validation requirements
216
+ * automatically "uniquify" certain fields like username
217
+ * save conflicting profile information to "pending user review" columns or a seperate table
218
+
219
+
220
+ ==== 3b. map_rpx_data_each_login: user profile mapping during login
221
+
222
+ map_rpx_data_each_login provides a hook to allow you to map RPX profile information every time the user logs in.
223
+
224
+ By default, nothing is mapped. If you have other fields you want to map, you can provide your own implementation of the map_rpx_data_each_login method in the UserSession model.
225
+
226
+ This would mainly be used to update relatively volatile information that you are maintaining in the user model (such as profile image url)
227
+
228
+ In the map_rpx_data_each_login procedure, you will be writing to fields of the "self.attempted_record" object, pulling data from the @rpx_data object. For example:
229
+
230
+ def map_rpx_data_each_login
231
+ # we'll always update photo_url
232
+ self.attempted_record.photo_url = @rpx_data['profile']['photo']
233
+ end
234
+
235
+ {See the source for the sample user_session.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/models/user_session.rb].
236
+
237
+
238
+ ==== 3c. map_added_rpx_data: user profile mapping when adding RPX to an existing account
239
+
240
+ map_added_rpx_data maps additional fields from the RPX response into the user object during the "add RPX to existing account" process.
241
+
242
+ By default, it only maps the rpx_identifier field. If you have other fields you want to map, you can provide your own implementation of the map_added_rpx_data method in the User model (NOT UserSession, unlike for map_rpx_data and map_rpx_data_each_login).
243
+
244
+ NB: If you override this method, you will be responsible for also mapping the rpx_identifier.
245
+
246
+ In the map_added_rpx_data procedure, you will be writing to fields of the "self" object, pulling data from the rpx_data parameter. For example:
247
+
248
+ def map_added_rpx_data( rpx_data )
249
+ self.rpx_identifier = rpx_data['profile']['identifier']
250
+
251
+ # map some additional fields, e.g. photo_url
252
+ self.photo_url = rpx_data['profile']['photo'] if photo_url.blank?
253
+ end
254
+
255
+ {See the source for the sample user.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/models/user.rb].
256
+
257
+
258
+ === 4. Add application controller helpers: current_user, current_user_session
259
+
260
+ We'll add current_user and current_user_session helpers. These can then be used in controllers and views to get a handle on the "current" logged in user.
261
+
262
+ class ApplicationController < ActionController::Base
263
+ helper :all # include all helpers, all the time
264
+ protect_from_forgery # See ActionController::RequestForgeryProtection for details
265
+
266
+ # Scrub sensitive parameters from your log
267
+ filter_parameter_logging :password, :password_confirmation
268
+
269
+ helper_method :current_user, :current_user_session
270
+
271
+ private
272
+
273
+ def current_user_session
274
+ return @current_user_session if defined?(@current_user_session)
275
+ @current_user_session = UserSession.find
276
+ end
277
+
278
+ def current_user
279
+ return @current_user if defined?(@current_user)
280
+ @current_user = current_user_session && current_user_session.record
281
+ end
282
+ end
283
+
284
+ {See the source for the sample user_session_controller.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/controllers/application_controller.rb].
285
+
286
+
287
+ === 5. Setup the Authlogic session controller
288
+
289
+ If you don't already have a user session controller, create one. There are four actions of significance for authlogic_rpx:
290
+
291
+ $ script/generate controller user_sessions index new create destroy
292
+
293
+ {See the source for the sample user_session_controller.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/controllers/user_sessions_controller.rb].
294
+
295
+ In config/routes.rb we can define the standard routes for this controller and two named routes for the main login/out (or singin/out if you prefer that terminology):
296
+
297
+ map.signin "signin", :controller => "user_sessions", :action => "new"
298
+ map.signout "signout", :controller => "user_sessions", :action => "destroy"
299
+ map.resources :user_sessions
300
+
301
+ ==== index
302
+ This is where RPX will return to if the user cancelled the login process, so it needs to be handled. You probably just want to redirect the user to an appropriate alternative:
303
+
304
+ def index
305
+ redirect_to current_user ? root_url : new_user_session_url
306
+ end
307
+
308
+ ==== new
309
+ Typically used to render a login form
310
+
311
+ def new
312
+ @user_session = UserSession.new
313
+ end
314
+
315
+ ==== create
316
+ This is where the magic happens for authentication. Authlogic hides all the underlying wiring, and you just need to "save" the session!
317
+
318
+ Authlogic_rpx provides two additional methods that you might want to use to tailor you application behaviour:
319
+ * new_registration? - if a new registration, e.g. force them to go via a registration follow-up page
320
+ * registration_complete? - if registration details not complete, e.g. bounce the user over the profile editing page
321
+
322
+ def create
323
+ @user_session = UserSession.new(params[:user_session])
324
+ if @user_session.save
325
+ if @user_session.new_registration?
326
+ flash[:notice] = "Welcome! As a new user, please review your registration details before continuing.."
327
+ redirect_to edit_user_path( :current )
328
+ else
329
+ if @user_session.registration_complete?
330
+ flash[:notice] = "Successfully signed in."
331
+ redirect_back_or_default articles_path
332
+ else
333
+ flash[:notice] = "Welcome back! Please complete required registration details before continuing.."
334
+ redirect_to edit_user_path( :current )
335
+ end
336
+ end
337
+ else
338
+ flash[:error] = "Failed to login or register."
339
+ redirect_to new_user_session_path
340
+ end
341
+ end
342
+
343
+ ==== destroy
344
+ The logout action..
345
+
346
+ def destroy
347
+ @user_session = current_user_session
348
+ @user_session.destroy if @user_session
349
+ flash[:notice] = "Successfully signed out."
350
+ redirect_to articles_path
351
+ end
352
+
353
+
354
+ === 6. Setup the Authlogic user controller
355
+
356
+ The users controller handles the actual user creation and editing actions. In it's standard form, it looks like any other controller with an underlying ActiveRecord model.
357
+
358
+ There are five basic actions to consider. If you don't already have a controller, create it:
359
+
360
+ $ script/generate controller users new create edit show update
361
+
362
+ {See the source for the sample users_controller.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/controllers/users_controller.rb].
363
+
364
+ The users controller just needs standard routes defined in config/routes.rb:
365
+
366
+ map.resources :users
367
+
368
+ ==== new
369
+ Stock standard form for a user to register on the site. Only required if you will allow users to register without using RPX auto-registration (using standard password authentication).
370
+
371
+ def new
372
+ @user = User.new
373
+ end
374
+
375
+ ==== create
376
+ As for new, stock standard and only required if you will allow users to register without using RPX auto-registration.
377
+
378
+ def create
379
+ @user = User.new(params[:user])
380
+ if @user.save
381
+ flash[:notice] = "Successfully registered user."
382
+ redirect_to articles_path
383
+ else
384
+ render :action => 'new'
385
+ end
386
+ end
387
+
388
+ ==== show
389
+ Display's the user's profile. Uses the current_user helper that we'll include in the application controller.
390
+
391
+ def show
392
+ @user = current_user
393
+ end
394
+
395
+ ==== edit
396
+ Allows the user to edit their profile. Calling valid? will ensure any validation errors are highlighted. This can be relevant with RPX since auto-registration may not include all the profile data you want to make "mandatory" for normal users.
397
+
398
+ def edit
399
+ @user = current_user
400
+ @user.valid?
401
+ end
402
+
403
+ ==== update
404
+ Handles the submission of the edit form. Again, uses the current_user helper that we'll include in the application controller.
405
+
406
+ def update
407
+ @user = current_user
408
+ @user.attributes = params[:user]
409
+ if @user.save
410
+ flash[:notice] = "Successfully updated user."
411
+ redirect_back_or_default articles_path
412
+ else
413
+ render :action => 'edit'
414
+ end
415
+ end
416
+
417
+
418
+ === 7. Use view helpers to provide login links
419
+
420
+ So how to put a "login" link on your page? Two helper methods are provided:
421
+ * <b>rpx_popup</b> helper to insert a link to pop-up RPX login
422
+ * <b>rpx_embed</b> helper to insert an embedded iframe RPX login form
423
+
424
+ Each takes an options hash:
425
+ * <tt>link_text:</tt> text to use in the link (only used by rpx_popup)
426
+ * <tt>app_name:</tt> name of the application you set when registering your service at rpxnow.com (will be prepended to RPX domain and used in RPX dialogues)
427
+ * <tt>return_url:</tt> url for the RPX callback (e.g. user_sessions_url)
428
+ * <tt>add_rpx:</tt> Optional. If true, requests RPX callback to add to current session. Else runs normal authentication process (default). See "8. Allow users to "Add RPX" to existing accounts"
429
+ * <tt>unobtrusive:</tt> true/false; sets javascript style for link. unobtrusive=true links directly to rpxnow site, whereas unobtrusive=false does a javascript pop-over. Default: true (only used by rpx_popup)
430
+
431
+ For example, to insert a login link in a navigation bar is as simple as this:
432
+
433
+ &lt;div id="user_nav"&gt;
434
+ &lt;%= link_to "Home", root_path %&gt; |
435
+ &lt;% if current_user %&gt;
436
+ &lt;%= link_to "Profile", user_path(:current) %&gt; |
437
+ &lt;%= link_to "Sign out", signout_path %&gt;
438
+ &lt;% else %&gt;
439
+ &lt;%= rpx_popup( :link_text => "Register/Sign in with RPX..", :app_name => "rails-authlogic-rpx-sample", :return_url => user_sessions_url, :unobtrusive => false ) %>&gt;
440
+ &lt;% end %&gt;
441
+ &lt;/div&gt;
442
+
443
+ === 8. Allow users to "Add RPX" to existing accounts (optional)
444
+
445
+ If you got this far and have a working application, you are ready to go, especially if you only plan to support RPX authentication.
446
+
447
+ However, if you support other authentication methods (e.g. by password), you probably want the ability to let user's add RPX to an existing account. This is not possible by default, however adding it is simply a matter of providing another method on your user controller.
448
+
449
+ The route may be called anything you like. Let's use "addrpxauth" for example.
450
+
451
+ # This action has the special purpose of receiving an update of the RPX identity information
452
+ # for current user - to add RPX authentication to an existing non-RPX account.
453
+ # RPX only supports :post, so this cannot simply go to update method (:put)
454
+ def addrpxauth
455
+ @user = current_user
456
+ if @user.save
457
+ flash[:notice] = "Successfully added RPX authentication for this account."
458
+ render :action => 'show'
459
+ else
460
+ render :action => 'edit'
461
+ end
462
+ end
463
+
464
+ {This is demonstrated in the sample users_controller.rb}[http://github.com/tardate/rails-authlogic-rpx-sample/blob/master/app/controllers/users_controller.rb].
465
+
466
+ You'll note this is almost identical to the "update". The main difference is that it needs to be enabled for :post by RPX. In config/routes.rb:
467
+
468
+ map.addrpxauth "addrpxauth", :controller => "users", :action => "addrpxauth", :method => :post
469
+
470
+ To make an "Add RPX authentication for this account.." link, use rpx_popup as for normal RPX login, but set the return_url to the "addrpxauth" callback you have provided, and set the option :add_rpx to tru:
471
+
472
+ &lt;%= rpx_popup( :link_text =&gt; "Add RPX authentication for this account..", :app_name =&gt; RPX_APP_NAME, :return_url =&gt; addrpxauth_url, :add_rpx =&gt; true, :unobtrusive =&gt; false ) %&gt;
473
+
474
+
475
+ === 9. There is no 9
476
+
477
+ That's all there is. To see Authlogic_RPX in action, check out the demonstration Rails application:
478
+ * <b>Live Demonstration Site:</b> [http://rails-authlogic-rpx-sample.heroku.com]
479
+ * <b>Demonstration site source repository:</b> [http://github.com/tardate/rails-authlogic-rpx-sample]
480
+
481
+
482
+ == Improving Authlogic_RPX: next steps; how to help
483
+
484
+ Authlogic_RPX is open source and hosted on {github}[http://github.com/tardate/authlogic_rpx]. Developer's are welcome to fork and play - if you have improvements or bug fixes, just send a request to pull from your fork.
485
+
486
+ If you have issues or feedback, please log them in the {issues list on github}[http://github.com/tardate/authlogic_rpx/issues]
487
+
488
+ Some of the improvements currently on the radar:
489
+ * Still figuring out how to write some good automated tests
490
+ * Implement/verify support for RPX "paid" service features of their "Plus" and "Pro" accounts (to date, only tested with free RPX "Basic" account)
491
+
492
+
493
+ == Note on programmatically grabbing an authenticated session
494
+
495
+ If you need to programmatically perform proxy authentication as a specific user (e.g. to run a batch process on behalf of the user), authlogic provides the necessary capability and this can be used with RPX-authenticated users too:
496
+
497
+ app.get "/" # force Authlogic::Session::Base.controller activation
498
+ user = User.find(:first)
499
+ session = UserSession.create(user, true) # skip authentication and log the user in directly, the true means "remember me"
500
+ session.valid?
501
+ => true
502
+
503
+
504
+ == Internals
505
+
506
+ Some design principles:
507
+ * Attempted to stay as close to binarylogic's "unobtrusive authentication" sensibility in Authlogic design
508
+ * All direct RPX processing is handled in the AuthlogicRpx::Session class (not in the ActiveRecord model)
509
+ * It uses the plug-in architecture introduced in Authlogic v2.0.
510
+
511
+ ==== building the gem
512
+
513
+ * increment the version in lib/authlogic_rpx/version.rb
514
+ * update gem version refs in README.rdoc
515
+ * update CHANGELOG.rdoc
516
+
517
+ # update manifest file
518
+ $ rake manifest
519
+ # update gemspec
520
+ $ rake build_gemspec
521
+ # build the gem
522
+ gem build authlogic_rpx.gemspec
523
+ # push the gem to gemcutter (e.g. for version 1.0.3)
524
+ gem push authlogic_rpx-1.0.3.gem
525
+
526
+
527
+ == Kudos and Kopywrite
528
+
529
+ Thanks to {binarylogic}[http://github.com/binarylogic] for cleaning up authentication in rails by creating Authlogic in the first place and offering it to the community.
530
+
531
+ The idea of adding RPX support to authlogic is not new. Some early ideas were found in the following projects, although it was decided not to base this implementation on a fork of these, since the approaches varied considerably:
532
+ * <b>http://github.com/hunter/authlogic_rpx</b> an initial start, based on authlogic_openid and using rpx_now
533
+ * <b>http://github.com/gampleman/authlogic_rpx/</b> similar, but including an implementation of the RPX api
534
+
535
+ authlogic_rpx was created by Paul Gallagher (tardate.com) and released under the MIT license.
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "kent-authlogic_rpx"
8
+ gem.summary = %Q{Authlogic Plugin for RPX}
9
+ gem.description = %Q{Authlogic Plugin for RPX}
10
+ gem.email = "kent.fenwick@gmail.com"
11
+ gem.homepage = "http://github.com/kent/authlogic_rpx"
12
+ gem.authors = ["Kent Fenwick"]
13
+ gem.add_development_dependency "thoughtbot-shoulda"
14
+ gem.add_dependency("kent-rpx_now", ">= 0.6.11")
15
+ gem.add_dependency("authlogic", ">= 2.1.1")
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/*_test.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ if File.exist?('VERSION')
49
+ version = File.read('VERSION')
50
+ else
51
+ version = ""
52
+ end
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "authlogic_rpx #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/lib/authlogic_rpx.rb"
@@ -0,0 +1,64 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{kent-authlogic_rpx}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kent Fenwick"]
12
+ s.date = %q{2009-12-22}
13
+ s.description = %q{Authlogic Plugin for RPX}
14
+ s.email = %q{kent.fenwick@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "init.rb",
27
+ "kent-authlogic_rpx.gemspec",
28
+ "lib/authlogic_rpx.rb",
29
+ "lib/authlogic_rpx/acts_as_authentic.rb",
30
+ "lib/authlogic_rpx/helper.rb",
31
+ "lib/authlogic_rpx/session.rb",
32
+ "lib/authlogic_rpx/version.rb",
33
+ "test/authlogic_rpx_test.rb",
34
+ "test/test_helper.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/kent/authlogic_rpx}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.5}
40
+ s.summary = %q{Authlogic Plugin for RPX}
41
+ s.test_files = [
42
+ "test/authlogic_rpx_test.rb",
43
+ "test/test_helper.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ s.add_runtime_dependency(%q<kent-rpx_now>, [">= 0.6.11"])
53
+ s.add_runtime_dependency(%q<authlogic>, [">= 2.1.1"])
54
+ else
55
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
56
+ s.add_dependency(%q<kent-rpx_now>, [">= 0.6.11"])
57
+ s.add_dependency(%q<authlogic>, [">= 2.1.1"])
58
+ end
59
+ else
60
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
61
+ s.add_dependency(%q<kent-rpx_now>, [">= 0.6.11"])
62
+ s.add_dependency(%q<authlogic>, [">= 2.1.1"])
63
+ end
64
+ end
@@ -0,0 +1,8 @@
1
+ require "authlogic_rpx/version"
2
+ require "authlogic_rpx/acts_as_authentic"
3
+ require "authlogic_rpx/session"
4
+ require "authlogic_rpx/helper"
5
+
6
+ ActiveRecord::Base.send(:include, AuthlogicRpx::ActsAsAuthentic)
7
+ Authlogic::Session::Base.send(:include, AuthlogicRpx::Session)
8
+ ActionController::Base.helper AuthlogicRpx::Helper
@@ -0,0 +1,114 @@
1
+ # This module is responsible for adding RPX functionality to Authlogic. Checkout the README for more info and please
2
+ # see the sub modules for detailed documentation.
3
+ module AuthlogicRpx
4
+ # This module is responsible for adding in the RPX functionality to your models. It hooks itself into the
5
+ # acts_as_authentic method provided by Authlogic.
6
+ module ActsAsAuthentic
7
+ # Adds in the neccesary modules for acts_as_authentic to include and also disabled password validation if
8
+ # RPX is being used.
9
+ def self.included(klass)
10
+ klass.class_eval do
11
+ extend Config
12
+ add_acts_as_authentic_module(Methods, :prepend)
13
+ end
14
+ end
15
+
16
+ module Config
17
+
18
+ # map_id is used to enable RPX identity mapping
19
+ # experimental - a feature of RPX paid accounts and not properly developed/tested yet
20
+ #
21
+ # * <tt>Default:</tt> false
22
+ # * <tt>Accepts:</tt> boolean
23
+ def map_id(value = false)
24
+ rw_config(:map_id, value, false)
25
+ end
26
+ alias_method :map_id=, :map_id
27
+
28
+ end
29
+
30
+ module Methods
31
+
32
+ # Set up some simple validations
33
+ def self.included(klass)
34
+ klass.class_eval do
35
+ validates_uniqueness_of :rpx_identifier, :scope => validations_scope, :if => :using_rpx?
36
+ validates_length_of_password_field_options validates_length_of_password_field_options.merge(:if => :validate_password_with_rpx?)
37
+ validates_confirmation_of_password_field_options validates_confirmation_of_password_field_options.merge(:if => :validate_password_with_rpx?)
38
+ validates_length_of_password_confirmation_field_options validates_length_of_password_confirmation_field_options.merge(:if => :validate_password_with_rpx?)
39
+ before_validation :adding_rpx_identifier
40
+ after_create :map_rpx_identifier
41
+ end
42
+ end
43
+
44
+ # support a block given to the save
45
+ def save(perform_validation = true, &block)
46
+ result = super perform_validation
47
+ yield(result) if block_given?
48
+ result
49
+ end
50
+
51
+ # test if account it using RPX authentication
52
+ def using_rpx?
53
+ self.column_names.include?("rpx_identifier") || !rpx_identifier.blank?
54
+ end
55
+
56
+ # test if account it using normal password authentication
57
+ def using_password?
58
+ !send(crypted_password_field).blank?
59
+ end
60
+
61
+ private
62
+
63
+ def validate_password_with_rpx?
64
+ !using_rpx? && require_password?
65
+ end
66
+
67
+ # hook for adding RPX identifier to an existing account. This is invoked prior to model validation.
68
+ # RPX information is plucked from the controller session object (where it was placed by the session model as a result
69
+ # of the RPX callback)
70
+ # The minimal action taken is to populate the rpx_identifier field in the user model.
71
+ #
72
+ # This procedure chains to the map_added_rpx_data, which may be over-ridden in your project to perform
73
+ # additional mapping of RPX information to the user model as may be desired.
74
+ #
75
+ def adding_rpx_identifier
76
+ return true unless session_class && session_class.controller
77
+ added_rpx_data = session_class.controller.session['added_rpx_data']
78
+ unless added_rpx_data.blank?
79
+ session_class.controller.session['added_rpx_data'] = nil
80
+ map_added_rpx_data( added_rpx_data )
81
+ end
82
+ return true
83
+ end
84
+
85
+ # map_added_rpx_data maps additional fields from the RPX response into the user object during the "add RPX to existing account" process.
86
+ # Override this in your user model to perform field mapping as may be desired
87
+ # See https://rpxnow.com/docs#profile_data for the definition of available attributes
88
+ #
89
+ # By default, it only maps the rpx_identifier field.
90
+ #
91
+ def map_added_rpx_data( rpx_data )
92
+ self.rpx_identifier = rpx_data['profile']['identifier']
93
+ end
94
+
95
+
96
+
97
+ # experimental - a feature of RPX paid accounts and not properly developed/tested yet
98
+ def map_id?
99
+ self.class.map_id
100
+ end
101
+
102
+ # experimental - a feature of RPX paid accounts and not properly developed/tested yet
103
+ def map_rpx_identifier
104
+ RPXNow.map(rpx_identifier, id) if using_rpx? && map_id?
105
+ end
106
+
107
+ # experimental - a feature of RPX paid accounts and not properly developed/tested yet
108
+ def unmap_rpx_identifer
109
+ RPXNow.unmap(rpx_identifier, id) if using_rpx? && map_id?
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,42 @@
1
+ module AuthlogicRpx
2
+ module Helper
3
+
4
+ # helper to insert an embedded iframe RPX login
5
+ # takes options hash:
6
+ # * <tt>app_name:</tt> name of the application (will be prepended to RPX domain and used in RPX dialogues)
7
+ # * <tt>return_url:</tt> url for the RPX callback (e.g. user_sessions_url)
8
+ # * <tt>add_rpx:</tt> if true, requests RPX callback to add to current session. Else runs normal authentication process (default)
9
+ #
10
+ def rpx_embed(options = {})
11
+ params = (
12
+ { :authenticity_token => form_authenticity_token, :add_rpx => options[:add_rpx] }.collect { |n| "#{n[0]}=#{ u(n[1]) }" if n[1] }
13
+ ).compact.join('&')
14
+ RPXNow.embed_code(options[:app_name], u( options[:return_url] + '?' + params ) )
15
+ end
16
+
17
+ # helper to insert a link to pop-up RPX login
18
+ # takes options hash:
19
+ # * <tt>link_text:</tt> text to use in the link
20
+ # * <tt>app_name:</tt> name of the application (will be prepended to RPX domain and used in RPX dialogues)
21
+ # * <tt>return_url:</tt> url for the RPX callback (e.g. user_sessions_url)
22
+ # * <tt>add_rpx:</tt> if true, requests RPX callback to add to current session. Else runs normal authentication process (default)
23
+ # * <tt>unobtrusive:</tt> true/false; sets javascript style for link. Default: true
24
+ #
25
+ # NB: i18n considerations? supports a :language parameter (not tested)
26
+ def rpx_popup(options = {})
27
+ params = (
28
+ { :authenticity_token => form_authenticity_token, :add_rpx => options[:add_rpx] }.collect { |n| "#{n[0]}=#{ u(n[1]) }" if n[1] }
29
+ ).compact.join('&')
30
+ unobtrusive = options[:unobtrusive].nil? ? true : options[:unobtrusive]
31
+ return_url = options[:return_url] + '?' + params
32
+ return_url = u( return_url ) if unobtrusive # double-encoding required only if unobtrusive mode used
33
+ RPXNow.popup_code(
34
+ options[:link_text],
35
+ options[:app_name],
36
+ return_url,
37
+ :unobtrusive=>unobtrusive
38
+ )
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,212 @@
1
+ module AuthlogicRpx
2
+ # This module is responsible for adding all of the RPX goodness to the Authlogic::Session::Base class.
3
+ module Session
4
+ # Add a simple rpx_identifier attribute and some validations for the field.
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ extend Config
8
+ include Methods
9
+ end
10
+ end
11
+
12
+ module Config
13
+
14
+ def find_by_rpx_identifier_method(value = nil)
15
+ rw_config(:find_by_rpx_identifier_method, value, :find_by_rpx_identifier)
16
+ end
17
+ alias_method :find_by_rpx_identifier_method=, :find_by_rpx_identifier_method
18
+
19
+ # Auto Register is enabled by default.
20
+ # Add this in your Session object if you need to disable auto-registration via rpx
21
+ #
22
+ def auto_register(value=true)
23
+ auto_register_value(value)
24
+ end
25
+ def auto_register_value(value=nil)
26
+ rw_config(:auto_register,value,true)
27
+ end
28
+ alias_method :auto_register=,:auto_register
29
+
30
+ # Add this in your Session object to set the RPX API key
31
+ # RPX won't work without the API key. Set it here if not already set in your app configuration.
32
+ #
33
+ def rpx_key(value=nil)
34
+ rpx_key_value(value)
35
+ end
36
+ def rpx_key_value(value=nil)
37
+ if ! inheritable_attributes.include?(:rpx_key)
38
+ RPXNow.api_key = value
39
+ end
40
+ rw_config(:rpx_key,value,false)
41
+ end
42
+ alias_method :rpx_key=,:rpx_key
43
+
44
+ # Add this in your Session object to set whether RPX returns extended user info
45
+ # By default, it will not, which is enough to get username, name, email and the rpx identified
46
+ # if you want to map additional information into your user details, you can request extended
47
+ # attributes (though not all providers give them - see the RPX docs)
48
+ #
49
+ def rpx_extended_info(value=true)
50
+ rpx_extended_info_value(value)
51
+ end
52
+ def rpx_extended_info_value(value=nil)
53
+ rw_config(:rpx_extended_info,value,false)
54
+ end
55
+ alias_method :rpx_extended_info=,:rpx_extended_info
56
+
57
+ end
58
+
59
+ module Methods
60
+
61
+ def self.included(klass)
62
+ klass.class_eval do
63
+ attr_accessor :new_registration
64
+ attr_accessor :rpx_identifier
65
+ after_persisting :add_rpx_identifier, :if => :adding_rpx_identifier?
66
+ validate :validate_by_rpx, :if => :authenticating_with_rpx?
67
+ end
68
+ end
69
+
70
+ # Determines if the authenticated user is also a new registration.
71
+ # For use in the session controller to help direct the most appropriate action to follow.
72
+ #
73
+ def new_registration?
74
+ new_registration
75
+ end
76
+
77
+ # Determines if the authenticated user has a complete registration (no validation errors)
78
+ # For use in the session controller to help direct the most appropriate action to follow.
79
+ #
80
+ def registration_complete?
81
+ attempted_record && attempted_record.valid?
82
+ end
83
+
84
+ private
85
+ # Tests if current request is for RPX authentication
86
+ #
87
+ def authenticating_with_rpx?
88
+ controller.params[:token] && !controller.params[:add_rpx]
89
+ end
90
+
91
+ # hook instance finder method to class
92
+ #
93
+ def find_by_rpx_identifier_method
94
+ self.class.find_by_rpx_identifier_method
95
+ end
96
+
97
+ # Tests if auto_registration is enabled (on by default)
98
+ #
99
+ def auto_register?
100
+ self.class.auto_register_value
101
+ end
102
+
103
+ # Tests if rpx_extended_info is enabled (off by default)
104
+ #
105
+ def rpx_extended_info?
106
+ self.class.rpx_extended_info_value
107
+ end
108
+
109
+ # Tests if current request is the special case of adding RPX to an existing account
110
+ #
111
+ def adding_rpx_identifier?
112
+ controller.params[:token] && controller.params[:add_rpx]
113
+ end
114
+
115
+ # Handles the special case of RPX being added to an existing account.
116
+ # At this point, a session has been established as a result of a "save" on the user model (which indirectly triggers user session validation).
117
+ # We do not directly add the RPX details to the user record here in order to avoid getting
118
+ # into a recursive dance between the session and user models.
119
+ # Rather, it uses the trick of adding the necessary RPX information to the session object,
120
+ # and the user model will pluck these values out before completing its validation step.
121
+ #
122
+ def add_rpx_identifier
123
+ data = RPXNow.user_data(controller.params[:token], :extended=> rpx_extended_info? ) {|raw| raw }
124
+ controller.session['added_rpx_data'] = data if data
125
+ end
126
+
127
+ # the main RPX magic. At this point, a session is being validated and we know RPX identifier
128
+ # has been provided. We'll callback to RPX to verify the token, and authenticate the matching
129
+ # user.
130
+ # If no user is found, and we have auto_register enabled (default) this method will also
131
+ # create the user registration stub.
132
+ #
133
+ # On return to the controller, you can test for new_registration? and registration_complete?
134
+ # to determine the most appropriate action
135
+ #
136
+ def validate_by_rpx
137
+ @rpx_data = RPXNow.user_data(controller.params[:token], :extended=> rpx_extended_info? ) {|raw| raw }
138
+ # If we don't have a valid sign-in, give-up at this point
139
+ if @rpx_data.nil?
140
+ errors.add_to_base("Authentication failed. Please try again.")
141
+ return false
142
+ end
143
+ rpx_id = @rpx_data['profile']['identifier']
144
+ if rpx_id.blank?
145
+ errors.add_to_base("Authentication failed. Please try again.")
146
+ return false
147
+ end
148
+
149
+ self.attempted_record = klass.send(find_by_rpx_identifier_method, rpx_id)
150
+
151
+ # so what do we do if we can't find an existing user matching the RPX authentication..
152
+ if !attempted_record
153
+ if auto_register?
154
+ self.attempted_record = klass.new( :rpx_identifier=> rpx_id )
155
+ map_rpx_data
156
+ # save the new user record - without session maintenance else we get caught in a self-referential hell,
157
+ # since both session and user objects invoke each other upon save
158
+ self.new_registration=true
159
+ self.attempted_record.save_without_session_maintenance
160
+ else
161
+ errors.add_to_base("We did not find any accounts with that login. Enter your details and create an account.")
162
+ return false
163
+ end
164
+ else
165
+ map_rpx_data_each_login
166
+ end
167
+
168
+ end
169
+
170
+ # map_rpx_data maps additional fields from the RPX response into the user object during auto-registration.
171
+ # Override this in your session model to change the field mapping
172
+ # See https://rpxnow.com/docs#profile_data for the definition of available attributes
173
+ #
174
+ # In this procedure, you will be writing to fields of the "self.attempted_record" object, pulling data from the @rpx_data object.
175
+ #
176
+ # WARNING: if you are using auto-registration, any fields you map should NOT have constraints enforced at the database level.
177
+ # authlogic_rpx will optimistically attempt to save the user record during registration, and
178
+ # violating a database constraint will cause the authentication/registration to fail.
179
+ #
180
+ # You can/should enforce any required validations at the model level e.g.
181
+ # validates_uniqueness_of :username, :case_sensitive => false
182
+ # This will allow the auto-registration to proceed, and the user can be given a chance to rectify the validation errors
183
+ # on your user profile page.
184
+ #
185
+ # If it is not acceptable in your application to have user records created with potential validation errors in auto-populated fields, you
186
+ # will need to override map_rpx_data and implement whatever special handling makes sense in your case. For example:
187
+ # - directly check for uniqueness and other validation requirements
188
+ # - automatically "uniquify" fields like username
189
+ # - save conflicting profile information to "pending user review" columns or a seperate table
190
+ #
191
+ def map_rpx_data
192
+ self.attempted_record.send("#{klass.login_field}=", @rpx_data['profile']['preferredUsername'] ) if attempted_record.send(klass.login_field).blank?
193
+ self.attempted_record.send("#{klass.email_field}=", @rpx_data['profile']['email'] ) if attempted_record.send(klass.email_field).blank?
194
+ end
195
+
196
+ # map_rpx_data_each_login provides a hook to allow you to map RPX profile information every time the user
197
+ # logs in.
198
+ # By default, nothing is mapped.
199
+ #
200
+ # This would mainly be used to update relatively volatile information that you are maintaining in the user model (such as profile image url)
201
+ #
202
+ # In this procedure, you will be writing to fields of the "self.attempted_record" object, pulling data from the @rpx_data object.
203
+ #
204
+ #
205
+ def map_rpx_data_each_login
206
+
207
+ end
208
+
209
+ end
210
+
211
+ end
212
+ end
@@ -0,0 +1,51 @@
1
+ module AuthlogicRpx
2
+ # A class for describing the current version of a library. The version
3
+ # consists of three parts: the +major+ number, the +minor+ number, and the
4
+ # +tiny+ (or +patch+) number.
5
+ class Version
6
+ include Comparable
7
+
8
+ # A convenience method for instantiating a new Version instance with the
9
+ # given +major+, +minor+, and +tiny+ components.
10
+ def self.[](major, minor, tiny)
11
+ new(major, minor, tiny)
12
+ end
13
+
14
+ attr_reader :major, :minor, :tiny
15
+
16
+ # Create a new Version object with the given components.
17
+ def initialize(major, minor, tiny)
18
+ @major, @minor, @tiny = major, minor, tiny
19
+ end
20
+
21
+ # Compare this version to the given +version+ object.
22
+ def <=>(version)
23
+ to_i <=> version.to_i
24
+ end
25
+
26
+ # Converts this version object to a string, where each of the three
27
+ # version components are joined by the '.' character. E.g., 2.0.0.
28
+ def to_s
29
+ @to_s ||= [@major, @minor, @tiny].join(".")
30
+ end
31
+
32
+ # Converts this version to a canonical integer that may be compared
33
+ # against other version objects.
34
+ def to_i
35
+ @to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
36
+ end
37
+
38
+ def to_a
39
+ [@major, @minor, @tiny]
40
+ end
41
+
42
+ MAJOR = 1
43
+ MINOR = 0
44
+ TINY = 4
45
+
46
+ # The current version as a Version instance
47
+ CURRENT = new(MAJOR, MINOR, TINY)
48
+ # The current version as a String
49
+ STRING = CURRENT.to_s
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class AuthlogicRpxTest < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'authlogic_rpx'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kent-authlogic_rpx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kent Fenwick
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-22 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thoughtbot-shoulda
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: kent-rpx_now
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.6.11
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: authlogic
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.1.1
44
+ version:
45
+ description: Authlogic Plugin for RPX
46
+ email: kent.fenwick@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.rdoc
59
+ - Rakefile
60
+ - VERSION
61
+ - init.rb
62
+ - kent-authlogic_rpx.gemspec
63
+ - lib/authlogic_rpx.rb
64
+ - lib/authlogic_rpx/acts_as_authentic.rb
65
+ - lib/authlogic_rpx/helper.rb
66
+ - lib/authlogic_rpx/session.rb
67
+ - lib/authlogic_rpx/version.rb
68
+ - test/authlogic_rpx_test.rb
69
+ - test/test_helper.rb
70
+ has_rdoc: true
71
+ homepage: http://github.com/kent/authlogic_rpx
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options:
76
+ - --charset=UTF-8
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ version:
91
+ requirements: []
92
+
93
+ rubyforge_project:
94
+ rubygems_version: 1.3.5
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: Authlogic Plugin for RPX
98
+ test_files:
99
+ - test/authlogic_rpx_test.rb
100
+ - test/test_helper.rb