Module: OpenID::Yadis

### Home Classes Methods

### In Files

- lib/openid/yadis/accept.rb
- lib/openid/yadis/constants.rb
- lib/openid/yadis/discovery.rb
- lib/openid/yadis/filters.rb
- lib/openid/yadis/parsehtml.rb
- lib/openid/yadis/services.rb
- lib/openid/yadis/xrds.rb
- lib/openid/yadis/xri.rb
- lib/openid/yadis/xrires.rb

### Namespace

- MODULE OpenID::Yadis::XRI
- CLASS OpenID::Yadis::BasicServiceEndpoint
- CLASS OpenID::Yadis::CompoundFilter
- CLASS OpenID::Yadis::DiscoveryResult
- CLASS OpenID::Yadis::TransformFilterMaker
- CLASS OpenID::Yadis::XRDSError
- CLASS OpenID::Yadis::XRDSFraud

### Methods

- ::apply_filter
- ::disable_entity_expansion
- ::discover
- ::each_service
- ::expand_service
- ::generate_accept_header
- ::get_acceptable
- ::get_canonical_id
- ::get_service_endpoints
- ::get_yadis_xrd
- ::html_yadis_location
- ::is_xrds?
- ::make_filter
- ::match_types
- ::mk_compound_filter
- ::parseXRDS
- ::parse_accept_header
- ::prio_sort
- ::services
- ::where_is_yadis?

### Files

- LICENSE

### Class/Module Index

Quicksearch

- OpenID
- OpenID::AX
- OpenID::AX::AXMessage
- OpenID::AX::AttrInfo
- OpenID::AX::Error
- OpenID::AX::FetchRequest
- OpenID::AX::FetchResponse
- OpenID::AX::KeyValueMessage
- OpenID::AX::StoreRequest
- OpenID::AX::StoreResponse
- OpenID::AssertionError
- OpenID::Association
- OpenID::AssociationNegotiator
- OpenID::Consumer
- OpenID::Consumer::AssociationManager
- OpenID::Consumer::CancelResponse
- OpenID::Consumer::CheckIDRequest
- OpenID::Consumer::DiffieHellmanSHA1Session
- OpenID::Consumer::DiffieHellmanSHA256Session
- OpenID::Consumer::DiffieHellmanSession
- OpenID::Consumer::DiscoveredServices
- OpenID::Consumer::DiscoveryManager
- OpenID::Consumer::FailureResponse
- OpenID::Consumer::IdResHandler
- OpenID::Consumer::NoEncryptionSession
- OpenID::Consumer::Response
- OpenID::Consumer::Session
- OpenID::Consumer::SetupNeededResponse
- OpenID::Consumer::SuccessResponse
- OpenID::CryptUtil
- OpenID::DiffieHellman
- OpenID::DiscoveryFailure
- OpenID::Extension
- OpenID::FetchingError
- OpenID::HTTPRedirectLimitReached
- OpenID::HTTPResponse
- OpenID::HTTPStatusError
- OpenID::InvalidOpenIDNamespace
- OpenID::KVFormError
- OpenID::KVPostNetworkError
- OpenID::Message
- OpenID::Message::KeyNotFound
- OpenID::NamespaceAliasRegistrationError
- OpenID::NamespaceMap
- OpenID::Nonce
- OpenID::OAuth
- OpenID::OAuth::Request
- OpenID::OAuth::Response
- OpenID::OpenIDError
- OpenID::OpenIDServiceEndpoint
- OpenID::PAPE
- OpenID::PAPE::Request
- OpenID::PAPE::Response
- OpenID::ProtocolError
- OpenID::RealmVerificationRedirected
- OpenID::SReg
- OpenID::SReg::NamespaceError
- OpenID::SReg::Request
- OpenID::SReg::Response
- OpenID::SSLFetchingError
- OpenID::Server
- OpenID::Server::AlreadySigned
- OpenID::Server::AssociateRequest
- OpenID::Server::BaseServerSession
- OpenID::Server::CheckAuthRequest
- OpenID::Server::CheckIDRequest
- OpenID::Server::Decoder
- OpenID::Server::DiffieHellmanSHA1ServerSession
- OpenID::Server::DiffieHellmanSHA256ServerSession
- OpenID::Server::Encoder
- OpenID::Server::EncodingError
- OpenID::Server::MalformedReturnURL
- OpenID::Server::MalformedTrustRoot
- OpenID::Server::NoReturnToError
- OpenID::Server::OpenIDRequest
- OpenID::Server::OpenIDResponse
- OpenID::Server::PlainTextServerSession
- OpenID::Server::ProtocolError
- OpenID::Server::Server
- OpenID::Server::Signatory
- OpenID::Server::SigningEncoder
- OpenID::Server::UntrustedReturnURL
- OpenID::Server::VersionError
- OpenID::Server::WebResponse
- OpenID::ServerError
- OpenID::StandardFetcher
- OpenID::Store
- OpenID::Store::Filesystem
- OpenID::Store::Interface
- OpenID::Store::Memcache
- OpenID::Store::Memory
- OpenID::TrustRoot
- OpenID::TrustRoot::TrustRoot
- OpenID::TypeURIMismatch
- OpenID::UI
- OpenID::UI::Request
- OpenID::URINorm
- OpenID::UndefinedOpenIDNamespace
- OpenID::Util
- OpenID::Yadis
- OpenID::Yadis::BasicServiceEndpoint
- OpenID::Yadis::CompoundFilter
- OpenID::Yadis::DiscoveryResult
- OpenID::Yadis::TransformFilterMaker
- OpenID::Yadis::XRDSError
- OpenID::Yadis::XRDSFraud
- OpenID::Yadis::XRI
- OpenID::Yadis::XRI::ProxyResolver
- OpenID::Yadis::XRI::XRIHTTPError
- HMAC
- HMAC::Base
- HMAC::SHA1
- HMAC::SHA256
- HMAC::SHA384
- HMAC::SHA512
- Net
- Net::HTTP
- HTMLComment
- HTMLTag
- HTMLText
- HTMLToken
- HTMLTokenizer
- HTMLTokenizerError
- Object

No matching classes.

# OpenID::Yadis

### Constants

XRDS_NAMESPACES
XRDS_NS
XRD_NS_2_0
YADIS_ACCEPT_HEADER

  A value suitable for using as an accept header when performing YADIS discovery, unless the application has special requirements

YADIS_CONTENT_TYPE
YADIS_HEADER_NAME

### Public Class Methods

apply_filter(normalized_uri, xrd_data, flt=nil) click to toggle source

    # File lib/openid/yadis/services.rb, line 27
    def Yadis.apply_filter(normalized_uri, xrd_data, flt=nil)
      # Generate an iterable of endpoint objects given this input data,
      # presumably from the result of performing the Yadis protocol.

      flt = Yadis.make_filter(flt)
      et = Yadis.parseXRDS(xrd_data)

      endpoints = []
      each_service(et) { |service_element|
        endpoints += flt.get_service_endpoints(normalized_uri, service_element)
      }

      return endpoints
    end

disable_entity_expansion() click to toggle source

    # File lib/openid/yadis/xrds.rb, line 110
    def Yadis::disable_entity_expansion
      _previous_ = REXML::Document::entity_expansion_limit
      REXML::Document::entity_expansion_limit = 0
      yield
    ensure
      REXML::Document::entity_expansion_limit = _previous_
    end

discover(uri) click to toggle source

Discover services for a given URI.

uri: The identity URI as a well-formed http or https URI. The well-formedness and the protocol are not checked, but the results of this function are undefined if those properties do not hold.

returns a DiscoveryResult object

Raises DiscoveryFailure when the HTTP response does not have a 200 code.

    # File lib/openid/yadis/discovery.rb, line 74
    def self.discover(uri)
      result = DiscoveryResult.new(uri)
      begin
        resp = OpenID.fetch(uri, nil, {'Accept' => YADIS_ACCEPT_HEADER})
      rescue Exception
        raise DiscoveryFailure.new("Failed to fetch identity URL #{uri} : #{$!}", $!)
      end
      if resp.code != "200" and resp.code != "206"
        raise DiscoveryFailure.new(
                "HTTP Response status from identity URL host is not \"200\"."                 "Got status #{resp.code.inspect} for #{resp.final_url}", resp)
      end

      # Note the URL after following redirects
      result.normalized_uri = resp.final_url

      # Attempt to find out where to go to discover the document or if
      # we already have it
      result.content_type = resp['content-type']

      result.xrds_uri = self.where_is_yadis?(resp)

      if result.xrds_uri and result.used_yadis_location?
        begin
          resp = OpenID.fetch(result.xrds_uri)
        rescue
          raise DiscoveryFailure.new("Failed to fetch Yadis URL #{result.xrds_uri} : #{$!}", $!)
        end
        if resp.code != "200" and resp.code != "206"
            exc = DiscoveryFailure.new(
                    "HTTP Response status from Yadis host is not \"200\". " +
                                       "Got status #{resp.code.inspect} for #{resp.final_url}", resp)
            exc.identity_url = result.normalized_uri
            raise exc
        end

        result.content_type = resp['content-type']
      end

      result.response_text = resp.body
      return result
    end

each_service(xrds_tree, &block) click to toggle source

aka iterServices in Python

    # File lib/openid/yadis/xrds.rb, line 135
    def Yadis::each_service(xrds_tree, &block)
      xrd = get_yadis_xrd(xrds_tree)
      xrd.each_element('Service', &block)
    end

expand_service(service_element) click to toggle source

    # File lib/openid/yadis/xrds.rb, line 148
    def Yadis::expand_service(service_element)
      es = service_element.elements
      uris = es.each('URI') { |u| }
      uris = prio_sort(uris)
      types = es.each('Type/text()')
      # REXML::Text objects are not strings.
      types = types.collect { |t| t.to_s }
      uris.collect { |uri| [types, uri.text, service_element] }
    end

generate_accept_header(*elements) click to toggle source

Generate an accept header value

str or (str, float)

  -> str

    # File lib/openid/yadis/accept.rb, line 8
    def self.generate_accept_header(*elements)
      parts = []
      elements.each { |element|
        if element.is_a?(String)
          qs = "1.0"
          mtype = element
        else
          mtype, q = element
          q = q.to_f
          if q > 1 or q <= 0
            raise ArgumentError.new("Invalid preference factor: #{q}")
          end
          qs = sprintf("%0.1f", q)
        end

        parts << [qs, mtype]
      }

      parts.sort!
      chunks = []
      parts.each { |q, mtype|
        if q == '1.0'
          chunks << mtype
        else
          chunks << sprintf("%s; q=%s", mtype, q)
        end
      }

      return chunks.join(', ')
    end

get_acceptable(accept_header, have_types) click to toggle source

    # File lib/openid/yadis/accept.rb, line 132
    def self.get_acceptable(accept_header, have_types)
      # Parse the accept header and return a list of available types
      # in preferred order. If a type is unacceptable, it will not be
      # in the resulting list.
      #
      # This is a convenience wrapper around matchTypes and
      # parse_accept_header
      #
      # (str, [str]) -> [str]
      accepted = self.parse_accept_header(accept_header)
      preferred = self.match_types(accepted, have_types)
      return preferred.collect { |mtype, _| mtype }
    end

get_canonical_id(iname, xrd_tree) click to toggle source

    # File lib/openid/yadis/xrds.rb, line 25
    def Yadis::get_canonical_id(iname, xrd_tree)
      # Return the CanonicalID from this XRDS document.
      #
      # @param iname: the XRI being resolved.
      # @type iname: unicode
      #
      # @param xrd_tree: The XRDS output from the resolver.
      #
      # @returns: The XRI CanonicalID or None.
      # @returntype: unicode or None

      xrd_list = []
      REXML::XPath::match(xrd_tree.root, '/xrds:XRDS/xrd:XRD', XRDS_NAMESPACES).each { |el|
        xrd_list << el
      }

      xrd_list.reverse!

      cid_elements = []

      if !xrd_list.empty?
        xrd_list[0].elements.each { |e|
          if !e.respond_to?('name')
            next
          end
          if e.name == 'CanonicalID'
            cid_elements << e
          end
        }
      end

      cid_element = cid_elements[0]

      if !cid_element
        return nil
      end

      canonicalID = XRI.make_xri(cid_element.text)

      childID = canonicalID.downcase

      xrd_list[1..-1].each { |xrd|
        parent_sought = childID[0...childID.rindex('!')]

        parent = XRI.make_xri(xrd.elements["CanonicalID"].text)

        if parent_sought != parent.downcase
          raise XRDSFraud.new(sprintf("%s can not come from %s", parent_sought,
                                      parent))
        end

        childID = parent_sought
      }

      root = XRI.root_authority(iname)
      if not XRI.provider_is_authoritative(root, childID)
        raise XRDSFraud.new(sprintf("%s can not come from root %s", childID, root))
      end

      return canonicalID
    end

get_service_endpoints(input_url, flt=nil) click to toggle source

    # File lib/openid/yadis/services.rb, line 8
    def Yadis.get_service_endpoints(input_url, flt=nil)
      # Perform the Yadis protocol on the input URL and return an
      # iterable of resulting endpoint objects.
      #
      # @param flt: A filter object or something that is convertable
      # to a filter object (using mkFilter) that will be used to
      # generate endpoint objects. This defaults to generating
      # BasicEndpoint objects.
      result = Yadis.discover(input_url)
      begin
        endpoints = Yadis.apply_filter(result.normalized_uri,
                                       result.response_text, flt)
      rescue XRDSError => err
        raise DiscoveryFailure.new(err.to_s, nil)
      end

      return [result.normalized_uri, endpoints]
    end

get_yadis_xrd(xrds_tree) click to toggle source

    # File lib/openid/yadis/xrds.rb, line 125
    def Yadis::get_yadis_xrd(xrds_tree)
      REXML::XPath.each(xrds_tree.root,
                        '/xrds:XRDS/xrd:XRD[last()]',
                        XRDS_NAMESPACES) { |el|
        return el
      }
      raise XRDSError.new("No XRD element found.")
    end

html_yadis_location(html) click to toggle source

    # File lib/openid/yadis/parsehtml.rb, line 6
    def Yadis.html_yadis_location(html)
      parser = HTMLTokenizer.new(html)

      # to keep track of whether or not we are in the head element
      in_head = false

      begin
        while el = parser.getTag('head', '/head', 'meta', 'body', '/body',
                                 'html', 'script')

          # we are leaving head or have reached body, so we bail
          return nil if ['/head', 'body', '/body'].member?(el.tag_name)

          if el.tag_name == 'head'
            unless el.to_s[-2] == // # tag ends with a /: a short tag
              in_head = true
            end
          end
          next unless in_head

          if el.tag_name == 'script'
            unless el.to_s[-2] == // # tag ends with a /: a short tag
              parser.getTag('/script')
            end
          end

          return nil if el.tag_name == 'html'

          if el.tag_name == 'meta' and (equiv = el.attr_hash['http-equiv'])
            if ['x-xrds-location','x-yadis-location'].member?(equiv.downcase) &&
                el.attr_hash.member?('content')
              return CGI::unescapeHTML(el.attr_hash['content'])
            end
          end
        end
      rescue HTMLTokenizerError # just stop parsing if there's an error
      end
    end

is_xrds?(xrds_tree) click to toggle source

    # File lib/openid/yadis/xrds.rb, line 118
    def Yadis::is_xrds?(xrds_tree)
      xrds_root = xrds_tree.root
      return (!xrds_root.nil? and
        xrds_root.name == 'XRDS' and
        xrds_root.namespace == XRDS_NS)
    end

make_filter(parts) click to toggle source

Convert a filter-convertable thing into a filter

parts should be a filter, an endpoint, a callable, or a list of any of these.

    # File lib/openid/yadis/filters.rb, line 145
    def self.make_filter(parts)
      # Convert the parts into a list, and pass to mk_compound_filter
      if parts.nil?
        parts = [BasicServiceEndpoint]
      end

      if parts.is_a?(Array)
        return mk_compound_filter(parts)
      else
        return mk_compound_filter([parts])
      end
    end

match_types(accept_types, have_types) click to toggle source

    # File lib/openid/yadis/accept.rb, line 80
    def self.match_types(accept_types, have_types)
      # Given the result of parsing an Accept: header, and the
      # available MIME types, return the acceptable types with their
      # quality markdowns.
      #
      # For example:
      #
      # >>> acceptable = parse_accept_header('text/html, text/plain; q=0.5')
      # >>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg'])
      # [('text/html', 1.0), ('text/plain', 0.5)]
      #
      # Type signature: ([(str, str, float)], [str]) -> [(str, float)]
      if accept_types.nil? or accept_types == []
        # Accept all of them
        default = 1
      else
        default = 0
      end

      match_main = {}
      match_sub = {}
      accept_types.each { |main, sub, q|
        if main == '*'
          default = [default, q].max
          next
        elsif sub == '*'
          match_main[main] = [match_main.fetch(main, 0), q].max
        else
          match_sub[[main, sub]] = [match_sub.fetch([main, sub], 0), q].max
        end
      }

      accepted_list = []
      order_maintainer = 0
      have_types.each { |mtype|
        main, sub = mtype.split('/', 2)
        if match_sub.member?([main, sub])
          q = match_sub[[main, sub]]
        else
          q = match_main.fetch(main, default)
        end

        if q != 0
          accepted_list << [1 - q, order_maintainer, q, mtype]
          order_maintainer += 1
        end
      }

      accepted_list.sort!
      return accepted_list.collect { |_, _, q, mtype| [mtype, q] }
    end

mk_compound_filter(parts) click to toggle source

Create a filter out of a list of filter-like things

Used by make_filter

parts should be a list of things that can be passed to make_filter

    # File lib/openid/yadis/filters.rb, line 163
    def self.mk_compound_filter(parts)

      if !parts.respond_to?('each')
        raise TypeError, "#{parts.inspect} is not iterable"
      end

      # Separate into a list of callables and a list of filter objects
      transformers = []
      filters = []
      parts.each { |subfilter|
        if !subfilter.is_a?(Array)
          # If it's not an iterable
          if subfilter.respond_to?('get_service_endpoints')
            # It's a full filter
            filters << subfilter
          elsif subfilter.respond_to?('from_basic_service_endpoint')
            # It's an endpoint object, so put its endpoint conversion
            # attribute into the list of endpoint transformers
            transformers << subfilter.method('from_basic_service_endpoint')
          elsif subfilter.respond_to?('call')
            # It's a proc, so add it to the list of endpoint
            # transformers
            transformers << subfilter
          else
            raise @@filter_type_error
          end
        else
          filters << mk_compound_filter(subfilter)
        end
      }

      if transformers.length > 0
        filters << TransformFilterMaker.new(transformers)
      end

      if filters.length == 1
        return filters[0]
      else
        return CompoundFilter.new(filters)
      end
    end

parseXRDS(text) click to toggle source

    # File lib/openid/yadis/xrds.rb, line 90
    def Yadis::parseXRDS(text)
      disable_entity_expansion do
        if text.nil?
          raise XRDSError.new("Not an XRDS document.")
        end

        begin
          d = REXML::Document.new(text)
        rescue RuntimeError
          raise XRDSError.new("Not an XRDS document. Failed to parse XML.")
        end

        if is_xrds?(d)
          return d
        else
          raise XRDSError.new("Not an XRDS document.")
        end
      end
    end

parse_accept_header(value) click to toggle source

    # File lib/openid/yadis/accept.rb, line 39
    def self.parse_accept_header(value)
      # Parse an accept header, ignoring any accept-extensions
      #
      # returns a list of tuples containing main MIME type, MIME
      # subtype, and quality markdown.
      #
      # str -> [(str, str, float)]
      chunks = value.split(',', -1).collect { |v| v.strip }
      accept = []
      chunks.each { |chunk|
        parts = chunk.split(";", -1).collect { |s| s.strip }

        mtype = parts.shift
        if mtype.index('/').nil?
          # This is not a MIME type, so ignore the bad data
          next
        end

        main, sub = mtype.split('/', 2)

        q = nil
        parts.each { |ext|
          if !ext.index('=').nil?
            k, v = ext.split('=', 2)
            if k == 'q'
              q = v.to_f
            end
          end
        }

        q = 1.0 if q.nil?

        accept << [q, main, sub]
      }

      accept.sort!
      accept.reverse!

      return accept.collect { |q, main, sub| [main, sub, q] }
    end

prio_sort(elements) click to toggle source

Sort a list of elements that have priority attributes.

    # File lib/openid/yadis/xrds.rb, line 159
    def Yadis::prio_sort(elements)
      elements.sort { |a,b|
        a.attribute('priority').to_s.to_i <=> b.attribute('priority').to_s.to_i
      }
    end

services(xrds_tree) click to toggle source

    # File lib/openid/yadis/xrds.rb, line 140
    def Yadis::services(xrds_tree)
      s = []
      each_service(xrds_tree) { |service|
        s << service
      }
      return s
    end

where_is_yadis?(resp) click to toggle source

Given a HTTPResponse, return the location of the Yadis document.

May be the URL just retrieved, another URL, or None, if I can't find any.

non-blocking

    # File lib/openid/yadis/discovery.rb, line 124
    def self.where_is_yadis?(resp)
      # Attempt to find out where to go to discover the document or if
      # we already have it
      content_type = resp['content-type']

      # According to the spec, the content-type header must be an
      # exact match, or else we have to look for an indirection.
      if (!content_type.nil? and !content_type.to_s.empty? and
          content_type.split(';', 2)[0].downcase == YADIS_CONTENT_TYPE)
        return resp.final_url
      else
        # Try the header
        yadis_loc = resp[YADIS_HEADER_NAME.downcase]

        if yadis_loc.nil?
          # Parse as HTML if the header is missing.
          #
          # XXX: do we want to do something with content-type, like
          # have a whitelist or a blacklist (for detecting that it's
          # HTML)?
          yadis_loc = Yadis.html_yadis_location(resp.body)
        end
      end

      return yadis_loc
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.