summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScytrin dai Kinthra <scytrin@gmail.com>2008-06-23 04:26:38 -0700
committerScytrin dai Kinthra <scytrin@gmail.com>2008-06-24 01:50:51 -0700
commitc429cc10f0c673f224d8281ef02e5d89c71cdf30 (patch)
tree1ea274d77446a857e8057307eed0bb664b31e63a
parent4420448857b538675b85bef5fb623fc5c14699c6 (diff)
downloadrack-c429cc10f0c673f224d8281ef02e5d89c71cdf30.tar.gz
OpenID2 moved to replace OpenID
-rw-r--r--lib/rack.rb1
-rw-r--r--lib/rack/auth/openid.rb393
-rw-r--r--lib/rack/auth/openid2.rb335
-rw-r--r--test/spec_rack_auth_openid.rb (renamed from test/spec_rack_auth_openid2.rb)30
4 files changed, 321 insertions, 438 deletions
diff --git a/lib/rack.rb b/lib/rack.rb
index 9c9bef97..607d0f5c 100644
--- a/lib/rack.rb
+++ b/lib/rack.rb
@@ -53,7 +53,6 @@ module Rack
autoload :AbstractRequest, "rack/auth/abstract/request"
autoload :AbstractHandler, "rack/auth/abstract/handler"
autoload :OpenID, "rack/auth/openid"
- autoload :OpenID2, "rack/auth/openid2"
module Digest
autoload :MD5, "rack/auth/digest/md5"
autoload :Nonce, "rack/auth/digest/nonce"
diff --git a/lib/rack/auth/openid.rb b/lib/rack/auth/openid.rb
index 9267a98e..520c40ab 100644
--- a/lib/rack/auth/openid.rb
+++ b/lib/rack/auth/openid.rb
@@ -1,115 +1,334 @@
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
-gem_require 'ruby-openid', '~> 1.0.0' if defined? Gem
-require 'rack/auth/abstract/handler'
-require 'openid'
+gem 'ruby-openid', '~> 2' if defined? Gem
+require 'rack/auth/abstract/handler' #rack
+require 'uri' #std
+require 'pp' #std
+require 'openid' #gem
+require 'openid/store/memory' #gem
module Rack
module Auth
- # Rack::Auth::OpenID provides a simple method for permitting openid
- # based logins. It requires the ruby-openid lib from janrain to operate,
- # as well as some method of session management of a Hash type.
+ # Rack::Auth::OpenID provides a simple method for permitting
+ # openid based logins. It requires the ruby-openid library from
+ # janrain to operate, as well as a rack method of session management.
#
- # After a transaction, the response status object is stored in the
- # environment at rack.auth.openid.status, which can be used in the
- # followup block or in a wrapping application to accomplish
- # additional data maniipulation.
+ # The ruby-openid home page is at <http://openidenabled.com/ruby-openid/>.
#
- # NOTE: Due to the amount of data that ruby-openid stores in the session,
- # Rack::Session::Cookie may fault.
+ # The OpenID specifications can be found at
+ # Mhttp://openid.net/specs/openid-authentication-1_1.html>
+ # and
+ # <http://openid.net/specs/openid-authentication-2_0.html>. Documentation
+ # for published OpenID extensions and related topics can be found at
+ # <http://openid.net/developers/specs/>.
#
- # A hash of data is stored in the session hash at the key of :openid.
- # The fully canonicalized identity url is stored within at 'identity'.
- # Extension data from 'openid.sreg.nickname' would be stored as
- # { 'nickname' => value }.
+ # It is recommended to read through the OpenID spec, as well as
+ # ruby-openid's documentation, to understand what exactly goes on. However
+ # a setup as simple as the presented examples is enough to provide
+ # functionality.
#
- # NOTE: To my knowledge there is no collision at this point from storage
- # of this manner, if there is please let me know so I may adjust this app
- # to cope.
- # NOTE: This rack application is only compatible with the 1.x.x versions
- # of the ruby-openid library. If rubygems is loaded at require time of
- # this app, the specification will be made. If it is not then the 'openid'
- # library will be required, and will fail if it is not compatible.
+ # This library strongly intends to utilize the openid 2.0 features of the
+ # ruby-openid library, while maintaining openid 1.0 compatiblity.
+ #
+ # All responses from this rack application will be 303 redirects unless an
+ # error occurs, with the exception of and authentication request requiring
+ # an HTML form submission.
+ #
+ # NOTE: Extensions are not currently supported by this implimentation of
+ # the OpenID rack application due to the complexity of the current
+ # ruby-openid extension handling.
+ #
+ # NOTE: Due to the amount of data that this library stores in the
+ # session, Rack::Session::Cookie may fault.
class OpenID < AbstractHandler
+ class NoSession < RuntimeError; end
# Required for ruby-openid
- OIDStore = ::OpenID::MemoryStore.new
+ OIDStore = ::OpenID::Store::Memory.new
+ HTML = '<html><head><title>%s</title></head><body>%s</body></html>'
# A Hash of options is taken as it's single initializing
- # argument. String keys are taken to be openid protocol
- # extension namespaces.
- #
- # For example: 'sreg' => { 'required' => # 'nickname' }
- #
- # Other keys are taken as options for Rack::Auth::OpenID, normally Symbols.
- # Only :return is required. :trust is highly recommended to be set.
- #
- # * :return defines the url to return to after the client authenticates
- # with the openid service provider. Should point to where this app is
- # mounted. (ex: 'http://mysite.com/openid')
- # * :trust defines the url identifying the site they are actually logging
- # into. (ex: 'http://mysite.com/')
- # * :session_key defines the key to the session hash in the env.
- # (by default it uses 'rack.session')
- def initialize(options={})
- raise ArgumentError, 'No return url provided.' unless options[:return]
- warn 'No trust url provided.' unless options[:trust]
- options[:trust] ||= options[:return]
-
- @options = {
- :session_key => 'rack.session'
+ # argument. For example:
+ #
+ # simple_oid = OpenID.new('http://mysite.com/')
+ #
+ # return_oid = OpenID.new('http://mysite.com/', {
+ # :return_to => 'http://mysite.com/openid'
+ # })
+ #
+ # page_oid = OpenID.new('http://mysite.com/',
+ # :login_good => 'http://mysite.com/auth_good'
+ # )
+ #
+ # -- Arguments
+ #
+ # The first argument is the realm, identifying the site they are trusting
+ # with their identity. This is required.
+ #
+ # NOTE: In OpenID 1.x, the realm or trust_root is optional and the
+ # return_to url is required. As this library strives tward ruby-openid
+ # 2.0, and OpenID 2.0 compatibiliy, the realm is required and return_to
+ # is optional. However, this implimentation is still backwards compatible
+ # with OpenID 1.0 servers.
+ #
+ # The optional second argument is a hash of options.
+ #
+ # -- Options
+ #
+ # :return_to defines the url to return to after the client authenticates
+ # with the openid service provider. This url should point to where
+ # Rack::Auth::OpenID is mounted. If :return_to is not provided, the url
+ # will be derived within the ruby-openid implementation.
+ #
+ # :session_key defines the key to the session hash in the env. It
+ # defaults to 'rack.session'.
+ #
+ # :openid_param defines at what key in the request parameters to find
+ # the identifier to resolve. As per the 2.0 spec, the default is
+ # 'openid_identifier'.
+ #
+ # :extensions will specify what extensions are to used with OpenID,
+ # of which the format and support of which is yet to be completed.
+ #
+ # :immediate as true will make immediate type of requests the default.
+ # See the specification documentation.
+ #
+ # -- URL options
+ #
+ # :login_good is the url to go to after the authentication process
+ # has completed.
+ #
+ # :login_fail is the url to go to after the authentication process
+ # has failed.
+ #
+ # :login_fail is the url to go to after the authentication process
+ # has been cancelled.
+ #
+ # -- Response options
+ #
+ # :no_session should be a rack response to be returned if no or an
+ # incompatible session is found.
+ #
+ # :auth_fail should be a rack response to be returned if an
+ # OpenID::DiscoveryFailure occurs. This is typically due to being
+ # unable to access the identity url or identity server.
+ #
+ # :error should be a rack response to return if any other generic error
+ # would occur AND options[:catch_errors] is true.
+ def initialize(realm, options={})
+ @realm = realm
+ realm = URI(realm)
+ if realm.path.empty?
+ raise ArgumentError, "Invalid realm path: '#{realm.path}'"
+ elsif not realm.absolute?
+ raise ArgumentError, "Realm '#{@realm}' not absolute"
+ end
+
+ [:return_to, :login_good, :login_fail, :login_quit].each do |key|
+ if options.key? key and luri = URI(options[key])
+ if !luri.absolute?
+ raise ArgumentError, ":#{key} is not an absolute uri: '#{luri}'"
+ end
+ end
+ end
+
+ if options[:return_to] and ruri = URI(options[:return_to])
+ if ruri.path.empty?
+ raise ArgumentError, "Invalid return_to path: '#{ruri.path}'"
+ elsif realm.path != ruri.path[0, realm.path.size]
+ raise ArgumentError, 'return_to not within realm.' \
+ end
+ end
+
+ # TODO: extension support
+ if options.has_key? :extensions
+ warn "Extensions are not currently supported by Rack::Auth::OpenID"
+ end
+
+ @options = {
+ :session_key => 'rack.session',
+ :openid_param => 'openid_identifier',
+ #:return_to, :login_good, :login_fail, :login_quit
+ #:no_session, :auth_fail, :error
+ :store => OIDStore,
+ :immediate => false,
+ :anonymous => false,
+ :catch_errors => false
}.merge(options)
end
+ attr_reader :options
+
+ # It sets up and uses session data at :openid within the session. It
+ # sets up the ::OpenID::Consumer using the store specified by
+ # options[:store].
+ #
+ # If the parameter specified by options[:openid_param] is present,
+ # processing is passed to #check and the result is returned.
+ #
+ # If the parameter 'openid.mode' is set, implying a followup from the
+ # openid server, processing is passed to #finish and the result is
+ # returned.
+ #
+ # If neither of these conditions are met, a 400 error is returned.
+ #
+ # If an error is thrown and options[:catch_errors] is false, the
+ # exception will be reraised. Otherwise a 500 error is returned.
def call(env)
+ env['rack.auth.openid'] = self
+ session = env[@options[:session_key]]
+ unless session and session.is_a? Hash
+ raise(NoSession, 'No compatible session')
+ end
+ # let us work in our own namespace...
+ session = (session[:openid] ||= {})
+ unless session and session.is_a? Hash
+ raise(NoSession, 'Incompatible session')
+ end
+
request = Rack::Request.new env
- return no_session unless session = request.env[@options[:session_key]]
- resp = if request.GET['openid.mode']
- finish session, request.GET, env
- elsif request.GET['openid_url']
- check session, request.GET['openid_url'], env
- else
- bad_request
- end
- end
+ consumer = ::OpenID::Consumer.new session, @options[:store]
- def check(session, oid_url, env)
- consumer = ::OpenID::Consumer.new session, OIDStore
- oid = consumer.begin oid_url
- return auth_fail unless oid.status == ::OpenID::SUCCESS
- @options.each do |ns,s|
- next unless ns.is_a? String
- s.each {|k,v| oid.add_extension_arg(ns, k, v) }
+ if request.params[@options[:openid_param]]
+ check consumer, session, request
+ elsif request.params['openid.mode']
+ finish consumer, session, request
+ else
+ env['rack.errors'].puts "No valid params provided."
+ bad_request
end
- r_url = @options.fetch :return do |k| request.url end
- t_url = @options.fetch :trust
- env['rack.auth.openid.status'] = oid
- return 303, {'Location'=>oid.redirect_url( t_url, r_url )}, []
- end
+ rescue NoSession
+ env['rack.errors'].puts($!.message, *$@)
- def finish(session, params, env)
- consumer = ::OpenID::Consumer.new session, OIDStore
- oid = consumer.complete params
- return bad_login unless oid.status == ::OpenID::SUCCESS
- session[:openid] = {'identity' => oid.identity_url}
- @options.each do |ns,s|
- next unless ns.is_a? String
- oid.extension_response(ns).each{|k,v| session[k]=v }
- end
- env['rack.auth.openid.status'] = oid
- return 303, {'Location'=>@options[:trust]}, []
- end
+ @options. ### Missing or incompatible session
+ fetch :no_session, [ 500,
+ {'Content-Type'=>'text/plain'},
+ $!.message ]
+ rescue
+ env['rack.errors'].puts($!.message, *$@)
- def no_session
+ if not @options[:catch_error]
+ raise($!)
+ end
@options.
- fetch :no_session, [500,{'Content-Type'=>'text/plain'},'No session available.']
+ fetch :error, [ 500,
+ {'Content-Type'=>'text/plain'},
+ 'OpenID has encountered an error.' ]
end
- def auth_fail
- @options.
- fetch :auth_fail, [500, {'Content-Type'=>'text/plain'},'Foreign server failure.']
+
+ # As the first part of OpenID consumer action, #check retrieves the
+ # data required for completion.
+ #
+ # * session[:openid][:openid_param] is the request parameter requested to
+ # be authenticated.
+ # * session[:openid][:site_return] is set as the request's HTTP_REFERER
+ # if previously unset.
+ # * env['rack.auth.openid.request'] is the openid checkidrequest.
+ def check(consumer, session, req)
+ session[:openid_param] = req.params[@options[:openid_param]]
+ oid = consumer.begin(session[:openid_param], @options[:anonymous])
+ pp oid if $DEBUG
+ req.env['rack.auth.openid.request'] = oid
+
+ session[:site_return] ||= req.env['HTTP_REFERER']
+
+ # SETUP_NEEDED check!
+ # see OpenID::Consumer::CheckIDRequest docs
+ query_args = [@realm, *@options.values_at(:return_to, :immediate)]
+ query_args[2] = false if session.key? :setup_needed
+ pp query_args if $DEBUG
+
+ if oid.send_redirect?(*query_args)
+ redirect = oid.redirect_url(*query_args)
+ [ 303, {'Location'=>redirect}, [] ]
+ else
+ # check on 'action' option.
+ formbody = oid.form_markup(*query_args)
+ body = HTML % ['Confirm...', formbody]
+ [ 200, {'Content-Type'=>'text/html'}, body.to_a ]
+ end
+ rescue ::OpenID::DiscoveryFailure => e
+ # thrown from inside OpenID::Consumer#begin by yadis stuff
+ req.env['rack.errors'].puts($!.message, *$@)
+
+ @options. ### Foreign server failed
+ fetch :auth_fail, [ 503,
+ {'Content-Type'=>'text/plain'},
+ 'Foreign server failure.' ]
end
- def bad_login
- @options.
- fetch :bad_login, [401, {'Content-Type'=>'text/plain'},'Identification has failed.']
+
+ # This is the final portion of authentication. Unless any errors outside
+ # of specification occur, a 303 redirect will be returned with Location
+ # determined by the OpenID response type. If none of the response type
+ # :login_* urls are set, the redirect will be set to
+ # session[:openid][:site_return]. If session[:openid][:site_return] is
+ # unset, the realm will be used.
+ #
+ # Any messages from OpenID's response are appended to the 303 response
+ # body.
+ #
+ # * env['rack.auth.openid.response'] is the openid response.
+ #
+ # The four valid possible outcomes are:
+ # * failure: options[:login_fail] or session[:site_return] or the realm
+ # * session[:openid] is cleared and any messages are send to rack.errors
+ # * session[:openid]['authenticated'] is false
+ # * success: options[:login_good] or session[:site_return] or the relam
+ # * session[:openid] is cleared
+ # * session[:openid]['authenticated'] is true
+ # * session[:openid]['identity'] is the actual identifier
+ # * session[:openid]['identifier'] is the pretty identifier
+ # * cancel: options[:login_quit] or session[:site_return] or the realm
+ # * session[:openid] is cleared
+ # * session[:openid]['authenticated'] is false
+ # * setup_needed: resubmits the authentication request. A flag is set
+ # for non-immediate handling.
+ # * session[:openid][:setup_needed] is set to true, which will
+ # prevent immediate style openid authentication.
+ def finish(consumer, session, req)
+ oid = consumer.complete(req.params, req.url)
+ pp oid if $DEBUG
+ req.env['rack.auth.openid.response'] = oid
+
+ goto = session.fetch :site_return, @realm
+ body = []
+
+ case oid.status
+ when ::OpenID::Consumer::FAILURE
+ session.clear
+ session['authenticated'] = false
+ req.env['rack.errors'].puts oid.message
+
+ goto = @options[:login_fail] if @option.key? :login_fail
+ body << "Authentication unsuccessful.\n"
+ when ::OpenID::Consumer::SUCCESS
+ session.clear
+ session['authenticated'] = true
+ # Value for unique identification and such
+ session['identity'] = oid.identity_url
+ # Value for display and UI labels
+ session['identifier'] = oid.display_identifier
+
+ goto = @options[:login_good] if @options.key? :login_good
+ body << "Authentication successful.\n"
+ when ::OpenID::Consumer::CANCEL
+ session.clear
+ session['authenticated'] = false
+
+ goto = @options[:login_fail] if @option.key? :login_fail
+ body << "Authentication cancelled.\n"
+ when ::OpenID::Consumer::SETUP_NEEDED
+ session[:setup_needed] = true
+ unless o_id = session[:openid_param]
+ raise('Required values missing.')
+ end
+
+ goto = req.script_name+
+ '?'+@options[:openid_param]+
+ '='+o_id
+ body << "Reauthentication required.\n"
+ end
+ body << oid.message if oid.message
+ [ 303, {'Location'=>goto}, body]
end
end
end
diff --git a/lib/rack/auth/openid2.rb b/lib/rack/auth/openid2.rb
deleted file mode 100644
index c59e93fa..00000000
--- a/lib/rack/auth/openid2.rb
+++ /dev/null
@@ -1,335 +0,0 @@
-# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
-
-gem 'ruby-openid', '~> 2' if defined? Gem
-require 'rack/auth/abstract/handler' #rack
-require 'uri' #std
-require 'pp' #std
-require 'openid' #gem
-require 'openid/store/memory' #gem
-
-module Rack
- module Auth
- # Rack::Auth::OpenID provides a simple method for permitting
- # openid based logins. It requires the ruby-openid library from
- # janrain to operate, as well as a rack method of session management.
- #
- # The ruby-openid home page is at <http://openidenabled.com/ruby-openid/>.
- #
- # The OpenID specifications can be found at
- # Mhttp://openid.net/specs/openid-authentication-1_1.html>
- # and
- # <http://openid.net/specs/openid-authentication-2_0.html>. Documentation
- # for published OpenID extensions and related topics can be found at
- # <http://openid.net/developers/specs/>.
- #
- # It is recommended to read through the OpenID spec, as well as
- # ruby-openid's documentation, to understand what exactly goes on. However
- # a setup as simple as the presented examples is enough to provide
- # functionality.
- #
- # This library strongly intends to utilize the openid 2.0 features of the
- # ruby-openid library, while maintaining openid 1.0 compatiblity.
- #
- # All responses from this rack application will be 303 redirects unless an
- # error occurs, with the exception of and authentication request requiring
- # an HTML form submission.
- #
- # NOTE: Extensions are not currently supported by this implimentation of
- # the OpenID rack application due to the complexity of the current
- # ruby-openid extension handling.
- #
- # NOTE: Due to the amount of data that this library stores in the
- # session, Rack::Session::Cookie may fault.
- class OpenID2 < AbstractHandler
- class NoSession < RuntimeError; end
- # Required for ruby-openid
- OIDStore = ::OpenID::Store::Memory.new
- HTML = '<html><head><title>%s</title></head><body>%s</body></html>'
-
- # A Hash of options is taken as it's single initializing
- # argument. For example:
- #
- # simple_oid = OpenID2.new('http://mysite.com/')
- #
- # return_oid = OpenID2.new('http://mysite.com/', {
- # :return_to => 'http://mysite.com/openid'
- # })
- #
- # page_oid = OpenID2.new('http://mysite.com/',
- # :login_good => 'http://mysite.com/auth_good'
- # )
- #
- # -- Arguments
- #
- # The first argument is the realm, identifying the site they are trusting
- # with their identity. This is required.
- #
- # NOTE: In OpenID 1.x, the realm or trust_root is optional and the
- # return_to url is required. As this library strives tward ruby-openid
- # 2.0, and OpenID 2.0 compatibiliy, the realm is required and return_to
- # is optional. However, this implimentation is still backwards compatible
- # with OpenID 1.0 servers.
- #
- # The optional second argument is a hash of options.
- #
- # -- Options
- #
- # :return_to defines the url to return to after the client authenticates
- # with the openid service provider. This url should point to where
- # Rack::Auth::OpenID is mounted. If :return_to is not provided, the url
- # will be derived within the ruby-openid implementation.
- #
- # :session_key defines the key to the session hash in the env. It
- # defaults to 'rack.session'.
- #
- # :openid_param defines at what key in the request parameters to find
- # the identifier to resolve. As per the 2.0 spec, the default is
- # 'openid_identifier'.
- #
- # :extensions will specify what extensions are to used with OpenID,
- # of which the format and support of which is yet to be completed.
- #
- # :immediate as true will make immediate type of requests the default.
- # See the specification documentation.
- #
- # -- URL options
- #
- # :login_good is the url to go to after the authentication process
- # has completed.
- #
- # :login_fail is the url to go to after the authentication process
- # has failed.
- #
- # :login_fail is the url to go to after the authentication process
- # has been cancelled.
- #
- # -- Response options
- #
- # :no_session should be a rack response to be returned if no or an
- # incompatible session is found.
- #
- # :auth_fail should be a rack response to be returned if an
- # OpenID::DiscoveryFailure occurs. This is typically due to being
- # unable to access the identity url or identity server.
- #
- # :error should be a rack response to return if any other generic error
- # would occur AND options[:catch_errors] is true.
- def initialize(realm, options={})
- @realm = realm
- realm = URI(realm)
- if realm.path.empty?
- raise ArgumentError, "Invalid realm path: '#{realm.path}'"
- elsif not realm.absolute?
- raise ArgumentError, "Realm '#{@realm}' not absolute"
- end
-
- [:return_to, :login_good, :login_fail, :login_quit].each do |key|
- if options.key? key and luri = URI(options[key])
- if !luri.absolute?
- raise ArgumentError, ":#{key} is not an absolute uri: '#{luri}'"
- end
- end
- end
-
- if options[:return_to] and ruri = URI(options[:return_to])
- if ruri.path.empty?
- raise ArgumentError, "Invalid return_to path: '#{ruri.path}'"
- elsif realm.path != ruri.path[0, realm.path.size]
- raise ArgumentError, 'return_to not within realm.' \
- end
- end
-
- # TODO: extension support
- if options.has_key? :extensions
- warn "Extensions are not currently supported by Rack::Auth::OpenID2"
- end
-
- @options = {
- :session_key => 'rack.session',
- :openid_param => 'openid_identifier',
- #:return_to, :login_good, :login_fail, :login_quit
- #:no_session, :auth_fail, :error
- :store => OIDStore,
- :immediate => false,
- :anonymous => false,
- :catch_errors => false
- }.merge(options)
- end
-
- attr_reader :options
-
- # It sets up and uses session data at :openid within the session. It
- # sets up the ::OpenID::Consumer using the store specified by
- # options[:store].
- #
- # If the parameter specified by options[:openid_param] is present,
- # processing is passed to #check and the result is returned.
- #
- # If the parameter 'openid.mode' is set, implying a followup from the
- # openid server, processing is passed to #finish and the result is
- # returned.
- #
- # If neither of these conditions are met, a 400 error is returned.
- #
- # If an error is thrown and options[:catch_errors] is false, the
- # exception will be reraised. Otherwise a 500 error is returned.
- def call(env)
- env['rack.auth.openid'] = self
- session = env[@options[:session_key]]
- unless session and session.is_a? Hash
- raise(NoSession, 'No compatible session')
- end
- # let us work in our own namespace...
- session = (session[:openid] ||= {})
- unless session and session.is_a? Hash
- raise(NoSession, 'Incompatible session')
- end
-
- request = Rack::Request.new env
- consumer = ::OpenID::Consumer.new session, @options[:store]
-
- if request.params[@options[:openid_param]]
- check consumer, session, request
- elsif request.params['openid.mode']
- finish consumer, session, request
- else
- env['rack.errors'].puts "No valid params provided."
- bad_request
- end
- rescue NoSession
- env['rack.errors'].puts($!.message, *$@)
-
- @options. ### Missing or incompatible session
- fetch :no_session, [ 500,
- {'Content-Type'=>'text/plain'},
- $!.message ]
- rescue
- env['rack.errors'].puts($!.message, *$@)
-
- if not @options[:catch_error]
- raise($!)
- end
- @options.
- fetch :error, [ 500,
- {'Content-Type'=>'text/plain'},
- 'OpenID has encountered an error.' ]
- end
-
- # As the first part of OpenID consumer action, #check retrieves the
- # data required for completion.
- #
- # * session[:openid][:openid_param] is the request parameter requested to
- # be authenticated.
- # * session[:openid][:site_return] is set as the request's HTTP_REFERER
- # if previously unset.
- # * env['rack.auth.openid.request'] is the openid checkidrequest.
- def check(consumer, session, req)
- session[:openid_param] = req.params[@options[:openid_param]]
- oid = consumer.begin(session[:openid_param], @options[:anonymous])
- pp oid if $DEBUG
- req.env['rack.auth.openid.request'] = oid
-
- session[:site_return] ||= req.env['HTTP_REFERER']
-
- # SETUP_NEEDED check!
- # see OpenID::Consumer::CheckIDRequest docs
- query_args = [@realm, *@options.values_at(:return_to, :immediate)]
- query_args[2] = false if session.key? :setup_needed
- pp query_args if $DEBUG
-
- if oid.send_redirect?(*query_args)
- redirect = oid.redirect_url(*query_args)
- [ 303, {'Location'=>redirect}, [] ]
- else
- # check on 'action' option.
- formbody = oid.form_markup(*query_args)
- body = HTML % ['Confirm...', formbody]
- [ 200, {'Content-Type'=>'text/html'}, body.to_a ]
- end
- rescue ::OpenID::DiscoveryFailure => e
- # thrown from inside OpenID::Consumer#begin by yadis stuff
- req.env['rack.errors'].puts($!.message, *$@)
-
- @options. ### Foreign server failed
- fetch :auth_fail, [ 503,
- {'Content-Type'=>'text/plain'},
- 'Foreign server failure.' ]
- end
-
- # This is the final portion of authentication. Unless any errors outside
- # of specification occur, a 303 redirect will be returned with Location
- # determined by the OpenID response type. If none of the response type
- # :login_* urls are set, the redirect will be set to
- # session[:openid][:site_return]. If session[:openid][:site_return] is
- # unset, the realm will be used.
- #
- # Any messages from OpenID's response are appended to the 303 response
- # body.
- #
- # * env['rack.auth.openid.response'] is the openid response.
- #
- # The four valid possible outcomes are:
- # * failure: options[:login_fail] or session[:site_return] or the realm
- # * session[:openid] is cleared and any messages are send to rack.errors
- # * session[:openid]['authenticated'] is false
- # * success: options[:login_good] or session[:site_return] or the relam
- # * session[:openid] is cleared
- # * session[:openid]['authenticated'] is true
- # * session[:openid]['identity'] is the actual identifier
- # * session[:openid]['identifier'] is the pretty identifier
- # * cancel: options[:login_quit] or session[:site_return] or the realm
- # * session[:openid] is cleared
- # * session[:openid]['authenticated'] is false
- # * setup_needed: resubmits the authentication request. A flag is set
- # for non-immediate handling.
- # * session[:openid][:setup_needed] is set to true, which will
- # prevent immediate style openid authentication.
- def finish(consumer, session, req)
- oid = consumer.complete(req.params, req.url)
- pp oid if $DEBUG
- req.env['rack.auth.openid.response'] = oid
-
- goto = session.fetch :site_return, @realm
- body = []
-
- case oid.status
- when ::OpenID::Consumer::FAILURE
- session.clear
- session['authenticated'] = false
- req.env['rack.errors'].puts oid.message
-
- goto = @options[:login_fail] if @option.key? :login_fail
- body << "Authentication unsuccessful.\n"
- when ::OpenID::Consumer::SUCCESS
- session.clear
- session['authenticated'] = true
- # Value for unique identification and such
- session['identity'] = oid.identity_url
- # Value for display and UI labels
- session['identifier'] = oid.display_identifier
-
- goto = @options[:login_good] if @options.key? :login_good
- body << "Authentication successful.\n"
- when ::OpenID::Consumer::CANCEL
- session.clear
- session['authenticated'] = false
-
- goto = @options[:login_fail] if @option.key? :login_fail
- body << "Authentication cancelled.\n"
- when ::OpenID::Consumer::SETUP_NEEDED
- session[:setup_needed] = true
- unless o_id = session[:openid_param]
- raise('Required values missing.')
- end
-
- goto = req.script_name+
- '?'+@options[:openid_param]+
- '='+o_id
- body << "Reauthentication required.\n"
- end
- body << oid.message if oid.message
- [ 303, {'Location'=>goto}, body]
- end
- end
- end
-end
diff --git a/test/spec_rack_auth_openid2.rb b/test/spec_rack_auth_openid.rb
index 2fd361ac..37d97e19 100644
--- a/test/spec_rack_auth_openid2.rb
+++ b/test/spec_rack_auth_openid.rb
@@ -1,53 +1,53 @@
require 'test/spec'
# requires the ruby-openid gem
-require 'rack/auth/openid2'
+require 'rack/auth/openid'
-context "Rack::Auth::OpenID2" do
- OID2 = Rack::Auth::OpenID2
+context "Rack::Auth::OpenID" do
+ OID = Rack::Auth::OpenID
realm = 'http://path/arf'
ruri = %w{arf arf/blargh}
auri = ruri.map{|u|'/'+u}
furi = auri.map{|u|'http://path'+u}
specify 'realm uri should be absolute and have a path' do
- lambda{OID2.new('/path')}.
+ lambda{OID.new('/path')}.
should.raise ArgumentError
- lambda{OID2.new('http://path')}.
+ lambda{OID.new('http://path')}.
should.raise ArgumentError
- lambda{OID2.new('http://path/')}.
+ lambda{OID.new('http://path/')}.
should.not.raise
- lambda{OID2.new('http://path/arf')}.
+ lambda{OID.new('http://path/arf')}.
should.not.raise
end
specify 'uri options should be absolute' do
[:login_good, :login_fail, :login_quit, :return_to].each do |param|
ruri.each do |uri|
- lambda{OID2.new(realm, {param=>uri})}.
+ lambda{OID.new(realm, {param=>uri})}.
should.raise ArgumentError
end
auri.each do |uri|
- lambda{OID2.new(realm, {param=>uri})}.
+ lambda{OID.new(realm, {param=>uri})}.
should.raise ArgumentError
end
furi.each do |uri|
- lambda{OID2.new(realm, {param=>uri})}.
+ lambda{OID.new(realm, {param=>uri})}.
should.not.raise
end
end
end
specify 'return_to should be absolute and be under the realm' do
- lambda{OID2.new(realm, {:return_to => 'http://path'})}.
+ lambda{OID.new(realm, {:return_to => 'http://path'})}.
should.raise ArgumentError
- lambda{OID2.new(realm, {:return_to => 'http://path/'})}.
+ lambda{OID.new(realm, {:return_to => 'http://path/'})}.
should.raise ArgumentError
- lambda{OID2.new(realm, {:return_to => 'http://path/arf'})}.
+ lambda{OID.new(realm, {:return_to => 'http://path/arf'})}.
should.not.raise
- lambda{OID2.new(realm, {:return_to => 'http://path/arf/'})}.
+ lambda{OID.new(realm, {:return_to => 'http://path/arf/'})}.
should.not.raise
- lambda{OID2.new(realm, {:return_to => 'http://path/arf/blargh'})}.
+ lambda{OID.new(realm, {:return_to => 'http://path/arf/blargh'})}.
should.not.raise
end
end