Esempio n. 1
0
    def _check_response(cls, response, content_type=None):
        """Check response content and its type.

        .. note::
           Checking is not strict: wrong server response ``Content-Type``
           HTTP header is ignored if response is an expected JSON object
           (c.f. Boulder #56).

        :param str content_type: Expected Content-Type response header.
            If JSON is expected and not present in server response, this
            function will raise an error. Otherwise, wrong Content-Type
            is ignored, but logged.

        :raises letsencrypt.messages2.Error: If server response body
            carries HTTP Problem (draft-ietf-appsawg-http-problem-00).
        :raises letsencrypt.errors.NetworkError: In case of other
            networking errors.

        """
        response_ct = response.headers.get('Content-Type')
        try:
            # TODO: response.json() is called twice, once here, and
            # once in _get and _post clients
            jobj = response.json()
        except ValueError as error:
            jobj = None

        if not response.ok:
            if jobj is not None:
                if response_ct != cls.JSON_ERROR_CONTENT_TYPE:
                    logging.debug(
                        'Ignoring wrong Content-Type (%r) for JSON Error',
                        response_ct)
                try:
                    logging.error("Error: %s", jobj)
                    logging.error("Response from server: %s", response.content)
                    raise messages2.Error.from_json(jobj)
                except jose.DeserializationError as error:
                    # Couldn't deserialize JSON object
                    raise errors.NetworkError((response, error))
            else:
                # response is not JSON object
                raise errors.NetworkError(response)
        else:
            if jobj is not None and response_ct != cls.JSON_CONTENT_TYPE:
                logging.debug(
                    'Ignoring wrong Content-Type (%r) for JSON decodable '
                    'response', response_ct)

            if content_type == cls.JSON_CONTENT_TYPE and jobj is None:
                raise errors.NetworkError(
                    'Unexpected response Content-Type: {0}'.format(response_ct))
Esempio n. 2
0
    def answer_challenge(self, challb, response):
        """Answer challenge.

        :param challb: Challenge Resource body.
        :type challb: `.ChallengeBody`

        :param response: Corresponding Challenge response
        :type response: `.challenges.ChallengeResponse`

        :returns: Challenge Resource with updated body.
        :rtype: `.ChallengeResource`

        :raises errors.UnexpectedUpdate:

        """
        response = self._post(challb.uri, self._wrap_in_jws(response))
        try:
            authzr_uri = response.links['up']['url']
        except KeyError:
            raise errors.NetworkError('"up" Link header missing')
        challr = messages2.ChallengeResource(
            authzr_uri=authzr_uri,
            body=messages2.ChallengeBody.from_json(response.json()))
        # TODO: check that challr.uri == response.headers['Location']?
        if challr.uri != challb.uri:
            raise errors.UnexpectedUpdate(challr.uri)
        return challr
Esempio n. 3
0
    def revoke(self, certr, when=messages2.Revocation.NOW):
        """Revoke certificate.

        :param when: When should the revocation take place? Takes
           the same values as `.messages2.Revocation.revoke`.

        """
        rev = messages2.Revocation(revoke=when, authorizations=tuple(
            authzr.uri for authzr in certr.authzrs))
        response = self._post(certr.uri, self._wrap_in_jws(rev))
        if response.status_code != httplib.OK:
            raise errors.NetworkError(
                'Successful revocation must return HTTP OK status')
Esempio n. 4
0
    def _get(self, uri, content_type=JSON_CONTENT_TYPE, **kwargs):
        """Send GET request.

        :raises letsencrypt.client.errors.NetworkError:

        :returns: HTTP Response
        :rtype: `requests.Response`

        """
        try:
            response = requests.get(uri, **kwargs)
        except requests.exceptions.RequestException as error:
            raise errors.NetworkError(error)
        self._check_response(response, content_type=content_type)
        return response
Esempio n. 5
0
    def _authzr_from_response(self, response, identifier,
                              uri=None, new_cert_uri=None):
        if new_cert_uri is None:
            try:
                new_cert_uri = response.links['next']['url']
            except KeyError:
                raise errors.NetworkError('"next" link missing')

        authzr = messages2.AuthorizationResource(
            body=messages2.Authorization.from_json(response.json()),
            uri=response.headers.get('Location', uri),
            new_cert_uri=new_cert_uri)
        if (authzr.body.key != self.key.public()
                or authzr.body.identifier != identifier):
            raise errors.UnexpectedUpdate(authzr)
        return authzr
Esempio n. 6
0
    def _regr_from_response(cls, response, uri=None, new_authzr_uri=None,
                            terms_of_service=None):
        terms_of_service = (
            response.links['terms-of-service']['url']
            if 'terms-of-service' in response.links else terms_of_service)

        if new_authzr_uri is None:
            try:
                new_authzr_uri = response.links['next']['url']
            except KeyError:
                raise errors.NetworkError('"next" link missing')

        return messages2.RegistrationResource(
            body=messages2.Registration.from_json(response.json()),
            uri=response.headers.get('Location', uri),
            new_authzr_uri=new_authzr_uri,
            terms_of_service=terms_of_service)
Esempio n. 7
0
    def check_cert(self, certr):
        """Check for new cert.

        :param certr: Certificate Resource
        :type certr: `.CertificateResource`

        :returns: Updated Certificate Resource.
        :rtype: `.CertificateResource`

        """
        # TODO: acme-spec 5.1 table action should be renamed to
        # "refresh cert", and this method integrated with self.refresh
        response, cert = self._get_cert(certr.uri)
        if 'Location' not in response.headers:
            raise errors.NetworkError('Location header missing')
        if response.headers['Location'] != certr.uri:
            raise errors.UnexpectedUpdate(response.text)
        return certr.update(body=cert)
Esempio n. 8
0
    def _post(self, uri, data, content_type=JSON_CONTENT_TYPE, **kwargs):
        """Send POST data.

        :param str content_type: Expected ``Content-Type``, fails if not set.

        :raises letsencrypt.acme.messages2.NetworkError:

        :returns: HTTP Response
        :rtype: `requests.Response`

        """
        logging.debug('Sending POST data: %s', data)
        try:
            response = requests.post(uri, data=data, **kwargs)
        except requests.exceptions.RequestException as error:
            raise errors.NetworkError(error)
        logging.debug('Received response %s: %s', response, response.text)

        self._check_response(response, content_type=content_type)
        return response
Esempio n. 9
0
    def request_issuance(self, csr, authzrs):
        """Request issuance.

        :param csr: CSR
        :type csr: `M2Crypto.X509.Request` wrapped in `.ComparableX509`

        :param authzrs: `list` of `.AuthorizationResource`

        :returns: Issued certificate
        :rtype: `.messages2.CertificateResource`

        """
        assert authzrs, "Authorizations list is empty"
        logging.debug("Requesting issuance...")

        # TODO: assert len(authzrs) == number of SANs
        req = messages2.CertificateRequest(
            csr=csr, authorizations=tuple(authzr.uri for authzr in authzrs))

        content_type = self.DER_CONTENT_TYPE  # TODO: add 'cert_type 'argument
        response = self._post(
            authzrs[0].new_cert_uri,  # TODO: acme-spec #90
            self._wrap_in_jws(req),
            content_type=content_type,
            headers={'Accept': content_type})

        cert_chain_uri = response.links.get('up', {}).get('url')

        try:
            uri = response.headers['Location']
        except KeyError:
            raise errors.NetworkError('"Location" Header missing')

        return messages2.CertificateResource(
            uri=uri, authzrs=authzrs, cert_chain_uri=cert_chain_uri,
            body=jose.ComparableX509(
                M2Crypto.X509.load_cert_der_string(response.content)))