#4242 Support multi emails register (#5994)

This change will allow registering/updating account with multi emails.
Detail is enclosed in #4242

* support multi emails register

* add more test cases

* update test to unregister before register

* update create path to support multi emaill

* refactor payload updating

* fix typo

* move command line doc to another place

* revert the change for updating account registration info, added unit test

* rearrange text for consistency
This commit is contained in:
Quang Vu 2018-05-22 15:32:44 -07:00 committed by Brad Warren
parent c9a206ca89
commit cfd4b8f363
7 changed files with 39 additions and 24 deletions

View file

@ -285,7 +285,7 @@ class Registration(ResourceBody):
if phone is not None:
details.append(cls.phone_prefix + phone)
if email is not None:
details.append(cls.email_prefix + email)
details.extend([cls.email_prefix + mail for mail in email.split(',')])
kwargs['contact'] = tuple(details)
return cls(**kwargs)

View file

@ -430,7 +430,7 @@ VERB_HELP = [
}),
("enhance", {
"short": "Add security enhancements to your existing configuration",
"opts": ("Helps to harden the TLS configration by adding security enhancements "
"opts": ("Helps to harden the TLS configuration by adding security enhancements "
"to already existing configuration."),
"usage": "\n\n certbot enhance [options]\n\n"
}),

View file

@ -185,8 +185,9 @@ def perform_registration(acme, config, tos_cb):
Actually register new account, trying repeatedly if there are email
problems
:param .IConfig config: Client configuration.
:param acme.client.Client client: ACME client object.
:param .IConfig config: Client configuration.
:param Callable tos_cb: a callback to handle Term of Service agreement.
:returns: Registration Resource.
:rtype: `acme.messages.RegistrationResource`

View file

@ -201,7 +201,9 @@ class IConfig(zope.interface.Interface):
"""
server = zope.interface.Attribute("ACME Directory Resource URI.")
email = zope.interface.Attribute(
"Email used for registration and recovery contact. (default: Ask)")
"Email used for registration and recovery contact. Use comma to "
"register multiple emails, ex: u1@example.com,u2@example.com. "
"(default: Ask).")
rsa_key_size = zope.interface.Attribute("Size of the RSA key.")
must_staple = zope.interface.Attribute(
"Adds the OCSP Must Staple extension to the certificate. "

View file

@ -487,6 +487,21 @@ def _determine_account(config):
:raises errors.Error: If unable to register an account with ACME server
"""
def _tos_cb(terms_of_service):
if config.tos:
return True
msg = ("Please read the Terms of Service at {0}. You "
"must agree in order to register with the ACME "
"server at {1}".format(
terms_of_service, config.server))
obj = zope.component.getUtility(interfaces.IDisplay)
result = obj.yesno(msg, "Agree", "Cancel",
cli_flag="--agree-tos", force_interactive=True)
if not result:
raise errors.Error(
"Registration cannot proceed without accepting "
"Terms of Service.")
account_storage = account.AccountFileStorage(config)
acme = None
@ -501,21 +516,6 @@ def _determine_account(config):
else: # no account registered yet
if config.email is None and not config.register_unsafely_without_email:
config.email = display_ops.get_email()
def _tos_cb(terms_of_service):
if config.tos:
return True
msg = ("Please read the Terms of Service at {0}. You "
"must agree in order to register with the ACME "
"server at {1}".format(
terms_of_service, config.server))
obj = zope.component.getUtility(interfaces.IDisplay)
result = obj.yesno(msg, "Agree", "Cancel",
cli_flag="--agree-tos", force_interactive=True)
if not result:
raise errors.Error(
"Registration cannot proceed without accepting "
"Terms of Service.")
try:
acc, acme = client.register(
config, account_storage, tos_cb=_tos_cb)
@ -735,8 +735,9 @@ def register(config, unused_plugins):
acc, acme = _determine_account(config)
cb_client = client.Client(config, acc, None, None, acme=acme)
# We rely on an exception to interrupt this process if it didn't work.
acc_contacts = ['mailto:' + email for email in config.email.split(',')]
acc.regr = cb_client.acme.update_registration(acc.regr.update(
body=acc.regr.body.update(contact=('mailto:' + config.email,))))
body=acc.regr.body.update(contact=acc_contacts)))
account_storage.save_regr(acc, cb_client.acme)
eff.handle_subscription(config)
add_msg("Your e-mail address was updated to {0}.".format(config.email))

View file

@ -1435,7 +1435,9 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
mocked_storage = mock.MagicMock()
mocked_account.AccountFileStorage.return_value = mocked_storage
mocked_storage.find_all.return_value = ["an account"]
mocked_det.return_value = (mock.MagicMock(), "foo")
mock_acc = mock.MagicMock()
mock_regr = mock_acc.regr
mocked_det.return_value = (mock_acc, "foo")
cb_client = mock.MagicMock()
mocked_client.Client.return_value = cb_client
x = self._call_no_clientmock(
@ -1445,8 +1447,10 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
self.assertTrue(x[0] is None)
# and we got supposedly did update the registration from
# the server
self.assertTrue(
cb_client.acme.update_registration.called)
reg_arg = cb_client.acme.update_registration.call_args[0][0]
# Test the return value of .update() was used because
# the regr is immutable.
self.assertEqual(reg_arg, mock_regr.update())
# and we saved the updated registration on disk
self.assertTrue(mocked_storage.save_regr.called)
self.assertTrue(

View file

@ -191,7 +191,14 @@ for dir in $renewal_hooks_dirs; do
exit 1
fi
done
common register --update-registration --email example@example.org
common unregister
common register --email ex1@domain.org,ex2@domain.org
common register --update-registration --email ex1@domain.org
common register --update-registration --email ex1@domain.org,ex2@domain.org
common plugins --init --prepare | grep webroot