diff --git a/acme/acme/client.py b/acme/acme/client.py index 690630876..1fbd9ca5b 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -94,18 +94,8 @@ class Client(object): # pylint: disable=too-many-instance-attributes return regr - def update_registration(self, regr): - """Update registration. - - :pram regr: Registration Resource. - :type regr: `.RegistrationResource` - - :returns: Updated Registration Resource. - :rtype: `.RegistrationResource` - - """ - response = self.net.post( - regr.uri, messages.UpdateRegistration(**dict(regr.body))) + def _send_recv_regr(self, regr, body): + response = self.net.post(regr.uri, body) # TODO: Boulder returns httplib.ACCEPTED #assert response.status_code == httplib.OK @@ -113,13 +103,37 @@ class Client(object): # pylint: disable=too-many-instance-attributes # TODO: Boulder does not set Location or Link on update # (c.f. acme-spec #94) - updated_regr = self._regr_from_response( + return self._regr_from_response( response, uri=regr.uri, new_authzr_uri=regr.new_authzr_uri, terms_of_service=regr.terms_of_service) + + def update_registration(self, regr, update=None): + """Update registration. + + :param messages.RegistrationResource regr: Registration Resource. + :param messages.Registration update: Updated body of the + resource. If not provided, body will be taken from `regr`. + + :returns: Updated Registration Resource. + :rtype: `.RegistrationResource` + + """ + update = regr.body if update is None else update + updated_regr = self._send_recv_regr( + regr, body=messages.UpdateRegistration(**dict(update))) if updated_regr != regr: raise errors.UnexpectedUpdate(regr) return updated_regr + def query_registration(self, regr): + """Query server about registration. + + :param messages.RegistrationResource: Existing Registration + Resource. + + """ + return self._send_recv_regr(regr, messages.UpdateRegistration()) + def agree_to_tos(self, regr): """Agree to the terms-of-service. @@ -275,8 +289,7 @@ class Client(object): # pylint: disable=too-many-instance-attributes logger.debug("Requesting issuance...") # TODO: assert len(authzrs) == number of SANs - req = messages.CertificateRequest( - csr=csr, authorizations=tuple(authzr.uri for authzr in authzrs)) + req = messages.CertificateRequest(csr=csr) content_type = self.DER_CONTENT_TYPE # TODO: add 'cert_type 'argument response = self.net.post( diff --git a/acme/acme/client_test.py b/acme/acme/client_test.py index 28226fe5a..dcc0832e3 100644 --- a/acme/acme/client_test.py +++ b/acme/acme/client_test.py @@ -111,6 +111,10 @@ class ClientTest(unittest.TestCase): self.assertRaises( errors.UnexpectedUpdate, self.client.update_registration, self.regr) + def test_query_registration(self): + self.response.json.return_value = self.regr.body.to_json() + self.assertEqual(self.regr, self.client.query_registration(self.regr)) + def test_agree_to_tos(self): self.client.update_registration = mock.Mock() self.client.agree_to_tos(self.regr) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 0855ae008..970cf4e6e 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -157,6 +157,10 @@ class Registration(ResourceBody): :ivar tuple contact: Contact information following ACME spec, `tuple` of `unicode`. :ivar unicode agreement: + :ivar unicode authorizations: URI where + `messages.Registration.Authorizations` can be found. + :ivar unicode certificates: URI where + `messages.Registration.Certificates` can be found. """ # on new-reg key server ignores 'key' and populates it based on @@ -164,6 +168,24 @@ class Registration(ResourceBody): key = jose.Field('key', omitempty=True, decoder=jose.JWK.from_json) contact = jose.Field('contact', omitempty=True, default=()) agreement = jose.Field('agreement', omitempty=True) + authorizations = jose.Field('authorizations', omitempty=True) + certificates = jose.Field('certificates', omitempty=True) + + class Authorizations(jose.JSONObjectWithFields): + """Authorizations granted to Account in the process of registration. + + :ivar tuple authorizations: URIs to Authorization Resources. + + """ + authorizations = jose.Field('authorizations') + + class Certificates(jose.JSONObjectWithFields): + """Certificates granted to Account in the process of registration. + + :ivar tuple certificates: URIs to Certificate Resources. + + """ + certificates = jose.Field('certificates') phone_prefix = 'tel:' email_prefix = 'mailto:' @@ -327,13 +349,11 @@ class CertificateRequest(jose.JSONObjectWithFields): :ivar acme.jose.util.ComparableX509 csr: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509` - :ivar tuple authorizations: `tuple` of URIs (`str`) """ resource_type = 'new-cert' resource = fields.Resource(resource_type) csr = jose.Field('csr', decoder=jose.decode_csr, encoder=jose.encode_csr) - authorizations = jose.Field('authorizations', decoder=tuple) class CertificateResource(ResourceWithURI): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index 051db9ae9..481c2e2a3 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -105,7 +105,8 @@ class RegistrationTest(unittest.TestCase): from acme.messages import Registration self.reg = Registration(key=key, contact=contact, agreement=agreement) - self.reg_none = Registration() + self.reg_none = Registration(authorizations='uri/authorizations', + certificates='uri/certificates') self.jobj_to = { 'contact': contact, @@ -141,6 +142,17 @@ class RegistrationTest(unittest.TestCase): hash(Registration.from_json(self.jobj_from)) +class UpdateRegistrationTest(unittest.TestCase): + """Tests for acme.messages.UpdateRegistration.""" + + def test_empty(self): + from acme.messages import UpdateRegistration + jstring = '{"resource": "reg"}' + self.assertEqual(jstring, UpdateRegistration().json_dumps()) + self.assertEqual( + UpdateRegistration(), UpdateRegistration.json_loads(jstring)) + + class RegistrationResourceTest(unittest.TestCase): """Tests for acme.messages.RegistrationResource.""" @@ -280,7 +292,7 @@ class CertificateRequestTest(unittest.TestCase): def setUp(self): from acme.messages import CertificateRequest - self.req = CertificateRequest(csr=CSR, authorizations=('foo',)) + self.req = CertificateRequest(csr=CSR) def test_json_de_serializable(self): self.assertTrue(isinstance(self.req, jose.JSONDeSerializable))