def update_configuration(lineagename, archive_dir, target, cli_config): """Modifies lineagename's config to contain the specified values. :param str lineagename: Name of the lineage being modified :param str archive_dir: Absolute path to the archive directory :param dict target: Maps ALL_FOUR to their symlink paths :param .NamespaceConfig cli_config: parsed command line arguments :returns: Configuration object for the updated config file :rtype: configobj.ConfigObj """ config_filename = renewal_filename_for_lineagename(cli_config, lineagename) temp_filename = config_filename + ".new" # If an existing tempfile exists, delete it if os.path.exists(temp_filename): os.unlink(temp_filename) # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) write_renewal_config(config_filename, temp_filename, archive_dir, target, values) misc.os_rename(temp_filename, config_filename) return configobj.ConfigObj(config_filename)
def _update_link_to(self, kind, version): """Make the specified item point at the specified version. (Note that this method doesn't verify that the specified version exists.) :param str kind: the lineage member item ("cert", "privkey", "chain", or "fullchain") :param int version: the desired version """ if kind not in ALL_FOUR: raise errors.CertStorageError("unknown kind of item") link = getattr(self, kind) filename = "{0}{1}.pem".format(kind, version) # Relative rather than absolute target directory target_directory = os.path.dirname(os.readlink(link)) # TODO: it could be safer to make the link first under a temporary # filename, then unlink the old link, then rename the new link # to the old link; this ensures that this process is able to # create symlinks. # TODO: we might also want to check consistency of related links # for the other corresponding items os.unlink(link) os.symlink(os.path.join(target_directory, filename), link)
def test_current_version(self): for ver in (1, 5, 10, 20): self._write_out_kind("cert", ver) os.unlink(self.test_rc.cert) os.symlink(os.path.join("..", "..", "archive", "example.org", "cert10.pem"), self.test_rc.cert) self.assertEqual(self.test_rc.current_version("cert"), 10)
def update_configuration(lineagename, archive_dir, target, cli_config): """Modifies lineagename's config to contain the specified values. :param str lineagename: Name of the lineage being modified :param str archive_dir: Absolute path to the archive directory :param dict target: Maps ALL_FOUR to their symlink paths :param .NamespaceConfig cli_config: parsed command line arguments :returns: Configuration object for the updated config file :rtype: configobj.ConfigObj """ config_filename = renewal_filename_for_lineagename(cli_config, lineagename) temp_filename = config_filename + ".new" # If an existing tempfile exists, delete it if os.path.exists(temp_filename): os.unlink(temp_filename) # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) write_renewal_config(config_filename, temp_filename, archive_dir, target, values) filesystem.replace(temp_filename, config_filename) return configobj.ConfigObj(config_filename)
def _update_link_to(self, kind, version): """Make the specified item point at the specified version. (Note that this method doesn't verify that the specified version exists.) :param str kind: the lineage member item ("cert", "privkey", "chain", or "fullchain") :param int version: the desired version """ if kind not in ALL_FOUR: raise errors.CertStorageError("unknown kind of item") link = getattr(self, kind) filename = "{0}{1}.pem".format(kind, version) # Relative rather than absolute target directory target_directory = os.path.dirname(filesystem.readlink(link)) # TODO: it could be safer to make the link first under a temporary # filename, then unlink the old link, then rename the new link # to the old link; this ensures that this process is able to # create symlinks. # TODO: we might also want to check consistency of related links # for the other corresponding items os.unlink(link) os.symlink(os.path.join(target_directory, filename), link)
def _symlink_to_accounts_dir(self, prev_server_path, server_path): accounts_dir = self.config.accounts_dir_for_server_path(server_path) if os.path.islink(accounts_dir): os.unlink(accounts_dir) else: os.rmdir(accounts_dir) prev_account_dir = self.config.accounts_dir_for_server_path(prev_server_path) os.symlink(prev_account_dir, accounts_dir)
def _update_symlinks(self): """Updates symlinks to use archive_dir""" for kind in ALL_FOUR: link = getattr(self, kind) previous_link = get_link_target(link) new_link = os.path.join(self.relative_archive_dir(link), os.path.basename(previous_link)) os.unlink(link) os.symlink(new_link, link)
def test_names(self): # Trying the current version self._write_out_kind("cert", 12, test_util.load_vector("cert-san_512.pem")) self.assertEqual(self.test_rc.names(), ["example.com", "www.example.com"]) # Trying missing cert os.unlink(self.test_rc.cert) self.assertRaises(errors.CertStorageError, self.test_rc.names)
def _write_out_kind(self, kind, ver, value=None): link = getattr(self.test_rc, kind) if os.path.lexists(link): os.unlink(link) os.symlink(os.path.join(os.path.pardir, os.path.pardir, "archive", "example.org", "{0}{1}.pem".format(kind, ver)), link) with open(link, "wb") as f: f.write(kind.encode('ascii') if value is None else value) if kind == "privkey": os.chmod(link, 0o600)
def _write_out_kind(self, kind, ver, value=None): link = getattr(self.test_rc, kind) if os.path.lexists(link): os.unlink(link) os.symlink( os.path.join(os.path.pardir, os.path.pardir, "archive", "example.org", "{0}{1}.pem".format(kind, ver)), link) with open(link, "wb") as f: f.write(kind.encode('ascii') if value is None else value) if kind == "privkey": filesystem.chmod(link, 0o600)
def test_renewal_bad_config(self): """Test that the RenewableCert constructor will complain if the renewal configuration file doesn't end in ".conf" """ from certbot._internal import storage broken = os.path.join(self.config.config_dir, "broken.conf") with open(broken, "w") as f: f.write("[No closing bracket for you!") self.assertRaises(errors.CertStorageError, storage.RenewableCert, broken, self.config) os.unlink(broken) self.assertRaises(errors.CertStorageError, storage.RenewableCert, "fun", self.config)
def test_renewal_bad_config(self): """Test that the RenewableCert constructor will complain if the renewal configuration file doesn't end in ".conf" """ from certbot import storage broken = os.path.join(self.config.config_dir, "broken.conf") with open(broken, "w") as f: f.write("[No closing bracket for you!") self.assertRaises(errors.CertStorageError, storage.RenewableCert, broken, self.config) os.unlink(broken) self.assertRaises(errors.CertStorageError, storage.RenewableCert, "fun", self.config)
def update_all_links_to(self, version): """Change all member objects to point to the specified version. :param int version: the desired version """ with error_handler.ErrorHandler(self._fix_symlinks): previous_links = self._previous_symlinks() for kind, link in previous_links: os.symlink(self.current_target(kind), link) for kind in ALL_FOUR: self._update_link_to(kind, version) for _, link in previous_links: os.unlink(link)
def test_current_target(self): # Relative path logic self._write_out_kind("cert", 17) self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"), os.path.join(self.config.config_dir, "archive", "example.org", "cert17.pem"))) # Absolute path logic os.unlink(self.test_rc.cert) os.symlink(os.path.join(self.config.config_dir, "archive", "example.org", "cert17.pem"), self.test_rc.cert) with open(self.test_rc.cert, "w") as f: f.write("cert") self.assertTrue(os.path.samefile(self.test_rc.current_target("cert"), os.path.join(self.config.config_dir, "archive", "example.org", "cert17.pem")))
def _fix_symlinks(self): """Fixes symlinks in the event of an incomplete version update. If there is no problem with the current symlinks, this function has no effect. """ previous_symlinks = self._previous_symlinks() if all(os.path.exists(link[1]) for link in previous_symlinks): for kind, previous_link in previous_symlinks: current_link = getattr(self, kind) if os.path.lexists(current_link): os.unlink(current_link) os.symlink(filesystem.readlink(previous_link), current_link) for _, link in previous_symlinks: if os.path.exists(link): os.unlink(link)
def _fix_symlinks(self): """Fixes symlinks in the event of an incomplete version update. If there is no problem with the current symlinks, this function has no effect. """ previous_symlinks = self._previous_symlinks() if all(os.path.exists(link[1]) for link in previous_symlinks): for kind, previous_link in previous_symlinks: current_link = getattr(self, kind) if os.path.lexists(current_link): os.unlink(current_link) os.symlink(os.readlink(previous_link), current_link) for _, link in previous_symlinks: if os.path.exists(link): os.unlink(link)
def test_consistent(self): # pylint: disable=protected-access oldcert = self.test_rc.cert self.test_rc.cert = "relative/path" # Absolute path for item requirement self.assertFalse(self.test_rc._consistent()) self.test_rc.cert = oldcert # Items must exist requirement self.assertFalse(self.test_rc._consistent()) # Items must be symlinks requirements fill_with_sample_data(self.test_rc) self.assertFalse(self.test_rc._consistent()) unlink_all(self.test_rc) # Items must point to desired place if they are relative for kind in ALL_FOUR: os.symlink(os.path.join("..", kind + "17.pem"), getattr(self.test_rc, kind)) self.assertFalse(self.test_rc._consistent()) unlink_all(self.test_rc) # Items must point to desired place if they are absolute for kind in ALL_FOUR: os.symlink(os.path.join(self.config.config_dir, kind + "17.pem"), getattr(self.test_rc, kind)) self.assertFalse(self.test_rc._consistent()) unlink_all(self.test_rc) # Items must point to things that exist for kind in ALL_FOUR: os.symlink( os.path.join("..", "..", "archive", "example.org", kind + "17.pem"), getattr(self.test_rc, kind)) self.assertFalse(self.test_rc._consistent()) # This version should work fill_with_sample_data(self.test_rc) self.assertTrue(self.test_rc._consistent()) # Items must point to things that follow the naming convention os.unlink(self.test_rc.fullchain) os.symlink( os.path.join("..", "..", "archive", "example.org", "fullchain_17.pem"), self.test_rc.fullchain) with open(self.test_rc.fullchain, "w") as f: f.write("wrongly-named fullchain") self.assertFalse(self.test_rc._consistent())
def test_consistent(self): # pylint: disable=too-many-statements,protected-access oldcert = self.test_rc.cert self.test_rc.cert = "relative/path" # Absolute path for item requirement self.assertFalse(self.test_rc._consistent()) self.test_rc.cert = oldcert # Items must exist requirement self.assertFalse(self.test_rc._consistent()) # Items must be symlinks requirements fill_with_sample_data(self.test_rc) self.assertFalse(self.test_rc._consistent()) unlink_all(self.test_rc) # Items must point to desired place if they are relative for kind in ALL_FOUR: os.symlink(os.path.join("..", kind + "17.pem"), getattr(self.test_rc, kind)) self.assertFalse(self.test_rc._consistent()) unlink_all(self.test_rc) # Items must point to desired place if they are absolute for kind in ALL_FOUR: os.symlink(os.path.join(self.config.config_dir, kind + "17.pem"), getattr(self.test_rc, kind)) self.assertFalse(self.test_rc._consistent()) unlink_all(self.test_rc) # Items must point to things that exist for kind in ALL_FOUR: os.symlink(os.path.join("..", "..", "archive", "example.org", kind + "17.pem"), getattr(self.test_rc, kind)) self.assertFalse(self.test_rc._consistent()) # This version should work fill_with_sample_data(self.test_rc) self.assertTrue(self.test_rc._consistent()) # Items must point to things that follow the naming convention os.unlink(self.test_rc.fullchain) os.symlink(os.path.join("..", "..", "archive", "example.org", "fullchain_17.pem"), self.test_rc.fullchain) with open(self.test_rc.fullchain, "w") as f: f.write("wrongly-named fullchain") self.assertFalse(self.test_rc._consistent())
def _delete_links_and_find_target_dir( self, server_path: str, link_func: Callable[[str], str]) -> str: """Delete symlinks and return the nonsymlinked directory path. :param str server_path: file path based on server :param callable link_func: callable that returns possible links given a server_path :returns: the final, non-symlinked target :rtype: str """ dir_path = link_func(server_path) # does an appropriate directory link to me? if so, make sure that's gone reused_servers = {} for k, v in constants.LE_REUSE_SERVERS.items(): reused_servers[v] = k # is there a next one up? possible_next_link = True while possible_next_link: possible_next_link = False if server_path in reused_servers: next_server_path = reused_servers[server_path] next_dir_path = link_func(next_server_path) if os.path.islink(next_dir_path) and filesystem.readlink( next_dir_path) == dir_path: possible_next_link = True server_path = next_server_path dir_path = next_dir_path # if there's not a next one up to delete, then delete me # and whatever I link to while os.path.islink(dir_path): target = filesystem.readlink(dir_path) os.unlink(dir_path) dir_path = target return dir_path
def _delete_links_and_find_target_dir(self, server_path, link_func): """Delete symlinks and return the nonsymlinked directory path. :param str server_path: file path based on server :param callable link_func: callable that returns possible links given a server_path :returns: the final, non-symlinked target :rtype: str """ dir_path = link_func(server_path) # does an appropriate directory link to me? if so, make sure that's gone reused_servers = {} for k in constants.LE_REUSE_SERVERS: reused_servers[constants.LE_REUSE_SERVERS[k]] = k # is there a next one up? possible_next_link = True while possible_next_link: possible_next_link = False if server_path in reused_servers: next_server_path = reused_servers[server_path] next_dir_path = link_func(next_server_path) if os.path.islink(next_dir_path) and os.readlink(next_dir_path) == dir_path: possible_next_link = True server_path = next_server_path dir_path = next_dir_path # if there's not a next one up to delete, then delete me # and whatever I link to while os.path.islink(dir_path): target = os.readlink(dir_path) os.unlink(dir_path) dir_path = target return dir_path
def test_names(self): # Trying the current version self._write_out_kind("cert", 12, test_util.load_vector("cert-san_512.pem")) self.assertEqual(self.test_rc.names(), ["example.com", "www.example.com"]) # Trying a non-current version self._write_out_kind("cert", 15, test_util.load_vector("cert_512.pem")) self.assertEqual(self.test_rc.names(12), ["example.com", "www.example.com"]) # Testing common name is listed first self._write_out_kind("cert", 12, test_util.load_vector("cert-5sans_512.pem")) self.assertEqual(self.test_rc.names(12), ["example.com"] + ["{0}.example.com".format(c) for c in "abcd"]) # Trying missing cert os.unlink(self.test_rc.cert) self.assertRaises(errors.CertStorageError, self.test_rc.names)
def test_names(self): # Trying the current version self._write_out_kind("cert", 12, test_util.load_vector("cert-san_512.pem")) self.assertEqual(self.test_rc.names(), ["example.com", "www.example.com"]) # Trying a non-current version self._write_out_kind("cert", 15, test_util.load_vector("cert_512.pem")) self.assertEqual(self.test_rc.names(12), ["example.com", "www.example.com"]) # Testing common name is listed first self._write_out_kind( "cert", 12, test_util.load_vector("cert-5sans_512.pem")) self.assertEqual( self.test_rc.names(12), ["example.com"] + ["{0}.example.com".format(c) for c in "abcd"]) # Trying missing cert os.unlink(self.test_rc.cert) self.assertRaises(errors.CertStorageError, self.test_rc.names)
def unlink_all(rc_object): """Unlink all four items associated with this RenewableCert.""" for kind in ALL_FOUR: os.unlink(getattr(rc_object, kind))