mirror of
https://github.com/certbot/certbot.git
synced 2026-08-02 08:03:19 +02:00
Implement delete command (#3913)
* organize cert_manager.py * add delete files to cert manager and storage * add tests * add to main and cli * Clean up all related files we can find, even if some are missing. * error messages, debug logs, and remove RenewerConfiguration * add logs for failure to remove * remove renewer_config_file
This commit is contained in:
+112
-93
@@ -6,10 +6,8 @@ import pytz
|
||||
import traceback
|
||||
import zope.component
|
||||
|
||||
from certbot import configuration
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot import renewal
|
||||
from certbot import storage
|
||||
from certbot import util
|
||||
|
||||
@@ -17,6 +15,10 @@ from certbot.display import util as display_util
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
###################
|
||||
# Commands
|
||||
###################
|
||||
|
||||
def update_live_symlinks(config):
|
||||
"""Update the certificate file family symlinks to use archive_dir.
|
||||
|
||||
@@ -26,36 +28,22 @@ def update_live_symlinks(config):
|
||||
.. note:: This assumes that the installation is using a Reverter object.
|
||||
|
||||
:param config: Configuration.
|
||||
:type config: :class:`certbot.interfaces.IConfig`
|
||||
:type config: :class:`certbot.configuration.NamespaceConfig`
|
||||
|
||||
"""
|
||||
renewer_config = configuration.RenewerConfiguration(config)
|
||||
for renewal_file in renewal.renewal_conf_files(renewer_config):
|
||||
storage.RenewableCert(renewal_file,
|
||||
configuration.RenewerConfiguration(renewer_config),
|
||||
update_symlinks=True)
|
||||
for renewal_file in storage.renewal_conf_files(config):
|
||||
storage.RenewableCert(renewal_file, config, update_symlinks=True)
|
||||
|
||||
def rename_lineage(config):
|
||||
"""Rename the specified lineage to the new name.
|
||||
|
||||
:param config: Configuration.
|
||||
:type config: :class:`certbot.interfaces.IConfig`
|
||||
:type config: :class:`certbot.configuration.NamespaceConfig`
|
||||
|
||||
"""
|
||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||
renewer_config = configuration.RenewerConfiguration(config)
|
||||
|
||||
certname = config.certname
|
||||
if not certname:
|
||||
filenames = renewal.renewal_conf_files(renewer_config)
|
||||
choices = [storage.lineagename_for_filename(name) for name in filenames]
|
||||
if not choices:
|
||||
raise errors.Error("No existing certificates found.")
|
||||
code, index = disp.menu("Which certificate would you like to rename?",
|
||||
choices, ok_label="Select", flag="--cert-name")
|
||||
if code != display_util.OK or not index in range(0, len(choices)):
|
||||
raise errors.Error("User ended interaction.")
|
||||
certname = choices[index]
|
||||
certname = _get_certname(config, "rename")
|
||||
|
||||
new_certname = config.new_certname
|
||||
if not new_certname:
|
||||
@@ -68,10 +56,110 @@ def rename_lineage(config):
|
||||
if not lineage:
|
||||
raise errors.ConfigurationError("No existing certificate with name "
|
||||
"{0} found.".format(certname))
|
||||
storage.rename_renewal_config(certname, new_certname, renewer_config)
|
||||
storage.rename_renewal_config(certname, new_certname, config)
|
||||
disp.notification("Successfully renamed {0} to {1}."
|
||||
.format(certname, new_certname), pause=False)
|
||||
|
||||
def certificates(config):
|
||||
"""Display information about certs configured with Certbot
|
||||
|
||||
:param config: Configuration.
|
||||
:type config: :class:`certbot.configuration.NamespaceConfig`
|
||||
"""
|
||||
parsed_certs = []
|
||||
parse_failures = []
|
||||
for renewal_file in storage.renewal_conf_files(config):
|
||||
try:
|
||||
renewal_candidate = storage.RenewableCert(renewal_file, config)
|
||||
parsed_certs.append(renewal_candidate)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
logger.warning("Renewal configuration file %s produced an "
|
||||
"unexpected error: %s. Skipping.", renewal_file, e)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
parse_failures.append(renewal_file)
|
||||
|
||||
# Describe all the certs
|
||||
_describe_certs(parsed_certs, parse_failures)
|
||||
|
||||
def delete(config):
|
||||
"""Delete Certbot files associated with a certificate lineage."""
|
||||
certname = _get_certname(config, "delete")
|
||||
storage.delete_files(config, certname)
|
||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||
disp.notification("Deleted all files relating to certificate {0}."
|
||||
.format(certname), pause=False)
|
||||
|
||||
###################
|
||||
# Public Helpers
|
||||
###################
|
||||
|
||||
def lineage_for_certname(config, certname):
|
||||
"""Find a lineage object with name certname."""
|
||||
def update_cert_for_name_match(candidate_lineage, rv):
|
||||
"""Return cert if it has name certname, else return rv
|
||||
"""
|
||||
matching_lineage_name_cert = rv
|
||||
if candidate_lineage.lineagename == certname:
|
||||
matching_lineage_name_cert = candidate_lineage
|
||||
return matching_lineage_name_cert
|
||||
return _search_lineages(config, update_cert_for_name_match, None)
|
||||
|
||||
def domains_for_certname(config, certname):
|
||||
"""Find the domains in the cert with name certname."""
|
||||
def update_domains_for_name_match(candidate_lineage, rv):
|
||||
"""Return domains if certname matches, else return rv
|
||||
"""
|
||||
matching_domains = rv
|
||||
if candidate_lineage.lineagename == certname:
|
||||
matching_domains = candidate_lineage.names()
|
||||
return matching_domains
|
||||
return _search_lineages(config, update_domains_for_name_match, None)
|
||||
|
||||
def find_duplicative_certs(config, domains):
|
||||
"""Find existing certs that duplicate the request."""
|
||||
def update_certs_for_domain_matches(candidate_lineage, rv):
|
||||
"""Return cert as identical_names_cert if it matches,
|
||||
or subset_names_cert if it matches as subset
|
||||
"""
|
||||
# TODO: Handle these differently depending on whether they are
|
||||
# expired or still valid?
|
||||
identical_names_cert, subset_names_cert = rv
|
||||
candidate_names = set(candidate_lineage.names())
|
||||
if candidate_names == set(domains):
|
||||
identical_names_cert = candidate_lineage
|
||||
elif candidate_names.issubset(set(domains)):
|
||||
# This logic finds and returns the largest subset-names cert
|
||||
# in the case where there are several available.
|
||||
if subset_names_cert is None:
|
||||
subset_names_cert = candidate_lineage
|
||||
elif len(candidate_names) > len(subset_names_cert.names()):
|
||||
subset_names_cert = candidate_lineage
|
||||
return (identical_names_cert, subset_names_cert)
|
||||
|
||||
return _search_lineages(config, update_certs_for_domain_matches, (None, None))
|
||||
|
||||
|
||||
###################
|
||||
# Private Helpers
|
||||
###################
|
||||
|
||||
def _get_certname(config, verb):
|
||||
"""Get certname from flag, interactively, or error out.
|
||||
"""
|
||||
certname = config.certname
|
||||
if not certname:
|
||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||
filenames = storage.renewal_conf_files(config)
|
||||
choices = [storage.lineagename_for_filename(name) for name in filenames]
|
||||
if not choices:
|
||||
raise errors.Error("No existing certificates found.")
|
||||
code, index = disp.menu("Which certificate would you like to {0}?".format(verb),
|
||||
choices, ok_label="Select", flag="--cert-name")
|
||||
if code != display_util.OK or not index in range(0, len(choices)):
|
||||
raise errors.Error("User ended interaction.")
|
||||
certname = choices[index]
|
||||
return certname
|
||||
|
||||
def _report_lines(msgs):
|
||||
"""Format a results report for a category of single-line renewal outcomes"""
|
||||
return " " + "\n ".join(str(msg) for msg in msgs)
|
||||
@@ -126,42 +214,18 @@ def _describe_certs(parsed_certs, parse_failures):
|
||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||
disp.notification("\n".join(out), pause=False, wrap=False)
|
||||
|
||||
def certificates(config):
|
||||
"""Display information about certs configured with Certbot
|
||||
|
||||
:param config: Configuration.
|
||||
:type config: :class:`certbot.interfaces.IConfig`
|
||||
"""
|
||||
renewer_config = configuration.RenewerConfiguration(config)
|
||||
parsed_certs = []
|
||||
parse_failures = []
|
||||
for renewal_file in renewal.renewal_conf_files(renewer_config):
|
||||
try:
|
||||
renewal_candidate = storage.RenewableCert(renewal_file,
|
||||
configuration.RenewerConfiguration(config))
|
||||
parsed_certs.append(renewal_candidate)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
logger.warning("Renewal configuration file %s produced an "
|
||||
"unexpected error: %s. Skipping.", renewal_file, e)
|
||||
logger.debug("Traceback was:\n%s", traceback.format_exc())
|
||||
parse_failures.append(renewal_file)
|
||||
|
||||
# Describe all the certs
|
||||
_describe_certs(parsed_certs, parse_failures)
|
||||
|
||||
def _search_lineages(config, func, initial_rv):
|
||||
def _search_lineages(cli_config, func, initial_rv):
|
||||
"""Iterate func over unbroken lineages, allowing custom return conditions.
|
||||
|
||||
Allows flexible customization of return values, including multiple
|
||||
return values and complex checks.
|
||||
"""
|
||||
cli_config = configuration.RenewerConfiguration(config)
|
||||
configs_dir = cli_config.renewal_configs_dir
|
||||
# Verify the directory is there
|
||||
util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())
|
||||
|
||||
rv = initial_rv
|
||||
for renewal_file in renewal.renewal_conf_files(cli_config):
|
||||
for renewal_file in storage.renewal_conf_files(cli_config):
|
||||
try:
|
||||
candidate_lineage = storage.RenewableCert(renewal_file, cli_config)
|
||||
except (errors.CertStorageError, IOError):
|
||||
@@ -170,48 +234,3 @@ def _search_lineages(config, func, initial_rv):
|
||||
continue
|
||||
rv = func(candidate_lineage, rv)
|
||||
return rv
|
||||
|
||||
def lineage_for_certname(config, certname):
|
||||
"""Find a lineage object with name certname."""
|
||||
def update_cert_for_name_match(candidate_lineage, rv):
|
||||
"""Return cert if it has name certname, else return rv
|
||||
"""
|
||||
matching_lineage_name_cert = rv
|
||||
if candidate_lineage.lineagename == certname:
|
||||
matching_lineage_name_cert = candidate_lineage
|
||||
return matching_lineage_name_cert
|
||||
return _search_lineages(config, update_cert_for_name_match, None)
|
||||
|
||||
def domains_for_certname(config, certname):
|
||||
"""Find the domains in the cert with name certname."""
|
||||
def update_domains_for_name_match(candidate_lineage, rv):
|
||||
"""Return domains if certname matches, else return rv
|
||||
"""
|
||||
matching_domains = rv
|
||||
if candidate_lineage.lineagename == certname:
|
||||
matching_domains = candidate_lineage.names()
|
||||
return matching_domains
|
||||
return _search_lineages(config, update_domains_for_name_match, None)
|
||||
|
||||
def find_duplicative_certs(config, domains):
|
||||
"""Find existing certs that duplicate the request."""
|
||||
def update_certs_for_domain_matches(candidate_lineage, rv):
|
||||
"""Return cert as identical_names_cert if it matches,
|
||||
or subset_names_cert if it matches as subset
|
||||
"""
|
||||
# TODO: Handle these differently depending on whether they are
|
||||
# expired or still valid?
|
||||
identical_names_cert, subset_names_cert = rv
|
||||
candidate_names = set(candidate_lineage.names())
|
||||
if candidate_names == set(domains):
|
||||
identical_names_cert = candidate_lineage
|
||||
elif candidate_names.issubset(set(domains)):
|
||||
# This logic finds and returns the largest subset-names cert
|
||||
# in the case where there are several available.
|
||||
if subset_names_cert is None:
|
||||
subset_names_cert = candidate_lineage
|
||||
elif len(candidate_names) > len(subset_names_cert.names()):
|
||||
subset_names_cert = candidate_lineage
|
||||
return (identical_names_cert, subset_names_cert)
|
||||
|
||||
return _search_lineages(config, update_certs_for_domain_matches, (None, None))
|
||||
|
||||
Reference in New Issue
Block a user