Compare commits
12 Commits
test-apach
...
test-faste
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
714f3d2938 | ||
|
|
ba8adfc22a | ||
|
|
9b9138eed6 | ||
|
|
e59408ca25 | ||
|
|
4e7b930d4b | ||
|
|
ffd64adf82 | ||
|
|
7b92d6dc95 | ||
|
|
6a8d78c5a3 | ||
|
|
04cc1f4fa7 | ||
|
|
63f8dff67f | ||
|
|
1cf5b9f43e | ||
|
|
a1d4f47ccc |
24
.travis.yml
24
.travis.yml
@@ -20,14 +20,14 @@ matrix:
|
||||
- python: "2.7"
|
||||
env: TOXENV=lint
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27-oldest BOULDER_INTEGRATION=1
|
||||
env: TOXENV=py27-oldest
|
||||
sudo: required
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
services: docker
|
||||
- python: "2.6"
|
||||
env: TOXENV=py26 BOULDER_INTEGRATION=1
|
||||
env: TOXENV=py26
|
||||
sudo: required
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
@@ -40,6 +40,12 @@ matrix:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
services: docker
|
||||
- language: generic
|
||||
env: TOXENV=py27 OS=OSX # $OS is ignored, makes job list clearer
|
||||
os: osx
|
||||
- language: generic
|
||||
env: TOXENV=py36 OS=OSX
|
||||
os: osx
|
||||
- sudo: required
|
||||
env: TOXENV=apache_compat
|
||||
services: docker
|
||||
@@ -79,28 +85,28 @@ matrix:
|
||||
env: TOXENV=apacheconftest
|
||||
sudo: required
|
||||
- python: "3.3"
|
||||
env: TOXENV=py33 BOULDER_INTEGRATION=1
|
||||
env: TOXENV=py33
|
||||
sudo: required
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
services: docker
|
||||
- python: "3.4"
|
||||
env: TOXENV=py34 BOULDER_INTEGRATION=1
|
||||
env: TOXENV=py34
|
||||
sudo: required
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
services: docker
|
||||
- python: "3.5"
|
||||
env: TOXENV=py35 BOULDER_INTEGRATION=1
|
||||
env: TOXENV=py35
|
||||
sudo: required
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
- ps aux | grep mysql
|
||||
services: docker
|
||||
- python: "3.6"
|
||||
env: TOXENV=py36 BOULDER_INTEGRATION=1
|
||||
env: TOXENV=py36
|
||||
sudo: required
|
||||
after_failure:
|
||||
- sudo cat /var/log/mysql/error.log
|
||||
@@ -108,12 +114,6 @@ matrix:
|
||||
services: docker
|
||||
- python: "2.7"
|
||||
env: TOXENV=nginxroundtrip
|
||||
- language: generic
|
||||
env: TOXENV=py27
|
||||
os: osx
|
||||
- language: generic
|
||||
env: TOXENV=py36
|
||||
os: osx
|
||||
|
||||
|
||||
# Only build pushes to the master branch, PRs, and branches beginning with
|
||||
|
||||
@@ -48,6 +48,8 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
:ivar messages.Directory directory:
|
||||
:ivar key: `.JWK` (private)
|
||||
:ivar account: `.Account` (private)
|
||||
:ivar acme_version: `int` (private)
|
||||
:ivar alg: `.JWASignature`
|
||||
:ivar bool verify_ssl: Verify SSL certificates?
|
||||
:ivar .ClientNetwork net: Client network. Useful for testing. If not
|
||||
@@ -56,8 +58,9 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, directory, key, alg=jose.RS256, verify_ssl=True,
|
||||
net=None):
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, directory, key, account=None, acme_version=1, alg=jose.RS256,
|
||||
verify_ssl=True, net=None):
|
||||
"""Initialize.
|
||||
|
||||
:param directory: Directory Resource (`.messages.Directory`) or
|
||||
@@ -65,7 +68,10 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
"""
|
||||
self.key = key
|
||||
self.net = ClientNetwork(key, alg, verify_ssl) if net is None else net
|
||||
self.account = account
|
||||
self.acme_version = acme_version
|
||||
self.net = ClientNetwork(key, account=account, acme_version=acme_version,
|
||||
alg=alg, verify_ssl=verify_ssl) if net is None else net
|
||||
|
||||
if isinstance(directory, six.string_types):
|
||||
self.directory = messages.Directory.from_json(
|
||||
@@ -93,9 +99,12 @@ class Client(object): # pylint: disable=too-many-instance-attributes
|
||||
|
||||
"""
|
||||
new_reg = messages.NewRegistration() if new_reg is None else new_reg
|
||||
assert isinstance(new_reg, messages.NewRegistration)
|
||||
if self.acme_version == 2:
|
||||
url = self.directory.new_account
|
||||
else:
|
||||
url = self.directory.new_reg
|
||||
|
||||
response = self.net.post(self.directory[new_reg], new_reg)
|
||||
response = self.net.post(url, new_reg)
|
||||
# TODO: handle errors
|
||||
assert response.status_code == http_client.CREATED
|
||||
|
||||
@@ -509,15 +518,19 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
JSON_ERROR_CONTENT_TYPE = 'application/problem+json'
|
||||
REPLAY_NONCE_HEADER = 'Replay-Nonce'
|
||||
|
||||
def __init__(self, key, alg=jose.RS256, verify_ssl=True,
|
||||
user_agent='acme-python', timeout=DEFAULT_NETWORK_TIMEOUT):
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, key, account=None, alg=jose.RS256, verify_ssl=True,
|
||||
user_agent='acme-python', timeout=DEFAULT_NETWORK_TIMEOUT,
|
||||
acme_version=2):
|
||||
self.key = key
|
||||
self.account = account
|
||||
self.alg = alg
|
||||
self.verify_ssl = verify_ssl
|
||||
self._nonces = set()
|
||||
self.user_agent = user_agent
|
||||
self.session = requests.Session()
|
||||
self._default_timeout = timeout
|
||||
self.acme_version = acme_version
|
||||
|
||||
def __del__(self):
|
||||
# Try to close the session, but don't show exceptions to the
|
||||
@@ -527,7 +540,7 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
|
||||
def _wrap_in_jws(self, obj, nonce):
|
||||
def _wrap_in_jws(self, obj, nonce, url):
|
||||
"""Wrap `JSONDeSerializable` object in JWS.
|
||||
|
||||
.. todo:: Implement ``acmePath``.
|
||||
@@ -539,9 +552,18 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
"""
|
||||
jobj = obj.json_dumps(indent=2).encode()
|
||||
logger.debug('JWS payload:\n%s', jobj)
|
||||
return jws.JWS.sign(
|
||||
payload=jobj, key=self.key, alg=self.alg,
|
||||
nonce=nonce).json_dumps(indent=2)
|
||||
kwargs = {
|
||||
"alg": self.alg,
|
||||
"nonce": nonce
|
||||
}
|
||||
if self.acme_version is 2:
|
||||
# new ACME spec
|
||||
kwargs["url"] = url
|
||||
if self.account is not None:
|
||||
kwargs["kid"] = self.account["uri"]
|
||||
kwargs["key"] = self.key
|
||||
# pylint: disable=star-args
|
||||
return jws.JWS.sign(jobj, **kwargs).json_dumps(indent=2)
|
||||
|
||||
@classmethod
|
||||
def _check_response(cls, response, content_type=None):
|
||||
@@ -715,7 +737,7 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
raise
|
||||
|
||||
def _post_once(self, url, obj, content_type=JOSE_CONTENT_TYPE, **kwargs):
|
||||
data = self._wrap_in_jws(obj, self._get_nonce(url))
|
||||
data = self._wrap_in_jws(obj, self._get_nonce(url), url)
|
||||
kwargs.setdefault('headers', {'Content-Type': content_type})
|
||||
response = self._send_request('POST', url, data=data, **kwargs)
|
||||
self._add_nonce(response)
|
||||
|
||||
@@ -104,6 +104,23 @@ class ClientTest(unittest.TestCase):
|
||||
self.assertEqual(self.regr, self.client.register(self.new_reg))
|
||||
# TODO: test POST call arguments
|
||||
|
||||
def test_register_v2(self):
|
||||
directory = messages.Directory({
|
||||
"new-account": 'https://www.letsencrypt-demo.org/acme/new-account',
|
||||
})
|
||||
from acme.client import Client
|
||||
client = Client(directory=directory, key=KEY, acme_version=2, net=self.net)
|
||||
self.response.status_code = http_client.CREATED
|
||||
self.response.json.return_value = self.regr.body.to_json()
|
||||
self.response.headers['Location'] = self.regr.uri
|
||||
|
||||
self.regr = messages.RegistrationResource(
|
||||
body=messages.Registration(
|
||||
contact=self.contact, key=KEY.public_key()),
|
||||
uri='https://www.letsencrypt-demo.org/acme/reg/1')
|
||||
|
||||
self.assertEqual(self.regr, client.register(self.regr))
|
||||
|
||||
def test_update_registration(self):
|
||||
# "Instance of 'Field' has no to_json/update member" bug:
|
||||
# pylint: disable=no-member
|
||||
@@ -467,11 +484,21 @@ class ClientNetworkTest(unittest.TestCase):
|
||||
|
||||
# pylint: disable=protected-access
|
||||
jws_dump = self.net._wrap_in_jws(
|
||||
MockJSONDeSerializable('foo'), nonce=b'Tg')
|
||||
MockJSONDeSerializable('foo'), nonce=b'Tg', url="url")
|
||||
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.net.account = {'uri': 'acct-uri'}
|
||||
jws_dump = self.net._wrap_in_jws(
|
||||
MockJSONDeSerializable('foo'), nonce=b'Tg', url="url")
|
||||
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 = {}
|
||||
@@ -701,13 +728,13 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
|
||||
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.obj, jose.b64decode(self.all_nonces.pop()), "uri")
|
||||
|
||||
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()))
|
||||
self.obj, jose.b64decode(self.all_nonces.pop()), "uri")
|
||||
|
||||
def test_post_wrong_initial_nonce(self): # HEAD
|
||||
self.available_nonces = [b'f', jose.b64encode(b'good')]
|
||||
|
||||
@@ -250,6 +250,7 @@ class Registration(ResourceBody):
|
||||
contact = jose.Field('contact', omitempty=True, default=())
|
||||
agreement = jose.Field('agreement', omitempty=True)
|
||||
status = jose.Field('status', omitempty=True)
|
||||
terms_of_service_agreed = jose.Field('terms-of-service-agreed', omitempty=True)
|
||||
|
||||
phone_prefix = 'tel:'
|
||||
email_prefix = 'mailto:'
|
||||
|
||||
Reference in New Issue
Block a user