slayer-authlogic_rpx 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG.rdoc +46 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +37 -0
  5. data/README.rdoc +755 -0
  6. data/Rakefile +55 -0
  7. data/generators/add_authlogic_rpx_migration/USAGE +18 -0
  8. data/generators/add_authlogic_rpx_migration/add_authlogic_rpx_migration_generator.rb +44 -0
  9. data/generators/add_authlogic_rpx_migration/templates/migration_internal_mapping.rb +34 -0
  10. data/generators/add_authlogic_rpx_migration/templates/migration_no_mapping.rb +29 -0
  11. data/init.rb +1 -0
  12. data/lib/authlogic_rpx.rb +9 -0
  13. data/lib/authlogic_rpx/acts_as_authentic.rb +297 -0
  14. data/lib/authlogic_rpx/helper.rb +54 -0
  15. data/lib/authlogic_rpx/rpx_identifier.rb +4 -0
  16. data/lib/authlogic_rpx/session.rb +237 -0
  17. data/lib/authlogic_rpx/version.rb +51 -0
  18. data/rails/init.rb +1 -0
  19. data/slayer-authlogic_rpx.gemspec +102 -0
  20. data/test/fixtures/rpxresponses.yml +20 -0
  21. data/test/fixtures/users.yml +20 -0
  22. data/test/integration/basic_authentication_and_registration_test.rb +53 -0
  23. data/test/integration/internal_mapping/basic_authentication_and_registration_test.rb +3 -0
  24. data/test/integration/internal_mapping/settings_test.rb +10 -0
  25. data/test/integration/no_mapping/basic_authentication_and_registration_test.rb +3 -0
  26. data/test/integration/no_mapping/settings_test.rb +10 -0
  27. data/test/libs/ext_test_unit.rb +30 -0
  28. data/test/libs/mock_rpx_now.rb +34 -0
  29. data/test/libs/rails_trickery.rb +41 -0
  30. data/test/libs/rpxresponse.rb +3 -0
  31. data/test/libs/user.rb +3 -0
  32. data/test/libs/user_session.rb +3 -0
  33. data/test/test_helper.rb +85 -0
  34. data/test/test_internal_mapping_helper.rb +93 -0
  35. data/test/unit/acts_as_authentic_settings_test.rb +42 -0
  36. data/test/unit/session_settings_test.rb +38 -0
  37. data/test/unit/session_validation_test.rb +16 -0
  38. data/test/unit/verify_rpx_mock_test.rb +29 -0
  39. metadata +168 -0
@@ -0,0 +1,54 @@
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
+ # The options hash may include other options as supported by rpx_now (see http://github.com/grosser/rpx_now)
11
+ #
12
+ def rpx_embed(options = {})
13
+ app_name = options.delete( :app_name )
14
+ token_url = build_token_url!( options )
15
+ html = RPXNow.embed_code(app_name, token_url, options )
16
+ if defined? raw
17
+ raw html
18
+ else
19
+ html
20
+ end
21
+ end
22
+
23
+ # helper to insert a link to pop-up RPX login
24
+ # takes options hash:
25
+ # * <tt>link_text:</tt> text to use in the link
26
+ # * <tt>app_name:</tt> name of the application (will be prepended to RPX domain and used in RPX dialogues)
27
+ # * <tt>return_url:</tt> url for the RPX callback (e.g. user_sessions_url)
28
+ # * <tt>add_rpx:</tt> if true, requests RPX callback to add to current session. Else runs normal authentication process (default)
29
+ # * <tt>unobtrusive:</tt> true/false; sets javascript style for link. Default: true
30
+ #
31
+ # The options hash may include other options as supported by rpx_now (see http://github.com/grosser/rpx_now)
32
+ #
33
+ def rpx_popup(options = {})
34
+ options = { :unobtrusive => true, :add_rpx => false }.merge( options )
35
+ app_name = options.delete( :app_name )
36
+ link_text = options.delete( :link_text )
37
+ token_url = build_token_url!( options )
38
+ html = RPXNow.popup_code( link_text, app_name, token_url, options )
39
+ if defined? raw
40
+ raw html
41
+ else
42
+ html
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def build_token_url!( options )
49
+ options.delete( :return_url ) + '?' + (
50
+ { :authenticity_token => form_authenticity_token, :add_rpx => options.delete( :add_rpx ) }.collect { |n| "#{n[0]}=#{ u(n[1]) }" if n[1] }
51
+ ).compact.join('&')
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,4 @@
1
+ class RPXIdentifier < ActiveRecord::Base
2
+ validates_presence_of :identifier
3
+ validates_uniqueness_of :identifier
4
+ end
@@ -0,0 +1,237 @@
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
+ after_persisting :add_rpx_identifier, :if => :adding_rpx_identifier?
65
+ validate :validate_by_rpx, :if => :authenticating_with_rpx?
66
+ end
67
+ end
68
+
69
+ # Determines if the authenticated user is also a new registration.
70
+ # For use in the session controller to help direct the most appropriate action to follow.
71
+ #
72
+ def new_registration?
73
+ new_registration || !new_registration.nil?
74
+ end
75
+
76
+ # Determines if the authenticated user has a complete registration (no validation errors)
77
+ # For use in the session controller to help direct the most appropriate action to follow.
78
+ #
79
+ def registration_complete?
80
+ attempted_record && attempted_record.valid?
81
+ end
82
+
83
+ # TODO: rails 3 authlogic monkeypatch. Not sure if this code is quite right yet
84
+ def to_key
85
+ new_record? ? nil : [ self.send(self.class.primary_key) ]
86
+ end
87
+
88
+
89
+ private
90
+ # Tests if current request is for RPX authentication
91
+ #
92
+ def authenticating_with_rpx?
93
+ controller.params[:token] && !controller.params[:add_rpx]
94
+ end
95
+
96
+ # hook instance finder method to class
97
+ #
98
+ def find_by_rpx_identifier_method
99
+ self.class.find_by_rpx_identifier_method
100
+ end
101
+
102
+ # Tests if auto_registration is enabled (on by default)
103
+ #
104
+ def auto_register?
105
+ self.class.auto_register_value
106
+ end
107
+
108
+ # Tests if rpx_extended_info is enabled (off by default)
109
+ #
110
+ def rpx_extended_info?
111
+ self.class.rpx_extended_info_value
112
+ end
113
+
114
+ # Tests if current request is the special case of adding RPX to an existing account
115
+ #
116
+ def adding_rpx_identifier?
117
+ controller.params[:token] && controller.params[:add_rpx]
118
+ end
119
+
120
+ # Handles the special case of RPX being added to an existing account.
121
+ # At this point, a session has been established as a result of a "save" on the user model (which indirectly triggers user session validation).
122
+ # We do not directly add the RPX details to the user record here in order to avoid getting
123
+ # into a recursive dance between the session and user models.
124
+ # Rather, it uses the trick of adding the necessary RPX information to the session object,
125
+ # and the user model will pluck these values out before completing its validation step.
126
+ #
127
+ def add_rpx_identifier
128
+ data = RPXNow.user_data(controller.params[:token], :extended=> rpx_extended_info? ) {|raw| raw }
129
+ controller.session['added_rpx_data'] = data if data
130
+ end
131
+
132
+ # the main RPX magic. At this point, a session is being validated and we know RPX identifier
133
+ # has been provided. We'll callback to RPX to verify the token, and authenticate the matching
134
+ # user.
135
+ # If no user is found, and we have auto_register enabled (default) this method will also
136
+ # create the user registration stub.
137
+ #
138
+ # On return to the controller, you can test for new_registration? and registration_complete?
139
+ # to determine the most appropriate action
140
+ #
141
+ def validate_by_rpx
142
+ @rpx_data = RPXNow.user_data(
143
+ controller.params[:token],
144
+ :extended => rpx_extended_info?) { |raw| raw }
145
+
146
+ # If we don't have a valid sign-in, give-up at this point
147
+ if @rpx_data.nil? || @rpx_data['profile'].nil?
148
+ errors.add_to_base("Authentication failed. Please try again.")
149
+ return false
150
+ end
151
+
152
+ rpx_id = @rpx_data['profile']['identifier']
153
+ rpx_provider_name = @rpx_data['profile']['providerName']
154
+ if rpx_id.blank?
155
+ errors.add_to_base("Authentication failed. Please try again.")
156
+ return false
157
+ end
158
+
159
+ self.attempted_record = klass.send(find_by_rpx_identifier_method, rpx_id)
160
+
161
+ return false unless rpx_data_valid?
162
+
163
+ # so what do we do if we can't find an existing user matching the RPX authentication...
164
+ if !attempted_record
165
+ if auto_register?
166
+ self.attempted_record = klass.new()
167
+ map_rpx_data
168
+
169
+ # save the new user record - without session maintenance else we
170
+ # get caught in a self-referential hell, since both session and
171
+ # user objects invoke each other upon save
172
+ self.new_registration = true
173
+ self.attempted_record.add_rpx_identifier( rpx_id, rpx_provider_name)
174
+ self.attempted_record.save_without_session_maintenance
175
+ else
176
+ errors.add_to_base("We did not find any accounts with that login. Enter your details and create an account.")
177
+ return false
178
+ end
179
+ else
180
+ map_rpx_data_each_login
181
+ end
182
+
183
+ end
184
+
185
+ # map_rpx_data maps additional fields from the RPX response into the user object during auto-registration.
186
+ # Override this in your session model to change the field mapping
187
+ # See https://rpxnow.com/docs#profile_data for the definition of available attributes
188
+ #
189
+ # In this procedure, you will be writing to fields of the "self.attempted_record" object, pulling data from the @rpx_data object.
190
+ #
191
+ # WARNING: if you are using auto-registration, any fields you map should NOT have constraints enforced at the database level.
192
+ # authlogic_rpx will optimistically attempt to save the user record during registration, and
193
+ # violating a database constraint will cause the authentication/registration to fail.
194
+ #
195
+ # You can/should enforce any required validations at the model level e.g.
196
+ # validates_uniqueness_of :username, :case_sensitive => false
197
+ # This will allow the auto-registration to proceed, and the user can be given a chance to rectify the validation errors
198
+ # on your user profile page.
199
+ #
200
+ # If it is not acceptable in your application to have user records created with potential validation errors in auto-populated fields, you
201
+ # will need to override map_rpx_data and implement whatever special handling makes sense in your case. For example:
202
+ # - directly check for uniqueness and other validation requirements
203
+ # - automatically "uniquify" fields like username
204
+ # - save conflicting profile information to "pending user review" columns or a seperate table
205
+ #
206
+ def map_rpx_data
207
+ self.attempted_record.send("#{klass.login_field}=", @rpx_data['profile']['preferredUsername'] ) if attempted_record.send(klass.login_field).blank?
208
+ self.attempted_record.send("#{klass.email_field}=", @rpx_data['profile']['email'] ) if attempted_record.send(klass.email_field).blank?
209
+ end
210
+
211
+ # map_rpx_data_each_login provides a hook to allow you to map RPX profile information every time the user
212
+ # logs in.
213
+ # By default, nothing is mapped.
214
+ #
215
+ # This would mainly be used to update relatively volatile information that you are maintaining in the user model (such as profile image url)
216
+ #
217
+ # In this procedure, you will be writing to fields of the "self.attempted_record" object, pulling data from the @rpx_data object.
218
+ #
219
+ #
220
+ def map_rpx_data_each_login
221
+
222
+ end
223
+
224
+ # rpx_data_valid? provides a hook to allow you to validate RPX profile information every time the user
225
+ # logs in.
226
+ # By default, no validation is performed
227
+ #
228
+ # For instance you can check if user with @rpx_data['profile']['email'] is found in database
229
+ #
230
+ def rpx_data_valid?
231
+ true
232
+ end
233
+
234
+ end
235
+
236
+ end
237
+ 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 = 2
44
+ TINY = 1
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/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "authlogic_rpx"
@@ -0,0 +1,102 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{slayer-authlogic_rpx}
8
+ s.version = "1.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Paul Gallagher / tardate <gallagher.paul@gmail.com>", "Vladislav Moskovets <github@vlad.org.ua>"]
12
+ s.date = %q{2011-07-06}
13
+ s.description = %q{Authlogic extension/plugin that provides RPX (rpxnow.com) authentication support}
14
+ s.email = %q{gallagher.paul@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "CHANGELOG.rdoc",
21
+ "MIT-LICENSE",
22
+ "Manifest",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "generators/add_authlogic_rpx_migration/USAGE",
26
+ "generators/add_authlogic_rpx_migration/add_authlogic_rpx_migration_generator.rb",
27
+ "generators/add_authlogic_rpx_migration/templates/migration_internal_mapping.rb",
28
+ "generators/add_authlogic_rpx_migration/templates/migration_no_mapping.rb",
29
+ "init.rb",
30
+ "lib/authlogic_rpx.rb",
31
+ "lib/authlogic_rpx/acts_as_authentic.rb",
32
+ "lib/authlogic_rpx/helper.rb",
33
+ "lib/authlogic_rpx/rpx_identifier.rb",
34
+ "lib/authlogic_rpx/session.rb",
35
+ "lib/authlogic_rpx/version.rb",
36
+ "rails/init.rb",
37
+ "slayer-authlogic_rpx.gemspec",
38
+ "test/fixtures/rpxresponses.yml",
39
+ "test/fixtures/users.yml",
40
+ "test/integration/basic_authentication_and_registration_test.rb",
41
+ "test/integration/internal_mapping/basic_authentication_and_registration_test.rb",
42
+ "test/integration/internal_mapping/settings_test.rb",
43
+ "test/integration/no_mapping/basic_authentication_and_registration_test.rb",
44
+ "test/integration/no_mapping/settings_test.rb",
45
+ "test/libs/ext_test_unit.rb",
46
+ "test/libs/mock_rpx_now.rb",
47
+ "test/libs/rails_trickery.rb",
48
+ "test/libs/rpxresponse.rb",
49
+ "test/libs/user.rb",
50
+ "test/libs/user_session.rb",
51
+ "test/test_helper.rb",
52
+ "test/test_internal_mapping_helper.rb",
53
+ "test/unit/acts_as_authentic_settings_test.rb",
54
+ "test/unit/session_settings_test.rb",
55
+ "test/unit/session_validation_test.rb",
56
+ "test/unit/verify_rpx_mock_test.rb"
57
+ ]
58
+ s.homepage = %q{http://github.com/tardate/authlogic_rpx}
59
+ s.rdoc_options = ["--charset=UTF-8"]
60
+ s.require_paths = ["lib"]
61
+ s.rubygems_version = %q{1.3.7}
62
+ s.summary = %q{Authlogic plug-in for RPX support}
63
+ s.test_files = [
64
+ "test/test_internal_mapping_helper.rb",
65
+ "test/libs/rpxresponse.rb",
66
+ "test/libs/user_session.rb",
67
+ "test/libs/rails_trickery.rb",
68
+ "test/libs/mock_rpx_now.rb",
69
+ "test/libs/user.rb",
70
+ "test/libs/ext_test_unit.rb",
71
+ "test/unit/session_settings_test.rb",
72
+ "test/unit/verify_rpx_mock_test.rb",
73
+ "test/unit/acts_as_authentic_settings_test.rb",
74
+ "test/unit/session_validation_test.rb",
75
+ "test/test_helper.rb",
76
+ "test/integration/no_mapping/basic_authentication_and_registration_test.rb",
77
+ "test/integration/no_mapping/settings_test.rb",
78
+ "test/integration/internal_mapping/basic_authentication_and_registration_test.rb",
79
+ "test/integration/internal_mapping/settings_test.rb",
80
+ "test/integration/basic_authentication_and_registration_test.rb"
81
+ ]
82
+
83
+ if s.respond_to? :specification_version then
84
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
85
+ s.specification_version = 3
86
+
87
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
88
+ s.add_runtime_dependency(%q<authlogic>, [">= 3.0.3"])
89
+ s.add_runtime_dependency(%q<rpx_now>, [">= 0.6.23"])
90
+ s.add_development_dependency(%q<test-unit>, [">= 2.1.1"])
91
+ else
92
+ s.add_dependency(%q<authlogic>, [">= 3.0.3"])
93
+ s.add_dependency(%q<rpx_now>, [">= 0.6.23"])
94
+ s.add_dependency(%q<test-unit>, [">= 2.1.1"])
95
+ end
96
+ else
97
+ s.add_dependency(%q<authlogic>, [">= 3.0.3"])
98
+ s.add_dependency(%q<rpx_now>, [">= 0.6.23"])
99
+ s.add_dependency(%q<test-unit>, [">= 2.1.1"])
100
+ end
101
+ end
102
+