예제 #1
0
    def setup_acme_client(self, authority):
        if not authority.options:
            raise InvalidAuthority("Invalid authority. Options not set")
        options = {}

        for option in json.loads(authority.options):
            options[option["name"]] = option.get("value")
        email = options.get('email', current_app.config.get('ACME_EMAIL'))
        tel = options.get('telephone', current_app.config.get('ACME_TEL'))
        directory_url = options.get('acme_url', current_app.config.get('ACME_DIRECTORY_URL'))

        existing_key = options.get('acme_private_key', current_app.config.get('ACME_PRIVATE_KEY'))
        existing_regr = options.get('acme_regr', current_app.config.get('ACME_REGR'))

        if existing_key and existing_regr:
            # Reuse the same account for each certificate issuance
            key = jose.JWK.json_loads(existing_key)
            regr = messages.RegistrationResource.json_loads(existing_regr)
            current_app.logger.debug("Connecting with directory at {0}".format(directory_url))
            net = ClientNetwork(key, account=regr)
            client = BackwardsCompatibleClientV2(net, key, directory_url)
            return client, {}
        else:
            # Create an account for each certificate issuance
            key = jose.JWKRSA(key=generate_private_key('RSA2048'))

            current_app.logger.debug("Connecting with directory at {0}".format(directory_url))

            net = ClientNetwork(key, account=None)
            client = BackwardsCompatibleClientV2(net, key, directory_url)
            registration = client.new_account_and_tos(messages.NewRegistration.from_data(email=email))
            current_app.logger.debug("Connected: {0}".format(registration.uri))

        return client, registration
예제 #2
0
    def setUp(self):
        from acme.client import ClientNetwork
        self.net = ClientNetwork(key=None, alg=None)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}
        self.checked_response = mock.MagicMock()
        self.obj = mock.MagicMock()
        self.wrapped_obj = mock.MagicMock()
        self.content_type = mock.sentinel.content_type

        self.all_nonces = [
            jose.b64encode(b'Nonce'),
            jose.b64encode(b'Nonce2'),
            jose.b64encode(b'Nonce3')
        ]
        self.available_nonces = self.all_nonces[:]

        def send_request(*args, **kwargs):
            # pylint: disable=unused-argument,missing-docstring
            if self.available_nonces:
                self.response.headers = {
                    self.net.REPLAY_NONCE_HEADER:
                    self.available_nonces.pop().decode()
                }
            else:
                self.response.headers = {}
            return self.response

        # pylint: disable=protected-access
        self.net._send_request = self.send_request = mock.MagicMock(
            side_effect=send_request)
        self.net._check_response = self.check_response
        self.net._wrap_in_jws = mock.MagicMock(return_value=self.wrapped_obj)
예제 #3
0
    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import ClientNetwork
        self.net = ClientNetwork(
            key=KEY, alg=jose.RS256, verify_ssl=self.verify_ssl)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}
예제 #4
0
def wildcard_revoke(cert_pem,account):

		#Check if registrar exists
		if account not in os.listdir(REG_DIRECTORY):
			print "This account does not exists, register it first with new_account.py"
			sys.exit(1)

		#Load files from disk
		with open(REG_DIRECTORY + "/" + account + "/private.key", "rb") as key_file:
			privkey = serialization.load_pem_private_key(
				key_file.read(),
				password=None,
				backend=default_backend()
			)
		with open(REG_DIRECTORY + "/" + account + "/reguri.txt", "r") as reguri_file:
			reg_uri = reguri_file.read()

		#Compose registration resource (regr)
		key = jose.JWKRSA(key=privkey)
		regr = messages.RegistrationResource(
			body=messages.Registration(
				key=key.public_key()),
			uri = reg_uri)

		#Init ACME
		net = ClientNetwork(key)
		directory = net.get(DIRECTORY_URL).json()

		acme = client.ClientV2(directory, net)


		#Check if registration is valid
		if acme.query_registration(regr).body.status == u'valid':
			print "Registration valid"
		else:
			print "Registration invalid"
			sys.exit(1)

		#Deserialize key from variable
		cert = jose.ComparableX509(OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem))

		#Try to revoke cert, return false on error or revoked-already state
		try:
			revokation = acme.revoke(cert,1)
		except messages.Error,acme_exc:
			if str(acme_exc) == str("urn:ietf:params:acme:error:alreadyRevoked :: Certificate already revoked"):
				return ["Certificate already revoked",False]
			else:
				return [acme_exc, False]
예제 #5
0
def create_registration():
    global privkey, regr
    privkey = rsa.generate_private_key(public_exponent=65537,
                                       key_size=BITS,
                                       backend=default_backend())
    key = jose.JWKRSA(key=privkey)
    net = ClientNetwork(key)
    directory = net.get(DIRECTORY_URL).json()
    acme = client.ClientV2(directory, net)
    regbody = dict(
        messages.Registration(contact=('mailto:[email protected]', ),
                              terms_of_service_agreed=True,
                              key=key.public_key()))
    #NEED TO SAVE REGBODY VARIABLE TO FILE
    regr = acme.new_account(messages.NewRegistration(**regbody))
예제 #6
0
    def setUp(self):
        from acme.client import ClientNetwork
        self.net = ClientNetwork(key=None, alg=None)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}
        self.checked_response = mock.MagicMock()
        self.obj = mock.MagicMock()
        self.wrapped_obj = mock.MagicMock()
        self.content_type = mock.sentinel.content_type

        self.all_nonces = [jose.b64encode(b'Nonce'), jose.b64encode(b'Nonce2')]
        self.available_nonces = self.all_nonces[:]

        def send_request(*args, **kwargs):
            # pylint: disable=unused-argument,missing-docstring
            if self.available_nonces:
                self.response.headers = {
                    self.net.REPLAY_NONCE_HEADER:
                    self.available_nonces.pop().decode()}
            else:
                self.response.headers = {}
            return self.response

        # pylint: disable=protected-access
        self.net._send_request = self.send_request = mock.MagicMock(
            side_effect=send_request)
        self.net._check_response = self.check_response
        self.net._wrap_in_jws = mock.MagicMock(return_value=self.wrapped_obj)
예제 #7
0
    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import ClientNetwork
        self.net = ClientNetwork(
            key=KEY, alg=jose.RS256, verify_ssl=self.verify_ssl)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}
예제 #8
0
    def __init__(self, account_id=None):
        if account_id:
            self.account = Account.query.get(account_id)

            self.key = jose.JWKRSA(
                key=crypto.load_private_key(self.account.key.encode("utf8")))

            regr = RegistrationResource.from_json(
                json.loads(self.account.contents))
            net = ClientNetwork(self.key, account=regr)
            self.client = BackwardsCompatibleClientV2(
                net, self.key, self.account.directory_uri)
        else:
            print("Setup ACME Account")
예제 #9
0
 def register_account(
     cls,
     email: str,
     directory_url: str = "https://acme-v02.api.letsencrypt.org/directory",
 ):
     """
     create a new acme account
     :param email: the email address that should be used for the account
     :param directory_url: the url of the acme directory that should be used
     :return: a new ACME client instance
     """
     keypair = cls._generate_keypair()
     client_network = ClientNetwork(keypair)
     directory = cls._get_directory(directory_url)
     registration = Registration.from_data(email=email,
                                           terms_of_service_agreed=True)
     client = ClientV2(directory, client_network)
     result = client.new_account(registration)
     return ACMEService(client)
예제 #10
0
    def setup_acme_account(self,
                           user_id,
                           email=None,
                           server=None,
                           rsa_key=None,
                           organization: str = None,
                           organizational_unit: str = None,
                           country: str = None,
                           state: str = None,
                           location: str = None):
        if not email:
            email = config.CERTIFIRE_EMAIL
        if not server:
            server = config.LETS_ENCRYPT_PRODUCTION
        if not rsa_key:
            rsa_key = crypto.generate_rsa_key()

        account = Account(user_id,
                          email,
                          server,
                          organization=organization,
                          organizational_unit=organizational_unit,
                          country=country,
                          state=state,
                          location=location)
        key = jose.JWKRSA(key=rsa_key)
        net = ClientNetwork(key, account=None, timeout=3600)
        client = BackwardsCompatibleClientV2(net, key, account.directory_uri)
        registration = client.new_account_and_tos(
            messages.NewRegistration.from_data(email=email))
        account.key = crypto.export_private_key(rsa_key).decode("utf-8")
        account.uri = registration.uri
        account.contents = json.dumps(registration.to_json())

        database.add(account)
        self.account = account
        self.key = key
        self.client = client
        return account
예제 #11
0
def setup_acme_client(authority):
    if not authority.options:
        raise InvalidAuthority("Invalid authority. Options not set")
    options = {}

    for option in json.loads(authority.options):
        options[option["name"]] = option.get("value")
    email = options.get('email', current_app.config.get('ACME_EMAIL'))
    tel = options.get('telephone', current_app.config.get('ACME_TEL'))
    directory_url = options.get('acme_url',
                                current_app.config.get('ACME_DIRECTORY_URL'))

    key = jose.JWKRSA(key=generate_private_key('RSA2048'))

    current_app.logger.debug(
        "Connecting with directory at {0}".format(directory_url))

    net = ClientNetwork(key, account=None)
    client = BackwardsCompatibleClientV2(net, key, directory_url)
    registration = client.new_account_and_tos(
        messages.NewRegistration.from_data(email=email))
    current_app.logger.debug("Connected: {0}".format(registration.uri))

    return client, registration
예제 #12
0
class ClientNetworkTest(unittest.TestCase):
    """Tests for acme.client.ClientNetwork."""
    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import ClientNetwork
        self.net = ClientNetwork(key=KEY,
                                 alg=jose.RS256,
                                 verify_ssl=self.verify_ssl,
                                 user_agent='acme-python-test')

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}

    def test_init(self):
        self.assertTrue(self.net.verify_ssl is self.verify_ssl)

    def test_wrap_in_jws(self):
        class MockJSONDeSerializable(jose.JSONDeSerializable):
            # pylint: disable=missing-docstring
            def __init__(self, value):
                self.value = value

            def to_partial_json(self):
                return {'foo': self.value}

            @classmethod
            def from_json(cls, value):
                pass  # pragma: no cover

        # pylint: disable=protected-access
        jws_dump = self.net._wrap_in_jws(MockJSONDeSerializable('foo'),
                                         nonce=b'Tg')
        jws = acme_jws.JWS.json_loads(jws_dump)
        self.assertEqual(json.loads(jws.payload.decode()), {'foo': 'foo'})
        self.assertEqual(jws.signature.combined.nonce, b'Tg')

    def test_check_response_not_ok_jobj_no_error(self):
        self.response.ok = False
        self.response.json.return_value = {}
        with mock.patch('acme.client.messages.Error.from_json') as from_json:
            from_json.side_effect = jose.DeserializationError
            # pylint: disable=protected-access
            self.assertRaises(errors.ClientError, self.net._check_response,
                              self.response)

    def test_check_response_not_ok_jobj_error(self):
        self.response.ok = False
        self.response.json.return_value = messages.Error(
            detail='foo', typ='serverInternal', title='some title').to_json()
        # pylint: disable=protected-access
        self.assertRaises(messages.Error, self.net._check_response,
                          self.response)

    def test_check_response_not_ok_no_jobj(self):
        self.response.ok = False
        self.response.json.side_effect = ValueError
        # pylint: disable=protected-access
        self.assertRaises(errors.ClientError, self.net._check_response,
                          self.response)

    def test_check_response_ok_no_jobj_ct_required(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access
            self.assertRaises(errors.ClientError,
                              self.net._check_response,
                              self.response,
                              content_type=self.net.JSON_CONTENT_TYPE)

    def test_check_response_ok_no_jobj_no_ct(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response,
                             self.net._check_response(self.response))

    def test_check_response_jobj(self):
        self.response.json.return_value = {}
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response,
                             self.net._check_response(self.response))

    def test_send_request(self):
        self.net.session = mock.MagicMock()
        self.net.session.request.return_value = self.response
        # pylint: disable=protected-access
        self.assertEqual(
            self.response,
            self.net._send_request('HEAD',
                                   'http://example.com/',
                                   'foo',
                                   bar='baz'))
        self.net.session.request.assert_called_once_with('HEAD',
                                                         'http://example.com/',
                                                         'foo',
                                                         headers=mock.ANY,
                                                         verify=mock.ANY,
                                                         timeout=mock.ANY,
                                                         bar='baz')

    @mock.patch('acme.client.logger')
    def test_send_request_get_der(self, mock_logger):
        self.net.session = mock.MagicMock()
        self.net.session.request.return_value = mock.MagicMock(
            ok=True,
            status_code=http_client.OK,
            headers={"Content-Type": "application/pkix-cert"},
            content=b"hi")
        # pylint: disable=protected-access
        self.net._send_request('HEAD',
                               'http://example.com/',
                               'foo',
                               timeout=mock.ANY,
                               bar='baz')
        mock_logger.debug.assert_called_once_with(
            'Received response:\nHTTP %d\n%s\n\n%s', 200,
            'Content-Type: application/pkix-cert', b'aGk=')

    def test_send_request_post(self):
        self.net.session = mock.MagicMock()
        self.net.session.request.return_value = self.response
        # pylint: disable=protected-access
        self.assertEqual(
            self.response,
            self.net._send_request('POST',
                                   'http://example.com/',
                                   'foo',
                                   data='qux',
                                   bar='baz'))
        self.net.session.request.assert_called_once_with('POST',
                                                         'http://example.com/',
                                                         'foo',
                                                         headers=mock.ANY,
                                                         verify=mock.ANY,
                                                         timeout=mock.ANY,
                                                         data='qux',
                                                         bar='baz')

    def test_send_request_verify_ssl(self):
        # pylint: disable=protected-access
        for verify in True, False:
            self.net.session = mock.MagicMock()
            self.net.session.request.return_value = self.response
            self.net.verify_ssl = verify
            # pylint: disable=protected-access
            self.assertEqual(
                self.response,
                self.net._send_request('GET', 'http://example.com/'))
            self.net.session.request.assert_called_once_with(
                'GET',
                'http://example.com/',
                verify=verify,
                timeout=mock.ANY,
                headers=mock.ANY)

    def test_send_request_user_agent(self):
        self.net.session = mock.MagicMock()
        # pylint: disable=protected-access
        self.net._send_request('GET',
                               'http://example.com/',
                               headers={'bar': 'baz'})
        self.net.session.request.assert_called_once_with(
            'GET',
            'http://example.com/',
            verify=mock.ANY,
            timeout=mock.ANY,
            headers={
                'User-Agent': 'acme-python-test',
                'bar': 'baz'
            })

        self.net._send_request('GET',
                               'http://example.com/',
                               headers={'User-Agent': 'foo2'})
        self.net.session.request.assert_called_with(
            'GET',
            'http://example.com/',
            verify=mock.ANY,
            timeout=mock.ANY,
            headers={'User-Agent': 'foo2'})

    def test_send_request_timeout(self):
        self.net.session = mock.MagicMock()
        # pylint: disable=protected-access
        self.net._send_request('GET',
                               'http://example.com/',
                               headers={'bar': 'baz'})
        self.net.session.request.assert_called_once_with(mock.ANY,
                                                         mock.ANY,
                                                         verify=mock.ANY,
                                                         headers=mock.ANY,
                                                         timeout=45)

    def test_del(self):
        sess = mock.MagicMock()
        self.net.session = sess
        del self.net
        sess.close.assert_called_once_with()

    @mock.patch('acme.client.requests')
    def test_requests_error_passthrough(self, mock_requests):
        mock_requests.exceptions = requests.exceptions
        mock_requests.request.side_effect = requests.exceptions.RequestException
        # pylint: disable=protected-access
        self.assertRaises(requests.exceptions.RequestException,
                          self.net._send_request, 'GET', 'uri')
예제 #13
0
    def setup_acme_client(self, authority):
        if not authority.options:
            raise InvalidAuthority("Invalid authority. Options not set")
        options = {}

        for option in json.loads(authority.options):
            options[option["name"]] = option.get("value")
        email = options.get("email", current_app.config.get("ACME_EMAIL"))
        tel = options.get("telephone", current_app.config.get("ACME_TEL"))
        directory_url = options.get(
            "acme_url", current_app.config.get("ACME_DIRECTORY_URL"))

        existing_key = options.get("acme_private_key",
                                   current_app.config.get("ACME_PRIVATE_KEY"))
        existing_regr = options.get("acme_regr",
                                    current_app.config.get("ACME_REGR"))

        eab_kid = options.get("eab_kid", None)
        eab_hmac_key = options.get("eab_hmac_key", None)

        if existing_key and existing_regr:
            current_app.logger.debug("Reusing existing ACME account")
            # Reuse the same account for each certificate issuance

            # existing_key might be encrypted
            if not is_json(existing_key):
                # decrypt the private key, if not already in plaintext (json format)
                existing_key = data_decrypt(existing_key)

            key = jose.JWK.json_loads(existing_key)
            regr = messages.RegistrationResource.json_loads(existing_regr)
            current_app.logger.debug(
                "Connecting with directory at {0}".format(directory_url))
            net = ClientNetwork(key, account=regr)
            client = BackwardsCompatibleClientV2(net, key, directory_url)
            return client, {}
        else:
            # Create an account for each certificate issuance
            key = jose.JWKRSA(key=generate_private_key("RSA2048"))

            current_app.logger.debug("Creating a new ACME account")
            current_app.logger.debug(
                "Connecting with directory at {0}".format(directory_url))

            net = ClientNetwork(key, account=None, timeout=3600)
            client = BackwardsCompatibleClientV2(net, key, directory_url)
            if eab_kid and eab_hmac_key:
                # external account binding (eab_kid and eab_hmac_key could be potentially single use to establish
                # long-term credentials)
                eab = messages.ExternalAccountBinding.from_data(
                    account_public_key=key.public_key(),
                    kid=eab_kid,
                    hmac_key=eab_hmac_key,
                    directory=client.directory)
                registration = client.new_account_and_tos(
                    messages.NewRegistration.from_data(
                        email=email, external_account_binding=eab))
            else:
                registration = client.new_account_and_tos(
                    messages.NewRegistration.from_data(email=email))

            # if store_account is checked, add the private_key and registration resources to the options
            if options['store_account']:
                new_options = json.loads(authority.options)
                # the key returned by fields_to_partial_json is missing the key type, so we add it manually
                key_dict = key.fields_to_partial_json()
                key_dict["kty"] = "RSA"
                acme_private_key = {
                    "name": "acme_private_key",
                    "value": data_encrypt(json.dumps(key_dict))
                }
                new_options.append(acme_private_key)

                acme_regr = {
                    "name": "acme_regr",
                    "value": json.dumps({
                        "body": {},
                        "uri": registration.uri
                    })
                }
                new_options.append(acme_regr)

                authorities_service.update_options(
                    authority.id, options=json.dumps(new_options))

            current_app.logger.debug("Connected: {0}".format(registration.uri))

        return client, registration
예제 #14
0
 def build_sync(cls, directory_url, account_key, **kw):
     net = ClientNetwork(account_key, user_agent=USER_AGENT, **kw)
     directory = Directory.from_json(net.get(directory_url).json())
     return cls(_ClientV2PlusPlus(directory, net=net))
예제 #15
0
    def setup_acme_client(self, authority):
        if not authority.options:
            raise InvalidAuthority("Invalid authority. Options not set")
        options = {}

        for option in json.loads(authority.options):
            options[option["name"]] = option.get("value")
        email = options.get("email", current_app.config.get("ACME_EMAIL"))
        tel = options.get("telephone", current_app.config.get("ACME_TEL"))
        directory_url = options.get(
            "acme_url", current_app.config.get("ACME_DIRECTORY_URL")
        )

        existing_key = options.get(
            "acme_private_key", current_app.config.get("ACME_PRIVATE_KEY")
        )
        existing_regr = options.get("acme_regr", current_app.config.get("ACME_REGR"))

        if existing_key and existing_regr:
            current_app.logger.debug("Reusing existing ACME account")
            # Reuse the same account for each certificate issuance
            key = jose.JWK.json_loads(existing_key)
            regr = messages.RegistrationResource.json_loads(existing_regr)
            current_app.logger.debug(
                "Connecting with directory at {0}".format(directory_url)
            )
            net = ClientNetwork(key, account=regr)
            client = BackwardsCompatibleClientV2(net, key, directory_url)
            return client, {}
        else:
            # Create an account for each certificate issuance
            key = jose.JWKRSA(key=generate_private_key("RSA2048"))

            current_app.logger.debug("Creating a new ACME account")
            current_app.logger.debug(
                "Connecting with directory at {0}".format(directory_url)
            )

            net = ClientNetwork(key, account=None, timeout=3600)
            client = BackwardsCompatibleClientV2(net, key, directory_url)
            registration = client.new_account_and_tos(
                messages.NewRegistration.from_data(email=email)
            )

            # if store_account is checked, add the private_key and registration resources to the options
            if options['store_account']:
                new_options = json.loads(authority.options)
                # the key returned by fields_to_partial_json is missing the key type, so we add it manually
                key_dict = key.fields_to_partial_json()
                key_dict["kty"] = "RSA"
                acme_private_key = {
                    "name": "acme_private_key",
                    "value": json.dumps(key_dict)
                }
                new_options.append(acme_private_key)

                acme_regr = {
                    "name": "acme_regr",
                    "value": json.dumps({"body": {}, "uri": registration.uri})
                }
                new_options.append(acme_regr)

                authorities_service.update_options(authority.id, options=json.dumps(new_options))

            current_app.logger.debug("Connected: {0}".format(registration.uri))

        return client, registration
예제 #16
0
class ClientNetworkTest(unittest.TestCase):
    """Tests for acme.client.ClientNetwork."""

    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import ClientNetwork

        self.net = ClientNetwork(key=KEY, alg=jose.RS256, verify_ssl=self.verify_ssl)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}

    def test_init(self):
        self.assertTrue(self.net.verify_ssl is self.verify_ssl)

    def test_wrap_in_jws(self):
        class MockClientRequestableResource(jose.JSONDeSerializable):
            # pylint: disable=missing-docstring
            resource_type = "mock"

            def __init__(self, value):
                self.value = value

            def to_partial_json(self):
                return {"foo": self.value}

            @classmethod
            def from_json(cls, value):
                pass  # pragma: no cover

        # pylint: disable=protected-access
        jws_dump = self.net._wrap_in_jws(MockClientRequestableResource("foo"), nonce=b"Tg")
        jws = acme_jws.JWS.json_loads(jws_dump)
        self.assertEqual(json.loads(jws.payload.decode()), {"foo": "foo", "resource": "mock"})
        self.assertEqual(jws.signature.combined.nonce, b"Tg")

    def test_check_response_not_ok_jobj_no_error(self):
        self.response.ok = False
        self.response.json.return_value = {}
        # pylint: disable=protected-access
        self.assertRaises(errors.ClientError, self.net._check_response, self.response)

    def test_check_response_not_ok_jobj_error(self):
        self.response.ok = False
        self.response.json.return_value = messages.Error(
            detail="foo", typ="serverInternal", title="some title"
        ).to_json()
        # pylint: disable=protected-access
        self.assertRaises(messages.Error, self.net._check_response, self.response)

    def test_check_response_not_ok_no_jobj(self):
        self.response.ok = False
        self.response.json.side_effect = ValueError
        # pylint: disable=protected-access
        self.assertRaises(errors.ClientError, self.net._check_response, self.response)

    def test_check_response_ok_no_jobj_ct_required(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, "foo"]:
            self.response.headers["Content-Type"] = response_ct
            # pylint: disable=protected-access
            self.assertRaises(
                errors.ClientError, self.net._check_response, self.response, content_type=self.net.JSON_CONTENT_TYPE
            )

    def test_check_response_ok_no_jobj_no_ct(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, "foo"]:
            self.response.headers["Content-Type"] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response, self.net._check_response(self.response))

    def test_check_response_jobj(self):
        self.response.json.return_value = {}
        for response_ct in [self.net.JSON_CONTENT_TYPE, "foo"]:
            self.response.headers["Content-Type"] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response, self.net._check_response(self.response))

    @mock.patch("acme.client.requests")
    def test_send_request(self, mock_requests):
        mock_requests.request.return_value = self.response
        # pylint: disable=protected-access
        self.assertEqual(self.response, self.net._send_request("HEAD", "url", "foo", bar="baz"))
        mock_requests.request.assert_called_once_with("HEAD", "url", "foo", verify=mock.ANY, bar="baz")

    @mock.patch("acme.client.requests")
    def test_send_request_verify_ssl(self, mock_requests):
        # pylint: disable=protected-access
        for verify in True, False:
            mock_requests.request.reset_mock()
            mock_requests.request.return_value = self.response
            self.net.verify_ssl = verify
            # pylint: disable=protected-access
            self.assertEqual(self.response, self.net._send_request("GET", "url"))
            mock_requests.request.assert_called_once_with("GET", "url", verify=verify)

    @mock.patch("acme.client.requests")
    def test_requests_error_passthrough(self, mock_requests):
        mock_requests.exceptions = requests.exceptions
        mock_requests.request.side_effect = requests.exceptions.RequestException
        # pylint: disable=protected-access
        self.assertRaises(requests.exceptions.RequestException, self.net._send_request, "GET", "uri")
예제 #17
0
class ClientNetworkTest(unittest.TestCase):
    """Tests for acme.client.ClientNetwork."""

    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import ClientNetwork
        self.net = ClientNetwork(
            key=KEY, alg=jose.RS256, verify_ssl=self.verify_ssl,
            user_agent='acme-python-test')

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}

    def test_init(self):
        self.assertTrue(self.net.verify_ssl is self.verify_ssl)

    def test_wrap_in_jws(self):
        class MockJSONDeSerializable(jose.JSONDeSerializable):
            # pylint: disable=missing-docstring
            def __init__(self, value):
                self.value = value

            def to_partial_json(self):
                return {'foo': self.value}

            @classmethod
            def from_json(cls, value):
                pass  # pragma: no cover

        # pylint: disable=protected-access
        jws_dump = self.net._wrap_in_jws(
            MockJSONDeSerializable('foo'), nonce=b'Tg')
        jws = acme_jws.JWS.json_loads(jws_dump)
        self.assertEqual(json.loads(jws.payload.decode()), {'foo': 'foo'})
        self.assertEqual(jws.signature.combined.nonce, b'Tg')

    def test_check_response_not_ok_jobj_no_error(self):
        self.response.ok = False
        self.response.json.return_value = {}
        with mock.patch('acme.client.messages.Error.from_json') as from_json:
            from_json.side_effect = jose.DeserializationError
            # pylint: disable=protected-access
            self.assertRaises(
                errors.ClientError, self.net._check_response, self.response)

    def test_check_response_not_ok_jobj_error(self):
        self.response.ok = False
        self.response.json.return_value = messages.Error(
            detail='foo', typ='serverInternal', title='some title').to_json()
        # pylint: disable=protected-access
        self.assertRaises(
            messages.Error, self.net._check_response, self.response)

    def test_check_response_not_ok_no_jobj(self):
        self.response.ok = False
        self.response.json.side_effect = ValueError
        # pylint: disable=protected-access
        self.assertRaises(
            errors.ClientError, self.net._check_response, self.response)

    def test_check_response_ok_no_jobj_ct_required(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access
            self.assertRaises(
                errors.ClientError, self.net._check_response, self.response,
                content_type=self.net.JSON_CONTENT_TYPE)

    def test_check_response_ok_no_jobj_no_ct(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(
                self.response, self.net._check_response(self.response))

    def test_check_response_jobj(self):
        self.response.json.return_value = {}
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(
                self.response, self.net._check_response(self.response))

    @mock.patch('acme.client.requests')
    def test_send_request(self, mock_requests):
        mock_requests.request.return_value = self.response
        # pylint: disable=protected-access
        self.assertEqual(self.response, self.net._send_request(
            'HEAD', 'url', 'foo', bar='baz'))
        mock_requests.request.assert_called_once_with(
            'HEAD', 'url', 'foo', verify=mock.ANY, bar='baz', headers=mock.ANY)

    @mock.patch('acme.client.requests')
    def test_send_request_verify_ssl(self, mock_requests):
        # pylint: disable=protected-access
        for verify in True, False:
            mock_requests.request.reset_mock()
            mock_requests.request.return_value = self.response
            self.net.verify_ssl = verify
            # pylint: disable=protected-access
            self.assertEqual(
                self.response, self.net._send_request('GET', 'url'))
            mock_requests.request.assert_called_once_with(
                'GET', 'url', verify=verify, headers=mock.ANY)

    @mock.patch('acme.client.requests')
    def test_send_request_user_agent(self, mock_requests):
        mock_requests.request.return_value = self.response
        # pylint: disable=protected-access
        self.net._send_request('GET', 'url', headers={'bar': 'baz'})
        mock_requests.request.assert_called_once_with(
            'GET', 'url', verify=mock.ANY,
            headers={'User-Agent': 'acme-python-test', 'bar': 'baz'})

        self.net._send_request('GET', 'url', headers={'User-Agent': 'foo2'})
        mock_requests.request.assert_called_with(
            'GET', 'url', verify=mock.ANY, headers={'User-Agent': 'foo2'})

    @mock.patch('acme.client.requests')
    def test_requests_error_passthrough(self, mock_requests):
        mock_requests.exceptions = requests.exceptions
        mock_requests.request.side_effect = requests.exceptions.RequestException
        # pylint: disable=protected-access
        self.assertRaises(requests.exceptions.RequestException,
                          self.net._send_request, 'GET', 'uri')
예제 #18
0
def wildcard_request(cn, account):
    def dns_check_ns1():
        recieved_data_dup = []
        recieved_data = []

        ns1_resolver = dns.resolver.Resolver()
        #ns1_resolver.nameservers = ['130.193.8.82','2a03:b780::1:1']
        ns1_resolver.nameservers = ['173.245.58.51']

        for data in validation_data:
            domainname = data[1]
            #challenge = data[0]
            answers = ns1_resolver.query(domainname, 'txt')
            for rdata in answers:
                recieved_data_dup.append(
                    [str(rdata).replace('"', ''), domainname])

        #Deduplication of ns records (in case of more cnames)
        for i in recieved_data_dup:
            if i not in recieved_data:
                recieved_data.append(i)

        # print sorted(recieved_data)
        # print sorted(validation_data)
        if sorted(validation_data) == sorted(recieved_data):
            return True
        else:
            return False

    #Check if CN is valid domain
    domain_regex = re.compile(
        "^([a-zA-Z0-9]([\-a-zA-Z0-9]{0,61}[a-zA-Z0-9])?\.)*([a-zA-Z0-9]([\-a-zA-Z0-9]{0,61}[a-zA-Z0-9])+\.)([a-zA-Z0-9]+([\-a-zA-Z0-9]{0,61}[a-zA-Z])+)$"
    )
    if not domain_regex.match(cn):
        print 'First argument is not valid CN'
        sys.exit(1)

    #Check if registrar exists
    if account not in os.listdir(REG_DIRECTORY):
        print "This account does not exists, register it first with new_account.py"
        sys.exit(1)

    #Load files from disk
    with open(REG_DIRECTORY + "/" + account + "/private.key",
              "rb") as key_file:
        privkey = serialization.load_pem_private_key(key_file.read(),
                                                     password=None,
                                                     backend=default_backend())
    with open(REG_DIRECTORY + "/" + account + "/reguri.txt",
              "r") as reguri_file:
        reg_uri = reguri_file.read()

    #Compose regr
    key = jose.JWKRSA(key=privkey)
    regr = messages.RegistrationResource(
        body=messages.Registration(key=key.public_key()), uri=reg_uri)

    #Init ACME
    net = ClientNetwork(key)
    directory = net.get(DIRECTORY_URL).json()

    acme = client.ClientV2(directory, net)

    #Check if registration is valid
    if acme.query_registration(regr).body.status == u'valid':
        print "Registration valid"
    else:
        print "Registration invalid"
        sys.exit(1)

    #Generate private key for certificate
    pkey = OpenSSL.crypto.PKey()
    pkey.generate_key(OpenSSL.crypto.TYPE_RSA, BITS)

    #Serialize key for output
    pkey_printable = OpenSSL.crypto.dump_privatekey(
        OpenSSL.crypto.FILETYPE_PEM, pkey, cipher=None, passphrase=None)

    #Compose request for acme
    req = crypto_util.make_csr(
        OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, pkey),
        [cn, '*.' + cn])

    #begin order
    orderr = acme.new_order(req)

    validation_data = []

    for authr in orderr.authorizations:
        for chalr in authr.body.challenges:
            if type(chalr.chall) == type(challenges.DNS01()):
                validation_data.append([
                    str(chalr.chall.validation(key)),
                    chalr.chall.validation_domain_name(cn)
                ])
    #print validation_data
    #Now, call DNS writing function to apply challenges
    dns_apply(cn, validation_data)

    #Check if DNS is valid on our server
    sys.stdin.readline()  #DEBUG: wait for manual DNS input
    limiter = 2
    while not dns_check_ns1():
        if limiter != 0:
            print "DNS records are not correct, trying again in few seconds"
            limiter = limiter - 1
            time.sleep(5)
        else:
            print "DNS are not correct even after several tries. Aborting"
            sys.exit(1)

    for authr in orderr.authorizations:
        for chalr in authr.body.challenges:
            if type(chalr.chall) == type(challenges.DNS01()):
                try:
                    acme.answer_challenge(chalr, challenges.DNS01Response())
                except:
                    print chalr.chall.encode(
                        'token'
                    ) + " already answered (challenge failed, you have to generate new one)"

    #After filling DNS and waiting for propagation, finalize order
    try:
        res = acme.poll_and_finalize(orderr)
    finally:
        dns_remove(cn)
    #logging.info(res)

    cert = x509.load_pem_x509_certificate(str(res.fullchain_pem),
                                          default_backend())

    output_data = {
        'wildcard': {
            'cn': cn,
            'private_key': str(pkey_printable),
            'certificate': str(res.fullchain_pem),
            'expiration': cert.not_valid_after.strftime(
                "%x %X"
            )  #Locale-specific time+date representation. Edit to your need
        }
    }

    print json.dumps(output_data)
예제 #19
0
class ClientNetworkTest(unittest.TestCase):
    """Tests for acme.client.ClientNetwork."""

    # pylint: disable=too-many-public-methods

    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import ClientNetwork
        self.net = ClientNetwork(key=KEY,
                                 alg=jose.RS256,
                                 verify_ssl=self.verify_ssl,
                                 user_agent='acme-python-test')

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}

    def test_init(self):
        self.assertTrue(self.net.verify_ssl is self.verify_ssl)

    def test_wrap_in_jws(self):
        # pylint: disable=protected-access
        jws_dump = self.net._wrap_in_jws(MockJSONDeSerializable('foo'),
                                         nonce=b'Tg',
                                         url="url",
                                         acme_version=1)
        jws = acme_jws.JWS.json_loads(jws_dump)
        self.assertEqual(json.loads(jws.payload.decode()), {'foo': 'foo'})
        self.assertEqual(jws.signature.combined.nonce, b'Tg')

    def test_wrap_in_jws_v2(self):
        self.net.account = {'uri': 'acct-uri'}
        # pylint: disable=protected-access
        jws_dump = self.net._wrap_in_jws(MockJSONDeSerializable('foo'),
                                         nonce=b'Tg',
                                         url="url",
                                         acme_version=2)
        jws = acme_jws.JWS.json_loads(jws_dump)
        self.assertEqual(json.loads(jws.payload.decode()), {'foo': 'foo'})
        self.assertEqual(jws.signature.combined.nonce, b'Tg')
        self.assertEqual(jws.signature.combined.kid, u'acct-uri')
        self.assertEqual(jws.signature.combined.url, u'url')

    def test_check_response_not_ok_jobj_no_error(self):
        self.response.ok = False
        self.response.json.return_value = {}
        with mock.patch('acme.client.messages.Error.from_json') as from_json:
            from_json.side_effect = jose.DeserializationError
            # pylint: disable=protected-access
            self.assertRaises(errors.ClientError, self.net._check_response,
                              self.response)

    def test_check_response_not_ok_jobj_error(self):
        self.response.ok = False
        self.response.json.return_value = messages.Error(
            detail='foo', typ='serverInternal', title='some title').to_json()
        # pylint: disable=protected-access
        self.assertRaises(messages.Error, self.net._check_response,
                          self.response)

    def test_check_response_not_ok_no_jobj(self):
        self.response.ok = False
        self.response.json.side_effect = ValueError
        # pylint: disable=protected-access
        self.assertRaises(errors.ClientError, self.net._check_response,
                          self.response)

    def test_check_response_ok_no_jobj_ct_required(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access
            self.assertRaises(errors.ClientError,
                              self.net._check_response,
                              self.response,
                              content_type=self.net.JSON_CONTENT_TYPE)

    def test_check_response_ok_no_jobj_no_ct(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response,
                             self.net._check_response(self.response))

    def test_check_response_conflict(self):
        self.response.ok = False
        self.response.status_code = 409
        # pylint: disable=protected-access
        self.assertRaises(errors.ConflictError, self.net._check_response,
                          self.response)

    def test_check_response_jobj(self):
        self.response.json.return_value = {}
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response,
                             self.net._check_response(self.response))

    def test_send_request(self):
        self.net.session = mock.MagicMock()
        self.net.session.request.return_value = self.response
        # pylint: disable=protected-access
        self.assertEqual(
            self.response,
            self.net._send_request('HEAD',
                                   'http://example.com/',
                                   'foo',
                                   bar='baz'))
        self.net.session.request.assert_called_once_with('HEAD',
                                                         'http://example.com/',
                                                         'foo',
                                                         headers=mock.ANY,
                                                         verify=mock.ANY,
                                                         timeout=mock.ANY,
                                                         bar='baz')

    @mock.patch('acme.client.logger')
    def test_send_request_get_der(self, mock_logger):
        self.net.session = mock.MagicMock()
        self.net.session.request.return_value = mock.MagicMock(
            ok=True,
            status_code=http_client.OK,
            headers={"Content-Type": "application/pkix-cert"},
            content=b"hi")
        # pylint: disable=protected-access
        self.net._send_request('HEAD',
                               'http://example.com/',
                               'foo',
                               timeout=mock.ANY,
                               bar='baz')
        mock_logger.debug.assert_called_with(
            'Received response:\nHTTP %d\n%s\n\n%s', 200,
            'Content-Type: application/pkix-cert', b'aGk=')

    def test_send_request_post(self):
        self.net.session = mock.MagicMock()
        self.net.session.request.return_value = self.response
        # pylint: disable=protected-access
        self.assertEqual(
            self.response,
            self.net._send_request('POST',
                                   'http://example.com/',
                                   'foo',
                                   data='qux',
                                   bar='baz'))
        self.net.session.request.assert_called_once_with('POST',
                                                         'http://example.com/',
                                                         'foo',
                                                         headers=mock.ANY,
                                                         verify=mock.ANY,
                                                         timeout=mock.ANY,
                                                         data='qux',
                                                         bar='baz')

    def test_send_request_verify_ssl(self):
        # pylint: disable=protected-access
        for verify in True, False:
            self.net.session = mock.MagicMock()
            self.net.session.request.return_value = self.response
            self.net.verify_ssl = verify
            # pylint: disable=protected-access
            self.assertEqual(
                self.response,
                self.net._send_request('GET', 'http://example.com/'))
            self.net.session.request.assert_called_once_with(
                'GET',
                'http://example.com/',
                verify=verify,
                timeout=mock.ANY,
                headers=mock.ANY)

    def test_send_request_user_agent(self):
        self.net.session = mock.MagicMock()
        # pylint: disable=protected-access
        self.net._send_request('GET',
                               'http://example.com/',
                               headers={'bar': 'baz'})
        self.net.session.request.assert_called_once_with(
            'GET',
            'http://example.com/',
            verify=mock.ANY,
            timeout=mock.ANY,
            headers={
                'User-Agent': 'acme-python-test',
                'bar': 'baz'
            })

        self.net._send_request('GET',
                               'http://example.com/',
                               headers={'User-Agent': 'foo2'})
        self.net.session.request.assert_called_with(
            'GET',
            'http://example.com/',
            verify=mock.ANY,
            timeout=mock.ANY,
            headers={'User-Agent': 'foo2'})

    def test_send_request_timeout(self):
        self.net.session = mock.MagicMock()
        # pylint: disable=protected-access
        self.net._send_request('GET',
                               'http://example.com/',
                               headers={'bar': 'baz'})
        self.net.session.request.assert_called_once_with(mock.ANY,
                                                         mock.ANY,
                                                         verify=mock.ANY,
                                                         headers=mock.ANY,
                                                         timeout=45)

    def test_del(self, close_exception=None):
        sess = mock.MagicMock()

        if close_exception is not None:
            sess.close.side_effect = close_exception

        self.net.session = sess
        del self.net
        sess.close.assert_called_once_with()

    def test_del_error(self):
        self.test_del(ReferenceError)

    @mock.patch('acme.client.requests')
    def test_requests_error_passthrough(self, mock_requests):
        mock_requests.exceptions = requests.exceptions
        mock_requests.request.side_effect = requests.exceptions.RequestException
        # pylint: disable=protected-access
        self.assertRaises(requests.exceptions.RequestException,
                          self.net._send_request, 'GET', 'uri')

    def test_urllib_error(self):
        # Using a connection error to test a properly formatted error message
        try:
            # pylint: disable=protected-access
            self.net._send_request('GET',
                                   "http://localhost:19123/nonexistent.txt")

        # Value Error Generated Exceptions
        except ValueError as y:
            self.assertEqual(
                "Requesting localhost/nonexistent: "
                "Connection refused", str(y))

        # Requests Library Exceptions
        except requests.exceptions.ConnectionError as z:  #pragma: no cover
            self.assertEqual(
                "('Connection aborted.', "
                "error(111, 'Connection refused'))", str(z))
예제 #20
0
class ClientNetworkWithMockedResponseTest(unittest.TestCase):
    """Tests for acme.client.ClientNetwork which mock out response."""

    # pylint: disable=too-many-instance-attributes

    def setUp(self):
        from acme.client import ClientNetwork
        self.net = ClientNetwork(key=None, alg=None)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}
        self.checked_response = mock.MagicMock()
        self.obj = mock.MagicMock()
        self.wrapped_obj = mock.MagicMock()
        self.content_type = mock.sentinel.content_type

        self.all_nonces = [
            jose.b64encode(b'Nonce'),
            jose.b64encode(b'Nonce2'),
            jose.b64encode(b'Nonce3')
        ]
        self.available_nonces = self.all_nonces[:]

        def send_request(*args, **kwargs):
            # pylint: disable=unused-argument,missing-docstring
            if self.available_nonces:
                self.response.headers = {
                    self.net.REPLAY_NONCE_HEADER:
                    self.available_nonces.pop().decode()
                }
            else:
                self.response.headers = {}
            return self.response

        # pylint: disable=protected-access
        self.net._send_request = self.send_request = mock.MagicMock(
            side_effect=send_request)
        self.net._check_response = self.check_response
        self.net._wrap_in_jws = mock.MagicMock(return_value=self.wrapped_obj)

    def check_response(self, response, content_type):
        # pylint: disable=missing-docstring
        self.assertEqual(self.response, response)
        self.assertEqual(self.content_type, content_type)
        return self.checked_response

    def test_head(self):
        self.assertEqual(
            self.response,
            self.net.head('http://example.com/', 'foo', bar='baz'))
        self.send_request.assert_called_once_with('HEAD',
                                                  'http://example.com/',
                                                  'foo',
                                                  bar='baz')

    def test_get(self):
        self.assertEqual(
            self.checked_response,
            self.net.get('http://example.com/',
                         content_type=self.content_type,
                         bar='baz'))
        self.send_request.assert_called_once_with('GET',
                                                  'http://example.com/',
                                                  bar='baz')

    def test_post_no_content_type(self):
        self.content_type = self.net.JOSE_CONTENT_TYPE
        self.assertEqual(self.checked_response, self.net.post('uri', self.obj))

    def test_post(self):
        # pylint: disable=protected-access
        self.assertEqual(
            self.checked_response,
            self.net.post('uri', self.obj, content_type=self.content_type))
        self.net._wrap_in_jws.assert_called_once_with(
            self.obj, jose.b64decode(self.all_nonces.pop()))

        self.available_nonces = []
        self.assertRaises(errors.MissingNonce,
                          self.net.post,
                          'uri',
                          self.obj,
                          content_type=self.content_type)
        self.net._wrap_in_jws.assert_called_with(
            self.obj, jose.b64decode(self.all_nonces.pop()))

    def test_post_wrong_initial_nonce(self):  # HEAD
        self.available_nonces = [b'f', jose.b64encode(b'good')]
        self.assertRaises(errors.BadNonce,
                          self.net.post,
                          'uri',
                          self.obj,
                          content_type=self.content_type)

    def test_post_wrong_post_response_nonce(self):
        self.available_nonces = [jose.b64encode(b'good'), b'f']
        self.assertRaises(errors.BadNonce,
                          self.net.post,
                          'uri',
                          self.obj,
                          content_type=self.content_type)

    def test_post_failed_retry(self):
        check_response = mock.MagicMock()
        check_response.side_effect = messages.Error.with_code('badNonce')

        # pylint: disable=protected-access
        self.net._check_response = check_response
        self.assertRaises(messages.Error,
                          self.net.post,
                          'uri',
                          self.obj,
                          content_type=self.content_type)

    def test_post_not_retried(self):
        check_response = mock.MagicMock()
        check_response.side_effect = [
            messages.Error.with_code('malformed'), self.checked_response
        ]

        # pylint: disable=protected-access
        self.net._check_response = check_response
        self.assertRaises(messages.Error,
                          self.net.post,
                          'uri',
                          self.obj,
                          content_type=self.content_type)

    def test_post_successful_retry(self):
        check_response = mock.MagicMock()
        check_response.side_effect = [
            messages.Error.with_code('badNonce'), self.checked_response
        ]

        # pylint: disable=protected-access
        self.net._check_response = check_response
        self.assertEqual(
            self.checked_response,
            self.net.post('uri', self.obj, content_type=self.content_type))

    def test_head_get_post_error_passthrough(self):
        self.send_request.side_effect = requests.exceptions.RequestException
        for method in self.net.head, self.net.get:
            self.assertRaises(requests.exceptions.RequestException, method,
                              'GET', 'uri')
        self.assertRaises(requests.exceptions.RequestException,
                          self.net.post,
                          'uri',
                          obj=self.obj)
예제 #21
0
class ClientNetworkWithMockedResponseTest(unittest.TestCase):
    """Tests for acme.client.ClientNetwork which mock out response."""
    # pylint: disable=too-many-instance-attributes

    def setUp(self):
        from acme.client import ClientNetwork
        self.net = ClientNetwork(key=None, alg=None)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}
        self.checked_response = mock.MagicMock()
        self.obj = mock.MagicMock()
        self.wrapped_obj = mock.MagicMock()
        self.content_type = mock.sentinel.content_type

        self.all_nonces = [jose.b64encode(b'Nonce'), jose.b64encode(b'Nonce2')]
        self.available_nonces = self.all_nonces[:]

        def send_request(*args, **kwargs):
            # pylint: disable=unused-argument,missing-docstring
            if self.available_nonces:
                self.response.headers = {
                    self.net.REPLAY_NONCE_HEADER:
                    self.available_nonces.pop().decode()}
            else:
                self.response.headers = {}
            return self.response

        # pylint: disable=protected-access
        self.net._send_request = self.send_request = mock.MagicMock(
            side_effect=send_request)
        self.net._check_response = self.check_response
        self.net._wrap_in_jws = mock.MagicMock(return_value=self.wrapped_obj)

    def check_response(self, response, content_type):
        # pylint: disable=missing-docstring
        self.assertEqual(self.response, response)
        self.assertEqual(self.content_type, content_type)
        return self.checked_response

    def test_head(self):
        self.assertEqual(self.response, self.net.head('url', 'foo', bar='baz'))
        self.send_request.assert_called_once_with(
            'HEAD', 'url', 'foo', bar='baz')

    def test_get(self):
        self.assertEqual(self.checked_response, self.net.get(
            'url', content_type=self.content_type, bar='baz'))
        self.send_request.assert_called_once_with('GET', 'url', bar='baz')

    def test_post(self):
        # pylint: disable=protected-access
        self.assertEqual(self.checked_response, self.net.post(
            'uri', self.obj, content_type=self.content_type))
        self.net._wrap_in_jws.assert_called_once_with(
            self.obj, jose.b64decode(self.all_nonces.pop()))

        assert not self.available_nonces
        self.assertRaises(errors.MissingNonce, self.net.post,
                          'uri', self.obj, content_type=self.content_type)
        self.net._wrap_in_jws.assert_called_with(
            self.obj, jose.b64decode(self.all_nonces.pop()))

    def test_post_wrong_initial_nonce(self):  # HEAD
        self.available_nonces = [b'f', jose.b64encode(b'good')]
        self.assertRaises(errors.BadNonce, self.net.post, 'uri',
                          self.obj, content_type=self.content_type)

    def test_post_wrong_post_response_nonce(self):
        self.available_nonces = [jose.b64encode(b'good'), b'f']
        self.assertRaises(errors.BadNonce, self.net.post, 'uri',
                          self.obj, content_type=self.content_type)

    def test_head_get_post_error_passthrough(self):
        self.send_request.side_effect = requests.exceptions.RequestException
        for method in self.net.head, self.net.get:
            self.assertRaises(
                requests.exceptions.RequestException, method, 'GET', 'uri')
        self.assertRaises(requests.exceptions.RequestException,
                          self.net.post, 'uri', obj=self.obj)
예제 #22
0
class ClientNetworkTest(unittest.TestCase):
    """Tests for acme.client.ClientNetwork."""
    def setUp(self):
        self.verify_ssl = mock.MagicMock()
        self.wrap_in_jws = mock.MagicMock(return_value=mock.sentinel.wrapped)

        from acme.client import ClientNetwork
        self.net = ClientNetwork(key=KEY,
                                 alg=jose.RS256,
                                 verify_ssl=self.verify_ssl)

        self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
        self.response.headers = {}
        self.response.links = {}

    def test_init(self):
        self.assertTrue(self.net.verify_ssl is self.verify_ssl)

    def test_wrap_in_jws(self):
        class MockJSONDeSerializable(jose.JSONDeSerializable):
            # pylint: disable=missing-docstring
            def __init__(self, value):
                self.value = value

            def to_partial_json(self):
                return {'foo': self.value}

            @classmethod
            def from_json(cls, value):
                pass  # pragma: no cover

        # pylint: disable=protected-access
        jws_dump = self.net._wrap_in_jws(MockJSONDeSerializable('foo'),
                                         nonce=b'Tg')
        jws = acme_jws.JWS.json_loads(jws_dump)
        self.assertEqual(json.loads(jws.payload.decode()), {'foo': 'foo'})
        self.assertEqual(jws.signature.combined.nonce, b'Tg')

    def test_check_response_not_ok_jobj_no_error(self):
        self.response.ok = False
        self.response.json.return_value = {}
        # pylint: disable=protected-access
        self.assertRaises(errors.ClientError, self.net._check_response,
                          self.response)

    def test_check_response_not_ok_jobj_error(self):
        self.response.ok = False
        self.response.json.return_value = messages.Error(
            detail='foo', typ='serverInternal', title='some title').to_json()
        # pylint: disable=protected-access
        self.assertRaises(messages.Error, self.net._check_response,
                          self.response)

    def test_check_response_not_ok_no_jobj(self):
        self.response.ok = False
        self.response.json.side_effect = ValueError
        # pylint: disable=protected-access
        self.assertRaises(errors.ClientError, self.net._check_response,
                          self.response)

    def test_check_response_ok_no_jobj_ct_required(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access
            self.assertRaises(errors.ClientError,
                              self.net._check_response,
                              self.response,
                              content_type=self.net.JSON_CONTENT_TYPE)

    def test_check_response_ok_no_jobj_no_ct(self):
        self.response.json.side_effect = ValueError
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response,
                             self.net._check_response(self.response))

    def test_check_response_jobj(self):
        self.response.json.return_value = {}
        for response_ct in [self.net.JSON_CONTENT_TYPE, 'foo']:
            self.response.headers['Content-Type'] = response_ct
            # pylint: disable=protected-access,no-value-for-parameter
            self.assertEqual(self.response,
                             self.net._check_response(self.response))

    @mock.patch('acme.client.requests')
    def test_send_request(self, mock_requests):
        mock_requests.request.return_value = self.response
        # pylint: disable=protected-access
        self.assertEqual(
            self.response,
            self.net._send_request('HEAD', 'url', 'foo', bar='baz'))
        mock_requests.request.assert_called_once_with('HEAD',
                                                      'url',
                                                      'foo',
                                                      verify=mock.ANY,
                                                      bar='baz')

    @mock.patch('acme.client.requests')
    def test_send_request_verify_ssl(self, mock_requests):
        # pylint: disable=protected-access
        for verify in True, False:
            mock_requests.request.reset_mock()
            mock_requests.request.return_value = self.response
            self.net.verify_ssl = verify
            # pylint: disable=protected-access
            self.assertEqual(self.response,
                             self.net._send_request('GET', 'url'))
            mock_requests.request.assert_called_once_with('GET',
                                                          'url',
                                                          verify=verify)

    @mock.patch('acme.client.requests')
    def test_requests_error_passthrough(self, mock_requests):
        mock_requests.exceptions = requests.exceptions
        mock_requests.request.side_effect = requests.exceptions.RequestException
        # pylint: disable=protected-access
        self.assertRaises(requests.exceptions.RequestException,
                          self.net._send_request, 'GET', 'uri')