authlogic_rpx 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +10 -0
- data/Manifest +19 -2
- data/README.rdoc +284 -72
- data/Rakefile +30 -2
- data/authlogic_rpx.gemspec +11 -11
- data/generators/add_authlogic_rpx_migration/USAGE +18 -0
- data/generators/add_authlogic_rpx_migration/add_authlogic_rpx_migration_generator.rb +44 -0
- data/generators/add_authlogic_rpx_migration/templates/migration_internal_mapping.rb +34 -0
- data/generators/add_authlogic_rpx_migration/templates/migration_no_mapping.rb +29 -0
- data/lib/authlogic_rpx.rb +1 -0
- data/lib/authlogic_rpx/acts_as_authentic.rb +199 -32
- data/lib/authlogic_rpx/helper.rb +21 -13
- data/lib/authlogic_rpx/rpx_identifier.rb +5 -0
- data/lib/authlogic_rpx/session.rb +23 -16
- data/lib/authlogic_rpx/version.rb +2 -2
- data/test/fixtures/rpxresponses.yml +20 -0
- data/test/fixtures/users.yml +20 -5
- data/test/integration/basic_authentication_and_registration_test.rb +53 -0
- data/test/integration/internal_mapping/basic_authentication_and_registration_test.rb +3 -0
- data/test/integration/internal_mapping/settings_test.rb +10 -0
- data/test/integration/no_mapping/basic_authentication_and_registration_test.rb +3 -0
- data/test/integration/no_mapping/settings_test.rb +10 -0
- data/test/libs/ext_test_unit.rb +30 -0
- data/test/libs/mock_rpx_now.rb +34 -0
- data/test/libs/rails_trickery.rb +2 -2
- data/test/libs/rpxresponse.rb +3 -0
- data/test/libs/user_session.rb +1 -1
- data/test/test_helper.rb +35 -21
- data/test/test_internal_mapping_helper.rb +95 -0
- data/test/unit/acts_as_authentic_settings_test.rb +42 -0
- data/test/unit/session_settings_test.rb +38 -0
- data/test/unit/session_validation_test.rb +16 -0
- data/test/unit/verify_rpx_mock_test.rb +29 -0
- metadata +34 -8
- data/test/acts_as_authentic_test.rb +0 -5
- data/test/session_test.rb +0 -10
data/lib/authlogic_rpx/helper.rb
CHANGED
@@ -22,20 +22,28 @@ module AuthlogicRpx
|
|
22
22
|
# * <tt>add_rpx:</tt> if true, requests RPX callback to add to current session. Else runs normal authentication process (default)
|
23
23
|
# * <tt>unobtrusive:</tt> true/false; sets javascript style for link. Default: true
|
24
24
|
#
|
25
|
-
#
|
25
|
+
# The options hash may include other options as supported by rpx_now (see http://github.com/grosser/rpx_now)
|
26
|
+
#
|
26
27
|
def rpx_popup(options = {})
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
return_url =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
28
|
+
options = { :unobtrusive => true, :add_rpx => false }.merge( options )
|
29
|
+
unobtrusive = options[:unobtrusive]
|
30
|
+
add_rpx = options.delete( :add_rpx )
|
31
|
+
app_name = options.delete( :app_name )
|
32
|
+
link_text = options.delete( :link_text )
|
33
|
+
return_url = options.delete( :return_url ) + '?' + (
|
34
|
+
{ :authenticity_token => form_authenticity_token, :add_rpx => add_rpx }.collect { |n| "#{n[0]}=#{ u(n[1]) }" if n[1] }
|
35
|
+
).compact.join('&')
|
36
|
+
|
37
|
+
# as of rpx_now 0.6.11, there is still an issue with url-encoding of the return path containing additional parameters
|
38
|
+
# (as we are using here to get the form_authenticity_token returned.
|
39
|
+
# To get around this, we need to ensure hrefs are url-encoded,
|
40
|
+
# while the link provided to the rpx_now javascript popup should _not_ be url-encoded
|
41
|
+
if unobtrusive
|
42
|
+
RPXNow.popup_code( link_text, app_name, u( return_url ), options )
|
43
|
+
else
|
44
|
+
RPXNow.popup_code( link_text, app_name, u( return_url ), options.merge( :unobtrusive => true ) ) +
|
45
|
+
RPXNow.popup_source(app_name, return_url, options )
|
46
|
+
end
|
39
47
|
end
|
40
48
|
|
41
49
|
end
|
@@ -10,7 +10,7 @@ module AuthlogicRpx
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module Config
|
13
|
-
|
13
|
+
|
14
14
|
def find_by_rpx_identifier_method(value = nil)
|
15
15
|
rw_config(:find_by_rpx_identifier_method, value, :find_by_rpx_identifier)
|
16
16
|
end
|
@@ -34,7 +34,7 @@ module AuthlogicRpx
|
|
34
34
|
rpx_key_value(value)
|
35
35
|
end
|
36
36
|
def rpx_key_value(value=nil)
|
37
|
-
if !
|
37
|
+
if !inheritable_attributes.include?(:rpx_key)
|
38
38
|
RPXNow.api_key = value
|
39
39
|
end
|
40
40
|
rw_config(:rpx_key,value,false)
|
@@ -61,7 +61,6 @@ module AuthlogicRpx
|
|
61
61
|
def self.included(klass)
|
62
62
|
klass.class_eval do
|
63
63
|
attr_accessor :new_registration
|
64
|
-
attr_accessor :rpx_identifier
|
65
64
|
after_persisting :add_rpx_identifier, :if => :adding_rpx_identifier?
|
66
65
|
validate :validate_by_rpx, :if => :authenticating_with_rpx?
|
67
66
|
end
|
@@ -71,7 +70,7 @@ module AuthlogicRpx
|
|
71
70
|
# For use in the session controller to help direct the most appropriate action to follow.
|
72
71
|
#
|
73
72
|
def new_registration?
|
74
|
-
new_registration
|
73
|
+
new_registration || !new_registration.nil?
|
75
74
|
end
|
76
75
|
|
77
76
|
# Determines if the authenticated user has a complete registration (no validation errors)
|
@@ -88,8 +87,8 @@ module AuthlogicRpx
|
|
88
87
|
controller.params[:token] && !controller.params[:add_rpx]
|
89
88
|
end
|
90
89
|
|
91
|
-
|
92
|
-
|
90
|
+
# hook instance finder method to class
|
91
|
+
#
|
93
92
|
def find_by_rpx_identifier_method
|
94
93
|
self.class.find_by_rpx_identifier_method
|
95
94
|
end
|
@@ -134,35 +133,43 @@ module AuthlogicRpx
|
|
134
133
|
# to determine the most appropriate action
|
135
134
|
#
|
136
135
|
def validate_by_rpx
|
137
|
-
@rpx_data = RPXNow.user_data(
|
136
|
+
@rpx_data = RPXNow.user_data(
|
137
|
+
controller.params[:token],
|
138
|
+
:extended => rpx_extended_info?) { |raw| raw }
|
139
|
+
|
138
140
|
# If we don't have a valid sign-in, give-up at this point
|
139
|
-
if @rpx_data.nil?
|
141
|
+
if @rpx_data.nil? || @rpx_data['profile'].nil?
|
140
142
|
errors.add_to_base("Authentication failed. Please try again.")
|
141
143
|
return false
|
142
144
|
end
|
145
|
+
|
143
146
|
rpx_id = @rpx_data['profile']['identifier']
|
147
|
+
rpx_provider_name = @rpx_data['profile']['providerName']
|
144
148
|
if rpx_id.blank?
|
145
149
|
errors.add_to_base("Authentication failed. Please try again.")
|
146
150
|
return false
|
147
|
-
end
|
151
|
+
end
|
148
152
|
|
149
153
|
self.attempted_record = klass.send(find_by_rpx_identifier_method, rpx_id)
|
150
154
|
|
151
|
-
# so what do we do if we can't find an existing user matching the RPX authentication
|
155
|
+
# so what do we do if we can't find an existing user matching the RPX authentication...
|
152
156
|
if !attempted_record
|
153
|
-
if auto_register?
|
154
|
-
self.attempted_record = klass.new(
|
157
|
+
if auto_register?
|
158
|
+
self.attempted_record = klass.new()
|
155
159
|
map_rpx_data
|
156
|
-
|
157
|
-
#
|
158
|
-
self
|
160
|
+
|
161
|
+
# save the new user record - without session maintenance else we
|
162
|
+
# get caught in a self-referential hell, since both session and
|
163
|
+
# user objects invoke each other upon save
|
164
|
+
self.new_registration = true
|
165
|
+
self.attempted_record.add_rpx_identifier( rpx_id, rpx_provider_name)
|
159
166
|
self.attempted_record.save_without_session_maintenance
|
160
167
|
else
|
161
168
|
errors.add_to_base("We did not find any accounts with that login. Enter your details and create an account.")
|
162
169
|
return false
|
163
170
|
end
|
164
171
|
else
|
165
|
-
|
172
|
+
map_rpx_data_each_login
|
166
173
|
end
|
167
174
|
|
168
175
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
valid_rpx_auth_user_one:
|
2
|
+
identifier : http://provider.one/valid_rpx_auth_user_one
|
3
|
+
provider_name: provider.one
|
4
|
+
username: valid_rpx_auth_user_one
|
5
|
+
verified_email: valid_rpx_auth_user_one@provider.one
|
6
|
+
display_name: valid rpx auth user one
|
7
|
+
|
8
|
+
valid_rpx_auth_user_two:
|
9
|
+
identifier : http://provider.one/valid_rpx_auth_user_two
|
10
|
+
provider_name: provider.one
|
11
|
+
username: valid_rpx_auth_user_two
|
12
|
+
verified_email: valid_rpx_auth_user_two@provider.one
|
13
|
+
display_name: valid rpx auth user two
|
14
|
+
|
15
|
+
unregistered_rpx_auth_user_one:
|
16
|
+
identifier : http://provider.one/unregistered_rpx_auth_user_one
|
17
|
+
provider_name: provider.one
|
18
|
+
username: unregistered_rpx_auth_user_one
|
19
|
+
verified_email: unregistered_rpx_auth_user_one@provider.one
|
20
|
+
display_name: unregistered rpx auth user one
|
data/test/fixtures/users.yml
CHANGED
@@ -1,5 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
valid_rpx_auth_user_one:
|
2
|
+
login: valid_rpx_auth_user_one
|
3
|
+
email: valid_rpx_auth_user_one@provider.one
|
4
|
+
persistence_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
|
5
|
+
single_access_token: <%= Authlogic::Random.friendly_token %>
|
6
|
+
perishable_token: <%= Authlogic::Random.friendly_token %>
|
7
|
+
rpx_identifier : http://provider.one/valid_rpx_auth_user_one
|
8
|
+
|
9
|
+
valid_rpx_auth_user_two:
|
10
|
+
login: valid_rpx_auth_user_two
|
11
|
+
email: valid_rpx_auth_user_two@provider.one
|
12
|
+
persistence_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
|
13
|
+
single_access_token: <%= Authlogic::Random.friendly_token %>
|
14
|
+
perishable_token: <%= Authlogic::Random.friendly_token %>
|
15
|
+
rpx_identifier : http://provider.one/valid_rpx_auth_user_two
|
16
|
+
|
17
|
+
invalid_rpx_auth_user_one:
|
18
|
+
login: invalid_rpx_auth_user_one
|
19
|
+
|
20
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# requires test_helper to be loaded first
|
2
|
+
|
3
|
+
class BasicAuthenticationAndRegistrationTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
must "authenticate valid existing user" do
|
6
|
+
test_user = users(:valid_rpx_auth_user_one)
|
7
|
+
controller.params[:token] = test_user.login
|
8
|
+
session = UserSession.new
|
9
|
+
assert_true session.save, "should be a valid session"
|
10
|
+
assert_false session.new_registration?, "should not be a new registration"
|
11
|
+
assert_true session.registration_complete?, "registration should be complete"
|
12
|
+
assert_equal test_user, session.record
|
13
|
+
end
|
14
|
+
|
15
|
+
must "do not authenticate invalidate non-existent user" do
|
16
|
+
controller.params[:token] = ''
|
17
|
+
session = UserSession.new
|
18
|
+
assert_false session.save, "should not be a valid session"
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
must "auto-register an unregistered user" do
|
23
|
+
# enforce Authlogic settings required for test
|
24
|
+
UserSession.auto_register true
|
25
|
+
User.account_merge_enabled false
|
26
|
+
User.account_mapping_mode :none
|
27
|
+
|
28
|
+
# get response template. set the controller token (used by RPX mock to match mock response)
|
29
|
+
test_user = rpxresponses(:unregistered_rpx_auth_user_one)
|
30
|
+
controller.params[:token] = test_user.username
|
31
|
+
|
32
|
+
session = UserSession.new
|
33
|
+
assert_true session.save, "should be a valid session"
|
34
|
+
assert_true session.new_registration?, "should be a new registration"
|
35
|
+
assert_true session.registration_complete?, "registration should be complete"
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
must "auto-register disabled for an unregistered user" do
|
40
|
+
# enforce Authlogic settings required for test
|
41
|
+
UserSession.auto_register false
|
42
|
+
User.account_merge_enabled false
|
43
|
+
User.account_mapping_mode :none
|
44
|
+
|
45
|
+
# get response template. set the controller token (used by RPX mock to match mock response)
|
46
|
+
test_user = rpxresponses(:unregistered_rpx_auth_user_one)
|
47
|
+
controller.params[:token] = test_user.username
|
48
|
+
|
49
|
+
session = UserSession.new
|
50
|
+
assert_false session.save, "should not be a valid session"
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_internal_mapping_helper.rb'
|
2
|
+
|
3
|
+
class SettingsTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
must "account_mapping_mode :auto should resolve to :internal" do
|
6
|
+
assert_equal :auto, User.account_mapping_mode
|
7
|
+
assert_equal :internal, User.account_mapping_mode_used
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../test_helper.rb'
|
2
|
+
|
3
|
+
class SettingsTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
must "account_mapping_mode :auto should resolve to :none" do
|
6
|
+
assert_equal :auto, User.account_mapping_mode
|
7
|
+
assert_equal :none, User.account_mapping_mode_used
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Test::Unit
|
2
|
+
|
3
|
+
class TestCase
|
4
|
+
|
5
|
+
def self.must(name, &block)
|
6
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
7
|
+
defined = instance_method(test_name) rescue false
|
8
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
9
|
+
if block_given?
|
10
|
+
define_method(test_name, &block)
|
11
|
+
else
|
12
|
+
define_method(test_name) do
|
13
|
+
flunk "No implementation provided for #{name}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
module Test::Unit::Assertions
|
23
|
+
def assert_false(object, message="")
|
24
|
+
assert_equal(false, object, message)
|
25
|
+
end
|
26
|
+
def assert_true(object, message="")
|
27
|
+
assert_equal(true, object, message)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/rpxresponse.rb'
|
2
|
+
|
3
|
+
module RPXNow
|
4
|
+
|
5
|
+
def self.user_data(token, options={})
|
6
|
+
data = get_test_data(token)
|
7
|
+
if block_given? then yield(data) else parse_user_data(data) end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.parse_user_data(data)
|
11
|
+
data
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_test_data(token)
|
15
|
+
|
16
|
+
response = Rpxresponse.find_by_username(token)
|
17
|
+
if response
|
18
|
+
data = {}
|
19
|
+
data['profile'] = {}
|
20
|
+
data['profile']['identifier'] = response.identifier
|
21
|
+
data['profile']['providerName'] = response.provider_name
|
22
|
+
data['profile']['preferredUsername'] = response.username
|
23
|
+
data['profile']['email'] = response.verified_email
|
24
|
+
|
25
|
+
data[:identifier] = data['profile']['identifier']
|
26
|
+
data[:providerName] = data['profile']['providerName']
|
27
|
+
data[:email] = response.verified_email
|
28
|
+
data[:username] = data['profile']['preferredUsername']
|
29
|
+
data[:name] = response.display_name
|
30
|
+
end
|
31
|
+
|
32
|
+
data
|
33
|
+
end
|
34
|
+
end
|
data/test/libs/rails_trickery.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
# these constants be present. The only other alternative is to use an entire rails application for testing
|
3
3
|
# which is a little too overboard for this, I think.
|
4
4
|
|
5
|
-
RAILS_ROOT = ''
|
5
|
+
#RAILS_ROOT = ''
|
6
6
|
|
7
|
-
class
|
7
|
+
class ActionControllerEx < Authlogic::TestCase::MockController
|
8
8
|
class Request < Authlogic::TestCase::MockRequest
|
9
9
|
def request_method
|
10
10
|
""
|
data/test/libs/user_session.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,14 +1,37 @@
|
|
1
|
-
require "test/unit"
|
2
1
|
require "rubygems"
|
2
|
+
begin
|
3
|
+
gem 'test-unit'
|
4
|
+
require "test/unit"
|
5
|
+
require File.dirname(__FILE__) + "/libs/ext_test_unit"
|
6
|
+
rescue Gem::LoadError
|
7
|
+
# assume using stdlib Test:Unit
|
8
|
+
require "test/unit"
|
9
|
+
require File.dirname(__FILE__) + "/libs/ext_test_unit"
|
10
|
+
end
|
11
|
+
|
3
12
|
require "ruby-debug"
|
4
13
|
require "active_record"
|
5
14
|
require "action_controller"
|
6
15
|
|
7
16
|
ActiveRecord::Schema.verbose = false
|
8
|
-
|
17
|
+
|
18
|
+
begin
|
19
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
20
|
+
rescue ArgumentError
|
21
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
22
|
+
end
|
23
|
+
|
9
24
|
ActiveRecord::Base.configurations = true
|
10
25
|
ActiveRecord::Schema.define(:version => 1) do
|
11
|
-
|
26
|
+
|
27
|
+
create_table :rpxresponses do |t|
|
28
|
+
t.string :identifier
|
29
|
+
t.string :provider_name
|
30
|
+
t.string :username
|
31
|
+
t.string :verified_email
|
32
|
+
t.string :display_name
|
33
|
+
end
|
34
|
+
|
12
35
|
create_table :users do |t|
|
13
36
|
t.datetime :created_at
|
14
37
|
t.datetime :updated_at
|
@@ -31,24 +54,24 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
31
54
|
t.string :current_login_ip
|
32
55
|
t.string :last_login_ip
|
33
56
|
end
|
57
|
+
|
34
58
|
end
|
35
59
|
|
36
60
|
require "active_record/fixtures"
|
37
|
-
|
38
|
-
|
61
|
+
require "authlogic"
|
39
62
|
require "authlogic/test_case"
|
40
63
|
|
41
|
-
|
64
|
+
require "rpx_now"
|
65
|
+
RPX_API_KEY = 'abcdefghijklmnopqrstuvwxyz' unless defined? RPX_API_KEY
|
42
66
|
|
67
|
+
require File.dirname(__FILE__) + "/../lib/authlogic_rpx"
|
43
68
|
|
44
|
-
|
45
|
-
|
46
|
-
|
69
|
+
require File.dirname(__FILE__) + "/libs/rails_trickery"
|
70
|
+
require File.dirname(__FILE__) + '/libs/rpxresponse'
|
71
|
+
require File.dirname(__FILE__) + '/libs/mock_rpx_now'
|
47
72
|
require File.dirname(__FILE__) + '/libs/user'
|
48
73
|
require File.dirname(__FILE__) + '/libs/user_session'
|
49
74
|
|
50
|
-
require File.dirname(__FILE__) + "./../rails/init.rb"
|
51
|
-
|
52
75
|
class ActiveSupport::TestCase
|
53
76
|
include ActiveRecord::TestFixtures
|
54
77
|
self.fixture_path = File.dirname(__FILE__) + "/fixtures"
|
@@ -59,15 +82,6 @@ class ActiveSupport::TestCase
|
|
59
82
|
setup :activate_authlogic
|
60
83
|
|
61
84
|
private
|
62
|
-
def activate_authlogic
|
63
|
-
Authlogic::Session::Base.controller = controller
|
64
|
-
end
|
65
|
-
|
66
|
-
def controller
|
67
|
-
@controller ||= Authlogic::TestCase::ControllerAdapter.new(ActionController.new)
|
68
|
-
end
|
69
85
|
|
70
|
-
|
71
|
-
controller.redirecting_to.to_s =~ /^http:\/\/rpxnow.com/
|
72
|
-
end
|
86
|
+
|
73
87
|
end
|