1 """
2 This module contains the C{L{TrustRoot}} class, which helps handle
3 trust root checking. This module is used by the
4 C{L{openid.server.server}} module, but it is also available to server
5 implementers who wish to use it for additional trust root checking.
6 """
7
8 from urlparse import urlparse, urlunparse
9
10
11 _protocols = ['http', 'https']
12 _top_level_domains = (
13 'com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|ac|ad|ae|'
14 'af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|'
15 'bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|'
16 'cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|'
17 'fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|'
18 'ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|'
19 'kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|'
20 'mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|'
21 'nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|'
22 'sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|sv|sy|sz|tc|td|tf|tg|th|'
23 'tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|'
24 'vn|vu|wf|ws|ye|yt|yu|za|zm|zw'
25 ).split('|')
26
27
29 proto, netloc, path, params, query, frag = urlparse(url)
30 if not path:
31
32 if not query and '?' in netloc:
33 netloc, query = netloc.split('?', 1)
34
35 path = '/'
36
37 path = urlunparse(('', '', path, params, query, frag))
38
39 if ':' in netloc:
40 try:
41 host, port = netloc.split(':')
42 except ValueError:
43 return None
44 else:
45 host = netloc
46 port = ''
47
48 host = host.lower()
49 return proto, host, port, path
50
52 """
53 This class represents an OpenID trust root. The C{L{parse}}
54 classmethod accepts a trust root string, producing a
55 C{L{TrustRoot}} object. The method OpenID server implementers
56 would be most likely to use is the C{L{isSane}} method, which
57 checks the trust root for given patterns that indicate that the
58 trust root is too broad or points to a local network resource.
59
60 @sort: parse, isSane
61 """
62
63 - def __init__(self, unparsed, proto, wildcard, host, port, path):
64 self.unparsed = unparsed
65 self.proto = proto
66 self.wildcard = wildcard
67 self.host = host
68 self.port = port
69 self.path = path
70
72 """
73 This method checks the to see if a trust root represents a
74 reasonable (sane) set of URLs. 'http://*.com/', for example
75 is not a reasonable pattern, as it cannot meaningfully specify
76 the site claiming it. This function attempts to find many
77 related examples, but it can only work via heuristics.
78 Negative responses from this method should be treated as
79 advisory, used only to alert the user to examine the trust
80 root carefully.
81
82
83 @return: Whether the trust root is sane
84
85 @rtype: C{bool}
86 """
87
88 if self.host == 'localhost':
89 return True
90
91 host_parts = self.host.split('.')
92 if self.wildcard:
93 assert host_parts[0] == '', host_parts
94 del host_parts[0]
95
96
97
98 if host_parts and not host_parts[-1]:
99 del host_parts[-1]
100
101 if not host_parts:
102 return False
103
104
105 if '' in host_parts:
106 return False
107
108 tld = host_parts[-1]
109 if tld not in _top_level_domains:
110 return False
111
112 if len(host_parts) == 1:
113 return False
114
115 if self.wildcard:
116 if len(tld) == 2 and len(host_parts[-2]) <= 3:
117
118
119
120 return len(host_parts) > 2
121
122
123 return True
124
126 """
127 Validates a URL against this trust root.
128
129
130 @param url: The URL to check
131
132 @type url: C{str}
133
134
135 @return: Whether the given URL is within this trust root.
136
137 @rtype: C{bool}
138 """
139
140 url_parts = _parseURL(url)
141 if url_parts is None:
142 return False
143
144 proto, host, port, path = url_parts
145
146 if proto != self.proto:
147 return False
148
149 if port != self.port:
150 return False
151
152 if '*' in host:
153 return False
154
155 if not self.wildcard:
156 if host != self.host:
157 return False
158 elif ((not host.endswith(self.host)) and
159 ('.' + host) != self.host):
160 return False
161
162 if path != self.path:
163 path_len = len(self.path)
164 trust_prefix = self.path[:path_len]
165 url_prefix = path[:path_len]
166
167
168 if trust_prefix != url_prefix:
169 return False
170
171
172
173
174 if '?' in self.path:
175 allowed = '&'
176 else:
177 allowed = '?/'
178
179 return (self.path[-1] in allowed or
180 path[path_len] in allowed)
181
182 return True
183
184 - def parse(cls, trust_root):
185 """
186 This method creates a C{L{TrustRoot}} instance from the given
187 input, if possible.
188
189
190 @param trust_root: This is the trust root to parse into a
191 C{L{TrustRoot}} object.
192
193 @type trust_root: C{str}
194
195
196 @return: A C{L{TrustRoot}} instance if trust_root parses as a
197 trust root, C{None} otherwise.
198
199 @rtype: C{NoneType} or C{L{TrustRoot}}
200 """
201 if not isinstance(trust_root, (str, unicode)):
202 return None
203
204 url_parts = _parseURL(trust_root)
205 if url_parts is None:
206 return None
207
208 proto, host, port, path = url_parts
209
210
211 if proto not in _protocols:
212 return None
213
214
215 if path.find('#') != -1:
216 return None
217
218
219 if host.find('*', 1) != -1:
220
221 return None
222
223 if host.startswith('*'):
224
225
226 if len(host) > 1 and host[1] != '.':
227 return None
228
229 host = host[1:]
230 wilcard = True
231 else:
232 wilcard = False
233
234
235 tr = cls(trust_root, proto, wilcard, host, port, path)
236
237 return tr
238
239 parse = classmethod(parse)
240
242 """str -> bool
243
244 is this a sane trust root?
245 """
246 return cls.parse(trust_root_string).isSane()
247
248 checkSanity = classmethod(checkSanity)
249
251 """quick func for validating a url against a trust root. See the
252 TrustRoot class if you need more control."""
253 tr = cls.parse(trust_root)
254 return tr is not None and tr.validateURL(url)
255
256 checkURL = classmethod(checkURL)
257
259 return "TrustRoot('%s', '%s', '%s', '%s', '%s', '%s')" % (
260 self.unparsed, self.proto, self.wildcard, self.host, self.port,
261 self.path)
262
264 return repr(self)
265