C0 code coverage information
Generated on Fri Jul 11 15:55:29 -0700 2008 with rcov 0.7.0
Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
1 require 'uri'
2 require 'openid/urinorm'
3
4 module OpenID
5
6 class RealmVerificationRedirected < Exception
7 # Attempting to verify this realm resulted in a redirect.
8 def initialize(relying_party_url, rp_url_after_redirects)
9 @relying_party_url = relying_party_url
10 @rp_url_after_redirects = rp_url_after_redirects
11 end
12
13 def to_s
14 return "Attempting to verify #{@relying_party_url} resulted in " +
15 "redirect to #{@rp_url_after_redirects}"
16 end
17 end
18
19 module TrustRoot
20 TOP_LEVEL_DOMAINS = %w'
21 ac ad ae aero af ag ai al am an ao aq ar arpa as asia at
22 au aw ax az ba bb bd be bf bg bh bi biz bj bm bn bo br bs bt
23 bv bw by bz ca cat cc cd cf cg ch ci ck cl cm cn co com coop
24 cr cu cv cx cy cz de dj dk dm do dz ec edu ee eg er es et eu
25 fi fj fk fm fo fr ga gb gd ge gf gg gh gi gl gm gn gov gp gq
26 gr gs gt gu gw gy hk hm hn hr ht hu id ie il im in info int
27 io iq ir is it je jm jo jobs jp ke kg kh ki km kn kp kr kw
28 ky kz la lb lc li lk lr ls lt lu lv ly ma mc md me mg mh mil
29 mk ml mm mn mo mobi mp mq mr ms mt mu museum mv mw mx my mz
30 na name nc ne net nf ng ni nl no np nr nu nz om org pa pe pf
31 pg ph pk pl pm pn pr pro ps pt pw py qa re ro rs ru rw sa sb
32 sc sd se sg sh si sj sk sl sm sn so sr st su sv sy sz tc td
33 tel tf tg th tj tk tl tm tn to tp tr travel tt tv tw tz ua
34 ug uk us uy uz va vc ve vg vi vn vu wf ws xn--0zwm56d
35 xn--11b5bs3a9aj6g xn--80akhbyknj4f xn--9t4b11yi5a
36 xn--deba0ad xn--g6w251d xn--hgbk6aj7f53bba
37 xn--hlcj6aya9esc7a xn--jxalpdlp xn--kgbechtv xn--zckzah ye
38 yt yu za zm zw'
39
40 ALLOWED_PROTOCOLS = ['http', 'https']
41
42 # The URI for relying party discovery, used in realm verification.
43 #
44 # XXX: This should probably live somewhere else (like in
45 # OpenID or OpenID::Yadis somewhere)
46 RP_RETURN_TO_URL_TYPE = 'http://specs.openid.net/auth/2.0/return_to'
47
48 # If the endpoint is a relying party OpenID return_to endpoint,
49 # return the endpoint URL. Otherwise, return None.
50 #
51 # This function is intended to be used as a filter for the Yadis
52 # filtering interface.
53 #
54 # endpoint: An XRDS BasicServiceEndpoint, as returned by
55 # performing Yadis dicovery.
56 #
57 # returns the endpoint URL or None if the endpoint is not a
58 # relying party endpoint.
59 def TrustRoot._extract_return_url(endpoint)
60 if endpoint.matchTypes([RP_RETURN_TO_URL_TYPE])
61 return endpoint.uri
62 else
63 return nil
64 end
65 end
66
67 # Is the return_to URL under one of the supplied allowed
68 # return_to URLs?
69 def TrustRoot.return_to_matches(allowed_return_to_urls, return_to)
70 allowed_return_to_urls.each { |allowed_return_to|
71 # A return_to pattern works the same as a realm, except that
72 # it's not allowed to use a wildcard. We'll model this by
73 # parsing it as a realm, and not trying to match it if it has
74 # a wildcard.
75
76 return_realm = TrustRoot.parse(allowed_return_to)
77 if (# Parses as a trust root
78 !return_realm.nil? and
79
80 # Does not have a wildcard
81 !return_realm.wildcard and
82
83 # Matches the return_to that we passed in with it
84 return_realm.validate_url(return_to)
85 )
86 return true
87 end
88 }
89
90 # No URL in the list matched
91 return false
92 end
93
94 # Given a relying party discovery URL return a list of return_to
95 # URLs.
96 def TrustRoot.get_allowed_return_urls(relying_party_url)
97 rp_url_after_redirects, return_to_urls = services.get_service_endpoints(
98 relying_party_url, _extract_return_url)
99
100 if rp_url_after_redirects != relying_party_url
101 # Verification caused a redirect
102 raise RealmVerificationRedirected.new(
103 relying_party_url, rp_url_after_redirects)
104 end
105
106 return return_to_urls
107 end
108
109 # Verify that a return_to URL is valid for the given realm.
110 #
111 # This function builds a discovery URL, performs Yadis discovery
112 # on it, makes sure that the URL does not redirect, parses out
113 # the return_to URLs, and finally checks to see if the current
114 # return_to URL matches the return_to.
115 #
116 # raises DiscoveryFailure when Yadis discovery fails returns
117 # true if the return_to URL is valid for the realm
118 def TrustRoot.verify_return_to(realm_str, return_to, _vrfy=nil)
119 # _vrfy parameter is there to make testing easier
120 if _vrfy.nil?
121 _vrfy = self.method('get_allowed_return_urls')
122 end
123
124 if !(_vrfy.is_a?(Proc) or _vrfy.is_a?(Method))
125 raise ArgumentError, "_vrfy must be a Proc or Method"
126 end
127
128 realm = TrustRoot.parse(realm_str)
129 if realm.nil?
130 # The realm does not parse as a URL pattern
131 return false
132 end
133
134 begin
135 allowable_urls = _vrfy.call(realm.build_discovery_url())
136 rescue RealmVerificationRedirected => err
137 Util.log(err.to_s)
138 return false
139 end
140
141 if return_to_matches(allowable_urls, return_to)
142 return true
143 else
144 Util.log("Failed to validate return_to #{return_to} for " +
145 "realm #{realm_str}, was not in #{allowable_urls}")
146 return false
147 end
148 end
149
150 class TrustRoot
151
152 attr_reader :unparsed, :proto, :wildcard, :host, :port, :path
153
154 @@empty_re = Regexp.new('^http[s]*:\/\/\*\/$')
155
156 def TrustRoot._build_path(path, query=nil, frag=nil)
157 s = path.dup
158
159 frag = nil if frag == ''
160 query = nil if query == ''
161
162 if query
163 s << "?" << query
164 end
165
166 if frag
167 s << "#" << frag
168 end
169
170 return s
171 end
172
173 def TrustRoot._parse_url(url)
174 begin
175 url = URINorm.urinorm(url)
176 rescue URI::InvalidURIError => err
177 nil
178 end
179
180 begin
181 parsed = URI::parse(url)
182 rescue URI::InvalidURIError
183 return nil
184 end
185
186 path = TrustRoot._build_path(parsed.path,
187 parsed.query,
188 parsed.fragment)
189
190 return [parsed.scheme || '', parsed.host || '',
191 parsed.port || '', path || '']
192 end
193
194 def TrustRoot.parse(trust_root)
195 trust_root = trust_root.dup
196 unparsed = trust_root.dup
197
198 # look for wildcard
199 wildcard = (not trust_root.index('://*.').nil?)
200 trust_root.sub!('*.', '') if wildcard
201
202 # handle http://*/ case
203 if not wildcard and @@empty_re.match(trust_root)
204 proto = trust_root.split(':')[0]
205 port = proto == 'http' ? 80 : 443
206 return new(unparsed, proto, true, '', port, '/')
207 end
208
209 parts = TrustRoot._parse_url(trust_root)
210 return nil if parts.nil?
211
212 proto, host, port, path = parts
213
214 # check for URI fragment
215 if path and !path.index('#').nil?
216 return nil
217 end
218
219 return nil unless ['http', 'https'].member?(proto)
220 return new(unparsed, proto, wildcard, host, port, path)
221 end
222
223 def TrustRoot.check_sanity(trust_root_string)
224 trust_root = TrustRoot.parse(trust_root_string)
225 if trust_root.nil?
226 return false
227 else
228 return trust_root.sane?
229 end
230 end
231
232 # quick func for validating a url against a trust root. See the
233 # TrustRoot class if you need more control.
234 def self.check_url(trust_root, url)
235 tr = self.parse(trust_root)
236 return (!tr.nil? and tr.validate_url(url))
237 end
238
239 # Return a discovery URL for this realm.
240 #
241 # This function does not check to make sure that the realm is
242 # valid. Its behaviour on invalid inputs is undefined.
243 #
244 # return_to:: The relying party return URL of the OpenID
245 # authentication request
246 #
247 # Returns the URL upon which relying party discovery should be
248 # run in order to verify the return_to URL
249 def build_discovery_url
250 if self.wildcard
251 # Use "www." in place of the star
252 www_domain = 'www.' + @host
253 port = (!@port.nil? and ![80, 443].member?(@port)) ? (":" + @port.to_s) : ''
254 return "#{@proto}://#{www_domain}#{port}#{@path}"
255 else
256 return @unparsed
257 end
258 end
259
260 def initialize(unparsed, proto, wildcard, host, port, path)
261 @unparsed = unparsed
262 @proto = proto
263 @wildcard = wildcard
264 @host = host
265 @port = port
266 @path = path
267 end
268
269 def sane?
270 return true if @host == 'localhost'
271
272 host_parts = @host.split('.')
273
274 # a note: ruby string split does not put an empty string at
275 # the end of the list if the split element is last. for
276 # example, 'foo.com.'.split('.') => ['foo','com']. Mentioned
277 # because the python code differs here.
278
279 return false if host_parts.length == 0
280
281 # no adjacent dots
282 return false if host_parts.member?('')
283
284 # last part must be a tld
285 tld = host_parts[-1]
286 return false unless TOP_LEVEL_DOMAINS.member?(tld)
287
288 return false if host_parts.length == 1
289
290 if @wildcard
291 if tld.length == 2 and host_parts[-2].length <= 3
292 # It's a 2-letter tld with a short second to last segment
293 # so there needs to be more than two segments specified
294 # (e.g. *.co.uk is insane)
295 return host_parts.length > 2
296 end
297 end
298
299 return true
300 end
301
302 def validate_url(url)
303 parts = TrustRoot._parse_url(url)
304 return false if parts.nil?
305
306 proto, host, port, path = parts
307
308 return false unless proto == @proto
309 return false unless port == @port
310 return false unless host.index('*').nil?
311
312 if !@wildcard
313 if host != @host
314 return false
315 end
316 elsif ((@host != '') and
317 (!host.ends_with?('.' + @host)) and
318 (host != @host))
319 return false
320 end
321
322 if path != @path
323 path_len = @path.length
324 trust_prefix = @path[0...path_len]
325 url_prefix = path[0...path_len]
326
327 # must be equal up to the length of the path, at least
328 if trust_prefix != url_prefix
329 return false
330 end
331
332 # These characters must be on the boundary between the end
333 # of the trust root's path and the start of the URL's path.
334 if !@path.index('?').nil?
335 allowed = '&'
336 else
337 allowed = '?/'
338 end
339
340 return (!allowed.index(@path[-1]).nil? or
341 !allowed.index(path[path_len]).nil?)
342 end
343
344 return true
345 end
346 end
347 end
348 end
349
Generated using the rcov code coverage analysis tool for Ruby version 0.7.0.