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,75 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
3
|
+
require "openid/extras"
|
|
4
|
+
|
|
5
|
+
module OpenID
|
|
6
|
+
|
|
7
|
+
module URINorm
|
|
8
|
+
public
|
|
9
|
+
def URINorm.urinorm(uri)
|
|
10
|
+
uri = URI.parse(uri)
|
|
11
|
+
|
|
12
|
+
raise URI::InvalidURIError.new('no scheme') unless uri.scheme
|
|
13
|
+
uri.scheme = uri.scheme.downcase
|
|
14
|
+
unless ['http','https'].member?(uri.scheme)
|
|
15
|
+
raise URI::InvalidURIError.new('Not an HTTP or HTTPS URI')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
raise URI::InvalidURIError.new('no host') unless uri.host
|
|
19
|
+
uri.host = uri.host.downcase
|
|
20
|
+
|
|
21
|
+
uri.path = remove_dot_segments(uri.path)
|
|
22
|
+
uri.path = '/' if uri.path.length == 0
|
|
23
|
+
|
|
24
|
+
uri = uri.normalize.to_s
|
|
25
|
+
uri = uri.gsub(PERCENT_ESCAPE_RE) {
|
|
26
|
+
sub = $&[1..2].to_i(16).chr
|
|
27
|
+
reserved(sub) ? $&.upcase : sub
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return uri
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
RESERVED_RE = /[A-Za-z0-9._~-]/
|
|
35
|
+
PERCENT_ESCAPE_RE = /%[0-9a-zA-Z]{2}/
|
|
36
|
+
|
|
37
|
+
def URINorm.reserved(chr)
|
|
38
|
+
not RESERVED_RE =~ chr
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def URINorm.remove_dot_segments(path)
|
|
42
|
+
result_segments = []
|
|
43
|
+
|
|
44
|
+
while path.length > 0
|
|
45
|
+
if path.starts_with?('../')
|
|
46
|
+
path = path[3..-1]
|
|
47
|
+
elsif path.starts_with?('./')
|
|
48
|
+
path = path[2..-1]
|
|
49
|
+
elsif path.starts_with?('/./')
|
|
50
|
+
path = path[2..-1]
|
|
51
|
+
elsif path == '/.'
|
|
52
|
+
path = '/'
|
|
53
|
+
elsif path.starts_with?('/../')
|
|
54
|
+
path = path[3..-1]
|
|
55
|
+
result_segments.pop if result_segments.length > 0
|
|
56
|
+
elsif path == '/..'
|
|
57
|
+
path = '/'
|
|
58
|
+
result_segments.pop if result_segments.length > 0
|
|
59
|
+
elsif path == '..' or path == '.'
|
|
60
|
+
path = ''
|
|
61
|
+
else
|
|
62
|
+
i = 0
|
|
63
|
+
i = 1 if path[0].chr == '/'
|
|
64
|
+
i = path.index('/', i)
|
|
65
|
+
i = path.length if i.nil?
|
|
66
|
+
result_segments << path[0...i]
|
|
67
|
+
path = path[i..-1]
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
return result_segments.join('')
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
data/lib/openid/util.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require "cgi"
|
|
2
|
+
require "uri"
|
|
3
|
+
require "logger"
|
|
4
|
+
|
|
5
|
+
require "openid/extras"
|
|
6
|
+
|
|
7
|
+
# See OpenID::Consumer or OpenID::Server modules, as well as the store classes
|
|
8
|
+
module OpenID
|
|
9
|
+
class AssertionError < Exception
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Exceptions that are raised by the library are subclasses of this
|
|
13
|
+
# exception type, so if you want to catch all exceptions raised by
|
|
14
|
+
# the library, you can catch OpenIDError
|
|
15
|
+
class OpenIDError < StandardError
|
|
16
|
+
def initialize(*msgs)
|
|
17
|
+
super(msgs.join(', '))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Util
|
|
22
|
+
|
|
23
|
+
BASE64_CHARS = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
|
|
24
|
+
'abcdefghijklmnopqrstuvwxyz0123456789+/')
|
|
25
|
+
BASE64_RE = Regexp.compile("
|
|
26
|
+
\\A
|
|
27
|
+
([#{BASE64_CHARS}]{4})*
|
|
28
|
+
([#{BASE64_CHARS}]{2}==|
|
|
29
|
+
[#{BASE64_CHARS}]{3}=)?
|
|
30
|
+
\\Z", Regexp::EXTENDED)
|
|
31
|
+
|
|
32
|
+
def Util.assert(value, message=nil)
|
|
33
|
+
if not value
|
|
34
|
+
raise AssertionError, message or value
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def Util.to_base64(s)
|
|
39
|
+
[s].pack('m').gsub("\n", "")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def Util.from_base64(s)
|
|
43
|
+
without_newlines = s.gsub(/[\r\n]+/, '')
|
|
44
|
+
if !BASE64_RE.match(without_newlines)
|
|
45
|
+
raise ArgumentError, "Malformed input: #{s.inspect}"
|
|
46
|
+
end
|
|
47
|
+
without_newlines.unpack('m').first
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def Util.urlencode(args)
|
|
51
|
+
a = []
|
|
52
|
+
args.each do |key, val|
|
|
53
|
+
val = '' unless val
|
|
54
|
+
a << (CGI::escape(key) + "=" + CGI::escape(val))
|
|
55
|
+
end
|
|
56
|
+
a.join("&")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def Util.parse_query(qs)
|
|
60
|
+
query = {}
|
|
61
|
+
CGI::parse(qs).each {|k,v| query[k] = v[0]}
|
|
62
|
+
return query
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def Util.append_args(url, args)
|
|
66
|
+
url = url.dup
|
|
67
|
+
return url if args.length == 0
|
|
68
|
+
|
|
69
|
+
if args.respond_to?('each_pair')
|
|
70
|
+
args = args.sort
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
url << (url.include?("?") ? "&" : "?")
|
|
74
|
+
url << Util.urlencode(args)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
@@logger = Logger.new(STDERR)
|
|
78
|
+
@@logger.progname = "OpenID"
|
|
79
|
+
|
|
80
|
+
def Util.logger=(logger)
|
|
81
|
+
@@logger = logger
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def Util.logger
|
|
85
|
+
@@logger
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# change the message below to do whatever you like for logging
|
|
89
|
+
def Util.log(message)
|
|
90
|
+
logger.info(message)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def Util.auto_submit_html(form, title='OpenID transaction in progress')
|
|
94
|
+
return "
|
|
95
|
+
<html>
|
|
96
|
+
<head>
|
|
97
|
+
<title>#{title}</title>
|
|
98
|
+
</head>
|
|
99
|
+
<body onload='document.forms[0].submit();'>
|
|
100
|
+
#{form}
|
|
101
|
+
<script>
|
|
102
|
+
var elements = document.forms[0].elements;
|
|
103
|
+
for (var i = 0; i < elements.length; i++) {
|
|
104
|
+
elements[i].style.display = \"none\";
|
|
105
|
+
}
|
|
106
|
+
</script>
|
|
107
|
+
</body>
|
|
108
|
+
</html>
|
|
109
|
+
"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
ESCAPE_TABLE = { '&' => '&', '<' => '<', '>' => '>', '"' => '"', "'" => ''' }
|
|
113
|
+
# Modified from ERb's html_encode
|
|
114
|
+
def Util.html_encode(s)
|
|
115
|
+
s.to_s.gsub(/[&<>"']/) {|s| ESCAPE_TABLE[s] }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
data/lib/openid/yadis.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'openid/yadis/accept'
|
|
2
|
+
require 'openid/yadis/constants'
|
|
3
|
+
require 'openid/yadis/discovery'
|
|
4
|
+
require 'openid/yadis/filters'
|
|
5
|
+
require 'openid/yadis/htmltokenizer'
|
|
6
|
+
require 'openid/yadis/parsehtml'
|
|
7
|
+
require 'openid/yadis/services'
|
|
8
|
+
require 'openid/yadis/xrds'
|
|
9
|
+
require 'openid/yadis/xri'
|
|
10
|
+
require 'openid/yadis/xrires'
|
|
11
|
+
|
|
12
|
+
module OpenID
|
|
13
|
+
module Yadis
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
module OpenID
|
|
2
|
+
|
|
3
|
+
module Yadis
|
|
4
|
+
|
|
5
|
+
# Generate an accept header value
|
|
6
|
+
#
|
|
7
|
+
# [str or (str, float)] -> str
|
|
8
|
+
def self.generate_accept_header(*elements)
|
|
9
|
+
parts = []
|
|
10
|
+
elements.each { |element|
|
|
11
|
+
if element.is_a?(String)
|
|
12
|
+
qs = "1.0"
|
|
13
|
+
mtype = element
|
|
14
|
+
else
|
|
15
|
+
mtype, q = element
|
|
16
|
+
q = q.to_f
|
|
17
|
+
if q > 1 or q <= 0
|
|
18
|
+
raise ArgumentError.new("Invalid preference factor: #{q}")
|
|
19
|
+
end
|
|
20
|
+
qs = sprintf("%0.1f", q)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
parts << [qs, mtype]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
parts.sort!
|
|
27
|
+
chunks = []
|
|
28
|
+
parts.each { |q, mtype|
|
|
29
|
+
if q == '1.0'
|
|
30
|
+
chunks << mtype
|
|
31
|
+
else
|
|
32
|
+
chunks << sprintf("%s; q=%s", mtype, q)
|
|
33
|
+
end
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return chunks.join(', ')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.parse_accept_header(value)
|
|
40
|
+
# Parse an accept header, ignoring any accept-extensions
|
|
41
|
+
#
|
|
42
|
+
# returns a list of tuples containing main MIME type, MIME
|
|
43
|
+
# subtype, and quality markdown.
|
|
44
|
+
#
|
|
45
|
+
# str -> [(str, str, float)]
|
|
46
|
+
chunks = value.split(',', -1).collect { |v| v.strip }
|
|
47
|
+
accept = []
|
|
48
|
+
chunks.each { |chunk|
|
|
49
|
+
parts = chunk.split(";", -1).collect { |s| s.strip }
|
|
50
|
+
|
|
51
|
+
mtype = parts.shift
|
|
52
|
+
if mtype.index('/').nil?
|
|
53
|
+
# This is not a MIME type, so ignore the bad data
|
|
54
|
+
next
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
main, sub = mtype.split('/', 2)
|
|
58
|
+
|
|
59
|
+
q = nil
|
|
60
|
+
parts.each { |ext|
|
|
61
|
+
if !ext.index('=').nil?
|
|
62
|
+
k, v = ext.split('=', 2)
|
|
63
|
+
if k == 'q'
|
|
64
|
+
q = v.to_f
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
q = 1.0 if q.nil?
|
|
70
|
+
|
|
71
|
+
accept << [q, main, sub]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
accept.sort!
|
|
75
|
+
accept.reverse!
|
|
76
|
+
|
|
77
|
+
return accept.collect { |q, main, sub| [main, sub, q] }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.match_types(accept_types, have_types)
|
|
81
|
+
# Given the result of parsing an Accept: header, and the
|
|
82
|
+
# available MIME types, return the acceptable types with their
|
|
83
|
+
# quality markdowns.
|
|
84
|
+
#
|
|
85
|
+
# For example:
|
|
86
|
+
#
|
|
87
|
+
# >>> acceptable = parse_accept_header('text/html, text/plain; q=0.5')
|
|
88
|
+
# >>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg'])
|
|
89
|
+
# [('text/html', 1.0), ('text/plain', 0.5)]
|
|
90
|
+
#
|
|
91
|
+
# Type signature: ([(str, str, float)], [str]) -> [(str, float)]
|
|
92
|
+
if accept_types.nil? or accept_types == []
|
|
93
|
+
# Accept all of them
|
|
94
|
+
default = 1
|
|
95
|
+
else
|
|
96
|
+
default = 0
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
match_main = {}
|
|
100
|
+
match_sub = {}
|
|
101
|
+
accept_types.each { |main, sub, q|
|
|
102
|
+
if main == '*'
|
|
103
|
+
default = [default, q].max
|
|
104
|
+
next
|
|
105
|
+
elsif sub == '*'
|
|
106
|
+
match_main[main] = [match_main.fetch(main, 0), q].max
|
|
107
|
+
else
|
|
108
|
+
match_sub[[main, sub]] = [match_sub.fetch([main, sub], 0), q].max
|
|
109
|
+
end
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
accepted_list = []
|
|
113
|
+
order_maintainer = 0
|
|
114
|
+
have_types.each { |mtype|
|
|
115
|
+
main, sub = mtype.split('/', 2)
|
|
116
|
+
if match_sub.member?([main, sub])
|
|
117
|
+
q = match_sub[[main, sub]]
|
|
118
|
+
else
|
|
119
|
+
q = match_main.fetch(main, default)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if q != 0
|
|
123
|
+
accepted_list << [1 - q, order_maintainer, q, mtype]
|
|
124
|
+
order_maintainer += 1
|
|
125
|
+
end
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
accepted_list.sort!
|
|
129
|
+
return accepted_list.collect { |_, _, q, mtype| [mtype, q] }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def self.get_acceptable(accept_header, have_types)
|
|
133
|
+
# Parse the accept header and return a list of available types
|
|
134
|
+
# in preferred order. If a type is unacceptable, it will not be
|
|
135
|
+
# in the resulting list.
|
|
136
|
+
#
|
|
137
|
+
# This is a convenience wrapper around matchTypes and
|
|
138
|
+
# parse_accept_header
|
|
139
|
+
#
|
|
140
|
+
# (str, [str]) -> [str]
|
|
141
|
+
accepted = self.parse_accept_header(accept_header)
|
|
142
|
+
preferred = self.match_types(accepted, have_types)
|
|
143
|
+
return preferred.collect { |mtype, _| mtype }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
require 'openid/yadis/accept'
|
|
3
|
+
|
|
4
|
+
module OpenID
|
|
5
|
+
|
|
6
|
+
module Yadis
|
|
7
|
+
|
|
8
|
+
YADIS_HEADER_NAME = 'X-XRDS-Location'
|
|
9
|
+
YADIS_CONTENT_TYPE = 'application/xrds+xml'
|
|
10
|
+
|
|
11
|
+
# A value suitable for using as an accept header when performing
|
|
12
|
+
# YADIS discovery, unless the application has special requirements
|
|
13
|
+
YADIS_ACCEPT_HEADER = generate_accept_header(
|
|
14
|
+
['text/html', 0.3],
|
|
15
|
+
['application/xhtml+xml', 0.5],
|
|
16
|
+
[YADIS_CONTENT_TYPE, 1.0]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
|
|
2
|
+
require 'openid/util'
|
|
3
|
+
require 'openid/fetchers'
|
|
4
|
+
require 'openid/yadis/constants'
|
|
5
|
+
require 'openid/yadis/parsehtml'
|
|
6
|
+
|
|
7
|
+
module OpenID
|
|
8
|
+
|
|
9
|
+
# Raised when a error occurs in the discovery process
|
|
10
|
+
class DiscoveryFailure < OpenIDError
|
|
11
|
+
attr_accessor :identity_url, :http_response
|
|
12
|
+
|
|
13
|
+
def initialize(message, http_response)
|
|
14
|
+
super(message)
|
|
15
|
+
@identity_url = nil
|
|
16
|
+
@http_response = http_response
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module Yadis
|
|
21
|
+
|
|
22
|
+
# Contains the result of performing Yadis discovery on a URI
|
|
23
|
+
class DiscoveryResult
|
|
24
|
+
|
|
25
|
+
# The result of following redirects from the request_uri
|
|
26
|
+
attr_accessor :normalize_uri
|
|
27
|
+
|
|
28
|
+
# The URI from which the response text was returned (set to
|
|
29
|
+
# nil if there was no XRDS document found)
|
|
30
|
+
attr_accessor :xrds_uri
|
|
31
|
+
|
|
32
|
+
# The content-type returned with the response_text
|
|
33
|
+
attr_accessor :content_type
|
|
34
|
+
|
|
35
|
+
# The document returned from the xrds_uri
|
|
36
|
+
attr_accessor :response_text
|
|
37
|
+
|
|
38
|
+
attr_accessor :request_uri, :normalized_uri
|
|
39
|
+
|
|
40
|
+
def initialize(request_uri)
|
|
41
|
+
# Initialize the state of the object
|
|
42
|
+
#
|
|
43
|
+
# sets all attributes to None except the request_uri
|
|
44
|
+
@request_uri = request_uri
|
|
45
|
+
@normalized_uri = nil
|
|
46
|
+
@xrds_uri = nil
|
|
47
|
+
@content_type = nil
|
|
48
|
+
@response_text = nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Was the Yadis protocol's indirection used?
|
|
52
|
+
def used_yadis_location?
|
|
53
|
+
return @normalized_uri != @xrds_uri
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Is the response text supposed to be an XRDS document?
|
|
57
|
+
def is_xrds
|
|
58
|
+
return (used_yadis_location?() or
|
|
59
|
+
@content_type == YADIS_CONTENT_TYPE)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Discover services for a given URI.
|
|
64
|
+
#
|
|
65
|
+
# uri: The identity URI as a well-formed http or https URI. The
|
|
66
|
+
# well-formedness and the protocol are not checked, but the
|
|
67
|
+
# results of this function are undefined if those properties do
|
|
68
|
+
# not hold.
|
|
69
|
+
#
|
|
70
|
+
# returns a DiscoveryResult object
|
|
71
|
+
#
|
|
72
|
+
# Raises DiscoveryFailure when the HTTP response does not have
|
|
73
|
+
# a 200 code.
|
|
74
|
+
def self.discover(uri)
|
|
75
|
+
result = DiscoveryResult.new(uri)
|
|
76
|
+
begin
|
|
77
|
+
resp = OpenID.fetch(uri, nil, {'Accept' => YADIS_ACCEPT_HEADER})
|
|
78
|
+
rescue Exception
|
|
79
|
+
raise DiscoveryFailure.new("Failed to fetch identity URL #{uri} : #{$!}", $!)
|
|
80
|
+
end
|
|
81
|
+
if resp.code != "200" and resp.code != "206"
|
|
82
|
+
raise DiscoveryFailure.new(
|
|
83
|
+
"HTTP Response status from identity URL host is not \"200\"."\
|
|
84
|
+
"Got status #{resp.code.inspect} for #{resp.final_url}", resp)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Note the URL after following redirects
|
|
88
|
+
result.normalized_uri = resp.final_url
|
|
89
|
+
|
|
90
|
+
# Attempt to find out where to go to discover the document or if
|
|
91
|
+
# we already have it
|
|
92
|
+
result.content_type = resp['content-type']
|
|
93
|
+
|
|
94
|
+
result.xrds_uri = self.where_is_yadis?(resp)
|
|
95
|
+
|
|
96
|
+
if result.xrds_uri and result.used_yadis_location?
|
|
97
|
+
begin
|
|
98
|
+
resp = OpenID.fetch(result.xrds_uri)
|
|
99
|
+
rescue
|
|
100
|
+
raise DiscoveryFailure.new("Failed to fetch Yadis URL #{result.xrds_uri} : #{$!}", $!)
|
|
101
|
+
end
|
|
102
|
+
if resp.code != "200" and resp.code != "206"
|
|
103
|
+
exc = DiscoveryFailure.new(
|
|
104
|
+
"HTTP Response status from Yadis host is not \"200\". " +
|
|
105
|
+
"Got status #{resp.code.inspect} for #{resp.final_url}", resp)
|
|
106
|
+
exc.identity_url = result.normalized_uri
|
|
107
|
+
raise exc
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
result.content_type = resp['content-type']
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
result.response_text = resp.body
|
|
114
|
+
return result
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Given a HTTPResponse, return the location of the Yadis
|
|
118
|
+
# document.
|
|
119
|
+
#
|
|
120
|
+
# May be the URL just retrieved, another URL, or None, if I
|
|
121
|
+
# can't find any.
|
|
122
|
+
#
|
|
123
|
+
# [non-blocking]
|
|
124
|
+
def self.where_is_yadis?(resp)
|
|
125
|
+
# Attempt to find out where to go to discover the document or if
|
|
126
|
+
# we already have it
|
|
127
|
+
content_type = resp['content-type']
|
|
128
|
+
|
|
129
|
+
# According to the spec, the content-type header must be an
|
|
130
|
+
# exact match, or else we have to look for an indirection.
|
|
131
|
+
if (!content_type.nil? and !content_type.to_s.empty? and
|
|
132
|
+
content_type.split(';', 2)[0].downcase == YADIS_CONTENT_TYPE)
|
|
133
|
+
return resp.final_url
|
|
134
|
+
else
|
|
135
|
+
# Try the header
|
|
136
|
+
yadis_loc = resp[YADIS_HEADER_NAME.downcase]
|
|
137
|
+
|
|
138
|
+
if yadis_loc.nil?
|
|
139
|
+
# Parse as HTML if the header is missing.
|
|
140
|
+
#
|
|
141
|
+
# XXX: do we want to do something with content-type, like
|
|
142
|
+
# have a whitelist or a blacklist (for detecting that it's
|
|
143
|
+
# HTML)?
|
|
144
|
+
yadis_loc = Yadis.html_yadis_location(resp.body)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
return yadis_loc
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end
|