Readability improvements for storage.py

This commit is contained in:
Seth Schoen
2015-05-14 17:36:30 -07:00
parent 9a144b46bc
commit 4a100490a1
+49 -37
View File
@@ -69,10 +69,12 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
def consistent(self): def consistent(self):
"""Is the structure of the archived files and links related to this """Is the structure of the archived files and links related to this
lineage correct and self-consistent?""" lineage correct and self-consistent?"""
# Each element must be referenced with an absolute path # Each element must be referenced with an absolute path
if any(not os.path.isabs(x) for x in if any(not os.path.isabs(x) for x in
(self.cert, self.privkey, self.chain, self.fullchain)): (self.cert, self.privkey, self.chain, self.fullchain)):
return False return False
# Each element must exist and be a symbolic link # Each element must exist and be a symbolic link
if any(not os.path.islink(x) for x in if any(not os.path.islink(x) for x in
(self.cert, self.privkey, self.chain, self.fullchain)): (self.cert, self.privkey, self.chain, self.fullchain)):
@@ -83,6 +85,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
target = os.readlink(link) target = os.readlink(link)
if not os.path.isabs(target): if not os.path.isabs(target):
target = os.path.join(where, target) target = os.path.join(where, target)
# Each element's link must point within the cert lineage's # Each element's link must point within the cert lineage's
# directory within the official archive directory # directory within the official archive directory
desired_directory = os.path.join( desired_directory = os.path.join(
@@ -90,14 +93,17 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
if not os.path.samefile(os.path.dirname(target), if not os.path.samefile(os.path.dirname(target),
desired_directory): desired_directory):
return False return False
# The link must point to a file that exists # The link must point to a file that exists
if not os.path.exists(target): if not os.path.exists(target):
return False return False
# The link must point to a file that follows the archive # The link must point to a file that follows the archive
# naming convention # naming convention
pattern = re.compile(r"^{0}([0-9]+)\.pem$".format(kind)) pattern = re.compile(r"^{0}([0-9]+)\.pem$".format(kind))
if not pattern.match(os.path.basename(target)): if not pattern.match(os.path.basename(target)):
return False return False
# It is NOT required that the link's target be a regular # It is NOT required that the link's target be a regular
# file (it may itself be a symlink). But we should probably # file (it may itself be a symlink). But we should probably
# do a recursive check that ultimately the target does # do a recursive check that ultimately the target does
@@ -109,8 +115,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
# XXX: All four of the targets are in the same directory # XXX: All four of the targets are in the same directory
# (This check is redundant with the check that they # (This check is redundant with the check that they
# are all in the desired directory!) # are all in the desired directory!)
# len(set(os.path.basename(self.current_target(x) # len(set(os.path.basename(self.current_target(x)
# for x in ALL_FOUR))) == 1 # for x in ALL_FOUR))) == 1
return True return True
def fix(self): def fix(self):
@@ -299,9 +305,11 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
# XXX: both self.ocsp_revoked() and self.notafter() are bugs # XXX: both self.ocsp_revoked() and self.notafter() are bugs
# here because we should be looking at the latest version, not # here because we should be looking at the latest version, not
# the current version! # the current version!
# Renewals on the basis of revocation # Renewals on the basis of revocation
if self.ocsp_revoked(): if self.ocsp_revoked():
return True return True
# Renewals on the basis of expiry time # Renewals on the basis of expiry time
interval = self.configuration.get("renew_before_expiry", "10 days") interval = self.configuration.get("renew_before_expiry", "10 days")
autorenew_interval = parse_time_interval(interval) autorenew_interval = parse_time_interval(interval)
@@ -326,6 +334,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
Returns a new RenewableCert object referring to the created Returns a new RenewableCert object referring to the created
lineage. (The actual lineage name, as well as all the relevant lineage. (The actual lineage name, as well as all the relevant
file paths, will be available within this object.)""" file paths, will be available within this object.)"""
# Examine the configuration and find the new lineage's name
configs_dir = config["renewal_configs_dir"] configs_dir = config["renewal_configs_dir"]
archive_dir = config["official_archive_dir"] archive_dir = config["official_archive_dir"]
live_dir = config["live_dir"] live_dir = config["live_dir"]
@@ -336,7 +346,9 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
lineagename) lineagename)
if not config_filename.endswith(".conf"): if not config_filename.endswith(".conf"):
raise ValueError("renewal config file name must end in .conf") raise ValueError("renewal config file name must end in .conf")
# lineagename will now potentially be modified based on what
# Determine where on disk everything will go
# lineagename will now potentially be modified based on which
# renewal configuration file could actually be created # renewal configuration file could actually be created
lineagename = os.path.basename(config_filename)[:-len(".conf")] lineagename = os.path.basename(config_filename)[:-len(".conf")]
archive = os.path.join(archive_dir, lineagename) archive = os.path.join(archive_dir, lineagename)
@@ -348,33 +360,28 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
os.mkdir(archive) os.mkdir(archive)
os.mkdir(live_dir) os.mkdir(live_dir)
relative_archive = os.path.join("..", "..", "archive", lineagename) relative_archive = os.path.join("..", "..", "archive", lineagename)
cert_target = os.path.join(live_dir, "cert.pem")
privkey_target = os.path.join(live_dir, "privkey.pem") # Put the data into the appropriate files on disk
chain_target = os.path.join(live_dir, "chain.pem") target = dict([(kind, os.path.join(live_dir, kind + ".pem"))
fullchain_target = os.path.join(live_dir, "fullchain.pem") for kind in ALL_FOUR])
os.symlink(os.path.join(relative_archive, "cert1.pem"), for kind in ALL_FOUR:
cert_target) os.symlink(os.path.join(relative_archive, kind + "1.pem"),
os.symlink(os.path.join(relative_archive, "privkey1.pem"), target[kind])
privkey_target) with open(target["cert"], "w") as f:
os.symlink(os.path.join(relative_archive, "chain1.pem"),
chain_target)
os.symlink(os.path.join(relative_archive, "fullchain1.pem"),
fullchain_target)
with open(cert_target, "w") as f:
f.write(cert) f.write(cert)
with open(privkey_target, "w") as f: with open(target["privkey"], "w") as f:
f.write(privkey) f.write(privkey)
# XXX: Let's make sure to get the file permissions right here # XXX: Let's make sure to get the file permissions right here
with open(chain_target, "w") as f: with open(target["chain"], "w") as f:
f.write(chain) f.write(chain)
with open(fullchain_target, "w") as f: with open(target["fullchain"], "w") as f:
f.write(cert + chain) f.write(cert + chain)
# Document what we've done in a new renewal config file
config_file.close() config_file.close()
new_config = configobj.ConfigObj(config_filename, create_empty=True) new_config = configobj.ConfigObj(config_filename, create_empty=True)
new_config["cert"] = cert_target for kind in ALL_FOUR:
new_config["privkey"] = privkey_target new_config[kind] = target[kind]
new_config["chain"] = chain_target
new_config["fullchain"] = fullchain_target
if renewalparams: if renewalparams:
new_config["renewalparams"] = renewalparams new_config["renewalparams"] = renewalparams
new_config.comments["renewalparams"] = ["", new_config.comments["renewalparams"] = ["",
@@ -385,6 +392,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
new_config.write() new_config.write()
return cls(new_config, config) return cls(new_config, config)
def save_successor(self, prior_version, new_cert, new_privkey, new_chain): def save_successor(self, prior_version, new_cert, new_privkey, new_chain):
"""Save a new cert and chain as a successor of a specific prior """Save a new cert and chain as a successor of a specific prior
version in this lineage. Returns the new version number that was version in this lineage. Returns the new version number that was
@@ -393,19 +401,19 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
# XXX: consider using os.open for availablity of os.O_EXCL # XXX: consider using os.open for availablity of os.O_EXCL
# XXX: ensure file permissions are correct; also create directories # XXX: ensure file permissions are correct; also create directories
# if needed (ensuring their permissions are correct) # if needed (ensuring their permissions are correct)
# Figure out what the new version is and hence where to save things
target_version = self.next_free_version() target_version = self.next_free_version()
archive = self.configuration["official_archive_dir"] archive = self.configuration["official_archive_dir"]
prefix = os.path.join(archive, self.lineagename) prefix = os.path.join(archive, self.lineagename)
cert_target = os.path.join( target = dict(
prefix, "cert{0}.pem".format(target_version)) [(kind,
privkey_target = os.path.join( os.path.join(prefix, "{0}{1}.pem".format(kind, target_version)))
prefix, "privkey{0}.pem".format(target_version)) for kind in ALL_FOUR])
chain_target = os.path.join(
prefix, "chain{0}.pem".format(target_version)) # Distinguish the cases where the privkey has changed and where it
fullchain_target = os.path.join( # has not changed (in the latter case, making an appropriate symlink
prefix, "fullchain{0}.pem".format(target_version)) # to an earlier privkey version)
with open(cert_target, "w") as f:
f.write(new_cert)
if new_privkey is None: if new_privkey is None:
# The behavior below keeps the prior key by creating a new # The behavior below keeps the prior key by creating a new
# symlink to the old key or the target of the old key symlink. # symlink to the old key or the target of the old key symlink.
@@ -415,12 +423,16 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
old_privkey = os.readlink(old_privkey) old_privkey = os.readlink(old_privkey)
else: else:
old_privkey = "privkey{0}.pem".format(prior_version) old_privkey = "privkey{0}.pem".format(prior_version)
os.symlink(old_privkey, privkey_target) os.symlink(old_privkey, target["privkey"])
else: else:
with open(privkey_target, "w") as f: with open(target["privkey"], "w") as f:
f.write(new_privkey) f.write(new_privkey)
with open(chain_target, "w") as f:
# Save everything else
with open(target["cert"], "w") as f:
f.write(new_cert)
with open(target["chain"], "w") as f:
f.write(new_chain) f.write(new_chain)
with open(fullchain_target, "w") as f: with open(target["fullchain"], "w") as f:
f.write(new_cert + new_chain) f.write(new_cert + new_chain)
return target_version return target_version