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
data/lib/openid/util.rb
CHANGED
|
@@ -1,147 +1,52 @@
|
|
|
1
1
|
require "base64"
|
|
2
2
|
require "cgi"
|
|
3
|
-
require "digest/sha1"
|
|
4
|
-
require "hmac-sha1"
|
|
5
3
|
require "uri"
|
|
4
|
+
require "logger"
|
|
6
5
|
|
|
7
|
-
require "openid/
|
|
6
|
+
require "openid/extras"
|
|
8
7
|
|
|
9
8
|
srand(Time.now.to_f)
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
h = {}
|
|
15
|
-
self.instance_variables.each { |k| h[k] = self.instance_variable_get(k) }
|
|
16
|
-
return h
|
|
10
|
+
# See OpenID::Consumer or OpenID::Server modules, as well as the store classes
|
|
11
|
+
module OpenID
|
|
12
|
+
class AssertionError < Exception
|
|
17
13
|
end
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class String
|
|
15
|
+
VERSION = "2.0.1"
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
# Exceptions that are raised by the library are subclasses of this
|
|
18
|
+
# exception type, so if you want to catch all exceptions raised by
|
|
19
|
+
# the library, you can catch OpenIDError
|
|
20
|
+
class OpenIDError < StandardError
|
|
27
21
|
end
|
|
28
22
|
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
module OpenID
|
|
33
|
-
|
|
34
|
-
# Code returned when either the of the
|
|
35
|
-
# OpenID::OpenIDConsumer.begin_auth or OpenID::OpenIDConsumer.complete_auth
|
|
36
|
-
# methods return successfully.
|
|
37
|
-
SUCCESS = 'success'
|
|
38
|
-
|
|
39
|
-
# Code OpenID::OpenIDConsumer.complete_auth
|
|
40
|
-
# returns when the value it received indicated an invalid login.
|
|
41
|
-
FAILURE = 'failure'
|
|
42
|
-
|
|
43
|
-
# Code returned by OpenIDConsumer.complete_auth when the user
|
|
44
|
-
# cancels the operation from the server.
|
|
45
|
-
CANCEL = 'cancel'
|
|
46
|
-
|
|
47
|
-
# Code returned by OpenID::OpenIDConsumer.complete_auth when the
|
|
48
|
-
# OpenIDConsumer instance is in immediate mode and ther server sends back a
|
|
49
|
-
# URL for the user to login with.
|
|
50
|
-
SETUP_NEEDED = 'setup needed'
|
|
51
|
-
|
|
52
|
-
# Code returned by OpenID::OpenIDConsumer.begin_auth when it is unable
|
|
53
|
-
# to fetch the URL given by the user.
|
|
54
|
-
HTTP_FAILURE = 'http failure'
|
|
55
|
-
|
|
56
|
-
# Code returned by OpenID::OpenIDConsumer.begin_auth when the page fetched
|
|
57
|
-
# from the OpenID URL doesn't contain the necessary link tags to function
|
|
58
|
-
# as an identity page.
|
|
59
|
-
PARSE_ERROR = 'parse error'
|
|
60
|
-
|
|
61
23
|
module Util
|
|
62
24
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
25
|
+
BASE64_CHARS = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
|
|
26
|
+
'abcdefghijklmnopqrstuvwxyz0123456789+/')
|
|
27
|
+
BASE64_RE = Regexp.compile("
|
|
28
|
+
\\A
|
|
29
|
+
([#{BASE64_CHARS}]{4})*
|
|
30
|
+
([#{BASE64_CHARS}]{2}==|
|
|
31
|
+
[#{BASE64_CHARS}]{3}=)?
|
|
32
|
+
\\Z", Regexp::EXTENDED)
|
|
33
|
+
|
|
34
|
+
def Util.assert(value, message=nil)
|
|
35
|
+
if not value
|
|
36
|
+
raise AssertionError, message or value
|
|
69
37
|
end
|
|
70
|
-
params
|
|
71
38
|
end
|
|
72
39
|
|
|
73
|
-
def Util.hmac_sha1(key, text)
|
|
74
|
-
HMAC::SHA1.digest(key, text)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def Util.sha1(s)
|
|
78
|
-
Digest::SHA1.digest(s)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
40
|
def Util.to_base64(s)
|
|
82
41
|
Base64.encode64(s).gsub("\n", "")
|
|
83
42
|
end
|
|
84
43
|
|
|
85
44
|
def Util.from_base64(s)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def Util.kvform(hash)
|
|
90
|
-
form = ""
|
|
91
|
-
hash.each do |k,v|
|
|
92
|
-
form << "#{k}:#{v}\n"
|
|
93
|
-
end
|
|
94
|
-
form
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def Util.parsekv(s)
|
|
98
|
-
s.strip!
|
|
99
|
-
form = {}
|
|
100
|
-
s.split("\n").each do |line|
|
|
101
|
-
pair = line.split(":", 2)
|
|
102
|
-
if pair.length == 2
|
|
103
|
-
k, v = pair
|
|
104
|
-
form[k.strip] = v.strip
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
form
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def Util.num_to_str(n)
|
|
111
|
-
bits = n.to_s(2)
|
|
112
|
-
prepend = (8 - bits.length % 8)
|
|
113
|
-
bits = ('0' * prepend) + bits
|
|
114
|
-
[bits].pack('B*')
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def Util.str_to_num(s)
|
|
118
|
-
# taken from openid-ruby 0.0.1
|
|
119
|
-
s = "\000" * (4 - (s.length % 4)) + s
|
|
120
|
-
num = 0
|
|
121
|
-
s.unpack('N*').each do |x|
|
|
122
|
-
num <<= 32
|
|
123
|
-
num |= x
|
|
124
|
-
end
|
|
125
|
-
num
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def Util.num_to_base64(l)
|
|
129
|
-
return to_base64(num_to_str(l))
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def Util.base64_to_num(s)
|
|
133
|
-
return str_to_num(from_base64(s))
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def Util.random_string(length, chars=nil)
|
|
137
|
-
s = ""
|
|
138
|
-
|
|
139
|
-
unless chars.nil?
|
|
140
|
-
length.times { s << chars[Util.rand(chars.length)] }
|
|
141
|
-
else
|
|
142
|
-
length.times { s << Util.rand(256).chr }
|
|
45
|
+
without_newlines = s.gsub(/[\r\n]+/, '')
|
|
46
|
+
if !BASE64_RE.match(without_newlines)
|
|
47
|
+
raise ArgumentError, "Malformed input: #{s.inspect}"
|
|
143
48
|
end
|
|
144
|
-
|
|
49
|
+
Base64.decode64(without_newlines)
|
|
145
50
|
end
|
|
146
51
|
|
|
147
52
|
def Util.urlencode(args)
|
|
@@ -152,123 +57,40 @@ module OpenID
|
|
|
152
57
|
end
|
|
153
58
|
a.join("&")
|
|
154
59
|
end
|
|
155
|
-
|
|
60
|
+
|
|
156
61
|
def Util.parse_query(qs)
|
|
157
62
|
query = {}
|
|
158
63
|
CGI::parse(qs).each {|k,v| query[k] = v[0]}
|
|
159
64
|
return query
|
|
160
65
|
end
|
|
161
|
-
|
|
66
|
+
|
|
162
67
|
def Util.append_args(url, args)
|
|
163
68
|
url = url.dup
|
|
164
|
-
url if args.length == 0
|
|
165
|
-
url << (url.include?("?") ? "&" : "?")
|
|
166
|
-
url << Util.urlencode(args)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def Util.strxor(s1, s2)
|
|
170
|
-
raise ArgumentError if s1.length != s2.length
|
|
171
|
-
length = [s1.length, s2.length].min - 1
|
|
172
|
-
a = (0..length).collect {|i| (s1[i]^s2[i]).chr}
|
|
173
|
-
a.join("")
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Sign the given fields from the reply with the specified key.
|
|
177
|
-
# Return [signed, sig]
|
|
178
|
-
def Util.sign_reply(reply, key, signed_fields, prefix="openid.")
|
|
179
|
-
token = []
|
|
180
|
-
signed_fields.each do |sf|
|
|
181
|
-
token << [sf+":"+reply[prefix+sf].to_s+"\n"]
|
|
182
|
-
end
|
|
183
|
-
text = token.join("")
|
|
184
|
-
signed = Util.to_base64(Util.hmac_sha1(key, text))
|
|
185
|
-
return [signed_fields.join(","), signed]
|
|
186
|
-
end
|
|
69
|
+
return url if args.length == 0
|
|
187
70
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
# This implementation is much faster than x ** n % q
|
|
191
|
-
def Util.powermod(x, n, q)
|
|
192
|
-
counter=0
|
|
193
|
-
n_p=n
|
|
194
|
-
y_p=1
|
|
195
|
-
z_p=x
|
|
196
|
-
while n_p != 0
|
|
197
|
-
if n_p[0]==1
|
|
198
|
-
y_p=(y_p*z_p) % q
|
|
199
|
-
end
|
|
200
|
-
n_p = n_p >> 1
|
|
201
|
-
z_p = (z_p * z_p) % q
|
|
202
|
-
counter += 1
|
|
71
|
+
if args.respond_to?('each_pair')
|
|
72
|
+
args = args.sort
|
|
203
73
|
end
|
|
204
|
-
return y_p
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# Generate a random number less than max. Uses urandom if available.
|
|
208
|
-
def Util.rand(max)
|
|
209
|
-
unless Util::HAS_URANDOM
|
|
210
|
-
return Kernel::rand(max)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
start = 0
|
|
214
|
-
stop = max
|
|
215
|
-
step = 1
|
|
216
|
-
r = ((stop-start)/step).to_i
|
|
217
74
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
nbytes = rbytes.length
|
|
221
|
-
nbytes -= 1 if rbytes[0].chr == "\000"
|
|
222
|
-
|
|
223
|
-
bytes = "\000" + Util::get_random_bytes(nbytes)
|
|
224
|
-
n = Util::str_to_num(bytes)
|
|
225
|
-
|
|
226
|
-
return start + (n % r) * step
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# change the message below to do whatever you like for logging
|
|
230
|
-
def Util.log(message)
|
|
231
|
-
STDERR.puts('OpenID Log: ' + message)
|
|
75
|
+
url << (url.include?("?") ? "&" : "?")
|
|
76
|
+
url << Util.urlencode(args)
|
|
232
77
|
end
|
|
233
78
|
|
|
79
|
+
@@logger = Logger.new(STDERR)
|
|
80
|
+
@@logger.progname = "OpenID"
|
|
234
81
|
|
|
235
|
-
def Util.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if Util::HAS_URANDOM
|
|
239
|
-
f = File.open("/dev/urandom")
|
|
240
|
-
while n != 0
|
|
241
|
-
_bytes = f.read(n)
|
|
242
|
-
n -= _bytes.length
|
|
243
|
-
bytes << _bytes
|
|
244
|
-
end
|
|
245
|
-
else
|
|
246
|
-
bytes = Util.random_string(n)
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
return bytes
|
|
82
|
+
def Util.logger=(logger)
|
|
83
|
+
@@logger = logger
|
|
250
84
|
end
|
|
251
85
|
|
|
252
|
-
def Util.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
unless url.starts_with?('http://') or url.starts_with?('https://')
|
|
256
|
-
url = 'http://' + url
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
begin
|
|
260
|
-
return Util.urinorm(url)
|
|
261
|
-
rescue URI::InvalidURIError
|
|
262
|
-
return nil
|
|
263
|
-
end
|
|
86
|
+
def Util.logger
|
|
87
|
+
@@logger
|
|
264
88
|
end
|
|
265
89
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return url1 == Util.normalize_url(url2)
|
|
90
|
+
# change the message below to do whatever you like for logging
|
|
91
|
+
def Util.log(message)
|
|
92
|
+
logger.info(message)
|
|
270
93
|
end
|
|
271
|
-
|
|
272
94
|
end
|
|
273
95
|
|
|
274
96
|
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.",$!)
|
|
80
|
+
end
|
|
81
|
+
if resp.code != "200"
|
|
82
|
+
raise DiscoveryFailure.new(
|
|
83
|
+
"HTTP Response status from identity URL host is not \"200\""\
|
|
84
|
+
". Got status #{resp.code.inspect}", 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.", $!)
|
|
101
|
+
end
|
|
102
|
+
if resp.code != "200"
|
|
103
|
+
exc = DiscoveryFailure.new(
|
|
104
|
+
"HTTP Response status from Yadis host is not \"200\". " +
|
|
105
|
+
"Got status #{resp.code.inspect}", 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
|
|
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
|