ruby-openid 1.1.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/INSTALL +0 -9
- data/README +21 -22
- data/UPGRADE +117 -0
- data/admin/runtests.rb +36 -0
- data/examples/README +13 -21
- data/examples/active_record_openid_store/README +8 -3
- data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +4 -8
- data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
- data/examples/active_record_openid_store/lib/association.rb +2 -0
- data/examples/active_record_openid_store/lib/openid_ar_store.rb +22 -47
- data/examples/active_record_openid_store/test/store_test.rb +78 -48
- data/examples/discover +46 -0
- data/examples/{rails_server → rails_openid}/README +0 -0
- data/examples/{rails_server → rails_openid}/Rakefile +0 -0
- data/examples/{rails_server → rails_openid}/app/controllers/application.rb +0 -0
- data/examples/rails_openid/app/controllers/consumer_controller.rb +115 -0
- data/examples/{rails_server → rails_openid}/app/controllers/login_controller.rb +10 -2
- data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
- data/examples/{rails_server → rails_openid}/app/helpers/application_helper.rb +0 -0
- data/examples/{rails_server → rails_openid}/app/helpers/login_helper.rb +0 -0
- data/examples/{rails_server → rails_openid}/app/helpers/server_helper.rb +0 -0
- data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
- data/examples/rails_openid/app/views/consumer/start.rhtml +8 -0
- data/examples/{rails_server → rails_openid}/app/views/layouts/server.rhtml +0 -0
- data/examples/{rails_server → rails_openid}/app/views/login/index.rhtml +1 -1
- data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
- data/examples/{rails_server → rails_openid}/config/boot.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/database.yml +0 -0
- data/examples/{rails_server → rails_openid}/config/environment.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/environments/development.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/environments/production.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/environments/test.rb +0 -0
- data/examples/{rails_server → rails_openid}/config/routes.rb +2 -1
- data/examples/{rails_server → rails_openid}/doc/README_FOR_APP +0 -0
- data/examples/{rails_server → rails_openid}/public/404.html +0 -0
- data/examples/{rails_server → rails_openid}/public/500.html +0 -0
- data/examples/{rails_server → rails_openid}/public/dispatch.cgi +0 -0
- data/examples/{rails_server → rails_openid}/public/dispatch.fcgi +0 -0
- data/examples/{rails_server → rails_openid}/public/dispatch.rb +0 -0
- data/examples/{rails_server → rails_openid}/public/favicon.ico +0 -0
- data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/controls.js +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/dragdrop.js +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/effects.js +0 -0
- data/examples/{rails_server → rails_openid}/public/javascripts/prototype.js +0 -0
- data/examples/{rails_server → rails_openid}/public/robots.txt +0 -0
- data/examples/{rails_server → rails_openid}/script/about +0 -0
- data/examples/{rails_server → rails_openid}/script/breakpointer +0 -0
- data/examples/{rails_server → rails_openid}/script/console +0 -0
- data/examples/{rails_server → rails_openid}/script/destroy +0 -0
- data/examples/{rails_server → rails_openid}/script/generate +0 -0
- data/examples/{rails_server → rails_openid}/script/performance/benchmarker +0 -0
- data/examples/{rails_server → rails_openid}/script/performance/profiler +0 -0
- data/examples/{rails_server → rails_openid}/script/plugin +0 -0
- data/examples/{rails_server → rails_openid}/script/process/reaper +0 -0
- data/examples/{rails_server → rails_openid}/script/process/spawner +0 -0
- data/examples/{rails_server → rails_openid}/script/process/spinner +0 -0
- data/examples/{rails_server → rails_openid}/script/runner +0 -0
- data/examples/{rails_server → rails_openid}/script/server +0 -0
- data/examples/{rails_server → rails_openid}/test/functional/login_controller_test.rb +0 -0
- data/examples/{rails_server → rails_openid}/test/functional/server_controller_test.rb +0 -0
- data/examples/{rails_server → rails_openid}/test/test_helper.rb +0 -0
- data/lib/{hmac.rb → hmac/hmac.rb} +0 -0
- data/lib/{hmac-sha1.rb → hmac/sha1.rb} +1 -1
- data/lib/{hmac-sha2.rb → hmac/sha2.rb} +1 -1
- data/lib/openid/association.rb +213 -73
- data/lib/openid/consumer/associationmanager.rb +338 -0
- data/lib/openid/consumer/checkid_request.rb +175 -0
- data/lib/openid/consumer/discovery.rb +480 -0
- data/lib/openid/consumer/discovery_manager.rb +123 -0
- data/lib/openid/consumer/html_parse.rb +136 -0
- data/lib/openid/consumer/idres.rb +525 -0
- data/lib/openid/consumer/responses.rb +133 -0
- data/lib/openid/consumer.rb +280 -807
- data/lib/openid/cryptutil.rb +85 -0
- data/lib/openid/dh.rb +60 -23
- data/lib/openid/extension.rb +31 -0
- data/lib/openid/extensions/ax.rb +506 -0
- data/lib/openid/extensions/pape.rb +182 -0
- data/lib/openid/extensions/sreg.rb +275 -0
- data/lib/openid/extras.rb +11 -0
- data/lib/openid/fetchers.rb +132 -93
- data/lib/openid/kvform.rb +133 -0
- data/lib/openid/kvpost.rb +56 -0
- data/lib/openid/message.rb +534 -0
- data/lib/openid/protocolerror.rb +6 -0
- data/lib/openid/server.rb +1215 -666
- data/lib/openid/store/filesystem.rb +271 -0
- data/lib/openid/store/interface.rb +75 -0
- data/lib/openid/store/memory.rb +84 -0
- data/lib/openid/store/nonce.rb +68 -0
- data/lib/openid/trustroot.rb +314 -87
- data/lib/openid/urinorm.rb +37 -34
- data/lib/openid/util.rb +42 -220
- data/lib/openid/yadis/accept.rb +148 -0
- data/lib/openid/yadis/constants.rb +21 -0
- data/lib/openid/yadis/discovery.rb +153 -0
- data/lib/openid/yadis/filters.rb +205 -0
- data/lib/openid/{htmltokenizer.rb → yadis/htmltokenizer.rb} +1 -54
- data/lib/openid/yadis/parsehtml.rb +36 -0
- data/lib/openid/yadis/services.rb +42 -0
- data/lib/openid/yadis/xrds.rb +171 -0
- data/lib/openid/yadis/xri.rb +90 -0
- data/lib/openid/yadis/xrires.rb +106 -0
- data/lib/openid.rb +1 -4
- data/test/data/accept.txt +124 -0
- data/test/data/dh.txt +29 -0
- data/test/data/example-xrds.xml +14 -0
- data/test/data/linkparse.txt +587 -0
- data/test/data/n2b64 +650 -0
- data/test/data/test1-discover.txt +137 -0
- data/test/data/test1-parsehtml.txt +128 -0
- data/test/data/test_discover/openid.html +11 -0
- data/test/data/test_discover/openid2.html +11 -0
- data/test/data/test_discover/openid2_xrds.xml +12 -0
- data/test/data/test_discover/openid2_xrds_no_local_id.xml +11 -0
- data/test/data/test_discover/openid_1_and_2.html +11 -0
- data/test/data/test_discover/openid_1_and_2_xrds.xml +16 -0
- data/test/data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
- data/test/data/test_discover/openid_and_yadis.html +12 -0
- data/test/data/test_discover/openid_no_delegate.html +10 -0
- data/test/data/test_discover/yadis_0entries.xml +12 -0
- data/test/data/test_discover/yadis_2_bad_local_id.xml +15 -0
- data/test/data/test_discover/yadis_2entries_delegate.xml +22 -0
- data/test/data/test_discover/yadis_2entries_idp.xml +21 -0
- data/test/data/test_discover/yadis_another_delegate.xml +14 -0
- data/test/data/test_discover/yadis_idp.xml +12 -0
- data/test/data/test_discover/yadis_idp_delegate.xml +13 -0
- data/test/data/test_discover/yadis_no_delegate.xml +11 -0
- data/test/data/test_xrds/=j3h.2007.11.14.xrds +25 -0
- data/test/data/test_xrds/README +12 -0
- data/test/data/test_xrds/delegated-20060809-r1.xrds +34 -0
- data/test/data/test_xrds/delegated-20060809-r2.xrds +34 -0
- data/test/data/test_xrds/delegated-20060809.xrds +34 -0
- data/test/data/test_xrds/no-xrd.xml +7 -0
- data/test/data/test_xrds/not-xrds.xml +2 -0
- data/test/data/test_xrds/prefixsometimes.xrds +34 -0
- data/test/data/test_xrds/ref.xrds +109 -0
- data/test/data/test_xrds/sometimesprefix.xrds +34 -0
- data/test/data/test_xrds/spoof1.xrds +25 -0
- data/test/data/test_xrds/spoof2.xrds +25 -0
- data/test/data/test_xrds/spoof3.xrds +37 -0
- data/test/data/test_xrds/status222.xrds +9 -0
- data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
- data/test/data/trustroot.txt +147 -0
- data/test/discoverdata.rb +131 -0
- data/test/test_accept.rb +170 -0
- data/test/test_association.rb +266 -0
- data/test/test_associationmanager.rb +899 -0
- data/test/test_ax.rb +587 -0
- data/test/test_checkid_request.rb +297 -0
- data/test/test_consumer.rb +257 -0
- data/test/test_cryptutil.rb +117 -0
- data/test/test_dh.rb +86 -0
- data/test/test_discover.rb +772 -0
- data/test/test_discovery_manager.rb +262 -0
- data/test/test_extras.rb +35 -0
- data/test/test_fetchers.rb +472 -0
- data/test/test_filters.rb +270 -0
- data/test/test_idres.rb +816 -0
- data/test/test_kvform.rb +165 -0
- data/test/test_kvpost.rb +65 -0
- data/test/test_linkparse.rb +101 -0
- data/test/test_message.rb +1058 -0
- data/test/test_nonce.rb +89 -0
- data/test/test_openid_yadis.rb +178 -0
- data/test/test_pape.rb +233 -0
- data/test/test_parsehtml.rb +80 -0
- data/test/test_responses.rb +63 -0
- data/test/test_server.rb +2270 -0
- data/test/test_sreg.rb +479 -0
- data/test/test_stores.rb +269 -0
- data/test/test_trustroot.rb +112 -0
- data/test/{urinorm.rb → test_urinorm.rb} +6 -3
- data/test/test_util.rb +144 -0
- data/test/test_xrds.rb +160 -0
- data/test/test_xri.rb +48 -0
- data/test/test_xrires.rb +63 -0
- data/test/test_yadis_discovery.rb +207 -0
- data/test/testutil.rb +116 -0
- data/test/util.rb +47 -50
- metadata +233 -143
- data/examples/consumer.rb +0 -290
- data/examples/rails_openid_login_generator/openid_login_generator-0.1.gem +0 -0
- data/examples/rails_server/app/controllers/server_controller.rb +0 -190
- data/examples/rails_server/app/views/server/decide.rhtml +0 -11
- data/examples/rails_server/public/images/rails.png +0 -0
- data/lib/hmac-md5.rb +0 -11
- data/lib/hmac-rmd160.rb +0 -11
- data/lib/openid/discovery.rb +0 -122
- data/lib/openid/filestore.rb +0 -315
- data/lib/openid/parse.rb +0 -23
- data/lib/openid/service.rb +0 -147
- data/lib/openid/stores.rb +0 -178
- data/test/assoc.rb +0 -38
- data/test/consumer.rb +0 -376
- data/test/data/brian.xrds +0 -16
- data/test/data/brianellin.mylid.xrds +0 -42
- data/test/dh.rb +0 -20
- data/test/extensions.rb +0 -30
- data/test/linkparse.rb +0 -305
- data/test/runtests.rb +0 -22
- data/test/server2.rb +0 -1053
- data/test/service.rb +0 -47
- data/test/storetestcase.rb +0 -172
- data/test/teststore.rb +0 -47
- data/test/trustroot.rb +0 -117
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# This file contains functions and classes used for extracting
|
|
2
|
+
# endpoint information out of a Yadis XRD file using the REXML
|
|
3
|
+
# XML parser.
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
module OpenID
|
|
7
|
+
module Yadis
|
|
8
|
+
class BasicServiceEndpoint
|
|
9
|
+
attr_reader :type_uris, :yadis_url, :uri, :service_element
|
|
10
|
+
|
|
11
|
+
# Generic endpoint object that contains parsed service
|
|
12
|
+
# information, as well as a reference to the service element
|
|
13
|
+
# from which it was generated. If there is more than one
|
|
14
|
+
# xrd:Type or xrd:URI in the xrd:Service, this object represents
|
|
15
|
+
# just one of those pairs.
|
|
16
|
+
#
|
|
17
|
+
# This object can be used as a filter, because it implements
|
|
18
|
+
# fromBasicServiceEndpoint.
|
|
19
|
+
#
|
|
20
|
+
# The simplest kind of filter you can write implements
|
|
21
|
+
# fromBasicServiceEndpoint, which takes one of these objects.
|
|
22
|
+
def initialize(yadis_url, type_uris, uri, service_element)
|
|
23
|
+
@type_uris = type_uris
|
|
24
|
+
@yadis_url = yadis_url
|
|
25
|
+
@uri = uri
|
|
26
|
+
@service_element = service_element
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Query this endpoint to see if it has any of the given type
|
|
30
|
+
# URIs. This is useful for implementing other endpoint classes
|
|
31
|
+
# that e.g. need to check for the presence of multiple
|
|
32
|
+
# versions of a single protocol.
|
|
33
|
+
def match_types(type_uris)
|
|
34
|
+
return @type_uris & type_uris
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Trivial transform from a basic endpoint to itself. This
|
|
38
|
+
# method exists to allow BasicServiceEndpoint to be used as a
|
|
39
|
+
# filter.
|
|
40
|
+
#
|
|
41
|
+
# If you are subclassing this object, re-implement this function.
|
|
42
|
+
def self.from_basic_service_endpoint(endpoint)
|
|
43
|
+
return endpoint
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# A hack to make both this class and its instances respond to
|
|
47
|
+
# this message since Ruby doesn't support static methods.
|
|
48
|
+
def from_basic_service_endpoint(endpoint)
|
|
49
|
+
return self.class.from_basic_service_endpoint(endpoint)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Take a list of basic filters and makes a filter that
|
|
55
|
+
# transforms the basic filter into a top-level filter. This is
|
|
56
|
+
# mostly useful for the implementation of make_filter, which
|
|
57
|
+
# should only be needed for special cases or internal use by
|
|
58
|
+
# this library.
|
|
59
|
+
#
|
|
60
|
+
# This object is useful for creating simple filters for services
|
|
61
|
+
# that use one URI and are specified by one Type (we expect most
|
|
62
|
+
# Types will fit this paradigm).
|
|
63
|
+
#
|
|
64
|
+
# Creates a BasicServiceEndpoint object and apply the filter
|
|
65
|
+
# functions to it until one of them returns a value.
|
|
66
|
+
class TransformFilterMaker
|
|
67
|
+
attr_reader :filter_procs
|
|
68
|
+
|
|
69
|
+
# Initialize the filter maker's state
|
|
70
|
+
#
|
|
71
|
+
# filter_functions are the endpoint transformer
|
|
72
|
+
# Procs to apply to the basic endpoint. These are called in
|
|
73
|
+
# turn until one of them does not return nil, and the result
|
|
74
|
+
# of that transformer is returned.
|
|
75
|
+
def initialize(filter_procs)
|
|
76
|
+
@filter_procs = filter_procs
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns an array of endpoint objects produced by the
|
|
80
|
+
# filter procs.
|
|
81
|
+
def get_service_endpoints(yadis_url, service_element)
|
|
82
|
+
endpoints = []
|
|
83
|
+
|
|
84
|
+
# Do an expansion of the service element by xrd:Type and
|
|
85
|
+
# xrd:URI
|
|
86
|
+
Yadis::expand_service(service_element).each { |type_uris, uri, _|
|
|
87
|
+
# Create a basic endpoint object to represent this
|
|
88
|
+
# yadis_url, Service, Type, URI combination
|
|
89
|
+
endpoint = BasicServiceEndpoint.new(
|
|
90
|
+
yadis_url, type_uris, uri, service_element)
|
|
91
|
+
|
|
92
|
+
e = apply_filters(endpoint)
|
|
93
|
+
if !e.nil?
|
|
94
|
+
endpoints << e
|
|
95
|
+
end
|
|
96
|
+
}
|
|
97
|
+
return endpoints
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def apply_filters(endpoint)
|
|
101
|
+
# Apply filter procs to an endpoint until one of them returns
|
|
102
|
+
# non-nil.
|
|
103
|
+
@filter_procs.each { |filter_proc|
|
|
104
|
+
e = filter_proc.call(endpoint)
|
|
105
|
+
if !e.nil?
|
|
106
|
+
# Once one of the filters has returned an endpoint, do not
|
|
107
|
+
# apply any more.
|
|
108
|
+
return e
|
|
109
|
+
end
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return nil
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
class CompoundFilter
|
|
117
|
+
attr_reader :subfilters
|
|
118
|
+
|
|
119
|
+
# Create a new filter that applies a set of filters to an
|
|
120
|
+
# endpoint and collects their results.
|
|
121
|
+
def initialize(subfilters)
|
|
122
|
+
@subfilters = subfilters
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Generate all endpoint objects for all of the subfilters of
|
|
126
|
+
# this filter and return their concatenation.
|
|
127
|
+
def get_service_endpoints(yadis_url, service_element)
|
|
128
|
+
endpoints = []
|
|
129
|
+
@subfilters.each { |subfilter|
|
|
130
|
+
endpoints += subfilter.get_service_endpoints(yadis_url, service_element)
|
|
131
|
+
}
|
|
132
|
+
return endpoints
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Exception raised when something is not able to be turned into a
|
|
137
|
+
# filter
|
|
138
|
+
@@filter_type_error = TypeError.new(
|
|
139
|
+
'Expected a filter, an endpoint, a callable or a list of any of these.')
|
|
140
|
+
|
|
141
|
+
# Convert a filter-convertable thing into a filter
|
|
142
|
+
#
|
|
143
|
+
# parts should be a filter, an endpoint, a callable, or a list of
|
|
144
|
+
# any of these.
|
|
145
|
+
def self.make_filter(parts)
|
|
146
|
+
# Convert the parts into a list, and pass to mk_compound_filter
|
|
147
|
+
if parts.nil?
|
|
148
|
+
parts = [BasicServiceEndpoint]
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
if parts.is_a?(Array)
|
|
152
|
+
return mk_compound_filter(parts)
|
|
153
|
+
else
|
|
154
|
+
return mk_compound_filter([parts])
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Create a filter out of a list of filter-like things
|
|
159
|
+
#
|
|
160
|
+
# Used by make_filter
|
|
161
|
+
#
|
|
162
|
+
# parts should be a list of things that can be passed to make_filter
|
|
163
|
+
def self.mk_compound_filter(parts)
|
|
164
|
+
|
|
165
|
+
if !parts.respond_to?('each')
|
|
166
|
+
raise TypeError, "#{parts.inspect} is not iterable"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Separate into a list of callables and a list of filter objects
|
|
170
|
+
transformers = []
|
|
171
|
+
filters = []
|
|
172
|
+
parts.each { |subfilter|
|
|
173
|
+
if !subfilter.is_a?(Array)
|
|
174
|
+
# If it's not an iterable
|
|
175
|
+
if subfilter.respond_to?('get_service_endpoints')
|
|
176
|
+
# It's a full filter
|
|
177
|
+
filters << subfilter
|
|
178
|
+
elsif subfilter.respond_to?('from_basic_service_endpoint')
|
|
179
|
+
# It's an endpoint object, so put its endpoint conversion
|
|
180
|
+
# attribute into the list of endpoint transformers
|
|
181
|
+
transformers << subfilter.method('from_basic_service_endpoint')
|
|
182
|
+
elsif subfilter.respond_to?('call')
|
|
183
|
+
# It's a proc, so add it to the list of endpoint
|
|
184
|
+
# transformers
|
|
185
|
+
transformers << subfilter
|
|
186
|
+
else
|
|
187
|
+
raise @@filter_type_error
|
|
188
|
+
end
|
|
189
|
+
else
|
|
190
|
+
filters << mk_compound_filter(subfilter)
|
|
191
|
+
end
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if transformers.length > 0
|
|
195
|
+
filters << TransformFilterMaker.new(transformers)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if filters.length == 1
|
|
199
|
+
return filters[0]
|
|
200
|
+
else
|
|
201
|
+
return CompoundFilter.new(filters)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -253,7 +253,7 @@ class HTMLTag < HTMLToken
|
|
|
253
253
|
if !@hashed
|
|
254
254
|
if !@end_tag
|
|
255
255
|
# Get the attributes
|
|
256
|
-
attr_arr = @raw.scan(/<[\w:-]+\s+(
|
|
256
|
+
attr_arr = @raw.scan(/<[\w:-]+\s+(.*?)\/?>/m)[0]
|
|
257
257
|
if attr_arr.kind_of?(Array)
|
|
258
258
|
# Attributes found, parse them
|
|
259
259
|
attrs = attr_arr[0]
|
|
@@ -300,56 +300,3 @@ class HTMLTag < HTMLToken
|
|
|
300
300
|
end
|
|
301
301
|
end
|
|
302
302
|
|
|
303
|
-
if $0 == __FILE__
|
|
304
|
-
require 'test/unit'
|
|
305
|
-
|
|
306
|
-
class TC_TestHTMLTokenizer < Test::Unit::TestCase
|
|
307
|
-
def test_bad_link
|
|
308
|
-
toke = HTMLTokenizer.new("<p><a href=http://bad.com/link>foo</a></p>")
|
|
309
|
-
assert("http://bad.com/link" == toke.getTag("a").attr_hash['href'])
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
def test_namespace
|
|
313
|
-
toke = HTMLTokenizer.new("<f:table xmlns:f=\"http://www.com/foo\">")
|
|
314
|
-
assert("http://www.com/foo" == toke.getTag("f:table").attr_hash['xmlns:f'])
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def test_comment
|
|
318
|
-
toke = HTMLTokenizer.new("<!-- comment on me -->")
|
|
319
|
-
t = toke.getNextToken
|
|
320
|
-
assert(HTMLComment == t.class)
|
|
321
|
-
assert("comment on me" == t.contents)
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
def test_full
|
|
326
|
-
page = "<HTML>
|
|
327
|
-
<HEAD>
|
|
328
|
-
<TITLE>This is the title</TITLE>
|
|
329
|
-
</HEAD>
|
|
330
|
-
<!-- Here comes the <a href=\"missing.link\">blah</a>
|
|
331
|
-
comment body
|
|
332
|
-
-->
|
|
333
|
-
<BODY>
|
|
334
|
-
<H1>This is the header</H1>
|
|
335
|
-
<P>
|
|
336
|
-
This is the paragraph, it contains
|
|
337
|
-
<a href=\"link.html\">links</a>,
|
|
338
|
-
<img src=\"blah.gif\" optional alt='images
|
|
339
|
-
are
|
|
340
|
-
really cool'>. Ok, here is some more text and
|
|
341
|
-
<A href=\"http://another.link.com/\" target=\"_blank\">another link</A>.
|
|
342
|
-
</P>
|
|
343
|
-
</body>
|
|
344
|
-
</HTML>
|
|
345
|
-
"
|
|
346
|
-
toke = HTMLTokenizer.new(page)
|
|
347
|
-
|
|
348
|
-
assert("<h1>" == toke.getTag("h1", "h2", "h3").to_s.downcase)
|
|
349
|
-
assert(HTMLTag.new("<a href=\"link.html\">") == toke.getTag("IMG", "A"))
|
|
350
|
-
assert("links" == toke.getTrimmedText)
|
|
351
|
-
assert(toke.getTag("IMG", "A").attr_hash['optional'])
|
|
352
|
-
assert("_blank" == toke.getTag("IMG", "A").attr_hash['target'])
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "openid/yadis/htmltokenizer"
|
|
2
|
+
require 'cgi'
|
|
3
|
+
|
|
4
|
+
module OpenID
|
|
5
|
+
module Yadis
|
|
6
|
+
def Yadis.html_yadis_location(html)
|
|
7
|
+
parser = HTMLTokenizer.new(html)
|
|
8
|
+
|
|
9
|
+
# to keep track of whether or not we are in the head element
|
|
10
|
+
in_head = false
|
|
11
|
+
|
|
12
|
+
while el = parser.getTag('head', '/head', 'meta', 'body', '/body', 'html')
|
|
13
|
+
|
|
14
|
+
# we are leaving head or have reached body, so we bail
|
|
15
|
+
return nil if ['/head', 'body', '/body'].member?(el.tag_name)
|
|
16
|
+
|
|
17
|
+
if el.tag_name == 'head'
|
|
18
|
+
unless el.to_s[-2] == 47 # tag ends with a /: a short tag
|
|
19
|
+
in_head = true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
next unless in_head
|
|
23
|
+
|
|
24
|
+
return nil if el.tag_name == 'html'
|
|
25
|
+
|
|
26
|
+
if el.tag_name == 'meta' and (equiv = el.attr_hash['http-equiv'])
|
|
27
|
+
if ['x-xrds-location','x-yadis-location'].member?(equiv.downcase)
|
|
28
|
+
return CGI::unescapeHTML(el.attr_hash['content'])
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
require 'openid/yadis/filters'
|
|
3
|
+
require 'openid/yadis/discovery'
|
|
4
|
+
require 'openid/yadis/xrds'
|
|
5
|
+
|
|
6
|
+
module OpenID
|
|
7
|
+
module Yadis
|
|
8
|
+
def Yadis.get_service_endpoints(input_url, flt=nil)
|
|
9
|
+
# Perform the Yadis protocol on the input URL and return an
|
|
10
|
+
# iterable of resulting endpoint objects.
|
|
11
|
+
#
|
|
12
|
+
# @param flt: A filter object or something that is convertable
|
|
13
|
+
# to a filter object (using mkFilter) that will be used to
|
|
14
|
+
# generate endpoint objects. This defaults to generating
|
|
15
|
+
# BasicEndpoint objects.
|
|
16
|
+
result = Yadis.discover(input_url)
|
|
17
|
+
begin
|
|
18
|
+
endpoints = Yadis.apply_filter(result.normalized_uri,
|
|
19
|
+
result.response_text, flt)
|
|
20
|
+
rescue XRDSError => err
|
|
21
|
+
raise DiscoveryFailure.new(err.to_s, nil)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
return [result.normalized_uri, endpoints]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def Yadis.apply_filter(normalized_uri, xrd_data, flt=nil)
|
|
28
|
+
# Generate an iterable of endpoint objects given this input data,
|
|
29
|
+
# presumably from the result of performing the Yadis protocol.
|
|
30
|
+
|
|
31
|
+
flt = Yadis.make_filter(flt)
|
|
32
|
+
et = Yadis.parseXRDS(xrd_data)
|
|
33
|
+
|
|
34
|
+
endpoints = []
|
|
35
|
+
each_service(et) { |service_element|
|
|
36
|
+
endpoints += flt.get_service_endpoints(normalized_uri, service_element)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return endpoints
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
require 'rexml/document'
|
|
2
|
+
require 'rexml/element'
|
|
3
|
+
require 'rexml/xpath'
|
|
4
|
+
|
|
5
|
+
module OpenID
|
|
6
|
+
module Yadis
|
|
7
|
+
|
|
8
|
+
XRD_NS_2_0 = 'xri://$xrd*($v*2.0)'
|
|
9
|
+
XRDS_NS = 'xri://$xrds'
|
|
10
|
+
|
|
11
|
+
XRDS_NAMESPACES = {
|
|
12
|
+
'xrds' => XRDS_NS,
|
|
13
|
+
'xrd' => XRD_NS_2_0,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class XRDSError < StandardError; end
|
|
17
|
+
|
|
18
|
+
# Raised when there's an assertion in the XRDS that it does not
|
|
19
|
+
# have the authority to make.
|
|
20
|
+
class XRDSFraud < XRDSError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def Yadis::get_canonical_id(iname, xrd_tree)
|
|
24
|
+
# Return the CanonicalID from this XRDS document.
|
|
25
|
+
#
|
|
26
|
+
# @param iname: the XRI being resolved.
|
|
27
|
+
# @type iname: unicode
|
|
28
|
+
#
|
|
29
|
+
# @param xrd_tree: The XRDS output from the resolver.
|
|
30
|
+
#
|
|
31
|
+
# @returns: The XRI CanonicalID or None.
|
|
32
|
+
# @returntype: unicode or None
|
|
33
|
+
|
|
34
|
+
xrd_list = []
|
|
35
|
+
REXML::XPath::match(xrd_tree.root, '/xrds:XRDS/xrd:XRD', XRDS_NAMESPACES).each { |el|
|
|
36
|
+
xrd_list << el
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
xrd_list.reverse!
|
|
40
|
+
|
|
41
|
+
cid_elements = []
|
|
42
|
+
|
|
43
|
+
if !xrd_list.empty?
|
|
44
|
+
xrd_list[0].elements.each { |e|
|
|
45
|
+
if !e.respond_to?('name')
|
|
46
|
+
next
|
|
47
|
+
end
|
|
48
|
+
if e.name == 'CanonicalID'
|
|
49
|
+
cid_elements << e
|
|
50
|
+
end
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
cid_element = cid_elements[-1]
|
|
55
|
+
|
|
56
|
+
if !cid_element
|
|
57
|
+
return nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
canonicalID = XRI.make_xri(cid_element.text)
|
|
61
|
+
|
|
62
|
+
childID = canonicalID
|
|
63
|
+
|
|
64
|
+
xrd_list[1..-1].each { |xrd|
|
|
65
|
+
parent_sought = childID[0...childID.rindex('!')]
|
|
66
|
+
|
|
67
|
+
parent_list = []
|
|
68
|
+
xrd.elements.each_element(CANONICALID_TAG) { |c|
|
|
69
|
+
XRI.make_xri(c.text)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if !parent_list.member?(parent_sought)
|
|
73
|
+
raise XRDSFraud.new(sprintf("%s can not come from any of %s", parent_sought,
|
|
74
|
+
parent_list))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
childID = parent_sought
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
root = XRI.root_authority(iname)
|
|
81
|
+
if not XRI.provider_is_authoritative(root, childID)
|
|
82
|
+
raise XRDSFraud.new(sprintf("%s can not come from root %s", childID, root))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return canonicalID
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def Yadis::mkXRDSTag(name)
|
|
89
|
+
e = REXML::Element.new('xrds:' + name)
|
|
90
|
+
e.add_namespace('xrds', XRDS_NS)
|
|
91
|
+
return e
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def Yadis::mkXRDTag(name)
|
|
95
|
+
e = REXML::Element.new('xrd:' + name)
|
|
96
|
+
e.add_namespace('xrd', XRD_NS_2_0)
|
|
97
|
+
return e
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
ROOT_TAG = Yadis::mkXRDSTag('XRDS')
|
|
101
|
+
CANONICALID_TAG = mkXRDTag('CanonicalID')
|
|
102
|
+
|
|
103
|
+
class XRDSError < StandardError
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def Yadis::parseXRDS(text)
|
|
107
|
+
if text.nil?
|
|
108
|
+
raise XRDSError.new("Not an XRDS document.")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
begin
|
|
112
|
+
d = REXML::Document.new(text)
|
|
113
|
+
rescue RuntimeError => why
|
|
114
|
+
raise XRDSError.new("Not an XRDS document. Failed to parse XML.")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if is_xrds?(d)
|
|
118
|
+
return d
|
|
119
|
+
else
|
|
120
|
+
raise XRDSError.new("Not an XRDS document.")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def Yadis::is_xrds?(xrds_tree)
|
|
125
|
+
xrds_root = xrds_tree.root
|
|
126
|
+
return (!xrds_root.nil? and
|
|
127
|
+
xrds_root.name == ROOT_TAG.name and
|
|
128
|
+
xrds_root.namespace == ROOT_TAG.namespace)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def Yadis::get_yadis_xrd(xrds_tree)
|
|
132
|
+
REXML::XPath.each(xrds_tree.root,
|
|
133
|
+
'/xrds:XRDS/xrd:XRD[last()]',
|
|
134
|
+
XRDS_NAMESPACES) { |el|
|
|
135
|
+
return el
|
|
136
|
+
}
|
|
137
|
+
raise XRDSError.new("No XRD element found.")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# aka iterServices in Python
|
|
141
|
+
def Yadis::each_service(xrds_tree, &block)
|
|
142
|
+
xrd = get_yadis_xrd(xrds_tree)
|
|
143
|
+
xrd.each_element('Service', &block)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def Yadis::services(xrds_tree)
|
|
147
|
+
s = []
|
|
148
|
+
each_service(xrds_tree) { |service|
|
|
149
|
+
s << service
|
|
150
|
+
}
|
|
151
|
+
return s
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def Yadis::expand_service(service_element)
|
|
155
|
+
es = service_element.elements
|
|
156
|
+
uris = es.each('URI') { |u| }
|
|
157
|
+
uris = prio_sort(uris)
|
|
158
|
+
types = es.each('Type/text()')
|
|
159
|
+
# REXML::Text objects are not strings.
|
|
160
|
+
types = types.collect { |t| t.to_s }
|
|
161
|
+
uris.collect { |uri| [types, uri.text, service_element] }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Sort a list of elements that have priority attributes.
|
|
165
|
+
def Yadis::prio_sort(elements)
|
|
166
|
+
elements.sort { |a,b|
|
|
167
|
+
a.attribute('priority').to_s.to_i <=> b.attribute('priority').to_s.to_i
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'openid/yadis/xrds'
|
|
2
|
+
require 'openid/fetchers'
|
|
3
|
+
|
|
4
|
+
module OpenID
|
|
5
|
+
module Yadis
|
|
6
|
+
module XRI
|
|
7
|
+
|
|
8
|
+
# The '(' is for cross-reference authorities, and hopefully has a
|
|
9
|
+
# matching ')' somewhere.
|
|
10
|
+
XRI_AUTHORITIES = ["!", "=", "@", "+", "$", "("]
|
|
11
|
+
|
|
12
|
+
def self.identifier_scheme(identifier)
|
|
13
|
+
if (!identifier.nil? and
|
|
14
|
+
identifier.length > 0 and
|
|
15
|
+
(identifier.match('^xri://') or
|
|
16
|
+
XRI_AUTHORITIES.member?(identifier[0].chr)))
|
|
17
|
+
return :xri
|
|
18
|
+
else
|
|
19
|
+
return :uri
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Transform an XRI reference to an IRI reference. Note this is
|
|
24
|
+
# not not idempotent, so do not apply this to an identifier more
|
|
25
|
+
# than once. XRI Syntax section 2.3.1
|
|
26
|
+
def self.to_iri_normal(xri)
|
|
27
|
+
iri = xri.dup
|
|
28
|
+
iri.insert(0, 'xri://') if not iri.match('^xri://')
|
|
29
|
+
return escape_for_iri(iri)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Note this is not not idempotent, so do not apply this more than
|
|
33
|
+
# once. XRI Syntax section 2.3.2
|
|
34
|
+
def self.escape_for_iri(xri)
|
|
35
|
+
esc = xri.dup
|
|
36
|
+
# encode all %
|
|
37
|
+
esc.gsub!(/%/, '%25')
|
|
38
|
+
esc.gsub!(/\((.*?)\)/) { |xref_match|
|
|
39
|
+
xref_match.gsub(/[\/\?\#]/) { |char_match|
|
|
40
|
+
CGI::escape(char_match)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return esc
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Transform an XRI reference to a URI reference. Note this is not
|
|
47
|
+
# not idempotent, so do not apply this to an identifier more than
|
|
48
|
+
# once. XRI Syntax section 2.3.1
|
|
49
|
+
def self.to_uri_normal(xri)
|
|
50
|
+
return iri_to_uri(to_iri_normal(xri))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# RFC 3987 section 3.1
|
|
54
|
+
def self.iri_to_uri(iri)
|
|
55
|
+
uri = iri.dup
|
|
56
|
+
# for char in ucschar or iprivate
|
|
57
|
+
# convert each char to %HH%HH%HH (as many %HH as octets)
|
|
58
|
+
return uri
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.provider_is_authoritative(provider_id, canonical_id)
|
|
62
|
+
lastbang = canonical_id.rindex('!')
|
|
63
|
+
return false unless lastbang
|
|
64
|
+
parent = canonical_id[0...lastbang]
|
|
65
|
+
return parent == provider_id
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.root_authority(xri)
|
|
69
|
+
xri = xri[6..-1] if xri.index('xri://') == 0
|
|
70
|
+
authority = xri.split('/', 2)[0]
|
|
71
|
+
if authority[0].chr == '('
|
|
72
|
+
root = authority[0...authority.index(')')+1]
|
|
73
|
+
elsif XRI_AUTHORITIES.member?(authority[0].chr)
|
|
74
|
+
root = authority[0].chr
|
|
75
|
+
else
|
|
76
|
+
root = authority.split(/[!*]/)[0]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
self.make_xri(root)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.make_xri(xri)
|
|
83
|
+
if xri.index('xri://') != 0
|
|
84
|
+
xri = 'xri://' + xri
|
|
85
|
+
end
|
|
86
|
+
return xri
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|