1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
# This file contains functions and classes used for extracting
# endpoint information out of a Yadis XRD file using the REXML
# XML parser.
#
module OpenID
module Yadis
class BasicServiceEndpoint
attr_reader :type_uris, :yadis_url, :uri, :service_element
# Generic endpoint object that contains parsed service
# information, as well as a reference to the service element
# from which it was generated. If there is more than one
# xrd:Type or xrd:URI in the xrd:Service, this object represents
# just one of those pairs.
#
# This object can be used as a filter, because it implements
# fromBasicServiceEndpoint.
#
# The simplest kind of filter you can write implements
# fromBasicServiceEndpoint, which takes one of these objects.
def initialize(yadis_url, type_uris, uri, service_element)
@type_uris = type_uris
@yadis_url = yadis_url
@uri = uri
@service_element = service_element
end
# Query this endpoint to see if it has any of the given type
# URIs. This is useful for implementing other endpoint classes
# that e.g. need to check for the presence of multiple
# versions of a single protocol.
def match_types(type_uris)
return @type_uris & type_uris
end
# Trivial transform from a basic endpoint to itself. This
# method exists to allow BasicServiceEndpoint to be used as a
# filter.
#
# If you are subclassing this object, re-implement this function.
def self.from_basic_service_endpoint(endpoint)
return endpoint
end
# A hack to make both this class and its instances respond to
# this message since Ruby doesn't support static methods.
def from_basic_service_endpoint(endpoint)
return self.class.from_basic_service_endpoint(endpoint)
end
end
# Take a list of basic filters and makes a filter that
# transforms the basic filter into a top-level filter. This is
# mostly useful for the implementation of make_filter, which
# should only be needed for special cases or internal use by
# this library.
#
# This object is useful for creating simple filters for services
# that use one URI and are specified by one Type (we expect most
# Types will fit this paradigm).
#
# Creates a BasicServiceEndpoint object and apply the filter
# functions to it until one of them returns a value.
class TransformFilterMaker
attr_reader :filter_procs
# Initialize the filter maker's state
#
# filter_functions are the endpoint transformer
# Procs to apply to the basic endpoint. These are called in
# turn until one of them does not return nil, and the result
# of that transformer is returned.
def initialize(filter_procs)
@filter_procs = filter_procs
end
# Returns an array of endpoint objects produced by the
# filter procs.
def get_service_endpoints(yadis_url, service_element)
endpoints = []
# Do an expansion of the service element by xrd:Type and
# xrd:URI
Yadis::expand_service(service_element).each { |type_uris, uri, _|
# Create a basic endpoint object to represent this
# yadis_url, Service, Type, URI combination
endpoint = BasicServiceEndpoint.new(
yadis_url, type_uris, uri, service_element)
e = apply_filters(endpoint)
if !e.nil?
endpoints << e
end
}
return endpoints
end
def apply_filters(endpoint)
# Apply filter procs to an endpoint until one of them returns
# non-nil.
@filter_procs.each { |filter_proc|
e = filter_proc.call(endpoint)
if !e.nil?
# Once one of the filters has returned an endpoint, do not
# apply any more.
return e
end
}
return nil
end
end
class CompoundFilter
attr_reader :subfilters
# Create a new filter that applies a set of filters to an
# endpoint and collects their results.
def initialize(subfilters)
@subfilters = subfilters
end
# Generate all endpoint objects for all of the subfilters of
# this filter and return their concatenation.
def get_service_endpoints(yadis_url, service_element)
endpoints = []
@subfilters.each { |subfilter|
endpoints += subfilter.get_service_endpoints(yadis_url, service_element)
}
return endpoints
end
end
# Exception raised when something is not able to be turned into a
# filter
@@filter_type_error = TypeError.new(
'Expected a filter, an endpoint, a callable or a list of any of these.')
# Convert a filter-convertable thing into a filter
#
# parts should be a filter, an endpoint, a callable, or a list of
# any of these.
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
# 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
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
end
end
|