def status(self): """ <Purpose> Determine the status of the project, including its delegated roles. status() checks if each role provides sufficient public keys, signatures, and that a valid metadata file is generated if write() were to be called. Metadata files are temporarily written to check that proper metadata files is written, where file hashes and lengths are calculated and referenced by the project. status() does not do a simple check for number of threshold keys and signatures. <Arguments> None. <Exceptions> securesystemslib.exceptions.Error, if the project, or any of its delegated roles, do not have a minimum threshold of signatures. <Side Effects> Generates and writes temporary metadata files. <Returns> None. """ temp_project_directory = None try: temp_project_directory = tempfile.mkdtemp() metadata_directory = os.path.join(temp_project_directory, 'metadata') targets_directory = self.targets_directory os.makedirs(metadata_directory) # TODO: We should do the schema check. filenames = {} filenames['targets'] = os.path.join(metadata_directory, self.project_name) # Delegated roles. delegated_roles = roledb.get_delegated_rolenames( self.project_name, self.repository_name) insufficient_keys = [] insufficient_signatures = [] for delegated_role in delegated_roles: try: _check_role_keys(delegated_role, self.repository_name) except exceptions.InsufficientKeysError: insufficient_keys.append(delegated_role) continue try: signable = _generate_and_write_metadata( delegated_role, filenames['targets'], False, targets_directory, False, repository_name=self.repository_name) self._log_status(delegated_role, signable[0], self.repository_name) except sslib_exceptions.Error: insufficient_signatures.append(delegated_role) if len(insufficient_keys): message = 'Delegated roles with insufficient keys: ' +\ repr(insufficient_keys) logger.info(message) return if len(insufficient_signatures): message = 'Delegated roles with insufficient signatures: ' +\ repr(insufficient_signatures) logger.info(message) return # Targets role. try: _check_role_keys(self.rolename, self.repository_name) except exceptions.InsufficientKeysError as e: logger.info(str(e)) return try: signable, junk = _generate_and_write_metadata( self.project_name, filenames['targets'], False, targets_directory, metadata_directory, self.repository_name) self._log_status(self.project_name, signable, self.repository_name) except exceptions.UnsignedMetadataError as e: # This error is raised if the metadata has insufficient signatures to # meet the threshold. self._log_status(self.project_name, e.signable, self.repository_name) return finally: shutil.rmtree(temp_project_directory, ignore_errors=True)
def write(self, write_partial=False): """ <Purpose> Write all the JSON Metadata objects to their corresponding files. write() raises an exception if any of the role metadata to be written to disk is invalid, such as an insufficient threshold of signatures, missing private keys, etc. <Arguments> write_partial: A boolean indicating whether partial metadata should be written to disk. Partial metadata may be written to allow multiple maintainters to independently sign and update role metadata. write() raises an exception if a metadata role cannot be written due to not having enough signatures. <Exceptions> securesystemslib.exceptions.Error, if any of the project roles do not have a minimum threshold of signatures. <Side Effects> Creates metadata files in the project's metadata directory. <Returns> None. """ # Does 'write_partial' have the correct format? # Ensure the arguments have the appropriate number of objects and object # types, and that all dict keys are properly named. # Raise 'securesystemslib.exceptions.FormatError' if any are improperly formatted. sslib_formats.BOOLEAN_SCHEMA.check_match(write_partial) # At this point the keydb and roledb stores must be fully # populated, otherwise write() throwns a 'tuf.Repository' exception if # any of the project roles are missing signatures, keys, etc. # Write the metadata files of all the delegated roles of the project. delegated_rolenames = roledb.get_delegated_rolenames( self.project_name, self.repository_name) for delegated_rolename in delegated_rolenames: delegated_filename = os.path.join( self.metadata_directory, delegated_rolename + METADATA_EXTENSION) # Ensure the parent directories of 'metadata_filepath' exist, otherwise an # IO exception is raised if 'metadata_filepath' is written to a # sub-directory. sslib_util.ensure_parent_dir(delegated_filename) _generate_and_write_metadata(delegated_rolename, delegated_filename, write_partial, self.targets_directory, prefix=self.prefix, repository_name=self.repository_name) # Generate the 'project_name' metadata file. targets_filename = self.project_name + METADATA_EXTENSION targets_filename = os.path.join(self.metadata_directory, targets_filename) junk, targets_filename = _generate_and_write_metadata( self.project_name, targets_filename, write_partial, self.targets_directory, prefix=self.prefix, repository_name=self.repository_name) # Save configuration information that is not stored in the project's # metadata _save_project_configuration(self.metadata_directory, self.targets_directory, self.keys, self.prefix, self.threshold, self.layout_type, self.project_name)