entp-ruby-openid 2.2
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/INSTALL +47 -0
- data/LICENSE +210 -0
- data/NOTICE +2 -0
- data/README +85 -0
- data/UPGRADE +127 -0
- data/admin/runtests.rb +45 -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 +125 -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/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.rb +22 -0
- data/lib/openid/association.rb +249 -0
- data/lib/openid/consumer.rb +395 -0
- data/lib/openid/consumer/associationmanager.rb +344 -0
- data/lib/openid/consumer/checkid_request.rb +186 -0
- data/lib/openid/consumer/discovery.rb +497 -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 +150 -0
- data/lib/openid/cryptutil.rb +115 -0
- data/lib/openid/dh.rb +89 -0
- data/lib/openid/extension.rb +39 -0
- data/lib/openid/extensions/ax.rb +539 -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 +258 -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 +12 -0
- data/lib/openid/server.rb +1544 -0
- data/lib/openid/store.rb +10 -0
- data/lib/openid/store/filesystem.rb +272 -0
- data/lib/openid/store/interface.rb +75 -0
- data/lib/openid/store/memcache.rb +109 -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 +119 -0
- data/lib/openid/version.rb +3 -0
- data/lib/openid/yadis.rb +15 -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 +91 -0
- data/test/data/test_discover/openid_utf8.html +11 -0
- data/test/support/test_data_mixin.rb +127 -0
- data/test/support/test_util.rb +53 -0
- data/test/support/yadis_data.rb +131 -0
- data/test/support/yadis_data/accept.txt +124 -0
- data/test/support/yadis_data/dh.txt +29 -0
- data/test/support/yadis_data/example-xrds.xml +14 -0
- data/test/support/yadis_data/linkparse.txt +587 -0
- data/test/support/yadis_data/n2b64 +650 -0
- data/test/support/yadis_data/test1-discover.txt +137 -0
- data/test/support/yadis_data/test1-parsehtml.txt +152 -0
- data/test/support/yadis_data/test_discover/malformed_meta_tag.html +19 -0
- data/test/support/yadis_data/test_discover/openid.html +11 -0
- data/test/support/yadis_data/test_discover/openid2.html +11 -0
- data/test/support/yadis_data/test_discover/openid2_xrds.xml +12 -0
- data/test/support/yadis_data/test_discover/openid2_xrds_no_local_id.xml +11 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2.html +11 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2_xrds.xml +16 -0
- data/test/support/yadis_data/test_discover/openid_1_and_2_xrds_bad_delegate.xml +17 -0
- data/test/support/yadis_data/test_discover/openid_and_yadis.html +12 -0
- data/test/support/yadis_data/test_discover/openid_no_delegate.html +10 -0
- data/test/support/yadis_data/test_discover/openid_utf8.html +11 -0
- data/test/support/yadis_data/test_discover/yadis_0entries.xml +12 -0
- data/test/support/yadis_data/test_discover/yadis_2_bad_local_id.xml +15 -0
- data/test/support/yadis_data/test_discover/yadis_2entries_delegate.xml +22 -0
- data/test/support/yadis_data/test_discover/yadis_2entries_idp.xml +21 -0
- data/test/support/yadis_data/test_discover/yadis_another_delegate.xml +14 -0
- data/test/support/yadis_data/test_discover/yadis_idp.xml +12 -0
- data/test/support/yadis_data/test_discover/yadis_idp_delegate.xml +13 -0
- data/test/support/yadis_data/test_discover/yadis_no_delegate.xml +11 -0
- data/test/support/yadis_data/test_xrds/=j3h.2007.11.14.xrds +25 -0
- data/test/support/yadis_data/test_xrds/README +12 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809-r1.xrds +34 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809-r2.xrds +34 -0
- data/test/support/yadis_data/test_xrds/delegated-20060809.xrds +34 -0
- data/test/support/yadis_data/test_xrds/no-xrd.xml +7 -0
- data/test/support/yadis_data/test_xrds/not-xrds.xml +2 -0
- data/test/support/yadis_data/test_xrds/prefixsometimes.xrds +34 -0
- data/test/support/yadis_data/test_xrds/ref.xrds +109 -0
- data/test/support/yadis_data/test_xrds/sometimesprefix.xrds +34 -0
- data/test/support/yadis_data/test_xrds/spoof1.xrds +25 -0
- data/test/support/yadis_data/test_xrds/spoof2.xrds +25 -0
- data/test/support/yadis_data/test_xrds/spoof3.xrds +37 -0
- data/test/support/yadis_data/test_xrds/status222.xrds +9 -0
- data/test/support/yadis_data/test_xrds/subsegments.xrds +58 -0
- data/test/support/yadis_data/test_xrds/valid-populated-xrds.xml +39 -0
- data/test/support/yadis_data/trustroot.txt +153 -0
- data/test/support/yadis_data/urinorm.txt +79 -0
- data/test/test_accept.rb +170 -0
- data/test/test_association.rb +268 -0
- data/test/test_associationmanager.rb +918 -0
- data/test/test_ax.rb +690 -0
- data/test/test_checkid_request.rb +293 -0
- data/test/test_consumer.rb +260 -0
- data/test/test_cryptutil.rb +119 -0
- data/test/test_dh.rb +85 -0
- data/test/test_discover.rb +848 -0
- data/test/test_discovery_manager.rb +259 -0
- data/test/test_extension.rb +46 -0
- data/test/test_extras.rb +35 -0
- data/test/test_fetchers.rb +554 -0
- data/test/test_filters.rb +269 -0
- data/test/test_helper.rb +4 -0
- data/test/test_idres.rb +961 -0
- data/test/test_kvform.rb +164 -0
- data/test/test_kvpost.rb +64 -0
- data/test/test_linkparse.rb +100 -0
- data/test/test_message.rb +1115 -0
- data/test/test_nonce.rb +89 -0
- data/test/test_oauth.rb +176 -0
- data/test/test_openid_yadis.rb +177 -0
- data/test/test_pape.rb +248 -0
- data/test/test_parsehtml.rb +79 -0
- data/test/test_responses.rb +63 -0
- data/test/test_server.rb +2455 -0
- data/test/test_sreg.rb +479 -0
- data/test/test_stores.rb +292 -0
- data/test/test_trustroot.rb +111 -0
- data/test/test_urinorm.rb +34 -0
- data/test/test_util.rb +145 -0
- data/test/test_xrds.rb +167 -0
- data/test/test_xri.rb +48 -0
- data/test/test_xrires.rb +67 -0
- data/test/test_yadis_discovery.rb +218 -0
- metadata +268 -0
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
begin
|
|
13
|
+
while el = parser.getTag('head', '/head', 'meta', 'body', '/body',
|
|
14
|
+
'html', 'script')
|
|
15
|
+
|
|
16
|
+
# we are leaving head or have reached body, so we bail
|
|
17
|
+
return nil if ['/head', 'body', '/body'].member?(el.tag_name)
|
|
18
|
+
|
|
19
|
+
if el.tag_name == 'head'
|
|
20
|
+
unless el.to_s[-2] == ?/ # tag ends with a /: a short tag
|
|
21
|
+
in_head = true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
next unless in_head
|
|
25
|
+
|
|
26
|
+
if el.tag_name == 'script'
|
|
27
|
+
unless el.to_s[-2] == ?/ # tag ends with a /: a short tag
|
|
28
|
+
parser.getTag('/script')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return nil if el.tag_name == 'html'
|
|
33
|
+
|
|
34
|
+
if el.tag_name == 'meta' and (equiv = el.attr_hash['http-equiv'])
|
|
35
|
+
if ['x-xrds-location','x-yadis-location'].member?(equiv.downcase) &&
|
|
36
|
+
el.attr_hash.member?('content')
|
|
37
|
+
return CGI::unescapeHTML(el.attr_hash['content'])
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
rescue HTMLTokenizerError # just stop parsing if there's an error
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -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,155 @@
|
|
|
1
|
+
require 'rexml/document'
|
|
2
|
+
require 'rexml/element'
|
|
3
|
+
require 'rexml/xpath'
|
|
4
|
+
|
|
5
|
+
require 'openid/yadis/xri'
|
|
6
|
+
|
|
7
|
+
module OpenID
|
|
8
|
+
module Yadis
|
|
9
|
+
|
|
10
|
+
XRD_NS_2_0 = 'xri://$xrd*($v*2.0)'
|
|
11
|
+
XRDS_NS = 'xri://$xrds'
|
|
12
|
+
|
|
13
|
+
XRDS_NAMESPACES = {
|
|
14
|
+
'xrds' => XRDS_NS,
|
|
15
|
+
'xrd' => XRD_NS_2_0,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class XRDSError < StandardError; end
|
|
19
|
+
|
|
20
|
+
# Raised when there's an assertion in the XRDS that it does not
|
|
21
|
+
# have the authority to make.
|
|
22
|
+
class XRDSFraud < XRDSError
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def Yadis::get_canonical_id(iname, xrd_tree)
|
|
26
|
+
# Return the CanonicalID from this XRDS document.
|
|
27
|
+
#
|
|
28
|
+
# @param iname: the XRI being resolved.
|
|
29
|
+
# @type iname: unicode
|
|
30
|
+
#
|
|
31
|
+
# @param xrd_tree: The XRDS output from the resolver.
|
|
32
|
+
#
|
|
33
|
+
# @returns: The XRI CanonicalID or None.
|
|
34
|
+
# @returntype: unicode or None
|
|
35
|
+
|
|
36
|
+
xrd_list = []
|
|
37
|
+
REXML::XPath::match(xrd_tree.root, '/xrds:XRDS/xrd:XRD', XRDS_NAMESPACES).each { |el|
|
|
38
|
+
xrd_list << el
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
xrd_list.reverse!
|
|
42
|
+
|
|
43
|
+
cid_elements = []
|
|
44
|
+
|
|
45
|
+
if !xrd_list.empty?
|
|
46
|
+
xrd_list[0].elements.each { |e|
|
|
47
|
+
if !e.respond_to?('name')
|
|
48
|
+
next
|
|
49
|
+
end
|
|
50
|
+
if e.name == 'CanonicalID'
|
|
51
|
+
cid_elements << e
|
|
52
|
+
end
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
cid_element = cid_elements[0]
|
|
57
|
+
|
|
58
|
+
if !cid_element
|
|
59
|
+
return nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
canonicalID = XRI.make_xri(cid_element.text)
|
|
63
|
+
|
|
64
|
+
childID = canonicalID.downcase
|
|
65
|
+
|
|
66
|
+
xrd_list[1..-1].each { |xrd|
|
|
67
|
+
parent_sought = childID[0...childID.rindex('!')]
|
|
68
|
+
|
|
69
|
+
parent = XRI.make_xri(xrd.elements["CanonicalID"].text)
|
|
70
|
+
|
|
71
|
+
if parent_sought != parent.downcase
|
|
72
|
+
raise XRDSFraud.new(sprintf("%s can not come from %s", parent_sought,
|
|
73
|
+
parent))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
childID = parent_sought
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
root = XRI.root_authority(iname)
|
|
80
|
+
if not XRI.provider_is_authoritative(root, childID)
|
|
81
|
+
raise XRDSFraud.new(sprintf("%s can not come from root %s", childID, root))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
return canonicalID
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class XRDSError < StandardError
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def Yadis::parseXRDS(text)
|
|
91
|
+
if text.nil?
|
|
92
|
+
raise XRDSError.new("Not an XRDS document.")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
begin
|
|
96
|
+
d = REXML::Document.new(text)
|
|
97
|
+
rescue RuntimeError => why
|
|
98
|
+
raise XRDSError.new("Not an XRDS document. Failed to parse XML.")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if is_xrds?(d)
|
|
102
|
+
return d
|
|
103
|
+
else
|
|
104
|
+
raise XRDSError.new("Not an XRDS document.")
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def Yadis::is_xrds?(xrds_tree)
|
|
109
|
+
xrds_root = xrds_tree.root
|
|
110
|
+
return (!xrds_root.nil? and
|
|
111
|
+
xrds_root.name == 'XRDS' and
|
|
112
|
+
xrds_root.namespace == XRDS_NS)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def Yadis::get_yadis_xrd(xrds_tree)
|
|
116
|
+
REXML::XPath.each(xrds_tree.root,
|
|
117
|
+
'/xrds:XRDS/xrd:XRD[last()]',
|
|
118
|
+
XRDS_NAMESPACES) { |el|
|
|
119
|
+
return el
|
|
120
|
+
}
|
|
121
|
+
raise XRDSError.new("No XRD element found.")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# aka iterServices in Python
|
|
125
|
+
def Yadis::each_service(xrds_tree, &block)
|
|
126
|
+
xrd = get_yadis_xrd(xrds_tree)
|
|
127
|
+
xrd.each_element('Service', &block)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def Yadis::services(xrds_tree)
|
|
131
|
+
s = []
|
|
132
|
+
each_service(xrds_tree) { |service|
|
|
133
|
+
s << service
|
|
134
|
+
}
|
|
135
|
+
return s
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def Yadis::expand_service(service_element)
|
|
139
|
+
es = service_element.elements
|
|
140
|
+
uris = es.each('URI') { |u| }
|
|
141
|
+
uris = prio_sort(uris)
|
|
142
|
+
types = es.each('Type/text()')
|
|
143
|
+
# REXML::Text objects are not strings.
|
|
144
|
+
types = types.collect { |t| t.to_s }
|
|
145
|
+
uris.collect { |uri| [types, uri.text, service_element] }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Sort a list of elements that have priority attributes.
|
|
149
|
+
def Yadis::prio_sort(elements)
|
|
150
|
+
elements.sort { |a,b|
|
|
151
|
+
a.attribute('priority').to_s.to_i <=> b.attribute('priority').to_s.to_i
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
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
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require "cgi"
|
|
2
|
+
require "openid/yadis/xri"
|
|
3
|
+
require "openid/yadis/xrds"
|
|
4
|
+
require "openid/fetchers"
|
|
5
|
+
|
|
6
|
+
module OpenID
|
|
7
|
+
|
|
8
|
+
module Yadis
|
|
9
|
+
|
|
10
|
+
module XRI
|
|
11
|
+
|
|
12
|
+
class XRIHTTPError < StandardError; end
|
|
13
|
+
|
|
14
|
+
class ProxyResolver
|
|
15
|
+
|
|
16
|
+
DEFAULT_PROXY = 'http://proxy.xri.net/' unless defined?(DEFAULT_PROXY)
|
|
17
|
+
|
|
18
|
+
def initialize(proxy_url=nil)
|
|
19
|
+
if proxy_url
|
|
20
|
+
@proxy_url = proxy_url
|
|
21
|
+
else
|
|
22
|
+
@proxy_url = DEFAULT_PROXY
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@proxy_url += '/' unless @proxy_url.match('/$')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def query_url(xri, service_type=nil)
|
|
29
|
+
# URI normal form has a leading xri://, but we need to strip
|
|
30
|
+
# that off again for the QXRI. This is under discussion for
|
|
31
|
+
# XRI Resolution WD 11.
|
|
32
|
+
qxri = XRI.to_uri_normal(xri)[6..-1]
|
|
33
|
+
hxri = @proxy_url + CGI::escape( qxri )
|
|
34
|
+
args = {'_xrd_r' => 'application/xrds+xml'}
|
|
35
|
+
args['_xrd_t'] = service_type if service_type
|
|
36
|
+
|
|
37
|
+
return XRI.append_args(hxri, args)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def query(xri)
|
|
41
|
+
# these can be query args or http headers, needn't be both.
|
|
42
|
+
# headers = {'Accept' => 'application/xrds+xml;sep=true'}
|
|
43
|
+
|
|
44
|
+
url = self.query_url(xri)
|
|
45
|
+
begin
|
|
46
|
+
response = OpenID.fetch(url)
|
|
47
|
+
rescue
|
|
48
|
+
raise XRIHTTPError, "Could not fetch #{xri}, #{$!}"
|
|
49
|
+
end
|
|
50
|
+
raise XRIHTTPError, "Fetching #{xri} returned nothing" if response.nil?
|
|
51
|
+
|
|
52
|
+
xrds = Yadis::parseXRDS(response.body)
|
|
53
|
+
raise XRIHTTPError, "Fetching #{xri} did not return an XRDS" if xrds.nil?
|
|
54
|
+
canonicalID = Yadis::get_canonical_id(xri, xrds)
|
|
55
|
+
|
|
56
|
+
return canonicalID, Yadis::services(xrds)
|
|
57
|
+
# TODO:
|
|
58
|
+
# * If we do get hits for multiple service_types, we're almost
|
|
59
|
+
# certainly going to have duplicated service entries and
|
|
60
|
+
# broken priority ordering.
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.urlencode(args)
|
|
65
|
+
a = []
|
|
66
|
+
args.each do |key, val|
|
|
67
|
+
a << (CGI::escape(key) + "=" + CGI::escape(val))
|
|
68
|
+
end
|
|
69
|
+
a.join("&")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.append_args(url, args)
|
|
73
|
+
return url if args.length == 0
|
|
74
|
+
|
|
75
|
+
# strip all trailing question marks
|
|
76
|
+
rstripped = url.dup.sub(/\?+\z/, '').sub(/(%3F)+\z/, '')
|
|
77
|
+
|
|
78
|
+
if rstripped.include?('?') or rstripped.include?('%3F')
|
|
79
|
+
sep = ( rstripped[-1] == '&' ? '' : '&' )
|
|
80
|
+
else
|
|
81
|
+
sep = '?'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
return rstripped + sep + XRI.urlencode(args)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Identity Page for Smoker</title>
|
|
5
|
+
<link rel="openid.server" href="http://www.myopenid.com/server" />
|
|
6
|
+
<link rel="openid.delegate" href="http://smoker.myopenid.com/" />
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<p>こんにちは</p>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
|
|
3
|
+
module OpenID
|
|
4
|
+
module TestDataMixin
|
|
5
|
+
TESTS_DIR = Pathname.new(__FILE__).dirname
|
|
6
|
+
TEST_DATA_DIR = Pathname.new('yadis_data')
|
|
7
|
+
|
|
8
|
+
def read_data_file(filename, lines=true, data_dir=TEST_DATA_DIR)
|
|
9
|
+
fname = TESTS_DIR.join(data_dir, filename)
|
|
10
|
+
|
|
11
|
+
if lines
|
|
12
|
+
fname.readlines
|
|
13
|
+
else
|
|
14
|
+
fname.read
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module FetcherMixin
|
|
20
|
+
def with_fetcher(fetcher)
|
|
21
|
+
original_fetcher = OpenID.fetcher
|
|
22
|
+
begin
|
|
23
|
+
OpenID.fetcher = fetcher
|
|
24
|
+
return yield
|
|
25
|
+
ensure
|
|
26
|
+
OpenID.fetcher = original_fetcher
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module Const
|
|
32
|
+
def const(symbol, value)
|
|
33
|
+
(class << self;self;end).instance_eval do
|
|
34
|
+
define_method(symbol) { value }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class MockResponse
|
|
40
|
+
attr_reader :code, :body
|
|
41
|
+
|
|
42
|
+
def initialize(code, body)
|
|
43
|
+
@code = code.to_s
|
|
44
|
+
@body = body
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
module ProtocolErrorMixin
|
|
49
|
+
def assert_protocol_error(str_prefix)
|
|
50
|
+
begin
|
|
51
|
+
result = yield
|
|
52
|
+
rescue ProtocolError => why
|
|
53
|
+
message = "Expected prefix #{str_prefix.inspect}, got "\
|
|
54
|
+
"#{why.message.inspect}"
|
|
55
|
+
assert(why.message.starts_with?(str_prefix), message)
|
|
56
|
+
else
|
|
57
|
+
fail("Expected ProtocolError. Got #{result.inspect}")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
module OverrideMethodMixin
|
|
63
|
+
def with_method_overridden(method_name, proc)
|
|
64
|
+
original = method(method_name)
|
|
65
|
+
begin
|
|
66
|
+
# TODO: find a combination of undef calls which prevent the warning
|
|
67
|
+
verbose, $VERBOSE = $VERBOSE, false
|
|
68
|
+
define_method(method_name, proc)
|
|
69
|
+
module_function(method_name)
|
|
70
|
+
$VERBOSE = verbose
|
|
71
|
+
yield
|
|
72
|
+
ensure
|
|
73
|
+
if original.respond_to? :owner
|
|
74
|
+
original.owner.send(:undef_method, method_name)
|
|
75
|
+
original.owner.send :define_method, method_name, original
|
|
76
|
+
else
|
|
77
|
+
define_method(method_name, original)
|
|
78
|
+
module_function(method_name)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# To use:
|
|
85
|
+
# > x = Object.new
|
|
86
|
+
# > x.extend(InstanceDefExtension)
|
|
87
|
+
# > x.instance_def(:monkeys) do
|
|
88
|
+
# > "bananas"
|
|
89
|
+
# > end
|
|
90
|
+
# > x.monkeys
|
|
91
|
+
#
|
|
92
|
+
module InstanceDefExtension
|
|
93
|
+
def instance_def(method_name, &proc)
|
|
94
|
+
(class << self;self;end).instance_eval do
|
|
95
|
+
# TODO: find a combination of undef calls which prevent the warning
|
|
96
|
+
verbose, $VERBOSE = $VERBOSE, false
|
|
97
|
+
define_method(method_name, proc)
|
|
98
|
+
$VERBOSE = verbose
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
GOODSIG = '[A Good Signature]'
|
|
104
|
+
|
|
105
|
+
class GoodAssoc
|
|
106
|
+
attr_accessor :handle, :expires_in
|
|
107
|
+
|
|
108
|
+
def initialize(handle='-blah-')
|
|
109
|
+
@handle = handle
|
|
110
|
+
@expires_in = 3600
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def check_message_signature(msg)
|
|
114
|
+
msg.get_arg(OPENID_NS, 'sig') == GOODSIG
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
class HTTPResponse
|
|
119
|
+
def self._from_raw_data(status, body="", headers={}, final_url=nil)
|
|
120
|
+
resp = Net::HTTPResponse.new('1.1', status.to_s, 'NONE')
|
|
121
|
+
me = self._from_net_response(resp, final_url)
|
|
122
|
+
me.initialize_http_header headers
|
|
123
|
+
me.body = body
|
|
124
|
+
return me
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|