kent-authlogic_rpx 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +535 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/kent-authlogic_rpx.gemspec +64 -0
- data/lib/authlogic_rpx.rb +8 -0
- data/lib/authlogic_rpx/acts_as_authentic.rb +114 -0
- data/lib/authlogic_rpx/helper.rb +42 -0
- data/lib/authlogic_rpx/session.rb +212 -0
- data/lib/authlogic_rpx/version.rb +51 -0
- data/test/authlogic_rpx_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- metadata +100 -0
data/.document
ADDED
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.
|
data/README.rdoc
ADDED
@@ -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
|
+
<div id="user_nav">
|
434
|
+
<%= link_to "Home", root_path %> |
|
435
|
+
<% if current_user %>
|
436
|
+
<%= link_to "Profile", user_path(:current) %> |
|
437
|
+
<%= link_to "Sign out", signout_path %>
|
438
|
+
<% else %>
|
439
|
+
<%= rpx_popup( :link_text => "Register/Sign in with RPX..", :app_name => "rails-authlogic-rpx-sample", :return_url => user_sessions_url, :unobtrusive => false ) %>>
|
440
|
+
<% end %>
|
441
|
+
</div>
|
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
|
+
<%= rpx_popup( :link_text => "Add RPX authentication for this account..", :app_name => RPX_APP_NAME, :return_url => addrpxauth_url, :add_rpx => true, :unobtrusive => false ) %>
|
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.
|
data/Rakefile
ADDED
@@ -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
|
data/test/test_helper.rb
ADDED
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
|