pelle-ruby-openid 2.1.8
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/CHANGELOG +215 -0
- data/CHANGES-2.1.0 +36 -0
- data/INSTALL +47 -0
- data/LICENSE +210 -0
- data/NOTICE +2 -0
- data/README +82 -0
- data/UPGRADE +127 -0
- data/VERSION +1 -0
- data/examples/README +32 -0
- data/examples/active_record_openid_store/README +58 -0
- data/examples/active_record_openid_store/XXX_add_open_id_store_to_db.rb +24 -0
- data/examples/active_record_openid_store/XXX_upgrade_open_id_store.rb +26 -0
- data/examples/active_record_openid_store/init.rb +8 -0
- data/examples/active_record_openid_store/lib/association.rb +10 -0
- data/examples/active_record_openid_store/lib/nonce.rb +3 -0
- data/examples/active_record_openid_store/lib/open_id_setting.rb +4 -0
- data/examples/active_record_openid_store/lib/openid_ar_store.rb +57 -0
- data/examples/active_record_openid_store/test/store_test.rb +212 -0
- data/examples/discover +49 -0
- data/examples/rails_openid/README +153 -0
- data/examples/rails_openid/Rakefile +10 -0
- data/examples/rails_openid/app/controllers/application.rb +4 -0
- data/examples/rails_openid/app/controllers/consumer_controller.rb +122 -0
- data/examples/rails_openid/app/controllers/login_controller.rb +45 -0
- data/examples/rails_openid/app/controllers/server_controller.rb +265 -0
- data/examples/rails_openid/app/helpers/application_helper.rb +3 -0
- data/examples/rails_openid/app/helpers/login_helper.rb +2 -0
- data/examples/rails_openid/app/helpers/server_helper.rb +9 -0
- data/examples/rails_openid/app/views/consumer/index.rhtml +81 -0
- data/examples/rails_openid/app/views/layouts/server.rhtml +68 -0
- data/examples/rails_openid/app/views/login/index.rhtml +56 -0
- data/examples/rails_openid/app/views/server/decide.rhtml +26 -0
- data/examples/rails_openid/config/boot.rb +19 -0
- data/examples/rails_openid/config/database.yml +74 -0
- data/examples/rails_openid/config/environment.rb +54 -0
- data/examples/rails_openid/config/environments/development.rb +19 -0
- data/examples/rails_openid/config/environments/production.rb +19 -0
- data/examples/rails_openid/config/environments/test.rb +19 -0
- data/examples/rails_openid/config/routes.rb +24 -0
- data/examples/rails_openid/doc/README_FOR_APP +2 -0
- data/examples/rails_openid/public/.htaccess +40 -0
- data/examples/rails_openid/public/404.html +8 -0
- data/examples/rails_openid/public/500.html +8 -0
- data/examples/rails_openid/public/dispatch.cgi +12 -0
- data/examples/rails_openid/public/dispatch.fcgi +26 -0
- data/examples/rails_openid/public/dispatch.rb +12 -0
- data/examples/rails_openid/public/favicon.ico +0 -0
- data/examples/rails_openid/public/images/openid_login_bg.gif +0 -0
- data/examples/rails_openid/public/javascripts/controls.js +750 -0
- data/examples/rails_openid/public/javascripts/dragdrop.js +584 -0
- data/examples/rails_openid/public/javascripts/effects.js +854 -0
- data/examples/rails_openid/public/javascripts/prototype.js +1785 -0
- data/examples/rails_openid/public/robots.txt +1 -0
- data/examples/rails_openid/script/about +3 -0
- data/examples/rails_openid/script/breakpointer +3 -0
- data/examples/rails_openid/script/console +3 -0
- data/examples/rails_openid/script/destroy +3 -0
- data/examples/rails_openid/script/generate +3 -0
- data/examples/rails_openid/script/performance/benchmarker +3 -0
- data/examples/rails_openid/script/performance/profiler +3 -0
- data/examples/rails_openid/script/plugin +3 -0
- data/examples/rails_openid/script/process/reaper +3 -0
- data/examples/rails_openid/script/process/spawner +3 -0
- data/examples/rails_openid/script/process/spinner +3 -0
- data/examples/rails_openid/script/runner +3 -0
- data/examples/rails_openid/script/server +3 -0
- data/examples/rails_openid/test/functional/login_controller_test.rb +18 -0
- data/examples/rails_openid/test/functional/server_controller_test.rb +18 -0
- data/examples/rails_openid/test/test_helper.rb +28 -0
- data/lib/hmac/hmac.rb +112 -0
- data/lib/hmac/sha1.rb +11 -0
- data/lib/hmac/sha2.rb +25 -0
- data/lib/openid/association.rb +249 -0
- data/lib/openid/consumer/associationmanager.rb +344 -0
- data/lib/openid/consumer/checkid_request.rb +186 -0
- data/lib/openid/consumer/discovery.rb +498 -0
- data/lib/openid/consumer/discovery_manager.rb +123 -0
- data/lib/openid/consumer/html_parse.rb +134 -0
- data/lib/openid/consumer/idres.rb +523 -0
- data/lib/openid/consumer/responses.rb +148 -0
- data/lib/openid/consumer.rb +395 -0
- data/lib/openid/cryptutil.rb +97 -0
- data/lib/openid/dh.rb +89 -0
- data/lib/openid/extension.rb +39 -0
- data/lib/openid/extensions/ax.rb +516 -0
- data/lib/openid/extensions/oauth.rb +91 -0
- data/lib/openid/extensions/pape.rb +179 -0
- data/lib/openid/extensions/sreg.rb +277 -0
- data/lib/openid/extras.rb +11 -0
- data/lib/openid/fetchers.rb +238 -0
- data/lib/openid/kvform.rb +136 -0
- data/lib/openid/kvpost.rb +58 -0
- data/lib/openid/message.rb +553 -0
- data/lib/openid/protocolerror.rb +8 -0
- data/lib/openid/server.rb +1544 -0
- data/lib/openid/store/filesystem.rb +271 -0
- data/lib/openid/store/interface.rb +75 -0
- data/lib/openid/store/memcache.rb +107 -0
- data/lib/openid/store/memory.rb +84 -0
- data/lib/openid/store/nonce.rb +68 -0
- data/lib/openid/trustroot.rb +349 -0
- data/lib/openid/urinorm.rb +75 -0
- data/lib/openid/util.rb +110 -0
- 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/yadis/htmltokenizer.rb +305 -0
- data/lib/openid/yadis/parsehtml.rb +45 -0
- data/lib/openid/yadis/services.rb +42 -0
- data/lib/openid/yadis/xrds.rb +155 -0
- data/lib/openid/yadis/xri.rb +90 -0
- data/lib/openid/yadis/xrires.rb +106 -0
- data/lib/openid.rb +20 -0
- data/setup.rb +1551 -0
- 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 +152 -0
- data/test/data/test_discover/malformed_meta_tag.html +19 -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/subsegments.xrds +58 -0
- data/test/data/test_xrds/valid-populated-xrds.xml +39 -0
- data/test/data/trustroot.txt +153 -0
- data/test/data/urinorm.txt +79 -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 +917 -0
- data/test/test_ax.rb +648 -0
- data/test/test_checkid_request.rb +294 -0
- data/test/test_consumer.rb +257 -0
- data/test/test_cryptutil.rb +119 -0
- data/test/test_dh.rb +86 -0
- data/test/test_discover.rb +838 -0
- data/test/test_discovery_manager.rb +262 -0
- data/test/test_extension.rb +46 -0
- data/test/test_extras.rb +35 -0
- data/test/test_fetchers.rb +538 -0
- data/test/test_filters.rb +270 -0
- data/test/test_idres.rb +963 -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 +1116 -0
- data/test/test_nonce.rb +89 -0
- data/test/test_oauth.rb +175 -0
- data/test/test_openid_yadis.rb +178 -0
- data/test/test_pape.rb +247 -0
- data/test/test_parsehtml.rb +80 -0
- data/test/test_responses.rb +63 -0
- data/test/test_server.rb +2457 -0
- data/test/test_sreg.rb +479 -0
- data/test/test_stores.rb +298 -0
- data/test/test_trustroot.rb +113 -0
- data/test/test_urinorm.rb +35 -0
- data/test/test_util.rb +145 -0
- data/test/test_xrds.rb +169 -0
- data/test/test_xri.rb +48 -0
- data/test/test_xrires.rb +63 -0
- data/test/test_yadis_discovery.rb +220 -0
- data/test/testutil.rb +127 -0
- data/test/util.rb +53 -0
- metadata +316 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
# Functions to discover OpenID endpoints from identifiers.
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'openid/util'
|
|
5
|
+
require 'openid/fetchers'
|
|
6
|
+
require 'openid/urinorm'
|
|
7
|
+
require 'openid/message'
|
|
8
|
+
require 'openid/yadis/discovery'
|
|
9
|
+
require 'openid/yadis/xrds'
|
|
10
|
+
require 'openid/yadis/xri'
|
|
11
|
+
require 'openid/yadis/services'
|
|
12
|
+
require 'openid/yadis/filters'
|
|
13
|
+
require 'openid/consumer/html_parse'
|
|
14
|
+
require 'openid/yadis/xrires'
|
|
15
|
+
|
|
16
|
+
module OpenID
|
|
17
|
+
|
|
18
|
+
OPENID_1_0_NS = 'http://openid.net/xmlns/1.0'
|
|
19
|
+
OPENID_IDP_2_0_TYPE = 'http://specs.openid.net/auth/2.0/server'
|
|
20
|
+
OPENID_2_0_TYPE = 'http://specs.openid.net/auth/2.0/signon'
|
|
21
|
+
OPENID_1_1_TYPE = 'http://openid.net/signon/1.1'
|
|
22
|
+
OPENID_1_0_TYPE = 'http://openid.net/signon/1.0'
|
|
23
|
+
|
|
24
|
+
OPENID_1_0_MESSAGE_NS = OPENID1_NS
|
|
25
|
+
OPENID_2_0_MESSAGE_NS = OPENID2_NS
|
|
26
|
+
|
|
27
|
+
# Object representing an OpenID service endpoint.
|
|
28
|
+
class OpenIDServiceEndpoint
|
|
29
|
+
|
|
30
|
+
# OpenID service type URIs, listed in order of preference. The
|
|
31
|
+
# ordering of this list affects yadis and XRI service discovery.
|
|
32
|
+
OPENID_TYPE_URIS = [
|
|
33
|
+
OPENID_IDP_2_0_TYPE,
|
|
34
|
+
|
|
35
|
+
OPENID_2_0_TYPE,
|
|
36
|
+
OPENID_1_1_TYPE,
|
|
37
|
+
OPENID_1_0_TYPE,
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# the verified identifier.
|
|
41
|
+
attr_accessor :claimed_id
|
|
42
|
+
|
|
43
|
+
# For XRI, the persistent identifier.
|
|
44
|
+
attr_accessor :canonical_id
|
|
45
|
+
|
|
46
|
+
attr_accessor :server_url, :type_uris, :local_id, :used_yadis
|
|
47
|
+
|
|
48
|
+
def initialize
|
|
49
|
+
@claimed_id = nil
|
|
50
|
+
@server_url = nil
|
|
51
|
+
@type_uris = []
|
|
52
|
+
@local_id = nil
|
|
53
|
+
@canonical_id = nil
|
|
54
|
+
@used_yadis = false # whether this came from an XRDS
|
|
55
|
+
@display_identifier = nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def display_identifier
|
|
59
|
+
return @display_identifier if @display_identifier
|
|
60
|
+
|
|
61
|
+
return @claimed_id if @claimed_id.nil?
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
parsed_identifier = URI.parse(@claimed_id)
|
|
65
|
+
rescue URI::InvalidURIError
|
|
66
|
+
raise ProtocolError, "Claimed identifier #{claimed_id} is not a valid URI"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
return @claimed_id if not parsed_identifier.fragment
|
|
70
|
+
|
|
71
|
+
disp = parsed_identifier
|
|
72
|
+
disp.fragment = nil
|
|
73
|
+
|
|
74
|
+
return disp.to_s
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def display_identifier=(display_identifier)
|
|
78
|
+
@display_identifier = display_identifier
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def uses_extension(extension_uri)
|
|
82
|
+
return @type_uris.member?(extension_uri)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def preferred_namespace
|
|
86
|
+
if (@type_uris.member?(OPENID_IDP_2_0_TYPE) or
|
|
87
|
+
@type_uris.member?(OPENID_2_0_TYPE))
|
|
88
|
+
return OPENID_2_0_MESSAGE_NS
|
|
89
|
+
else
|
|
90
|
+
return OPENID_1_0_MESSAGE_NS
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def supports_type(type_uri)
|
|
95
|
+
# Does this endpoint support this type?
|
|
96
|
+
#
|
|
97
|
+
# I consider C{/server} endpoints to implicitly support C{/signon}.
|
|
98
|
+
(
|
|
99
|
+
@type_uris.member?(type_uri) or
|
|
100
|
+
(type_uri == OPENID_2_0_TYPE and is_op_identifier())
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def compatibility_mode
|
|
105
|
+
return preferred_namespace() != OPENID_2_0_MESSAGE_NS
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def is_op_identifier
|
|
109
|
+
return @type_uris.member?(OPENID_IDP_2_0_TYPE)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def parse_service(yadis_url, uri, type_uris, service_element)
|
|
113
|
+
# Set the state of this object based on the contents of the
|
|
114
|
+
# service element.
|
|
115
|
+
@type_uris = type_uris
|
|
116
|
+
@server_url = uri
|
|
117
|
+
@used_yadis = true
|
|
118
|
+
|
|
119
|
+
if !is_op_identifier()
|
|
120
|
+
# XXX: This has crappy implications for Service elements that
|
|
121
|
+
# contain both 'server' and 'signon' Types. But that's a
|
|
122
|
+
# pathological configuration anyway, so I don't think I care.
|
|
123
|
+
@local_id = OpenID.find_op_local_identifier(service_element,
|
|
124
|
+
@type_uris)
|
|
125
|
+
@claimed_id = yadis_url
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def get_local_id
|
|
130
|
+
# Return the identifier that should be sent as the
|
|
131
|
+
# openid.identity parameter to the server.
|
|
132
|
+
if @local_id.nil? and @canonical_id.nil?
|
|
133
|
+
return @claimed_id
|
|
134
|
+
else
|
|
135
|
+
return (@local_id or @canonical_id)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def self.from_basic_service_endpoint(endpoint)
|
|
140
|
+
# Create a new instance of this class from the endpoint object
|
|
141
|
+
# passed in.
|
|
142
|
+
#
|
|
143
|
+
# @return: nil or OpenIDServiceEndpoint for this endpoint object"""
|
|
144
|
+
|
|
145
|
+
type_uris = endpoint.match_types(OPENID_TYPE_URIS)
|
|
146
|
+
|
|
147
|
+
# If any Type URIs match and there is an endpoint URI specified,
|
|
148
|
+
# then this is an OpenID endpoint
|
|
149
|
+
if (!type_uris.nil? and !type_uris.empty?) and !endpoint.uri.nil?
|
|
150
|
+
openid_endpoint = self.new
|
|
151
|
+
openid_endpoint.parse_service(
|
|
152
|
+
endpoint.yadis_url,
|
|
153
|
+
endpoint.uri,
|
|
154
|
+
endpoint.type_uris,
|
|
155
|
+
endpoint.service_element)
|
|
156
|
+
else
|
|
157
|
+
openid_endpoint = nil
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
return openid_endpoint
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def self.from_html(uri, html)
|
|
164
|
+
# Parse the given document as HTML looking for an OpenID <link
|
|
165
|
+
# rel=...>
|
|
166
|
+
#
|
|
167
|
+
# @rtype: [OpenIDServiceEndpoint]
|
|
168
|
+
|
|
169
|
+
discovery_types = [
|
|
170
|
+
[OPENID_2_0_TYPE, 'openid2.provider', 'openid2.local_id'],
|
|
171
|
+
[OPENID_1_1_TYPE, 'openid.server', 'openid.delegate'],
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
link_attrs = OpenID.parse_link_attrs(html)
|
|
175
|
+
services = []
|
|
176
|
+
discovery_types.each { |type_uri, op_endpoint_rel, local_id_rel|
|
|
177
|
+
|
|
178
|
+
op_endpoint_url = OpenID.find_first_href(link_attrs, op_endpoint_rel)
|
|
179
|
+
|
|
180
|
+
if !op_endpoint_url
|
|
181
|
+
next
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
service = self.new
|
|
185
|
+
service.claimed_id = uri
|
|
186
|
+
service.local_id = OpenID.find_first_href(link_attrs, local_id_rel)
|
|
187
|
+
service.server_url = op_endpoint_url
|
|
188
|
+
service.type_uris = [type_uri]
|
|
189
|
+
|
|
190
|
+
services << service
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return services
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def self.from_xrds(uri, xrds)
|
|
197
|
+
# Parse the given document as XRDS looking for OpenID services.
|
|
198
|
+
#
|
|
199
|
+
# @rtype: [OpenIDServiceEndpoint]
|
|
200
|
+
#
|
|
201
|
+
# @raises L{XRDSError}: When the XRDS does not parse.
|
|
202
|
+
return Yadis::apply_filter(uri, xrds, self)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def self.from_discovery_result(discoveryResult)
|
|
206
|
+
# Create endpoints from a DiscoveryResult.
|
|
207
|
+
#
|
|
208
|
+
# @type discoveryResult: L{DiscoveryResult}
|
|
209
|
+
#
|
|
210
|
+
# @rtype: list of L{OpenIDServiceEndpoint}
|
|
211
|
+
#
|
|
212
|
+
# @raises L{XRDSError}: When the XRDS does not parse.
|
|
213
|
+
if discoveryResult.is_xrds()
|
|
214
|
+
meth = self.method('from_xrds')
|
|
215
|
+
else
|
|
216
|
+
meth = self.method('from_html')
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
return meth.call(discoveryResult.normalized_uri,
|
|
220
|
+
discoveryResult.response_text)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def self.from_op_endpoint_url(op_endpoint_url)
|
|
224
|
+
# Construct an OP-Identifier OpenIDServiceEndpoint object for
|
|
225
|
+
# a given OP Endpoint URL
|
|
226
|
+
#
|
|
227
|
+
# @param op_endpoint_url: The URL of the endpoint
|
|
228
|
+
# @rtype: OpenIDServiceEndpoint
|
|
229
|
+
service = self.new
|
|
230
|
+
service.server_url = op_endpoint_url
|
|
231
|
+
service.type_uris = [OPENID_IDP_2_0_TYPE]
|
|
232
|
+
return service
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def to_s
|
|
236
|
+
return sprintf("<%s server_url=%s claimed_id=%s " +
|
|
237
|
+
"local_id=%s canonical_id=%s used_yadis=%s>",
|
|
238
|
+
self.class, @server_url, @claimed_id,
|
|
239
|
+
@local_id, @canonical_id, @used_yadis)
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def self.find_op_local_identifier(service_element, type_uris)
|
|
244
|
+
# Find the OP-Local Identifier for this xrd:Service element.
|
|
245
|
+
#
|
|
246
|
+
# This considers openid:Delegate to be a synonym for xrd:LocalID
|
|
247
|
+
# if both OpenID 1.X and OpenID 2.0 types are present. If only
|
|
248
|
+
# OpenID 1.X is present, it returns the value of
|
|
249
|
+
# openid:Delegate. If only OpenID 2.0 is present, it returns the
|
|
250
|
+
# value of xrd:LocalID. If there is more than one LocalID tag and
|
|
251
|
+
# the values are different, it raises a DiscoveryFailure. This is
|
|
252
|
+
# also triggered when the xrd:LocalID and openid:Delegate tags are
|
|
253
|
+
# different.
|
|
254
|
+
|
|
255
|
+
# XXX: Test this function on its own!
|
|
256
|
+
|
|
257
|
+
# Build the list of tags that could contain the OP-Local
|
|
258
|
+
# Identifier
|
|
259
|
+
local_id_tags = []
|
|
260
|
+
if type_uris.member?(OPENID_1_1_TYPE) or
|
|
261
|
+
type_uris.member?(OPENID_1_0_TYPE)
|
|
262
|
+
# local_id_tags << Yadis::nsTag(OPENID_1_0_NS, 'openid', 'Delegate')
|
|
263
|
+
service_element.add_namespace('openid', OPENID_1_0_NS)
|
|
264
|
+
local_id_tags << "openid:Delegate"
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
if type_uris.member?(OPENID_2_0_TYPE)
|
|
268
|
+
# local_id_tags.append(Yadis::nsTag(XRD_NS_2_0, 'xrd', 'LocalID'))
|
|
269
|
+
service_element.add_namespace('xrd', Yadis::XRD_NS_2_0)
|
|
270
|
+
local_id_tags << "xrd:LocalID"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Walk through all the matching tags and make sure that they all
|
|
274
|
+
# have the same value
|
|
275
|
+
local_id = nil
|
|
276
|
+
local_id_tags.each { |local_id_tag|
|
|
277
|
+
service_element.each_element(local_id_tag) { |local_id_element|
|
|
278
|
+
if local_id.nil?
|
|
279
|
+
local_id = local_id_element.text
|
|
280
|
+
elsif local_id != local_id_element.text
|
|
281
|
+
format = 'More than one %s tag found in one service element'
|
|
282
|
+
message = sprintf(format, local_id_tag)
|
|
283
|
+
raise DiscoveryFailure.new(message, nil)
|
|
284
|
+
end
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return local_id
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def self.normalize_xri(xri)
|
|
292
|
+
# Normalize an XRI, stripping its scheme if present
|
|
293
|
+
m = /^xri:\/\/(.*)/.match(xri)
|
|
294
|
+
xri = m[1] if m
|
|
295
|
+
return xri
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def self.normalize_url(url)
|
|
299
|
+
# Normalize a URL, converting normalization failures to
|
|
300
|
+
# DiscoveryFailure
|
|
301
|
+
begin
|
|
302
|
+
normalized = URINorm.urinorm(url)
|
|
303
|
+
rescue URI::Error => why
|
|
304
|
+
raise DiscoveryFailure.new("Error normalizing #{url}: #{why.message}", nil)
|
|
305
|
+
else
|
|
306
|
+
defragged = URI::parse(normalized)
|
|
307
|
+
defragged.fragment = nil
|
|
308
|
+
return defragged.normalize.to_s
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def self.best_matching_service(service, preferred_types)
|
|
313
|
+
# Return the index of the first matching type, or something higher
|
|
314
|
+
# if no type matches.
|
|
315
|
+
#
|
|
316
|
+
# This provides an ordering in which service elements that contain
|
|
317
|
+
# a type that comes earlier in the preferred types list come
|
|
318
|
+
# before service elements that come later. If a service element
|
|
319
|
+
# has more than one type, the most preferred one wins.
|
|
320
|
+
preferred_types.each_with_index { |value, index|
|
|
321
|
+
if service.type_uris.member?(value)
|
|
322
|
+
return index
|
|
323
|
+
end
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return preferred_types.length
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def self.arrange_by_type(service_list, preferred_types)
|
|
330
|
+
# Rearrange service_list in a new list so services are ordered by
|
|
331
|
+
# types listed in preferred_types. Return the new list.
|
|
332
|
+
|
|
333
|
+
# Build a list with the service elements in tuples whose
|
|
334
|
+
# comparison will prefer the one with the best matching service
|
|
335
|
+
prio_services = []
|
|
336
|
+
|
|
337
|
+
service_list.each_with_index { |s, index|
|
|
338
|
+
prio_services << [best_matching_service(s, preferred_types), index, s]
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
prio_services.sort!
|
|
342
|
+
|
|
343
|
+
# Now that the services are sorted by priority, remove the sort
|
|
344
|
+
# keys from the list.
|
|
345
|
+
(0...prio_services.length).each { |i|
|
|
346
|
+
prio_services[i] = prio_services[i][2]
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return prio_services
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def self.get_op_or_user_services(openid_services)
|
|
353
|
+
# Extract OP Identifier services. If none found, return the rest,
|
|
354
|
+
# sorted with most preferred first according to
|
|
355
|
+
# OpenIDServiceEndpoint.openid_type_uris.
|
|
356
|
+
#
|
|
357
|
+
# openid_services is a list of OpenIDServiceEndpoint objects.
|
|
358
|
+
#
|
|
359
|
+
# Returns a list of OpenIDServiceEndpoint objects.
|
|
360
|
+
|
|
361
|
+
op_services = arrange_by_type(openid_services, [OPENID_IDP_2_0_TYPE])
|
|
362
|
+
|
|
363
|
+
openid_services = arrange_by_type(openid_services,
|
|
364
|
+
OpenIDServiceEndpoint::OPENID_TYPE_URIS)
|
|
365
|
+
|
|
366
|
+
if !op_services.empty?
|
|
367
|
+
return op_services
|
|
368
|
+
else
|
|
369
|
+
return openid_services
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def self.discover_yadis(uri)
|
|
374
|
+
# Discover OpenID services for a URI. Tries Yadis and falls back
|
|
375
|
+
# on old-style <link rel='...'> discovery if Yadis fails.
|
|
376
|
+
#
|
|
377
|
+
# @param uri: normalized identity URL
|
|
378
|
+
# @type uri: str
|
|
379
|
+
#
|
|
380
|
+
# @return: (claimed_id, services)
|
|
381
|
+
# @rtype: (str, list(OpenIDServiceEndpoint))
|
|
382
|
+
#
|
|
383
|
+
# @raises DiscoveryFailure: when discovery fails.
|
|
384
|
+
|
|
385
|
+
# Might raise a yadis.discover.DiscoveryFailure if no document
|
|
386
|
+
# came back for that URI at all. I don't think falling back to
|
|
387
|
+
# OpenID 1.0 discovery on the same URL will help, so don't bother
|
|
388
|
+
# to catch it.
|
|
389
|
+
response = Yadis.discover(uri)
|
|
390
|
+
|
|
391
|
+
yadis_url = response.normalized_uri
|
|
392
|
+
body = response.response_text
|
|
393
|
+
|
|
394
|
+
begin
|
|
395
|
+
openid_services = OpenIDServiceEndpoint.from_xrds(yadis_url, body)
|
|
396
|
+
rescue Yadis::XRDSError
|
|
397
|
+
# Does not parse as a Yadis XRDS file
|
|
398
|
+
openid_services = []
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
if openid_services.empty?
|
|
402
|
+
# Either not an XRDS or there are no OpenID services.
|
|
403
|
+
|
|
404
|
+
if response.is_xrds
|
|
405
|
+
# if we got the Yadis content-type or followed the Yadis
|
|
406
|
+
# header, re-fetch the document without following the Yadis
|
|
407
|
+
# header, with no Accept header.
|
|
408
|
+
return self.discover_no_yadis(uri)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# Try to parse the response as HTML.
|
|
412
|
+
# <link rel="...">
|
|
413
|
+
openid_services = OpenIDServiceEndpoint.from_html(yadis_url, body)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
return [yadis_url, self.get_op_or_user_services(openid_services)]
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def self.discover_xri(iname)
|
|
420
|
+
endpoints = []
|
|
421
|
+
iname = self.normalize_xri(iname)
|
|
422
|
+
|
|
423
|
+
begin
|
|
424
|
+
canonical_id, services = Yadis::XRI::ProxyResolver.new().query(
|
|
425
|
+
iname, OpenIDServiceEndpoint::OPENID_TYPE_URIS)
|
|
426
|
+
|
|
427
|
+
if canonical_id.nil?
|
|
428
|
+
raise Yadis::XRDSError.new(sprintf('No CanonicalID found for XRI %s', iname))
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
flt = Yadis.make_filter(OpenIDServiceEndpoint)
|
|
432
|
+
|
|
433
|
+
services.each { |service_element|
|
|
434
|
+
endpoints += flt.get_service_endpoints(iname, service_element)
|
|
435
|
+
}
|
|
436
|
+
rescue Yadis::XRDSError => why
|
|
437
|
+
Util.log('xrds error on ' + iname + ': ' + why.to_s)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
endpoints.each { |endpoint|
|
|
441
|
+
# Is there a way to pass this through the filter to the endpoint
|
|
442
|
+
# constructor instead of tacking it on after?
|
|
443
|
+
endpoint.canonical_id = canonical_id
|
|
444
|
+
endpoint.claimed_id = canonical_id
|
|
445
|
+
endpoint.display_identifier = iname
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
# FIXME: returned xri should probably be in some normal form
|
|
449
|
+
return [iname, self.get_op_or_user_services(endpoints)]
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def self.discover_no_yadis(uri)
|
|
453
|
+
http_resp = OpenID.fetch(uri)
|
|
454
|
+
if http_resp.code != "200" and http_resp.code != "206"
|
|
455
|
+
raise DiscoveryFailure.new(
|
|
456
|
+
"HTTP Response status from identity URL host is not \"200\". "\
|
|
457
|
+
"Got status #{http_resp.code.inspect}", http_resp)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
claimed_id = http_resp.final_url
|
|
461
|
+
openid_services = OpenIDServiceEndpoint.from_html(
|
|
462
|
+
claimed_id, http_resp.body)
|
|
463
|
+
return [claimed_id, openid_services]
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def self.discover_uri(uri)
|
|
467
|
+
# Hack to work around URI parsing for URls with *no* scheme.
|
|
468
|
+
if uri.index("://").nil?
|
|
469
|
+
uri = 'http://' + uri
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
begin
|
|
473
|
+
parsed = URI::parse(uri)
|
|
474
|
+
rescue URI::InvalidURIError => why
|
|
475
|
+
raise DiscoveryFailure.new("URI is not valid: #{why.message}", nil)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
if !parsed.scheme.nil? and !parsed.scheme.empty?
|
|
479
|
+
if !['http', 'https'].member?(parsed.scheme)
|
|
480
|
+
raise DiscoveryFailure.new(
|
|
481
|
+
"URI scheme #{parsed.scheme} is not HTTP or HTTPS", nil)
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
uri = self.normalize_url(uri)
|
|
486
|
+
claimed_id, openid_services = self.discover_yadis(uri)
|
|
487
|
+
claimed_id = self.normalize_url(claimed_id)
|
|
488
|
+
return [claimed_id, openid_services]
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def self.discover(identifier)
|
|
492
|
+
if Yadis::XRI::identifier_scheme(identifier) == :xri
|
|
493
|
+
normalized_identifier, services = discover_xri(identifier)
|
|
494
|
+
else
|
|
495
|
+
return discover_uri(identifier)
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module OpenID
|
|
2
|
+
class Consumer
|
|
3
|
+
|
|
4
|
+
# A set of discovered services, for tracking which providers have
|
|
5
|
+
# been attempted for an OpenID identifier
|
|
6
|
+
class DiscoveredServices
|
|
7
|
+
attr_reader :current
|
|
8
|
+
|
|
9
|
+
def initialize(starting_url, yadis_url, services)
|
|
10
|
+
@starting_url = starting_url
|
|
11
|
+
@yadis_url = yadis_url
|
|
12
|
+
@services = services.dup
|
|
13
|
+
@current = nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def next
|
|
17
|
+
@current = @services.shift
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def for_url?(url)
|
|
21
|
+
[@starting_url, @yadis_url].member?(url)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def started?
|
|
25
|
+
!@current.nil?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def empty?
|
|
29
|
+
@services.empty?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Manages calling discovery and tracking which endpoints have
|
|
34
|
+
# already been attempted.
|
|
35
|
+
class DiscoveryManager
|
|
36
|
+
def initialize(session, url, session_key_suffix=nil)
|
|
37
|
+
@url = url
|
|
38
|
+
|
|
39
|
+
@session = session
|
|
40
|
+
@session_key_suffix = session_key_suffix || 'auth'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get_next_service
|
|
44
|
+
manager = get_manager
|
|
45
|
+
if !manager.nil? && manager.empty?
|
|
46
|
+
destroy_manager
|
|
47
|
+
manager = nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if manager.nil?
|
|
51
|
+
yadis_url, services = yield @url
|
|
52
|
+
manager = create_manager(yadis_url, services)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if !manager.nil?
|
|
56
|
+
service = manager.next
|
|
57
|
+
store(manager)
|
|
58
|
+
else
|
|
59
|
+
service = nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
return service
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def cleanup(force=false)
|
|
66
|
+
manager = get_manager(force)
|
|
67
|
+
if !manager.nil?
|
|
68
|
+
service = manager.current
|
|
69
|
+
destroy_manager(force)
|
|
70
|
+
else
|
|
71
|
+
service = nil
|
|
72
|
+
end
|
|
73
|
+
return service
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
protected
|
|
77
|
+
|
|
78
|
+
def get_manager(force=false)
|
|
79
|
+
manager = load
|
|
80
|
+
if force || manager.nil? || manager.for_url?(@url)
|
|
81
|
+
return manager
|
|
82
|
+
else
|
|
83
|
+
return nil
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def create_manager(yadis_url, services)
|
|
88
|
+
manager = get_manager
|
|
89
|
+
if !manager.nil?
|
|
90
|
+
raise StandardError, "There is already a manager for #{yadis_url}"
|
|
91
|
+
end
|
|
92
|
+
if services.empty?
|
|
93
|
+
return nil
|
|
94
|
+
end
|
|
95
|
+
manager = DiscoveredServices.new(@url, yadis_url, services)
|
|
96
|
+
store(manager)
|
|
97
|
+
return manager
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def destroy_manager(force=false)
|
|
101
|
+
if !get_manager(force).nil?
|
|
102
|
+
destroy!
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def session_key
|
|
107
|
+
'OpenID::Consumer::DiscoveredServices::' + @session_key_suffix
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def store(manager)
|
|
111
|
+
@session[session_key] = manager
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def load
|
|
115
|
+
@session[session_key]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def destroy!
|
|
119
|
+
@session[session_key] = nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|