mirror of
https://github.com/certbot/certbot.git
synced 2026-06-07 15:52:08 -04:00
Merge branch 'boulder' of https://github.com/kuba/lets-encrypt-preview into kuba-boulder
This commit is contained in:
commit
84fd90841c
8 changed files with 49 additions and 30 deletions
|
|
@ -18,7 +18,7 @@ class Error(jose.JSONObjectWithFields, Exception):
|
|||
'badCSR': 'The CSR is unacceptable (e.g., due to a short key)',
|
||||
}
|
||||
|
||||
# TODO: Boulder omits 'type' and 'instance', spec requires
|
||||
# TODO: Boulder omits 'type' and 'instance', spec requires, boulder#128
|
||||
typ = jose.Field('type', omitempty=True)
|
||||
title = jose.Field('title', omitempty=True)
|
||||
detail = jose.Field('detail')
|
||||
|
|
@ -164,8 +164,8 @@ class ChallengeBody(ResourceBody):
|
|||
|
||||
.. todo::
|
||||
Confusingly, this has a similar name to `.challenges.Challenge`,
|
||||
as well as `.achallenges.AnnotateChallenge`. Please use names
|
||||
such as ``challb`` to distinguish instanced of this class from
|
||||
as well as `.achallenges.AnnotatedChallenge`. Please use names
|
||||
such as ``challb`` to distinguish instances of this class from
|
||||
``achall``.
|
||||
|
||||
:ivar letsencrypt.acme.challenges.Challenge: Wrapped challenge.
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class Account(object):
|
|||
# Just make sure we don't get pwned
|
||||
# Make sure that it also doesn't start with a period or have two consecutive
|
||||
# periods <- this needs to be done in addition to the regex
|
||||
EMAIL_REGEX = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+$"
|
||||
EMAIL_REGEX = re.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+$")
|
||||
|
||||
def __init__(self, config, key, email=None, phone=None, regr=None):
|
||||
le_util.make_or_verify_dir(
|
||||
|
|
@ -55,6 +55,8 @@ class Account(object):
|
|||
"""URI link for new registrations."""
|
||||
if self.regr is not None:
|
||||
return self.regr.uri
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def new_authzr_uri(self): # pylint: disable=missing-docstring
|
||||
|
|
@ -65,16 +67,22 @@ class Account(object):
|
|||
# Default: spec says they "may" provide the header
|
||||
# ugh.. acme-spec #93
|
||||
return "https://%s/acme/new-authz" % self.config.server
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def terms_of_service(self): # pylint: disable=missing-docstring
|
||||
if self.regr is not None:
|
||||
return self.regr.terms_of_service
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def recovery_token(self): # pylint: disable=missing-docstring
|
||||
if self.regr is not None and self.regr.body is not None:
|
||||
return self.regr.body.recovery_token
|
||||
else:
|
||||
return None
|
||||
|
||||
def save(self):
|
||||
"""Save account to disk."""
|
||||
|
|
@ -112,7 +120,6 @@ class Account(object):
|
|||
@classmethod
|
||||
def from_existing_account(cls, config, email=None):
|
||||
"""Populate an account from an existing email."""
|
||||
|
||||
config_fp = os.path.join(
|
||||
config.accounts_dir, cls._get_config_filename(email))
|
||||
return cls._from_config_fp(config, config_fp)
|
||||
|
|
@ -185,13 +192,14 @@ class Account(object):
|
|||
"Enter email address (optional, press Enter to skip)")
|
||||
|
||||
if code == display_util.OK:
|
||||
if email == "" or cls.safe_email(email):
|
||||
email = email if email != "" else None
|
||||
if not email or cls.safe_email(email):
|
||||
email = email if email else None
|
||||
|
||||
le_util.make_or_verify_dir(
|
||||
config.account_keys_dir, 0o700, os.geteuid())
|
||||
key = crypto_util.init_save_key(
|
||||
config.rsa_key_size, config.account_keys_dir, email)
|
||||
config.rsa_key_size, config.account_keys_dir,
|
||||
cls._get_config_filename(email))
|
||||
return cls(config, key, email)
|
||||
else:
|
||||
return None
|
||||
|
|
@ -199,7 +207,8 @@ class Account(object):
|
|||
@classmethod
|
||||
def safe_email(cls, email):
|
||||
"""Scrub email address before using it."""
|
||||
if re.match(cls.EMAIL_REGEX, email):
|
||||
return bool(not email.startswith(".") and ".." not in email)
|
||||
logging.warn("Invalid email address.")
|
||||
return False
|
||||
if cls.EMAIL_REGEX.match(email):
|
||||
return not email.startswith(".") and ".." not in email
|
||||
else:
|
||||
logging.warn("Invalid email address.")
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -165,8 +165,8 @@ class Client(object):
|
|||
cert_file.write(certr.body.as_pem())
|
||||
finally:
|
||||
cert_file.close()
|
||||
logging.info(
|
||||
"Server issued certificate; certificate written to %s", cert_path)
|
||||
logging.info("Server issued certificate; certificate written to %s",
|
||||
act_cert_path)
|
||||
|
||||
if certr.cert_chain_uri:
|
||||
# TODO: Except
|
||||
|
|
|
|||
|
|
@ -201,13 +201,10 @@ class Network(object):
|
|||
"""
|
||||
details = (
|
||||
"mailto:" + account.email if account.email is not None else None,
|
||||
"tel:" + account.phone if account.phone is not None else None
|
||||
"tel:" + account.phone if account.phone is not None else None,
|
||||
)
|
||||
|
||||
contact_tuple = tuple(det for det in details if det is not None)
|
||||
|
||||
account.regr = self.register(contact=contact_tuple)
|
||||
|
||||
account.regr = self.register(contact=tuple(
|
||||
det for det in details if det is not None))
|
||||
return account
|
||||
|
||||
def update_registration(self, regr):
|
||||
|
|
@ -323,7 +320,7 @@ class Network(object):
|
|||
except KeyError:
|
||||
# TODO: Right now Boulder responds with the authorization resource
|
||||
# instead of a challenge resource... this can be uncommented
|
||||
# once the error is fixed.
|
||||
# once the error is fixed (boulder#130).
|
||||
return None
|
||||
# raise errors.NetworkError('"up" Link header missing')
|
||||
challr = messages2.ChallengeResource(
|
||||
|
|
@ -376,7 +373,6 @@ class Network(object):
|
|||
updated_authzr = self._authzr_from_response(
|
||||
response, authzr.body.identifier, authzr.uri, authzr.new_cert_uri)
|
||||
# TODO: check and raise UnexpectedUpdate
|
||||
|
||||
return updated_authzr, response
|
||||
|
||||
def request_issuance(self, csr, authzrs):
|
||||
|
|
@ -534,6 +530,8 @@ class Network(object):
|
|||
"""
|
||||
if certr.cert_chain_uri is not None:
|
||||
return self._get_cert(certr.cert_chain_uri)[1]
|
||||
else:
|
||||
return None
|
||||
|
||||
def revoke(self, certr, when=messages2.Revocation.NOW):
|
||||
"""Revoke certificate.
|
||||
|
|
|
|||
|
|
@ -64,14 +64,26 @@ class AccountTest(unittest.TestCase):
|
|||
zope.component.provideUtility(displayer)
|
||||
|
||||
mock_util().input.return_value = (display_util.OK, self.email)
|
||||
|
||||
mock_key.return_value = self.key
|
||||
acc = account.Account.from_prompts(self.config)
|
||||
|
||||
acc = account.Account.from_prompts(self.config)
|
||||
self.assertEqual(acc.email, self.email)
|
||||
self.assertEqual(acc.key, self.key)
|
||||
self.assertEqual(acc.config, self.config)
|
||||
|
||||
@mock.patch("letsencrypt.client.account.zope.component.getUtility")
|
||||
@mock.patch("letsencrypt.client.account.crypto_util.init_save_key")
|
||||
def test_prompts_empty_email(self, mock_key, mock_util):
|
||||
displayer = display_util.FileDisplay(sys.stdout)
|
||||
zope.component.provideUtility(displayer)
|
||||
|
||||
mock_util().input.return_value = (display_util.OK, "")
|
||||
acc = account.Account.from_prompts(self.config)
|
||||
self.assertTrue(acc.email is None)
|
||||
# _get_config_filename | pylint: disable=protected-access
|
||||
mock_key.assert_called_once_with(
|
||||
mock.ANY, mock.ANY, acc._get_config_filename(None))
|
||||
|
||||
@mock.patch("letsencrypt.client.account.zope.component.getUtility")
|
||||
def test_prompts_cancel(self, mock_util):
|
||||
# displayer = display_util.FileDisplay(sys.stdout)
|
||||
|
|
@ -169,7 +181,7 @@ class SafeEmailTest(unittest.TestCase):
|
|||
addrs = [
|
||||
"letsencrypt@letsencrypt.org",
|
||||
"tbd.ade@gmail.com",
|
||||
"abc_def.jdk@hotmail.museum"
|
||||
"abc_def.jdk@hotmail.museum",
|
||||
]
|
||||
for addr in addrs:
|
||||
self.assertTrue(self._call(addr), "%s failed." % addr)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ def chall_to_challb(chall, status): # pylint: disable=redefined-outer-name
|
|||
"""Return ChallengeBody from Challenge."""
|
||||
kwargs = {
|
||||
"chall": chall,
|
||||
"uri": chall.typ+"_uri",
|
||||
"uri": chall.typ + "_uri",
|
||||
"status": status,
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ def gen_authzr(authz_status, domain, challs, statuses, combos=True):
|
|||
now = datetime.datetime.now()
|
||||
authz_kwargs.update({
|
||||
"status": authz_status,
|
||||
"expires": datetime.datetime(now.year, now.month+1, now.day),
|
||||
"expires": datetime.datetime(now.year, now.month + 1, now.day),
|
||||
})
|
||||
else:
|
||||
authz_kwargs.update({
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import unittest
|
|||
import mock
|
||||
import zope.component
|
||||
|
||||
from letsencrypt.client import account
|
||||
from letsencrypt.client import le_util
|
||||
from letsencrypt.client.display import util as display_util
|
||||
|
||||
|
||||
class ChooseAuthenticatorTest(unittest.TestCase):
|
||||
"""Test choose_authenticator function."""
|
||||
def setUp(self):
|
||||
|
|
@ -59,7 +59,6 @@ class ChooseAuthenticatorTest(unittest.TestCase):
|
|||
class ChooseAccountTest(unittest.TestCase):
|
||||
"""Test choose_account."""
|
||||
def setUp(self):
|
||||
from letsencrypt.client import account
|
||||
zope.component.provideUtility(display_util.FileDisplay(sys.stdout))
|
||||
|
||||
self.accounts_dir = tempfile.mkdtemp("accounts")
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ class RecoveryTokenTest(unittest.TestCase):
|
|||
# SHOULD throw an error (OSError other than nonexistent file)
|
||||
self.assertRaises(
|
||||
OSError, self.rec_token.cleanup,
|
||||
achallenges.RecoveryToken(challb=None, domain="a"+"r"*10000+".com"))
|
||||
achallenges.RecoveryToken(
|
||||
challb=None, domain=("a" + "r" * 10000 + ".com")))
|
||||
|
||||
def test_perform_stored(self):
|
||||
self.rec_token.store_token("example4.com", 444)
|
||||
|
|
|
|||
Loading…
Reference in a new issue