Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/acme
diff options
context:
space:
mode:
authorDaniel Drexler <aeturnum@gmail.com>2020-08-27 01:22:51 +0300
committerGitHub <noreply@github.com>2020-08-27 01:22:51 +0300
commitae7b4a1755655b0bf87614ff6eb75531e6c454b0 (patch)
treee3db5fc065993bf150b5df9b4a66c665f17cac2b /acme
parentf66a592e37d9426edcd21a7db046043efa84536e (diff)
Support Register Unsafely in Update (#8212)
* Allow user to remove email using update command Fixes #3162. Slight change to control flow to replace current email addresses with an empty list. Also add appropriate result message when an email is removed. * Update ACME to allow update to remove fields - New field type "UnFalseyField" that treats all non-None fields as non-empty - Contact changed to new field type to allow sending of empty contact field - Certbot update adjusted to use tuple instead of None when empty - Test updated to check more logic - Unrelated type hint added to keep pycharm gods happy * Moved some mocks into decorators * Restore default to `contact` but do not serialize - Add `to_partial_json` and `fields_to_partial_json` to Registration - Store private variable noting if the value of the `contact` field was provided by the user. - Change message when updating without email to reflect removal of all contact info. - Add note in changelog that `update_account` with the `--register-unsafely-without-email` flag will remove contact from an account. * Reverse logic for field handling on serialization Now forcably add contact when serilizing, but go back to base `jose` field type. * Responding to Review - change out of date name - update several comments - update `from_data` function of `Registration` - Update test to remove superfluous mock * Responding to review - Change comments to make from_data more clear - Remove code worried about None (omitempty has got my back) - Update test to be more reliable - Add typing import with comment to avoid pylint bug
Diffstat (limited to 'acme')
-rw-r--r--acme/acme/messages.py56
-rw-r--r--acme/tests/messages_test.py13
2 files changed, 67 insertions, 2 deletions
diff --git a/acme/acme/messages.py b/acme/acme/messages.py
index 030e62d6c..8c9bf9701 100644
--- a/acme/acme/messages.py
+++ b/acme/acme/messages.py
@@ -315,6 +315,9 @@ class Registration(ResourceBody):
# on new-reg key server ignores 'key' and populates it based on
# JWS.signature.combined.jwk
key = jose.Field('key', omitempty=True, decoder=jose.JWK.from_json)
+ # Contact field implements special behavior to allow messages that clear existing
+ # contacts while not expecting the `contact` field when loading from json.
+ # This is implemented in the constructor and *_json methods.
contact = jose.Field('contact', omitempty=True, default=())
agreement = jose.Field('agreement', omitempty=True)
status = jose.Field('status', omitempty=True)
@@ -327,24 +330,73 @@ class Registration(ResourceBody):
@classmethod
def from_data(cls, phone=None, email=None, external_account_binding=None, **kwargs):
- """Create registration resource from contact details."""
+ """
+ Create registration resource from contact details.
+
+ The `contact` keyword being passed to a Registration object is meaningful, so
+ this function represents empty iterables in its kwargs by passing on an empty
+ `tuple`.
+ """
+
+ # Note if `contact` was in kwargs.
+ contact_provided = 'contact' in kwargs
+
+ # Pop `contact` from kwargs and add formatted email or phone numbers
details = list(kwargs.pop('contact', ()))
if phone is not None:
details.append(cls.phone_prefix + phone)
if email is not None:
details.extend([cls.email_prefix + mail for mail in email.split(',')])
- kwargs['contact'] = tuple(details)
+
+ # Insert formatted contact information back into kwargs
+ # or insert an empty tuple if `contact` provided.
+ if details or contact_provided:
+ kwargs['contact'] = tuple(details)
if external_account_binding:
kwargs['external_account_binding'] = external_account_binding
return cls(**kwargs)
+ def __init__(self, **kwargs):
+ """Note if the user provides a value for the `contact` member."""
+ if 'contact' in kwargs:
+ # Avoid the __setattr__ used by jose.TypedJSONObjectWithFields
+ object.__setattr__(self, '_add_contact', True)
+ super(Registration, self).__init__(**kwargs)
+
def _filter_contact(self, prefix):
return tuple(
detail[len(prefix):] for detail in self.contact # pylint: disable=not-an-iterable
if detail.startswith(prefix))
+ def _add_contact_if_appropriate(self, jobj):
+ """
+ The `contact` member of Registration objects should not be required when
+ de-serializing (as it would be if the Fields' `omitempty` flag were `False`), but
+ it should be included in serializations if it was provided.
+
+ :param jobj: Dictionary containing this Registrations' data
+ :type jobj: dict
+
+ :returns: Dictionary containing Registrations data to transmit to the server
+ :rtype: dict
+ """
+ if getattr(self, '_add_contact', False):
+ jobj['contact'] = self.encode('contact')
+
+ return jobj
+
+ def to_partial_json(self):
+ """Modify josepy.JSONDeserializable.to_partial_json()"""
+ jobj = super(Registration, self).to_partial_json()
+ return self._add_contact_if_appropriate(jobj)
+
+ def fields_to_partial_json(self):
+ """Modify josepy.JSONObjectWithFields.fields_to_partial_json()"""
+ jobj = super(Registration, self).fields_to_partial_json()
+ return self._add_contact_if_appropriate(jobj)
+
@property
def phones(self):
"""All phones found in the ``contact`` field."""
diff --git a/acme/tests/messages_test.py b/acme/tests/messages_test.py
index 890a5f413..3458105b2 100644
--- a/acme/tests/messages_test.py
+++ b/acme/tests/messages_test.py
@@ -254,6 +254,19 @@ class RegistrationTest(unittest.TestCase):
from acme.messages import Registration
hash(Registration.from_json(self.jobj_from))
+ def test_default_not_transmitted(self):
+ from acme.messages import NewRegistration
+ empty_new_reg = NewRegistration()
+ new_reg_with_contact = NewRegistration(contact=())
+
+ self.assertEqual(empty_new_reg.contact, ())
+ self.assertEqual(new_reg_with_contact.contact, ())
+
+ self.assertTrue('contact' not in empty_new_reg.to_partial_json())
+ self.assertTrue('contact' not in empty_new_reg.fields_to_partial_json())
+ self.assertTrue('contact' in new_reg_with_contact.to_partial_json())
+ self.assertTrue('contact' in new_reg_with_contact.fields_to_partial_json())
+
class UpdateRegistrationTest(unittest.TestCase):
"""Tests for acme.messages.UpdateRegistration."""