Package openid :: Package consumer :: Module discover
[frames] | no frames]

Source Code for Module openid.consumer.discover

  1  # -*- test-case-name: openid.test.test_discover -*- 
  2   
  3  from urljr import fetchers 
  4   
  5  from openid import oidutil 
  6   
  7  # If the Yadis library is available, use it. Otherwise, only use 
  8  # old-style discovery. 
  9  try: 
 10      import yadis 
 11  except ImportError: 
 12      yadis_available = False 
 13   
 14      oidutil.log('Consumer operating without Yadis support ' 
 15                  '(failed to import Yadis library)') 
 16   
17 - class DiscoveryFailure(RuntimeError):
18 """A failure to discover an OpenID server. 19 20 When the C{yadis} package is available, this is 21 C{yadis.discover.DiscoveryFailure}."""
22 else: 23 yadis_available = True 24 from yadis.etxrd import nsTag, XRDSError 25 from yadis.services import applyFilter as extractServices 26 from yadis.discover import discover as yadisDiscover 27 from yadis.discover import DiscoveryFailure 28 from yadis import xrires, filters 29 30 from openid.consumer.parse import openIDDiscover as parseOpenIDLinkRel 31 from openid.consumer.parse import ParseError 32 33 OPENID_1_0_NS = 'http://openid.net/xmlns/1.0' 34 OPENID_1_2_TYPE = 'http://openid.net/signon/1.2' 35 OPENID_1_1_TYPE = 'http://openid.net/signon/1.1' 36 OPENID_1_0_TYPE = 'http://openid.net/signon/1.0' 37
38 -class OpenIDServiceEndpoint(object):
39 """Object representing an OpenID service endpoint. 40 41 @ivar identity_url: the verified identifier. 42 @ivar canonicalID: For XRI, the persistent identifier. 43 """ 44 openid_type_uris = [ 45 OPENID_1_2_TYPE, 46 OPENID_1_1_TYPE, 47 OPENID_1_0_TYPE, 48 ] 49
50 - def __init__(self):
51 self.identity_url = None 52 self.server_url = None 53 self.type_uris = [] 54 self.delegate = None 55 self.canonicalID = None 56 self.used_yadis = False # whether this came from an XRDS
57
58 - def usesExtension(self, extension_uri):
59 return extension_uri in self.type_uris
60
61 - def parseService(self, yadis_url, uri, type_uris, service_element):
62 """Set the state of this object based on the contents of the 63 service element.""" 64 self.type_uris = type_uris 65 self.identity_url = yadis_url 66 self.server_url = uri 67 self.delegate = findDelegate(service_element) 68 self.used_yadis = True
69
70 - def getServerID(self):
71 """Return the identifier that should be sent as the 72 openid.identity_url parameter to the server.""" 73 if self.delegate is None: 74 return self.canonicalID or self.identity_url 75 else: 76 return self.delegate
77
78 - def fromBasicServiceEndpoint(cls, endpoint):
79 """Create a new instance of this class from the endpoint 80 object passed in. 81 82 @return: None or OpenIDServiceEndpoint for this endpoint object""" 83 type_uris = endpoint.matchTypes(cls.openid_type_uris) 84 85 # If any Type URIs match and there is an endpoint URI 86 # specified, then this is an OpenID endpoint 87 if type_uris and endpoint.uri is not None: 88 openid_endpoint = cls() 89 openid_endpoint.parseService( 90 endpoint.yadis_url, 91 endpoint.uri, 92 endpoint.type_uris, 93 endpoint.service_element) 94 else: 95 openid_endpoint = None 96 97 return openid_endpoint
98 99 fromBasicServiceEndpoint = classmethod(fromBasicServiceEndpoint) 100
101 - def fromHTML(cls, uri, html):
102 """Parse the given document as HTML looking for an OpenID <link 103 rel=...> 104 105 @raises: openid.consumer.parse.ParseError 106 """ 107 delegate_url, server_url = parseOpenIDLinkRel(html) 108 service = cls() 109 service.identity_url = uri 110 service.delegate = delegate_url 111 service.server_url = server_url 112 service.type_uris = [OPENID_1_0_TYPE] 113 return service
114 115 fromHTML = classmethod(fromHTML)
116
117 -def findDelegate(service_element):
118 """Extract a openid:Delegate value from a Yadis Service element 119 represented as an ElementTree Element object. If no delegate is 120 found, returns None.""" 121 # XXX: should this die if there is more than one delegate element? 122 delegate_tag = nsTag(OPENID_1_0_NS, 'Delegate') 123 124 delegates = service_element.findall(delegate_tag) 125 for delegate_element in delegates: 126 delegate = delegate_element.text 127 break 128 else: 129 delegate = None 130 131 return delegate
132
133 -def discoverYadis(uri):
134 """Discover OpenID services for a URI. Tries Yadis and falls back 135 on old-style <link rel='...'> discovery if Yadis fails. 136 137 @param uri: normalized identity URL 138 @type uri: str 139 140 @return: (identity_url, services) 141 @rtype: (str, list(OpenIDServiceEndpoint)) 142 143 @raises: DiscoveryFailure 144 """ 145 # Might raise a yadis.discover.DiscoveryFailure if no document 146 # came back for that URI at all. I don't think falling back 147 # to OpenID 1.0 discovery on the same URL will help, so don't 148 # bother to catch it. 149 response = yadisDiscover(uri) 150 151 identity_url = response.normalized_uri 152 try: 153 openid_services = extractServices( 154 response.normalized_uri, response.response_text, 155 OpenIDServiceEndpoint) 156 except XRDSError: 157 # Does not parse as a Yadis XRDS file 158 openid_services = [] 159 160 if not openid_services: 161 # Either not an XRDS or there are no OpenID services. 162 163 if response.isXRDS(): 164 # if we got the Yadis content-type or followed the Yadis 165 # header, re-fetch the document without following the Yadis 166 # header, with no Accept header. 167 return discoverNoYadis(uri) 168 else: 169 body = response.response_text 170 171 # Try to parse the response as HTML to get OpenID 1.0/1.1 172 # <link rel="..."> 173 try: 174 service = OpenIDServiceEndpoint.fromHTML(identity_url, body) 175 except ParseError: 176 pass # Parsing failed, so return an empty list 177 else: 178 openid_services = [service] 179 180 return (identity_url, openid_services)
181 182
183 -def discoverXRI(iname):
184 endpoints = [] 185 try: 186 canonicalID, services = xrires.ProxyResolver().query( 187 iname, OpenIDServiceEndpoint.openid_type_uris) 188 flt = filters.mkFilter(OpenIDServiceEndpoint) 189 for service_element in services: 190 endpoints.extend(flt.getServiceEndpoints(iname, service_element)) 191 except XRDSError: 192 oidutil.log('xrds error on ' + iname) 193 194 for endpoint in endpoints: 195 # Is there a way to pass this through the filter to the endpoint 196 # constructor instead of tacking it on after? 197 endpoint.canonicalID = canonicalID 198 199 # FIXME: returned xri should probably be in some normal form 200 return iname, endpoints
201 202
203 -def discoverNoYadis(uri):
204 http_resp = fetchers.fetch(uri) 205 if http_resp.status != 200: 206 raise DiscoveryFailure( 207 'HTTP Response status from identity URL host is not 200. ' 208 'Got status %r' % (http_resp.status,), http_resp) 209 identity_url = http_resp.final_url 210 211 # Try to parse the response as HTML to get OpenID 1.0/1.1 212 # <link rel="..."> 213 try: 214 service = OpenIDServiceEndpoint.fromHTML(identity_url, http_resp.body) 215 except ParseError: 216 openid_services = [] 217 else: 218 openid_services = [service] 219 220 return identity_url, openid_services
221 222 if yadis_available: 223 discover = discoverYadis 224 else: 225 discover = discoverNoYadis 226