diff options
author | Scytrin dai Kinthra <scytrin@gmail.com> | 2008-06-23 04:26:38 -0700 |
---|---|---|
committer | Scytrin dai Kinthra <scytrin@gmail.com> | 2008-06-24 01:50:51 -0700 |
commit | c429cc10f0c673f224d8281ef02e5d89c71cdf30 (patch) | |
tree | 1ea274d77446a857e8057307eed0bb664b31e63a | |
parent | 4420448857b538675b85bef5fb623fc5c14699c6 (diff) | |
download | rack-c429cc10f0c673f224d8281ef02e5d89c71cdf30.tar.gz |
OpenID2 moved to replace OpenID
-rw-r--r-- | lib/rack.rb | 1 | ||||
-rw-r--r-- | lib/rack/auth/openid.rb | 393 | ||||
-rw-r--r-- | lib/rack/auth/openid2.rb | 335 | ||||
-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 |