def _determine_account(config): """Determine which account to use. In order to make the renewer (configuration de/serialization) happy, if ``config.account`` is ``None``, it will be updated based on the user input. Same for ``config.email``. :param argparse.Namespace config: CLI arguments :param letsencrypt.interface.IConfig config: Configuration object :param .AccountStorage account_storage: Account storage. :returns: Account and optionally ACME client API (biproduct of new registration). :rtype: `tuple` of `letsencrypt.account.Account` and `acme.client.Client` """ account_storage = account.AccountFileStorage(config) acme = None if config.account is not None: acc = account_storage.load(config.account) else: accounts = account_storage.find_all() if len(accounts) > 1: acc = display_ops.choose_account(accounts) elif len(accounts) == 1: acc = accounts[0] else: # no account registered yet if config.email is None and not config.register_unsafely_without_email: config.namespace.email = display_ops.get_email() def _tos_cb(regr): 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(regr.terms_of_service, config.server)) obj = zope.component.getUtility(interfaces.IDisplay) return obj.yesno(msg, "Agree", "Cancel", cli_flag="--agree-tos") try: acc, acme = client.register(config, account_storage, tos_cb=_tos_cb) except errors.MissingCommandlineFlag: raise except errors.Error as error: logger.debug(error, exc_info=True) raise errors.Error( "Unable to register an account with ACME server") config.namespace.account = acc.id return acc, acme
def renew(cert, old_version): """Perform automated renewal of the referenced cert, if possible. :param letsencrypt.storage.RenewableCert cert: The certificate lineage to attempt to renew. :param int old_version: The version of the certificate lineage relative to which the renewal should be attempted. :returns: A number referring to newly created version of this cert lineage, or ``False`` if renewal was not successful. :rtype: `int` or `bool` """ # TODO: handle partial success (some names can be renewed but not # others) # TODO: handle obligatory key rotation vs. optional key rotation vs. # requested key rotation if "renewalparams" not in cert.configfile: # TODO: notify user? return False renewalparams = cert.configfile["renewalparams"] if "authenticator" not in renewalparams: # TODO: notify user? return False # Instantiate the appropriate authenticator plugins = plugins_disco.PluginsRegistry.find_all() config = configuration.NamespaceConfig(_AttrDict(renewalparams)) # XXX: this loses type data (for example, the fact that key_size # was an int, not a str) config.rsa_key_size = int(config.rsa_key_size) config.dvsni_port = int(config.dvsni_port) config.namespace.http01_port = int(config.namespace.http01_port) zope.component.provideUtility(config) try: authenticator = plugins[renewalparams["authenticator"]] except KeyError: # TODO: Notify user? (authenticator could not be found) return False authenticator = authenticator.init(config) authenticator.prepare() acc = account.AccountFileStorage(config).load( account_id=renewalparams["account"]) le_client = client.Client(config, acc, authenticator, None) with open(cert.version("cert", old_version)) as f: sans = crypto_util.get_sans_from_cert(f.read()) new_certr, new_chain, new_key, _ = le_client.obtain_certificate(sans) if new_chain: # XXX: Assumes that there was a key change. We need logic # for figuring out whether there was or not. Probably # best is to have obtain_certificate return None for # new_key if the old key is to be used (since save_successor # already understands this distinction!) return cert.save_successor( old_version, OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, new_certr.body), new_key.pem, crypto_util.dump_pyopenssl_chain(new_chain)) # TODO: Notify results else: # TODO: Notify negative results return False