okkez-open_id_authentication 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ * Fake HTTP method from OpenID server since they only support a GET. Eliminates the need to set an extra route to match the server's reply. [Josh Peek]
2
+
3
+ * OpenID 2.0 recommends that forms should use the field name "openid_identifier" rather than "openid_url" [Josh Peek]
4
+
5
+ * Return open_id_response.display_identifier to the application instead of .endpoints.claimed_id. [nbibler]
6
+
7
+ * Add Timeout protection [Rick]
8
+
9
+ * An invalid identity url passed through authenticate_with_open_id will no longer raise an InvalidOpenId exception. Instead it will return Result[:missing] to the completion block.
10
+
11
+ * Allow a return_to option to be used instead of the requested url [Josh Peek]
12
+
13
+ * Updated plugin to use Ruby OpenID 2.x.x [Josh Peek]
14
+
15
+ * Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH]
16
+
17
+ * Use URI instead of regexps to normalize the URL and gain free, better matching #8136 [dkubb]
18
+
19
+ * Allow -'s in #normalize_url [Rick]
20
+
21
+ * remove instance of mattr_accessor, it was breaking tests since they don't load ActiveSupport. Fix Timeout test [Rick]
22
+
23
+ * Throw a InvalidOpenId exception instead of just a RuntimeError when the URL can't be normalized [DHH]
24
+
25
+ * Just use the path for the return URL, so extra query parameters don't interfere [DHH]
26
+
27
+ * Added a new default database-backed store after experiencing trouble with the filestore on NFS. The file store is still available as an option [DHH]
28
+
29
+ * Added normalize_url and applied it to all operations going through the plugin [DHH]
30
+
31
+ * Removed open_id? as the idea of using the same input box for both OpenID and username has died -- use using_open_id? instead (which checks for the presence of params[:openid_url] by default) [DHH]
32
+
33
+ * Added OpenIdAuthentication::Result to make it easier to deal with default situations where you don't care to do something particular for each error state [DHH]
34
+
35
+ * Stop relying on root_url being defined, we can just grab the current url instead [DHH]
data/README ADDED
@@ -0,0 +1,231 @@
1
+ OpenIdAuthentication
2
+ ====================
3
+
4
+ Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
5
+
6
+ gem install ruby-openid
7
+
8
+ To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
9
+ from that gem.
10
+
11
+ The specification used is http://openid.net/specs/openid-authentication-2_0.html.
12
+
13
+
14
+ Prerequisites
15
+ =============
16
+
17
+ OpenID authentication uses the session, so be sure that you haven't turned that off. It also relies on a number of
18
+ database tables to store the authentication keys. So you'll have to run the migration to create these before you get started:
19
+
20
+ rake open_id_authentication:db:create
21
+
22
+ Or, use the included generators to install or upgrade:
23
+
24
+ ./script/generate open_id_authentication_tables MigrationName
25
+ ./script/generate upgrade_open_id_authentication_tables MigrationName
26
+
27
+ Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb:
28
+
29
+ OpenIdAuthentication.store = :file
30
+
31
+ This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
32
+ If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
33
+
34
+ The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb:
35
+
36
+ map.root :controller => 'articles'
37
+
38
+ This plugin relies on Rails Edge revision 6317 or newer.
39
+
40
+
41
+ Example
42
+ =======
43
+
44
+ This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
45
+ salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
46
+ not a destination.
47
+
48
+ Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever
49
+ model you are using for authentication.
50
+
51
+ Also of note is the following code block used in the example below:
52
+
53
+ authenticate_with_open_id do |result, identity_url|
54
+ ...
55
+ end
56
+
57
+ In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' -
58
+ If you are storing just 'example.com' with your user, the lookup will fail.
59
+
60
+ There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
61
+
62
+ OpenIdAuthentication.normalize_url(user.identity_url)
63
+
64
+ The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
65
+ It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
66
+ Use the above code in your User model and validate OpenID URLs before saving them.
67
+
68
+ config/routes.rb
69
+
70
+ map.root :controller => 'articles'
71
+ map.resource :session
72
+
73
+
74
+ app/views/sessions/new.erb
75
+
76
+ <% form_tag(session_url) do %>
77
+ <p>
78
+ <label for="name">Username:</label>
79
+ <%= text_field_tag "name" %>
80
+ </p>
81
+
82
+ <p>
83
+ <label for="password">Password:</label>
84
+ <%= password_field_tag %>
85
+ </p>
86
+
87
+ <p>
88
+ ...or use:
89
+ </p>
90
+
91
+ <p>
92
+ <label for="openid_identifier">OpenID:</label>
93
+ <%= text_field_tag "openid_identifier" %>
94
+ </p>
95
+
96
+ <p>
97
+ <%= submit_tag 'Sign in', :disable_with => "Signing in&hellip;" %>
98
+ </p>
99
+ <% end %>
100
+
101
+ app/controllers/sessions_controller.rb
102
+ class SessionsController < ApplicationController
103
+ def create
104
+ if using_open_id?
105
+ open_id_authentication
106
+ else
107
+ password_authentication(params[:name], params[:password])
108
+ end
109
+ end
110
+
111
+
112
+ protected
113
+ def password_authentication(name, password)
114
+ if @current_user = @account.users.authenticate(params[:name], params[:password])
115
+ successful_login
116
+ else
117
+ failed_login "Sorry, that username/password doesn't work"
118
+ end
119
+ end
120
+
121
+ def open_id_authentication
122
+ authenticate_with_open_id do |result, identity_url|
123
+ if result.successful?
124
+ if @current_user = @account.users.find_by_identity_url(identity_url)
125
+ successful_login
126
+ else
127
+ failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
128
+ end
129
+ else
130
+ failed_login result.message
131
+ end
132
+ end
133
+ end
134
+
135
+
136
+ private
137
+ def successful_login
138
+ session[:user_id] = @current_user.id
139
+ redirect_to(root_url)
140
+ end
141
+
142
+ def failed_login(message)
143
+ flash[:error] = message
144
+ redirect_to(new_session_url)
145
+ end
146
+ end
147
+
148
+
149
+
150
+ If you're fine with the result messages above and don't need individual logic on a per-failure basis,
151
+ you can collapse the case into a mere boolean:
152
+
153
+ def open_id_authentication
154
+ authenticate_with_open_id do |result, identity_url|
155
+ if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
156
+ successful_login
157
+ else
158
+ failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
159
+ end
160
+ end
161
+ end
162
+
163
+
164
+ Simple Registration OpenID Extension
165
+ ====================================
166
+
167
+ Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension
168
+
169
+ You can support it in your app by changing #open_id_authentication
170
+
171
+ def open_id_authentication(identity_url)
172
+ # Pass optional :required and :optional keys to specify what sreg fields you want.
173
+ # Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
174
+ authenticate_with_open_id(identity_url,
175
+ :required => [ :nickname, :email ],
176
+ :optional => :fullname) do |result, identity_url, registration|
177
+ case result.status
178
+ when :missing
179
+ failed_login "Sorry, the OpenID server couldn't be found"
180
+ when :invalid
181
+ failed_login "Sorry, but this does not appear to be a valid OpenID"
182
+ when :canceled
183
+ failed_login "OpenID verification was canceled"
184
+ when :failed
185
+ failed_login "Sorry, the OpenID verification failed"
186
+ when :successful
187
+ if @current_user = @account.users.find_by_identity_url(identity_url)
188
+ assign_registration_attributes!(registration)
189
+
190
+ if current_user.save
191
+ successful_login
192
+ else
193
+ failed_login "Your OpenID profile registration failed: " +
194
+ @current_user.errors.full_messages.to_sentence
195
+ end
196
+ else
197
+ failed_login "Sorry, no user by that identity URL exists"
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ # registration is a hash containing the valid sreg keys given above
204
+ # use this to map them to fields of your user model
205
+ def assign_registration_attributes!(registration)
206
+ model_to_registration_mapping.each do |model_attribute, registration_attribute|
207
+ unless registration[registration_attribute].blank?
208
+ @current_user.send("#{model_attribute}=", registration[registration_attribute])
209
+ end
210
+ end
211
+ end
212
+
213
+ def model_to_registration_mapping
214
+ { :login => 'nickname', :email => 'email', :display_name => 'fullname' }
215
+ end
216
+
217
+ Attribute Exchange OpenID Extension
218
+ ===================================
219
+
220
+ Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints. See more: http://openid.net/specs/openid-attribute-exchange-1_0.html
221
+
222
+ Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters. For example:
223
+
224
+ authenticate_with_open_id(identity_url,
225
+ :required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
226
+
227
+ This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
228
+
229
+
230
+
231
+ Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
@@ -0,0 +1,85 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/packagetask'
5
+ require 'rake/gempackagetask'
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => :test
9
+
10
+ desc 'Test the open_id_authentication plugin.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs << 'lib'
13
+ t.pattern = 'test/**/*_test.rb'
14
+ t.verbose = true
15
+ end
16
+
17
+ desc 'Generate documentation for the open_id_authentication plugin.'
18
+ Rake::RDocTask.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'OpenIdAuthentication'
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ rdoc.rdoc_files.include('README')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
25
+
26
+ NAME = ENV["GEMNAME"] || "open_id_authentication"
27
+ DESCRIPTION = "Provides a thin wrapper around the excellent ruby-openid gem from JanRan. gem version is unofficial, github fork of rails's by moro"
28
+ AUTHOR = "David Heinemeier Hansson"
29
+ EMAIL = ""
30
+ HOMEPAGE = "http://github.com/rails/open_id_authentication"
31
+
32
+ RDOC_OPTS = [
33
+ '--title', "#{NAME} documentation",
34
+ "--charset", "utf-8",
35
+ "--opname", "index.html",
36
+ "--line-numbers",
37
+ "--main", "README",
38
+ "--inline-source",
39
+ ]
40
+
41
+ spec = Gem::Specification.new do |s|
42
+ s.name = NAME
43
+ s.version = "1.0.1"
44
+ s.platform = Gem::Platform::RUBY
45
+ s.has_rdoc = true
46
+ s.extra_rdoc_files = ["README", "CHANGELOG"]
47
+ s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
48
+ s.summary = DESCRIPTION
49
+ s.description = DESCRIPTION
50
+ s.author = AUTHOR
51
+ s.email = EMAIL
52
+ s.homepage = HOMEPAGE
53
+ s.executables = []
54
+ s.require_path = "lib"
55
+ s.test_files = Dir["test/*_test.rb"]
56
+
57
+ s.add_dependency("ruby-openid", ">=2.0.4")
58
+ #s.add_dependency('activesupport', '>=1.3.1')
59
+ #s.required_ruby_version = '>= 1.8.2'
60
+
61
+ s.files = %w(README CHANGELOG Rakefile) +
62
+ Dir.glob("{bin,doc,test,lib,templates,generators,extras,website,script}/**/*") +
63
+ Dir.glob("ext/**/*.{h,c,rb}") +
64
+ Dir.glob("examples/**/*.rb") +
65
+ Dir.glob("tools/*.rb") +
66
+ Dir.glob("rails/*.rb")
67
+
68
+ s.extensions = FileList["ext/**/extconf.rb"].to_a
69
+ end
70
+
71
+ Rake::GemPackageTask.new(spec) do |p|
72
+ p.need_tar = true
73
+ p.gem_spec = spec
74
+ end
75
+
76
+ desc 'Show information about the gem.'
77
+ task :debug_gem do
78
+ puts spec.to_ruby
79
+ end
80
+
81
+ desc 'Update gem spec'
82
+ task :gemspec do
83
+ open("#{NAME}.gemspec", 'w').write spec.to_ruby
84
+ end
85
+
@@ -0,0 +1,11 @@
1
+ class OpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
2
+ def initialize(runtime_args, runtime_options = {})
3
+ super
4
+ end
5
+
6
+ def manifest
7
+ record do |m|
8
+ m.migration_template 'migration.rb', 'db/migrate'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ class <%= class_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :open_id_authentication_associations, :force => true do |t|
4
+ t.integer :issued, :lifetime
5
+ t.string :handle, :assoc_type
6
+ t.binary :server_url, :secret
7
+ end
8
+
9
+ create_table :open_id_authentication_nonces, :force => true do |t|
10
+ t.integer :timestamp, :null => false
11
+ t.string :server_url, :null => true
12
+ t.string :salt, :null => false
13
+ end
14
+ end
15
+
16
+ def self.down
17
+ drop_table :open_id_authentication_associations
18
+ drop_table :open_id_authentication_nonces
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ class <%= class_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ drop_table :open_id_authentication_settings
4
+ drop_table :open_id_authentication_nonces
5
+
6
+ create_table :open_id_authentication_nonces, :force => true do |t|
7
+ t.integer :timestamp, :null => false
8
+ t.string :server_url, :null => true
9
+ t.string :salt, :null => false
10
+ end
11
+ end
12
+
13
+ def self.down
14
+ drop_table :open_id_authentication_nonces
15
+
16
+ create_table :open_id_authentication_nonces, :force => true do |t|
17
+ t.integer :created
18
+ t.string :nonce
19
+ end
20
+
21
+ create_table :open_id_authentication_settings, :force => true do |t|
22
+ t.string :setting
23
+ t.binary :value
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ class UpgradeOpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
2
+ def initialize(runtime_args, runtime_options = {})
3
+ super
4
+ end
5
+
6
+ def manifest
7
+ record do |m|
8
+ m.migration_template 'migration.rb', 'db/migrate'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,237 @@
1
+ require 'uri'
2
+ require 'openid/extensions/sreg'
3
+ require 'openid/extensions/ax'
4
+ require 'openid/store/filesystem'
5
+
6
+ require File.dirname(__FILE__) + '/open_id_authentication/association'
7
+ require File.dirname(__FILE__) + '/open_id_authentication/nonce'
8
+ require File.dirname(__FILE__) + '/open_id_authentication/db_store'
9
+ require File.dirname(__FILE__) + '/open_id_authentication/request'
10
+ require File.dirname(__FILE__) + '/open_id_authentication/timeout_fixes' if OpenID::VERSION == "2.0.4"
11
+
12
+ module OpenIdAuthentication
13
+ OPEN_ID_AUTHENTICATION_DIR = RAILS_ROOT + "/tmp/openids"
14
+
15
+ def self.store
16
+ @@store
17
+ end
18
+
19
+ def self.store=(*store_option)
20
+ store, *parameters = *([ store_option ].flatten)
21
+
22
+ @@store = case store
23
+ when :db
24
+ OpenIdAuthentication::DbStore.new
25
+ when :file
26
+ OpenID::Store::Filesystem.new(OPEN_ID_AUTHENTICATION_DIR)
27
+ else
28
+ store
29
+ end
30
+ end
31
+
32
+ self.store = :db
33
+
34
+ class InvalidOpenId < StandardError
35
+ end
36
+
37
+ class Result
38
+ ERROR_MESSAGES = {
39
+ :missing => "Sorry, the OpenID server couldn't be found",
40
+ :invalid => "Sorry, but this does not appear to be a valid OpenID",
41
+ :canceled => "OpenID verification was canceled",
42
+ :failed => "OpenID verification failed",
43
+ :setup_needed => "OpenID verification needs setup"
44
+ }
45
+
46
+ def self.[](code)
47
+ new(code)
48
+ end
49
+
50
+ def initialize(code)
51
+ @code = code
52
+ end
53
+
54
+ def status
55
+ @code
56
+ end
57
+
58
+ ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
59
+
60
+ def successful?
61
+ @code == :successful
62
+ end
63
+
64
+ def unsuccessful?
65
+ ERROR_MESSAGES.keys.include?(@code)
66
+ end
67
+
68
+ def message
69
+ ERROR_MESSAGES[@code]
70
+ end
71
+ end
72
+
73
+ # normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
74
+ def self.normalize_identifier(identifier)
75
+ # clean up whitespace
76
+ identifier = identifier.to_s.strip
77
+
78
+ case OpenID::Yadis::XRI.identifier_scheme(identifier)
79
+ when :xri
80
+ identifier.gsub!(/xri:\/\//i, '')
81
+ else
82
+ identifier = "http://#{identifier}" unless identifier =~ /^http/i
83
+
84
+ # strip any fragments
85
+ identifier.gsub!(/\#(.*)$/, '')
86
+
87
+ begin
88
+ uri = URI.parse(identifier)
89
+ uri.scheme = uri.scheme.downcase # URI should do this
90
+ identifier = uri.normalize.to_s
91
+ rescue URI::InvalidURIError
92
+ raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
93
+ end
94
+ end
95
+
96
+ return identifier
97
+ end
98
+
99
+ # deprecated for OpenID 2.0, where not all OpenIDs are URLs
100
+ def self.normalize_url(url)
101
+ ActiveSupport::Deprecation.warn "normalize_url has been deprecated, use normalize_identifier instead"
102
+ self.normalize_identifier(url)
103
+ end
104
+
105
+ protected
106
+ def normalize_url(url)
107
+ OpenIdAuthentication.normalize_url(url)
108
+ end
109
+
110
+ def normalize_identifier(url)
111
+ OpenIdAuthentication.normalize_identifier(url)
112
+ end
113
+
114
+ # The parameter name of "openid_identifier" is used rather than the Rails convention "open_id_identifier"
115
+ # because that's what the specification dictates in order to get browser auto-complete working across sites
116
+ def using_open_id?(identity_url = nil) #:doc:
117
+ identity_url ||= params[:openid_identifier] || params[:openid_url]
118
+ !identity_url.blank? || params[:open_id_complete]
119
+ end
120
+
121
+ def authenticate_with_open_id(identity_url = nil, options = {}, &block) #:doc:
122
+ identity_url ||= params[:openid_identifier] || params[:openid_url]
123
+
124
+ if params[:open_id_complete].nil?
125
+ begin_open_id_authentication(identity_url, options, &block)
126
+ else
127
+ complete_open_id_authentication(&block)
128
+ end
129
+ end
130
+
131
+ private
132
+ def begin_open_id_authentication(identity_url, options = {})
133
+ identity_url = normalize_identifier(identity_url)
134
+ return_to = options.delete(:return_to)
135
+ method = options.delete(:method)
136
+
137
+ options[:required] ||= [] # reduces validation later
138
+ options[:optional] ||= []
139
+
140
+ open_id_request = open_id_consumer.begin(identity_url)
141
+ add_simple_registration_fields(open_id_request, options)
142
+ add_ax_fields(open_id_request, options)
143
+ redirect_to(open_id_redirect_url(open_id_request, return_to, method))
144
+ rescue OpenIdAuthentication::InvalidOpenId => e
145
+ yield Result[:invalid], identity_url, nil
146
+ rescue OpenID::OpenIDError, Timeout::Error => e
147
+ logger.error("[OPENID] #{e}")
148
+ yield Result[:missing], identity_url, nil
149
+ end
150
+
151
+ def complete_open_id_authentication
152
+ params_with_path = params.reject { |key, value| request.path_parameters[key] }
153
+ params_with_path.delete(:format)
154
+ open_id_response = timeout_protection_from_identity_server { open_id_consumer.complete(params_with_path, requested_url) }
155
+
156
+ case open_id_response.status
157
+ when OpenID::Consumer::SUCCESS
158
+ profile_data = {}
159
+
160
+ # merge the SReg data and the AX data into a single hash of profile data
161
+ [ OpenID::SReg::Response, OpenID::AX::FetchResponse ].each do |data_response|
162
+ if data_response.from_success_response( open_id_response )
163
+ profile_data.merge! data_response.from_success_response( open_id_response ).data
164
+ end
165
+ end
166
+
167
+ yield Result[:successful], open_id_response.identity_url, profile_data
168
+ when OpenID::Consumer::CANCEL
169
+ yield Result[:canceled], open_id_response.identity_url, nil
170
+ when OpenID::Consumer::FAILURE
171
+ yield Result[:failed], open_id_response.identity_url, nil
172
+ when OpenID::Consumer::SETUP_NEEDED
173
+ yield Result[:setup_needed], open_id_response.setup_url, nil
174
+ end
175
+ end
176
+
177
+ def open_id_consumer
178
+ OpenID::Consumer.new(session, OpenIdAuthentication.store)
179
+ end
180
+
181
+ def add_simple_registration_fields(open_id_request, fields)
182
+ sreg_request = OpenID::SReg::Request.new
183
+
184
+ # filter out AX identifiers (URIs)
185
+ required_fields = fields[:required].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
186
+ optional_fields = fields[:optional].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
187
+
188
+ sreg_request.request_fields(required_fields, true) unless required_fields.blank?
189
+ sreg_request.request_fields(optional_fields, false) unless optional_fields.blank?
190
+ sreg_request.policy_url = fields[:policy_url] if fields[:policy_url]
191
+ open_id_request.add_extension(sreg_request)
192
+ end
193
+
194
+ def add_ax_fields( open_id_request, fields )
195
+ ax_request = OpenID::AX::FetchRequest.new
196
+
197
+ # look through the :required and :optional fields for URIs (AX identifiers)
198
+ fields[:required].each do |f|
199
+ next unless f =~ /^https?:\/\//
200
+ ax_request.add( OpenID::AX::AttrInfo.new( f, nil, true ) )
201
+ end
202
+
203
+ fields[:optional].each do |f|
204
+ next unless f =~ /^https?:\/\//
205
+ ax_request.add( OpenID::AX::AttrInfo.new( f, nil, false ) )
206
+ end
207
+
208
+ open_id_request.add_extension( ax_request )
209
+ end
210
+
211
+ def open_id_redirect_url(open_id_request, return_to = nil, method = nil)
212
+ open_id_request.return_to_args['_method'] = (method || request.method).to_s
213
+ open_id_request.return_to_args['open_id_complete'] = '1'
214
+ open_id_request.redirect_url(root_url, return_to || requested_url)
215
+ end
216
+
217
+ def requested_url
218
+ relative_url_root = self.class.respond_to?(:relative_url_root) ?
219
+ self.class.relative_url_root.to_s :
220
+ request.relative_url_root
221
+ "#{request.protocol}#{request.host_with_port}#{relative_url_root}#{request.path}"
222
+ end
223
+
224
+ def timeout_protection_from_identity_server
225
+ yield
226
+ rescue Timeout::Error
227
+ Class.new do
228
+ def status
229
+ OpenID::FAILURE
230
+ end
231
+
232
+ def msg
233
+ "Identity server timed out"
234
+ end
235
+ end.new
236
+ end
237
+ end
@@ -0,0 +1,9 @@
1
+ module OpenIdAuthentication
2
+ class Association < ActiveRecord::Base
3
+ set_table_name :open_id_authentication_associations
4
+
5
+ def from_record
6
+ OpenID::Association.new(handle, secret, issued, lifetime, assoc_type)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,55 @@
1
+ require 'openid/store/interface'
2
+
3
+ module OpenIdAuthentication
4
+ class DbStore < OpenID::Store::Interface
5
+ def self.cleanup_nonces
6
+ now = Time.now.to_i
7
+ Nonce.delete_all(["timestamp > ? OR timestamp < ?", now + OpenID::Nonce.skew, now - OpenID::Nonce.skew])
8
+ end
9
+
10
+ def self.cleanup_associations
11
+ now = Time.now.to_i
12
+ Association.delete_all(['issued + lifetime > ?',now])
13
+ end
14
+
15
+ def store_association(server_url, assoc)
16
+ remove_association(server_url, assoc.handle)
17
+ Association.create(:server_url => server_url,
18
+ :handle => assoc.handle,
19
+ :secret => assoc.secret,
20
+ :issued => assoc.issued,
21
+ :lifetime => assoc.lifetime,
22
+ :assoc_type => assoc.assoc_type)
23
+ end
24
+
25
+ def get_association(server_url, handle = nil)
26
+ assocs = if handle.blank?
27
+ Association.find_all_by_server_url(server_url)
28
+ else
29
+ Association.find_all_by_server_url_and_handle(server_url, handle)
30
+ end
31
+
32
+ assocs.reverse.each do |assoc|
33
+ a = assoc.from_record
34
+ if a.expires_in == 0
35
+ assoc.destroy
36
+ else
37
+ return a
38
+ end
39
+ end if assocs.any?
40
+
41
+ return nil
42
+ end
43
+
44
+ def remove_association(server_url, handle)
45
+ Association.delete_all(['server_url = ? AND handle = ?', server_url, handle]) > 0
46
+ end
47
+
48
+ def use_nonce(server_url, timestamp, salt)
49
+ return false if Nonce.find_by_server_url_and_timestamp_and_salt(server_url, timestamp, salt)
50
+ return false if (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
51
+ Nonce.create(:server_url => server_url, :timestamp => timestamp, :salt => salt)
52
+ return true
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ module OpenIdAuthentication
2
+ class Nonce < ActiveRecord::Base
3
+ set_table_name :open_id_authentication_nonces
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ module OpenIdAuthentication
2
+ module Request
3
+ def self.included(base)
4
+ base.alias_method_chain :request_method, :openid
5
+ end
6
+
7
+ def request_method_with_openid
8
+ if !parameters[:_method].blank? && parameters[:open_id_complete] == '1'
9
+ parameters[:_method].to_sym
10
+ else
11
+ request_method_without_openid
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ # In Rails 2.3, the request object has been renamed
18
+ # from AbstractRequest to Request
19
+ if defined? ActionController::Request
20
+ ActionController::Request.send :include, OpenIdAuthentication::Request
21
+ else
22
+ ActionController::AbstractRequest.send :include, OpenIdAuthentication::Request
23
+ end
@@ -0,0 +1,20 @@
1
+ # http://trac.openidenabled.com/trac/ticket/156
2
+ module OpenID
3
+ @@timeout_threshold = 20
4
+
5
+ def self.timeout_threshold
6
+ @@timeout_threshold
7
+ end
8
+
9
+ def self.timeout_threshold=(value)
10
+ @@timeout_threshold = value
11
+ end
12
+
13
+ class StandardFetcher
14
+ def make_http(uri)
15
+ http = @proxy.new(uri.host, uri.port)
16
+ http.read_timeout = http.open_timeout = OpenID.timeout_threshold
17
+ http
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ if config.respond_to?(:gems)
2
+ config.gem 'ruby-openid', :lib => 'openid', :version => '>=2.0.4'
3
+ else
4
+ begin
5
+ require 'openid'
6
+ rescue LoadError
7
+ begin
8
+ gem 'ruby-openid', '>=2.0.4'
9
+ rescue Gem::LoadError
10
+ puts "Install the ruby-openid gem to enable OpenID support"
11
+ end
12
+ end
13
+ end
14
+
15
+ config.to_prepare do
16
+ OpenID::Util.logger = Rails.logger
17
+ ActionController::Base.send :include, OpenIdAuthentication
18
+ end
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class NormalizeTest < Test::Unit::TestCase
4
+ include OpenIdAuthentication
5
+
6
+ NORMALIZATIONS = {
7
+ "openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
8
+ "http://openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
9
+ "https://openid.aol.com/nextangler" => "https://openid.aol.com/nextangler",
10
+ "HTTP://OPENID.AOL.COM/NEXTANGLER" => "http://openid.aol.com/NEXTANGLER",
11
+ "HTTPS://OPENID.AOL.COM/NEXTANGLER" => "https://openid.aol.com/NEXTANGLER",
12
+ "loudthinking.com" => "http://loudthinking.com/",
13
+ "http://loudthinking.com" => "http://loudthinking.com/",
14
+ "http://loudthinking.com:80" => "http://loudthinking.com/",
15
+ "https://loudthinking.com:443" => "https://loudthinking.com/",
16
+ "http://loudthinking.com:8080" => "http://loudthinking.com:8080/",
17
+ "techno-weenie.net" => "http://techno-weenie.net/",
18
+ "http://techno-weenie.net" => "http://techno-weenie.net/",
19
+ "http://techno-weenie.net " => "http://techno-weenie.net/",
20
+ "=name" => "=name"
21
+ }
22
+
23
+ def test_normalizations
24
+ NORMALIZATIONS.each do |from, to|
25
+ assert_equal to, normalize_identifier(from)
26
+ end
27
+ end
28
+
29
+ def test_broken_open_id
30
+ assert_raises(InvalidOpenId) { normalize_identifier(nil) }
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class OpenIdAuthenticationTest < Test::Unit::TestCase
4
+ def setup
5
+ @controller = Class.new do
6
+ include OpenIdAuthentication
7
+ def params() {} end
8
+ end.new
9
+ end
10
+
11
+ def test_authentication_should_fail_when_the_identity_server_is_missing
12
+ open_id_consumer = mock()
13
+ open_id_consumer.expects(:begin).raises(OpenID::OpenIDError)
14
+ @controller.expects(:open_id_consumer).returns(open_id_consumer)
15
+ @controller.expects(:logger).returns(mock(:error => true))
16
+
17
+ @controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
18
+ assert result.missing?
19
+ assert_equal "Sorry, the OpenID server couldn't be found", result.message
20
+ end
21
+ end
22
+
23
+ def test_authentication_should_be_invalid_when_the_identity_url_is_invalid
24
+ @controller.send(:authenticate_with_open_id, "!") do |result, identity_url|
25
+ assert result.invalid?, "Result expected to be invalid but was not"
26
+ assert_equal "Sorry, but this does not appear to be a valid OpenID", result.message
27
+ end
28
+ end
29
+
30
+ def test_authentication_should_fail_when_the_identity_server_times_out
31
+ open_id_consumer = mock()
32
+ open_id_consumer.expects(:begin).raises(Timeout::Error, "Identity Server took too long.")
33
+ @controller.expects(:open_id_consumer).returns(open_id_consumer)
34
+ @controller.expects(:logger).returns(mock(:error => true))
35
+
36
+ @controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
37
+ assert result.missing?
38
+ assert_equal "Sorry, the OpenID server couldn't be found", result.message
39
+ end
40
+ end
41
+
42
+ def test_authentication_should_begin_when_the_identity_server_is_present
43
+ @controller.expects(:begin_open_id_authentication)
44
+ @controller.send(:authenticate_with_open_id, "http://someone.example.com")
45
+ end
46
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class StatusTest < Test::Unit::TestCase
4
+ include OpenIdAuthentication
5
+
6
+ def test_state_conditional
7
+ assert Result[:missing].missing?
8
+ assert Result[:missing].unsuccessful?
9
+ assert !Result[:missing].successful?
10
+
11
+ assert Result[:successful].successful?
12
+ assert !Result[:successful].unsuccessful?
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+
4
+ gem 'activesupport'
5
+ require 'active_support'
6
+
7
+ gem 'actionpack'
8
+ require 'action_controller'
9
+
10
+ gem 'mocha'
11
+ require 'mocha'
12
+
13
+ gem 'ruby-openid'
14
+ require 'openid'
15
+
16
+ RAILS_ROOT = File.dirname(__FILE__) unless defined? RAILS_ROOT
17
+ require File.dirname(__FILE__) + "/../lib/open_id_authentication"
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: okkez-open_id_authentication
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Heinemeier Hansson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-07 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ruby-openid
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.4
24
+ version:
25
+ description: Provides a thin wrapper around the excellent ruby-openid gem from JanRan. gem version is unofficial, github fork of rails's by moro
26
+ email: ""
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ - CHANGELOG
34
+ files:
35
+ - README
36
+ - CHANGELOG
37
+ - Rakefile
38
+ - test/status_test.rb
39
+ - test/normalize_test.rb
40
+ - test/test_helper.rb
41
+ - test/open_id_authentication_test.rb
42
+ - lib/open_id_authentication.rb
43
+ - lib/open_id_authentication/nonce.rb
44
+ - lib/open_id_authentication/timeout_fixes.rb
45
+ - lib/open_id_authentication/db_store.rb
46
+ - lib/open_id_authentication/association.rb
47
+ - lib/open_id_authentication/request.rb
48
+ - generators/upgrade_open_id_authentication_tables/upgrade_open_id_authentication_tables_generator.rb
49
+ - generators/upgrade_open_id_authentication_tables/templates/migration.rb
50
+ - generators/open_id_authentication_tables/open_id_authentication_tables_generator.rb
51
+ - generators/open_id_authentication_tables/templates/migration.rb
52
+ - rails/init.rb
53
+ has_rdoc: true
54
+ homepage: http://github.com/rails/open_id_authentication
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --title
60
+ - okkez-open_id_authentication documentation
61
+ - --charset
62
+ - utf-8
63
+ - --opname
64
+ - index.html
65
+ - --line-numbers
66
+ - --main
67
+ - README
68
+ - --inline-source
69
+ - --exclude
70
+ - ^(examples|extras)/
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.3.5
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Provides a thin wrapper around the excellent ruby-openid gem from JanRan. gem version is unofficial, github fork of rails's by moro
92
+ test_files:
93
+ - test/status_test.rb
94
+ - test/normalize_test.rb
95
+ - test/open_id_authentication_test.rb