def verify(self, assertion, audience=None): data = super(LocalBrowserIdVerifier, self).verify(assertion, audience) if self.allowed_issuers is not None: issuer = data.get('issuer') if issuer not in self.allowed_issuers: raise InvalidIssuerError("Issuer not allowed: %s" % (issuer,)) return data
def _fetch(self, hostname, requests, well_known_url=None, side_effect=None, response_text='', status_code=200): response = Mock() response.text = response_text response.status_code = status_code requests.request.side_effect = side_effect requests.request.return_value = response kwargs = {} if well_known_url is not None: kwargs['well_known_url'] = well_known_url supportdoc = fetch_support_document(hostname, **kwargs) try: key = supportdoc['public-key'] except KeyError: raise InvalidIssuerError('Host %r has malformed public key ' 'document' % hostname) return key
class RemoteVerifier(object): implements(IBrowserIdVerifier) def __init__(self, audiences=None, trusted_issuers=None, allowed_issuers=None, verifier_url=None): # Since we don't parse the assertion locally, we cannot support # list- or pattern-based audience strings. if audiences is not None: assert isinstance(audiences, basestring) self.audiences = audiences if isinstance(trusted_issuers, basestring): trusted_issuers = trusted_issuers.split() self.trusted_issuers = trusted_issuers if isinstance(allowed_issuers, basestring): allowed_issuers = allowed_issuers.split() self.allowed_issuers = allowed_issuers if verifier_url is None: verifier_url = "https://verifier.accounts.firefox.com/v2" self.verifier_url = verifier_url self.session = requests.Session() self.session.verify = True def verify(self, assertion, audience=None): if audience is None: audience = self.audiences body = {'assertion': assertion, 'audience': audience} if self.trusted_issuers is not None: body['trustedIssuers'] = self.trusted_issuers headers = {'content-type': 'application/json'} try: response = self.session.post(self.verifier_url, data=json.dumps(body), headers=headers) except (socket.error, requests.RequestException), e: msg = "Failed to POST %s. Reason: %s" % (self.verifier_url, str(e)) raise ConnectionError(msg) if response.status_code != 200: raise ConnectionError('server returned invalid response') try: data = json.loads(response.text) except ValueError: raise ConnectionError("server returned invalid response") if data.get('status') != "okay": reason = data.get('reason', 'unknown error') if "audience mismatch" in reason: raise AudienceMismatchError(data.get("audience"), audience) if "expired" in reason or "issued later than" in reason: raise ExpiredSignatureError(reason) raise InvalidSignatureError(reason) if self.allowed_issuers is not None: issuer = data.get('issuer') if issuer not in self.allowed_issuers: raise InvalidIssuerError("Issuer not allowed: %s" % (issuer, )) return data
def fetch_public_key(hostname, well_known_url=None): """Fetch the BrowserID public key for the given hostname. This function uses the well-known BrowserID meta-data file to extract the public key for the given hostname. """ if well_known_url is None: well_known_url = WELL_KNOWN_URL hostname = "https://" + hostname # Try to find the public key. If it can't be found then we # raise an InvalidIssuerError. Any other connection-related # errors are passed back up to the caller. try: # Try to read the well-known browserid file to load the key. try: browserid_url = urljoin(hostname, well_known_url) browserid_data = urlread(browserid_url) except ConnectionError, e: if "404" not in str(e): raise # The well-known file was not found, try falling back to # just "/pk". Not really a good idea, but that's currently # the only way to get browserid.org's public key. pubkey_url = urljoin(hostname, "/pk") key = urlread(urljoin(hostname, pubkey_url)) try: key = json.loads(key) except ValueError: msg = "Host %r has malformed public key document" raise InvalidIssuerError(msg % (hostname, )) else:
def fetch_public_key(url, *args): """Fetch the public key from the given URL.""" # Try to find the public key. If it can't be found then we # raise an InvalidIssuerError. Any other connection-related # errors are passed back up to the caller. response = _get(url) if response.status_code == 200: try: try: key = parse_jwt(response.text).payload['jwk'][0] except ValueError: key = json.loads(response.text)['jwk'][0] except (ValueError, KeyError): raise InvalidIssuerError('Host %r has malformed public key ' 'document' % url) else: raise InvalidIssuerError('Can not retrieve key from "%s"' % url) return key
def get_key(self, hostname): """Get the public key for verifying assertions from the given host.""" supportdoc = self.get_support_document(hostname) try: key = supportdoc['public-key'] except KeyError: raise InvalidIssuerError( "Host %r doesn't provide a public key" % hostname) return key
def fetch_support_document(hostname, well_known_url=None, verify=None): """Fetch the BrowserID well-known file for the given hostname. This function fetches and parses the well-known BrowserID meta-data file. :param verify: verify the certificate when requesting ssl resources """ if well_known_url is None: well_known_url = WELL_KNOWN_URL hostname = 'https://%s' % hostname # Try to find the support document. If it can't be found then we # raise an InvalidIssuerError. Any other connection-related # errors are passed back up to the caller. response = netutils.get(urljoin(hostname, well_known_url), verify=verify) if response.status_code == 200: try: data = json.loads(response.text) except ValueError: raise InvalidIssuerError('Host %r has malformed BrowserID ' 'support document' % hostname) else: # The well-known file was not found, try falling back to # just "/pk". response = netutils.get(urljoin(hostname, '/pk'), verify=verify) if response.status_code == 200: try: key = json.loads(response.text) except ValueError: raise InvalidIssuerError('Host %r has malformed BrowserID ' 'metadata document' % hostname) data = {"public-key": key} else: raise InvalidIssuerError('Host %r does not declare support for ' 'BrowserID' % hostname) return data
def verify(self, assertion, audience=None): if audience is None: audience = self.audiences body = {'assertion': assertion, 'audience': audience} if self.trusted_issuers is not None: body['trustedIssuers'] = self.trusted_issuers headers = {'content-type': 'application/json'} try: response = self.session.post(self.verifier_url, data=json.dumps(body), headers=headers, timeout=self.timeout) except (socket.error, requests.RequestException) as e: msg = "Failed to POST %s. Reason: %s" % (self.verifier_url, str(e)) raise ConnectionError(msg) if response.status_code != 200: raise ConnectionError('server returned invalid response code') try: data = json.loads(response.text) except ValueError: raise ConnectionError("server returned invalid response body") if data.get('status') != "okay": reason = data.get('reason', 'unknown error') if "audience mismatch" in reason: raise AudienceMismatchError(data.get("audience"), audience) if "expired" in reason or "issued later than" in reason: raise ExpiredSignatureError(reason) raise InvalidSignatureError(reason) if self.allowed_issuers is not None: issuer = data.get('issuer') if issuer not in self.allowed_issuers: raise InvalidIssuerError("Issuer not allowed: %s" % (issuer,)) return data
# the only way to get browserid.org's public key. pubkey_url = urljoin(hostname, "/pk") key = urlread(urljoin(hostname, pubkey_url)) try: key = json.loads(key) except ValueError: msg = "Host %r has malformed public key document" raise InvalidIssuerError(msg % (hostname, )) else: # The well-known file was found, it must contain the key # data as part of its JSON response. try: key = json.loads(browserid_data)["public-key"] except (ValueError, KeyError): msg = "Host %r has malformed BrowserID metadata document" raise InvalidIssuerError(msg % (hostname, )) return key except ConnectionError, e: if "404" not in str(e): raise msg = "Host %r does not declare support for BrowserID" % (hostname, ) raise InvalidIssuerError(msg) def urlread(url, data=None): """Read the given URL, return response as a string.""" # Anything that goes wrong inside this function will # be re-raised as an instance of ConnectionError. try: resp = secure_urlopen(url, data) try: