示例#1
0
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 secure_urlopen(url, data=None, timeout=None, ca_certs=None):
    """More secure replacement for urllib2.urlopen.

    This function provides an alternative to urllib2.urlopen which does
    basic validation and verification of HTTPS server certificates.
    """
    global DEFAULT_CACERT_FILE
    # Try to find platform default ca-cert file if none was specified.
    if ca_certs is None:
        ca_certs = DEFAULT_CACERT_FILE
        if ca_certs is None:
            for filenm in POSSIBLE_CACERT_FILES:
                if os.path.exists(filenm):
                    ca_certs = DEFAULT_CACERT_FILE = filenm
                    break
            if ca_certs is None:
                msg = "Could not locate a CA certificates file for HTTPS."\
                      " Your requests will be vulnerable to man-in-the-middle"\
                      " attacks.  It is *HIGHLY RECOMMENDED* that you specify"\
                      " the ca_certs parameter with the path to a valid"\
                      " certificates file."
                warnings.warn(msg, stacklevel=2)
    # Use a cached opener if possible.
    try:
        opener = _OPENER_CACHE[ca_certs]
    except KeyError:
        opener = urllib2.build_opener(ValidatingHTTPSHandler(ca_certs))
        _OPENER_CACHE[ca_certs] = opener
    try:
        return opener.open(url, data, timeout)
    except (EnvironmentError, httplib.HTTPException), e:
        raise ConnectionError(str(e))
def _get(url):
    """Fetch resource with requests."""
    try:
        return requests.get(url)
    except RequestException, e:
        msg = "Impossible to get %s. Reason: %s" % (url, str(e))
        raise ConnectionError(msg)
示例#4
0
 def verify(self, token):
     try:
         userinfo = self._client.verify_token(token, self.scope)
     except (socket.error, requests.RequestException), e:
         msg = 'Verification request to %s failed; reason: %s'
         msg %= (self.server_url, str(e))
         raise ConnectionError(msg)
def request(method, url, **kwds):
    """Make an HTTP request to the given URL."""
    try:
        return requests.request(method, url, **kwds)
    except (RequestException, socket.error) as e:
        msg = "Failed to %s %s. Reason: %s" % (method, url, str(e))
        raise ConnectionError(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:
            info = resp.info()
        except AttributeError:
            info = {}
        content_length = info.get("Content-Length")
        if content_length is None:
            data = resp.read()
        else:
            try:
                data = resp.read(int(content_length))
            except ValueError:
                raise ConnectionError("server sent invalid content-length")
    except Exception, e:
        raise ConnectionError(str(e))
示例#7
0
    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)
示例#8
0
    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
示例#9
0
 def verify(self, token):
     try:
         userinfo = self._client.verify_token(token, self.scope)
     except (socket.error, requests.RequestException) as e:
         msg = 'Verification request to %s failed; reason: %s'
         msg %= (self.server_url, str(e))
         raise ConnectionError(msg)
     issuer = userinfo.get('issuer', self.default_issuer)
     if not issuer or not isinstance(issuer, basestring):
         msg = 'Could not determine issuer from verifier response'
         raise fxa.errors.TrustError(msg)
     idpclaims = {}
     if userinfo.get('generation') is not None:
         idpclaims['fxa-generation'] = userinfo['generation']
     return {
       'email': userinfo['user'] + '@' + issuer,
       'idpClaims': idpclaims,
     }
示例#10
0
    def verify(self, assertion, audience=None):
        """Verify the given BrowserID assertion.

        This method posts the given BrowserID assertion to the remote verifier
        service.  If it is successfully verified then a dict giving the
        email and audience is returned.  If it is not valid then an error
        is raised.

        If the 'audience' argument is given, it first verifies that the
        audience of the assertion matches the one given.  This can help
        avoid doing lots of crypto for assertions that can't be valid.
        If you don't specify an audience, you *MUST* validate the audience
        value returned by this method.
        """
        # Check the audience locally.
        # No point talking to the network if we know it's going to fail.
        # If no explicit audience was given, this will also parse it out
        # for inclusion in the request to the remote verifier service.
        audience = self.check_audience(assertion, audience)

        response = netutils.post(self.verifier_url, {
            'assertion': assertion,
            'audience': audience
        })

        # BrowserID server sends "500 server error" for broken assertions.
        # For now, just translate that directly.  Should check by hand.
        if response.status_code == 500:
            raise ValueError('Malformed assertion')

        try:
            data = json.loads(response.text)
        except ValueError:
            raise ConnectionError("server returned invalid response")

        # Did it come back clean?
        if data.get('status') != "okay":
            raise InvalidSignatureError(str(data))
        if data.get('audience') != audience:
            raise AudienceMismatchError(data.get("audience"), audience)
        return data
 def test_handling_of_503_error_from_server(self):
     with patched_urlopen(exc=ConnectionError("503 Back Off")):
         self.assertRaises(ConnectionError, self.verifier.verify,
                           EXPIRED_ASSERTION)
示例#12
0
class RemoteVerifier(Verifier):
    """Class for remote verification of BrowserID identity assertions.

    This class submits assertions to the browserid.org verifier service
    for remote verification.  It's slower but potentially a little bit
    safer than the still-under-development LocalVerifier class.
    """
    def __init__(self, audiences=None, verifier_url=None):
        if verifier_url is None:
            verifier_url = BROWSERID_VERIFIER_URL
        super(RemoteVerifier, self).__init__(audiences)
        self.verifier_url = verifier_url

    def verify(self, assertion, audience=None):
        """Verify the given BrowserID assertion.

        This method posts the given BrowserID assertion to the remote verifier
        service.  If it is successfully verified then a dict giving the
        email and audience is returned.  If it is not valid then an error
        is raised.

        If the 'audience' argument is given, it first verifies that the
        audience of the assertion matches the one given.  This can help
        avoid doing lots of crypto for assertions that can't be valid.
        If you don't specify an audience, you *MUST* validate the audience
        value returned by this method.
        """
        # Check the audience locally.
        # No point talking to the network if we know it's going to fail.
        # If no explicit audience was given, this will also parse it out
        # for inclusion in the request to the remote verifier service.
        audience = self.check_audience(assertion, audience)
        # Encode the data into x-www-form-urlencoded.
        post_data = {"assertion": assertion, "audience": audience}
        post_data = "&".join("%s=%s" % item for item in post_data.items())
        # Post it to the verifier.
        try:
            resp = secure_urlopen(self.verifier_url, post_data)
        except ConnectionError, e:
            # BrowserID server sends "500 server error" for broken assertions.
            # For now, just translate that directly.  Should check by hand.
            if "500" in str(e):
                raise ValueError("Malformed assertion")
            raise
        # Read the response, being careful to raise an appropriate
        # error if the server does something funny.
        try:
            try:
                info = resp.info()
            except AttributeError:
                info = {}
            content_length = info.get("Content-Length")
            if content_length is None:
                data = resp.read()
            else:
                data = resp.read(int(content_length))
            data = json.loads(data)
        except ValueError:
            raise ConnectionError("server returned invalid response")
        # Did it come back clean?
        if data.get('status') != "okay":
            raise InvalidSignatureError(str(data))
        if data.get('audience') != audience:
            raise AudienceMismatchError(data.get("audience"), audience)
        return data