Package openid :: Package server :: Module trustroot
[frames] | no frames]

Source Code for Module openid.server.trustroot

  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|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   
28 -def _parseURL(url):
29 proto, netloc, path, params, query, frag = urlparse(url) 30 path = urlunparse(('', '', path, params, query, frag)) 31 32 if ':' in netloc: 33 try: 34 host, port = netloc.split(':') 35 except ValueError: 36 return None 37 else: 38 host = netloc 39 port = '' 40 41 host = host.lower() 42 if not path: 43 path = '/' 44 45 return proto, host, port, path
46
47 -class TrustRoot(object):
48 """ 49 This class represents an OpenID trust root. The C{L{parse}} 50 classmethod accepts a trust root string, producing a 51 C{L{TrustRoot}} object. The method OpenID server implementers 52 would be most likely to use is the C{L{isSane}} method, which 53 checks the trust root for given patterns that indicate that the 54 trust root is too broad or points to a local network resource. 55 56 @sort: parse, isSane 57 """ 58
59 - def __init__(self, unparsed, proto, wildcard, host, port, path):
60 self.unparsed = unparsed 61 self.proto = proto 62 self.wildcard = wildcard 63 self.host = host 64 self.port = port 65 self.path = path
66
67 - def isSane(self):
68 """ 69 This method checks the to see if a trust root represents a 70 reasonable (sane) set of URLs. 'http://*.com/', for example 71 is not a reasonable pattern, as it cannot meaningfully specify 72 the site claiming it. This function attempts to find many 73 related examples, but it can only work via heuristics. 74 Negative responses from this method should be treated as 75 advisory, used only to alert the user to examine the trust 76 root carefully. 77 78 79 @return: Whether the trust root is sane 80 81 @rtype: C{bool} 82 """ 83 84 if self.host == 'localhost': 85 return True 86 87 host_parts = self.host.split('.') 88 if self.wildcard: 89 assert host_parts[0] == '', host_parts 90 del host_parts[0] 91 92 # If it's an absolute domain name, remove the empty string 93 # from the end. 94 if host_parts and not host_parts[-1]: 95 del host_parts[-1] 96 97 if not host_parts: 98 return False 99 100 # Do not allow adjacent dots 101 if '' in host_parts: 102 return False 103 104 tld = host_parts[-1] 105 if tld not in _top_level_domains: 106 return False 107 108 if len(tld) == 2: 109 if len(host_parts) == 1: 110 # entire host part is 2-letter tld 111 return False 112 113 if len(host_parts[-2]) <= 3: 114 # It's a 2-letter tld with a short second to last segment 115 # so there needs to be more than two segments specified 116 # (e.g. *.co.uk is insane) 117 return len(host_parts) > 2 118 else: 119 # A long second to last segment is specified. 120 return len(host_parts) > 1 121 else: 122 # It's a regular tld, so it needs at least one more segment 123 return len(host_parts) > 1 124 125 # Fell through, so not sane 126 return False
127
128 - def validateURL(self, url):
129 """ 130 Validates a URL against this trust root. 131 132 133 @param url: The URL to check 134 135 @type url: C{str} 136 137 138 @return: Whether the given URL is within this trust root. 139 140 @rtype: C{bool} 141 """ 142 143 url_parts = _parseURL(url) 144 if url_parts is None: 145 return False 146 147 proto, host, port, path = url_parts 148 149 if proto != self.proto: 150 return False 151 152 if port != self.port: 153 return False 154 155 if '*' in host: 156 return False 157 158 if not self.wildcard: 159 if host != self.host: 160 return False 161 elif ((not host.endswith(self.host)) and 162 ('.' + host) != self.host): 163 return False 164 165 if path != self.path: 166 path_len = len(self.path) 167 trust_prefix = self.path[:path_len] 168 url_prefix = path[:path_len] 169 170 # must be equal up to the length of the path, at least 171 if trust_prefix != url_prefix: 172 return False 173 174 # These characters must be on the boundary between the end 175 # of the trust root's path and the start of the URL's 176 # path. 177 if '?' in self.path: 178 allowed = '&' 179 else: 180 allowed = '?/' 181 182 return (self.path[-1] in allowed or 183 path[path_len] in allowed) 184 185 return True
186
187 - def parse(cls, trust_root):
188 """ 189 This method creates a C{L{TrustRoot}} instance from the given 190 input, if possible. 191 192 193 @param trust_root: This is the trust root to parse into a 194 C{L{TrustRoot}} object. 195 196 @type trust_root: C{str} 197 198 199 @return: A C{L{TrustRoot}} instance if trust_root parses as a 200 trust root, C{None} otherwise. 201 202 @rtype: C{NoneType} or C{L{TrustRoot}} 203 """ 204 if not isinstance(trust_root, (str, unicode)): 205 return None 206 207 url_parts = _parseURL(trust_root) 208 if url_parts is None: 209 return None 210 211 proto, host, port, path = url_parts 212 213 # check for valid prototype 214 if proto not in _protocols: 215 return None 216 217 # extract wildcard if it is there 218 if host.find('*', 1) != -1: 219 # wildcard must be at start of domain: *.foo.com, not foo.*.com 220 return None 221 222 if host.startswith('*'): 223 # Starts with star, so must have a dot after it (if a 224 # domain is specified) 225 if len(host) > 1 and host[1] != '.': 226 return None 227 228 host = host[1:] 229 wilcard = True 230 else: 231 wilcard = False 232 233 # we have a valid trust root 234 tr = cls(trust_root, proto, wilcard, host, port, path) 235 236 return tr
237 238 parse = classmethod(parse) 239
240 - def checkSanity(cls, trust_root_string):
241 """str -> bool 242 243 is this a sane trust root? 244 """ 245 return cls.parse(trust_root_string).isSane()
246 247 checkSanity = classmethod(checkSanity) 248
249 - def checkURL(cls, trust_root, url):
250 """quick func for validating a url against a trust root. See the 251 TrustRoot class if you need more control.""" 252 tr = cls.parse(trust_root) 253 return tr is not None and tr.validateURL(url)
254 255 checkURL = classmethod(checkURL) 256
257 - def __repr__(self):
258 return "TrustRoot('%s', '%s', '%s', '%s', '%s', '%s')" % ( 259 self.unparsed, self.proto, self.wildcard, self.host, self.port, 260 self.path)
261
262 - def __str__(self):
263 return repr(self)
264