def run(config, plugins): """Obtain a certificate and install. :param config: Configuration object :type config: interfaces.IConfig :param plugins: List of plugins :type plugins: `list` of `str` :returns: `None` :rtype: None """ # TODO: Make run as close to auth + install as possible # Possible difficulties: config.csr was hacked into auth try: installer, authenticator = plug_sel.choose_configurator_plugins( config, plugins, "run") except errors.PluginSelectionError as e: return str(e) # Preflight check for enhancement support by the selected installer if not enhancements.are_supported(config, installer): raise errors.NotSupportedError( "One ore more of the requested enhancements " "are not supported by the selected installer") # TODO: Handle errors from _init_le_client? le_client = _init_le_client(config, authenticator, installer) domains, certname = _find_domains_or_certname(config, installer) should_get_cert, lineage = _find_cert(config, domains, certname) new_lineage = lineage if should_get_cert: new_lineage = _get_and_save_cert(le_client, config, domains, certname, lineage) cert_path = new_lineage.cert_path if new_lineage else None fullchain_path = new_lineage.fullchain_path if new_lineage else None key_path = new_lineage.key_path if new_lineage else None if should_get_cert: _report_new_cert(config, cert_path, fullchain_path, key_path) _install_cert(config, le_client, domains, new_lineage) if enhancements.are_requested(config) and new_lineage: enhancements.enable(new_lineage, domains, installer, config) if lineage is None or not should_get_cert: display_ops.success_installation(domains) else: display_ops.success_renewal(domains) _suggest_donation_if_appropriate(config) eff.handle_subscription(config, le_client.account) return None
def certonly(config, plugins): """Authenticate & obtain cert, but do not install it. This implements the 'certonly' subcommand. :param config: Configuration object :type config: interfaces.IConfig :param plugins: List of plugins :type plugins: `list` of `str` :returns: `None` :rtype: None :raises errors.Error: If specified plugin could not be used """ # SETUP: Select plugins and construct a client instance try: # installers are used in auth mode to determine domain names installer, auth = plug_sel.choose_configurator_plugins( config, plugins, "certonly") except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise le_client = _init_le_client(config, auth, installer) if config.csr: cert_path, fullchain_path = _csr_get_and_save_cert(config, le_client) _report_new_cert(config, cert_path, fullchain_path) _suggest_donation_if_appropriate(config) eff.handle_subscription(config, le_client.account) return domains, certname = _find_domains_or_certname(config, installer) should_get_cert, lineage = _find_cert(config, domains, certname) if not should_get_cert: notify = zope.component.getUtility(interfaces.IDisplay).notification notify("Certificate not yet due for renewal; no action taken.", pause=False) return lineage = _get_and_save_cert(le_client, config, domains, certname, lineage) cert_path = lineage.cert_path if lineage else None fullchain_path = lineage.fullchain_path if lineage else None key_path = lineage.key_path if lineage else None _report_new_cert(config, cert_path, fullchain_path, key_path) _suggest_donation_if_appropriate(config) eff.handle_subscription(config, le_client.account)
def update_account(config, unused_plugins): """Modify accounts on the server. :param config: Configuration object :type config: interfaces.IConfig :param unused_plugins: List of plugins (deprecated) :type unused_plugins: `list` of `str` :returns: `None` or a string indicating and error :rtype: None or str """ # Portion of _determine_account logic to see whether accounts already # exist or not. account_storage = account.AccountFileStorage(config) accounts = account_storage.find_all() reporter_util = zope.component.getUtility(interfaces.IReporter) add_msg = lambda m: reporter_util.add_message( m, reporter_util.MEDIUM_PRIORITY) if not accounts: return "Could not find an existing account to update." if config.email is None: if config.register_unsafely_without_email: return ("--register-unsafely-without-email provided, however, a " "new e-mail address must\ncurrently be provided when " "updating a registration.") config.email = display_ops.get_email(optional=False) 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(',')] prev_regr_uri = acc.regr.uri acc.regr = cb_client.acme.update_registration( acc.regr.update(body=acc.regr.body.update(contact=acc_contacts))) # A v1 account being used as a v2 account will result in changing the uri to # the v2 uri. Since it's the same object on disk, put it back to the v1 uri # so that we can also continue to use the account object with acmev1. acc.regr = acc.regr.update(uri=prev_regr_uri) 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)) return None
def _call(self): from certbot._internal.eff import handle_subscription handle_subscription(self.config, self.account)
def register(config, account_storage, tos_cb=None): """Register new account with an ACME CA. This function takes care of generating fresh private key, registering the account, optionally accepting CA Terms of Service and finally saving the account. It should be called prior to initialization of `Client`, unless account has already been created. :param .IConfig config: Client configuration. :param .AccountStorage account_storage: Account storage where newly registered account will be saved to. Save happens only after TOS acceptance step, so any account private keys or `.RegistrationResource` will not be persisted if `tos_cb` returns ``False``. :param tos_cb: If ACME CA requires the user to accept a Terms of Service before registering account, client action is necessary. For example, a CLI tool would prompt the user acceptance. `tos_cb` must be a callable that should accept `.RegistrationResource` and return a `bool`: ``True`` iff the Terms of Service present in the contained `.Registration.terms_of_service` is accepted by the client, and ``False`` otherwise. ``tos_cb`` will be called only if the client action is necessary, i.e. when ``terms_of_service is not None``. This argument is optional, if not supplied it will default to automatic acceptance! :raises certbot.errors.Error: In case of any client problems, in particular registration failure, or unaccepted Terms of Service. :raises acme.errors.Error: In case of any protocol problems. :returns: Newly registered and saved account, as well as protocol API handle (should be used in `Client` initialization). :rtype: `tuple` of `.Account` and `acme.client.Client` """ # Log non-standard actions, potentially wrong API calls if account_storage.find_all(): logger.info("There are already existing accounts for %s", config.server) if config.email is None: if not config.register_unsafely_without_email: msg = ("No email was provided and " "--register-unsafely-without-email was not present.") logger.warning(msg) raise errors.Error(msg) if not config.dry_run: logger.info("Registering without email!") # If --dry-run is used, and there is no staging account, create one with no email. if config.dry_run: config.email = None # Each new registration shall use a fresh new key rsa_key = generate_private_key(public_exponent=65537, key_size=config.rsa_key_size, backend=default_backend()) key = jose.JWKRSA(key=jose.ComparableRSAKey(rsa_key)) acme = acme_from_config_key(config, key) # TODO: add phone? regr = perform_registration(acme, config, tos_cb) acc = account.Account(regr, key) account.report_new_account(config) account_storage.save(acc, acme) eff.handle_subscription(config) return acc, acme
def _call(self): from certbot._internal.eff import handle_subscription return handle_subscription(self.config)