rack-openid-1.3.1/0000755000175000017500000000000012037033035013230 5ustar ondrejondrejrack-openid-1.3.1/LICENSE0000644000175000017500000000203712037033035014237 0ustar ondrejondrejCopyright (c) 2010 Joshua Peek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rack-openid-1.3.1/README.rdoc0000644000175000017500000000306412037033035015041 0ustar ondrejondrej= Rack::OpenID Provides a more HTTPish API around the ruby-openid library. === Usage You trigger an OpenID request similar to HTTP authentication. From your app, return a "401 Unauthorized" and a "WWW-Authenticate" header with the identifier you would like to validate. On competition, the OpenID response is automatically verified and assigned to env["rack.openid.response"]. === Rack Example MyApp = lambda { |env| if resp = env["rack.openid.response"] case resp.status when :success ... when :failure ... else [401, {"WWW-Authenticate" => 'OpenID identifier="http://example.com/"'}, []] end } use Rack::OpenID run MyApp === Sinatra Example # Session needs to be before Rack::OpenID use Rack::Session::Cookie require 'rack/openid' use Rack::OpenID get '/login' do erb :login end post '/login' do if resp = request.env["rack.openid.response"] if resp.status == :success "Welcome: #{resp.display_identifier}" else "Error: #{resp.status}" end else headers 'WWW-Authenticate' => Rack::OpenID.build_header( :identifier => params["openid_identifier"] ) throw :halt, [401, 'got openid?'] end end enable :inline_templates __END__ @@ login

rack-openid-1.3.1/lib/0000755000175000017500000000000012037033035013776 5ustar ondrejondrejrack-openid-1.3.1/lib/rack/0000755000175000017500000000000012037033035014716 5ustar ondrejondrejrack-openid-1.3.1/lib/rack/openid.rb0000644000175000017500000002013212037033035016517 0ustar ondrejondrejrequire 'rack/request' require 'rack/utils' require 'openid' require 'openid/consumer' require 'openid/extensions/sreg' require 'openid/extensions/ax' require 'openid/extensions/oauth' module Rack #:nodoc: # A Rack middleware that provides a more HTTPish API around the # ruby-openid library. # # You trigger an OpenID request similar to HTTP authentication. # From your app, return a "401 Unauthorized" and a "WWW-Authenticate" # header with the identifier you would like to validate. # # On competition, the OpenID response is automatically verified and # assigned to env["rack.openid.response"]. class OpenID # Helper method for building the "WWW-Authenticate" header value. # # Rack::OpenID.build_header(:identifier => "http://josh.openid.com/") # #=> OpenID identifier="http://josh.openid.com/" def self.build_header(params = {}) 'OpenID ' + params.map { |key, value| if value.is_a?(Array) "#{key}=\"#{value.join(',')}\"" else "#{key}=\"#{value}\"" end }.join(', ') end # Helper method for parsing "WWW-Authenticate" header values into # a hash. # # Rack::OpenID.parse_header("OpenID identifier='http://josh.openid.com/'") # #=> {:identifier => "http://josh.openid.com/"} def self.parse_header(str) params = {} if str =~ AUTHENTICATE_REGEXP str = str.gsub(/#{AUTHENTICATE_REGEXP}\s+/, '') str.split(', ').each { |pair| key, *value = pair.split('=') value = value.join('=') value.gsub!(/^\"/, '').gsub!(/\"$/, "") value = value.split(',') params[key] = value.length > 1 ? value : value.first } end params end class TimeoutResponse #:nodoc: include ::OpenID::Consumer::Response STATUS = :failure end class MissingResponse #:nodoc: include ::OpenID::Consumer::Response STATUS = :missing end # :stopdoc: HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS) RESPONSE = "rack.openid.response" AUTHENTICATE_HEADER = "WWW-Authenticate" AUTHENTICATE_REGEXP = /^OpenID/ URL_FIELD_SELECTOR = lambda { |field| field.to_s =~ %r{^https?://} } # :startdoc: # Initialize middleware with application and optional OpenID::Store. # If no store is given, OpenID::Store::Memory is used. # # use Rack::OpenID # # or # # use Rack::OpenID, OpenID::Store::Memcache.new def initialize(app, store = nil) @app = app @store = store || default_store end # Standard Rack +call+ dispatch that accepts an +env+ and # returns a [status, header, body] response. def call(env) req = Rack::Request.new(env) if req.params["openid.mode"] complete_authentication(env) end status, headers, body = @app.call(env) qs = headers[AUTHENTICATE_HEADER] if status.to_i == 401 && qs && qs.match(AUTHENTICATE_REGEXP) begin_authentication(env, qs) else [status, headers, body] end end private def begin_authentication(env, qs) req = Rack::Request.new(env) params = self.class.parse_header(qs) session = env["rack.session"] unless session raise RuntimeError, "Rack::OpenID requires a session" end consumer = ::OpenID::Consumer.new(session, @store) identifier = params['identifier'] || params['identity'] immediate = params['immediate'] == 'true' begin oidreq = consumer.begin(identifier) add_simple_registration_fields(oidreq, params) add_attribute_exchange_fields(oidreq, params) add_oauth_fields(oidreq, params) url = open_id_redirect_url(req, oidreq, params["trust_root"], params["return_to"], params["method"], immediate) return redirect_to(url) rescue ::OpenID::OpenIDError, Timeout::Error => e env[RESPONSE] = MissingResponse.new return @app.call(env) end end def complete_authentication(env) req = Rack::Request.new(env) session = env["rack.session"] unless session raise RuntimeError, "Rack::OpenID requires a session" end oidresp = timeout_protection_from_identity_server { consumer = ::OpenID::Consumer.new(session, @store) consumer.complete(flatten_params(req.params), req.url) } env[RESPONSE] = oidresp method = req.GET["_method"] override_request_method(env, method) sanitize_query_string(env) end def flatten_params(params) Rack::Utils.parse_query(Rack::Utils.build_nested_query(params)) end def override_request_method(env, method) return unless method method = method.upcase if HTTP_METHODS.include?(method) env["REQUEST_METHOD"] = method end end def sanitize_query_string(env) query_hash = env["rack.request.query_hash"] query_hash.delete("_method") query_hash.delete_if do |key, value| key =~ /^openid\./ end env["QUERY_STRING"] = env["rack.request.query_string"] = Rack::Utils.build_query(env["rack.request.query_hash"]) qs = env["QUERY_STRING"] request_uri = (env["PATH_INFO"] || "").dup request_uri << "?" + qs unless qs == "" env["REQUEST_URI"] = request_uri end def realm_url(req) url = req.scheme + "://" url << req.host scheme, port = req.scheme, req.port if scheme == "https" && port != 443 || scheme == "http" && port != 80 url << ":#{port}" end url end def request_url(req) url = realm_url(req) url << req.script_name url << req.path_info url << "?#{req.query_string}" if req.query_string.to_s.length > 0 url end def redirect_to(url) [303, {"Content-Type" => "text/html", "Location" => url}, []] end def open_id_redirect_url(req, oidreq, trust_root, return_to, method, immediate) request_url = request_url(req) if return_to method ||= "get" else return_to = request_url method ||= req.request_method end method = method.to_s.downcase oidreq.return_to_args['_method'] = method unless method == "get" oidreq.redirect_url(trust_root || realm_url(req), return_to || request_url, immediate) end def add_simple_registration_fields(oidreq, fields) sregreq = ::OpenID::SReg::Request.new required = Array(fields['required']).reject(&URL_FIELD_SELECTOR) sregreq.request_fields(required, true) if required.any? optional = Array(fields['optional']).reject(&URL_FIELD_SELECTOR) sregreq.request_fields(optional, false) if optional.any? policy_url = fields['policy_url'] sregreq.policy_url = policy_url if policy_url oidreq.add_extension(sregreq) end def add_attribute_exchange_fields(oidreq, fields) axreq = ::OpenID::AX::FetchRequest.new required = Array(fields['required']).select(&URL_FIELD_SELECTOR) optional = Array(fields['optional']).select(&URL_FIELD_SELECTOR) if required.any? || optional.any? required.each do |field| axreq.add(::OpenID::AX::AttrInfo.new(field, nil, true)) end optional.each do |field| axreq.add(::OpenID::AX::AttrInfo.new(field, nil, false)) end oidreq.add_extension(axreq) end end def add_oauth_fields(oidreq, fields) if (consumer = fields['oauth[consumer]']) && (scope = fields['oauth[scope]']) oauthreq = ::OpenID::OAuth::Request.new(consumer, Array(scope).join(' ')) oidreq.add_extension(oauthreq) end end def default_store require 'openid/store/memory' ::OpenID::Store::Memory.new end def timeout_protection_from_identity_server yield rescue Timeout::Error TimeoutResponse.new end end end rack-openid-1.3.1/lib/rack/openid/0000755000175000017500000000000012037033035016174 5ustar ondrejondrejrack-openid-1.3.1/lib/rack/openid/simple_auth.rb0000644000175000017500000000363512037033035021042 0ustar ondrejondrejrequire 'rack/openid' require 'rack/request' module Rack #:nodoc: class OpenID # A simple OpenID middleware that restricts access to # a single identifier. # # use Rack::OpenID::SimpleAuth, "http://example.org" # # SimpleAuth will automatically insert the required Rack::OpenID # middleware, so use Rack::OpenID is unnecessary. class SimpleAuth def self.new(*args) Rack::OpenID.new(super) end attr_reader :app, :identifier def initialize(app, identifier) @app = app @identifier = identifier end def call(env) if session_authenticated?(env) app.call(env) elsif successful_response?(env) authenticate_session(env) redirect_to requested_url(env) else authentication_request end end private def session(env) env['rack.session'] || raise_session_error end def raise_session_error raise RuntimeError, 'Rack::OpenID::SimpleAuth requires a session' end def session_authenticated?(env) session(env)['authenticated'] == true end def authenticate_session(env) session(env)['authenticated'] = true end def successful_response?(env) if resp = env[OpenID::RESPONSE] resp.status == :success && resp.display_identifier == identifier end end def requested_url(env) req = Rack::Request.new(env) req.url end def redirect_to(url) [303, {'Content-Type' => 'text/html', 'Location' => url}, []] end def authentication_request [401, { OpenID::AUTHENTICATE_HEADER => www_authenticate_header }, []] end def www_authenticate_header OpenID.build_header(:identifier => identifier) end end end end rack-openid-1.3.1/metadata.yml0000644000175000017500000000365012037033035015537 0ustar ondrejondrej--- !ruby/object:Gem::Specification name: rack-openid version: !ruby/object:Gem::Version hash: 25 prerelease: segments: - 1 - 3 - 1 version: 1.3.1 platform: ruby authors: - Joshua Peek autorequire: bindir: bin cert_chain: [] date: 2011-02-25 00:00:00 -06:00 default_executable: dependencies: - !ruby/object:Gem::Dependency name: rack prerelease: false requirement: &id001 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 19 segments: - 1 - 1 - 0 version: 1.1.0 type: :runtime version_requirements: *id001 - !ruby/object:Gem::Dependency name: ruby-openid prerelease: false requirement: &id002 !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 27 segments: - 2 - 1 - 8 version: 2.1.8 type: :runtime version_requirements: *id002 description: " Provides a more HTTPish API around the ruby-openid library\n" email: josh@joshpeek.com executables: [] extensions: [] extra_rdoc_files: [] files: - lib/rack/openid.rb - lib/rack/openid/simple_auth.rb - LICENSE - README.rdoc has_rdoc: true homepage: http://github.com/josh/rack-openid licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" requirements: [] rubyforge_project: rubygems_version: 1.5.2 signing_key: specification_version: 3 summary: Provides a more HTTPish API around the ruby-openid library test_files: []