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.
Files changed (36) hide show
  1. data/CHANGELOG.rdoc +10 -0
  2. data/Manifest +19 -2
  3. data/README.rdoc +284 -72
  4. data/Rakefile +30 -2
  5. data/authlogic_rpx.gemspec +11 -11
  6. data/generators/add_authlogic_rpx_migration/USAGE +18 -0
  7. data/generators/add_authlogic_rpx_migration/add_authlogic_rpx_migration_generator.rb +44 -0
  8. data/generators/add_authlogic_rpx_migration/templates/migration_internal_mapping.rb +34 -0
  9. data/generators/add_authlogic_rpx_migration/templates/migration_no_mapping.rb +29 -0
  10. data/lib/authlogic_rpx.rb +1 -0
  11. data/lib/authlogic_rpx/acts_as_authentic.rb +199 -32
  12. data/lib/authlogic_rpx/helper.rb +21 -13
  13. data/lib/authlogic_rpx/rpx_identifier.rb +5 -0
  14. data/lib/authlogic_rpx/session.rb +23 -16
  15. data/lib/authlogic_rpx/version.rb +2 -2
  16. data/test/fixtures/rpxresponses.yml +20 -0
  17. data/test/fixtures/users.yml +20 -5
  18. data/test/integration/basic_authentication_and_registration_test.rb +53 -0
  19. data/test/integration/internal_mapping/basic_authentication_and_registration_test.rb +3 -0
  20. data/test/integration/internal_mapping/settings_test.rb +10 -0
  21. data/test/integration/no_mapping/basic_authentication_and_registration_test.rb +3 -0
  22. data/test/integration/no_mapping/settings_test.rb +10 -0
  23. data/test/libs/ext_test_unit.rb +30 -0
  24. data/test/libs/mock_rpx_now.rb +34 -0
  25. data/test/libs/rails_trickery.rb +2 -2
  26. data/test/libs/rpxresponse.rb +3 -0
  27. data/test/libs/user_session.rb +1 -1
  28. data/test/test_helper.rb +35 -21
  29. data/test/test_internal_mapping_helper.rb +95 -0
  30. data/test/unit/acts_as_authentic_settings_test.rb +42 -0
  31. data/test/unit/session_settings_test.rb +38 -0
  32. data/test/unit/session_validation_test.rb +16 -0
  33. data/test/unit/verify_rpx_mock_test.rb +29 -0
  34. metadata +34 -8
  35. data/test/acts_as_authentic_test.rb +0 -5
  36. data/test/session_test.rb +0 -10
@@ -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
- # NB: i18n considerations? supports a :language parameter (not tested)
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
- 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
- )
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
@@ -0,0 +1,5 @@
1
+ class RPXIdentifier < ActiveRecord::Base
2
+ validates_presence_of :identifier
3
+ validates_uniqueness_of :identifier
4
+ validates_presence_of :user_id
5
+ 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 ! inheritable_attributes.include?(:rpx_key)
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
- # hook instance finder method to class
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(controller.params[:token], :extended=> rpx_extended_info? ) {|raw| raw }
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( :rpx_identifier=> rpx_id )
157
+ if auto_register?
158
+ self.attempted_record = klass.new()
155
159
  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
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
- map_rpx_data_each_login
172
+ map_rpx_data_each_login
166
173
  end
167
174
 
168
175
  end
@@ -40,8 +40,8 @@ module AuthlogicRpx
40
40
  end
41
41
 
42
42
  MAJOR = 1
43
- MINOR = 0
44
- TINY = 4
43
+ MINOR = 1
44
+ TINY = 0
45
45
 
46
46
  # The current version as a Version instance
47
47
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -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
@@ -1,5 +1,20 @@
1
- john:
2
- persistence_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
3
- single_access_token: <%= Authlogic::Random.friendly_token %>
4
- perishable_token: <%= Authlogic::Random.friendly_token %>
5
- rpx_identifier : johns_rpx_identifier
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,3 @@
1
+ require File.dirname(__FILE__) + '/../../test_internal_mapping_helper.rb'
2
+ require File.dirname(__FILE__) + '/../basic_authentication_and_registration_test.rb'
3
+
@@ -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,3 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper.rb'
2
+ require File.dirname(__FILE__) + '/../basic_authentication_and_registration_test.rb'
3
+
@@ -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
@@ -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 ActionController < Authlogic::TestCase::MockController
7
+ class ActionControllerEx < Authlogic::TestCase::MockController
8
8
  class Request < Authlogic::TestCase::MockRequest
9
9
  def request_method
10
10
  ""
@@ -0,0 +1,3 @@
1
+ class Rpxresponse < ActiveRecord::Base
2
+
3
+ end
@@ -1,3 +1,3 @@
1
1
  class UserSession < Authlogic::Session::Base
2
-
2
+ rpx_key RPX_API_KEY
3
3
  end
@@ -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
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
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
- Rails = true
38
-
61
+ require "authlogic"
39
62
  require "authlogic/test_case"
40
63
 
41
- include Authlogic::TestCase
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
- #require File.dirname(__FILE__) + "/../../authlogic/lib/authlogic"
45
- #require File.dirname(__FILE__) + "/../../authlogic/lib/authlogic/test_case"
46
- #require File.dirname(__FILE__) + "/libs/rails_trickery"
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
- def redirecting_to_rpx?
71
- controller.redirecting_to.to_s =~ /^http:\/\/rpxnow.com/
72
- end
86
+
73
87
  end