diff --git a/tuf/client/updater.py b/tuf/client/updater.py index 7dfe0435..926a5132 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -1892,7 +1892,9 @@ def all_targets(self): Get a list of the target information for all the trusted targets on the repository. This list also includes all the targets of - delegated roles. The list conforms to 'tuf.formats.TARGETFILES_SCHEMA' + delegated roles. Targets of the list returned are ordered according + the trusted order of the delegated roles, where parent roles come before + children. The list conforms to 'tuf.formats.TARGETFILES_SCHEMA' and has the form: [{'filepath': 'a/b/c.txt', @@ -1923,11 +1925,12 @@ def all_targets(self): self._refresh_targets_metadata(include_delegations=True) all_targets = [] + # Fetch the targets for the 'targets' role. all_targets = self._targets_of_role('targets', skip_refresh=True) - # Fetch the targets for the delegated roles. - for delegated_role in tuf.roledb.get_delegated_rolenames('targets'): + # Fetch the targets of the delegated roles. + for delegated_role in sorted(tuf.roledb.get_delegated_rolenames('targets')): all_targets = self._targets_of_role(delegated_role, all_targets, skip_refresh=True) @@ -2061,7 +2064,7 @@ def refresh_targets_metadata_chain(self, rolename): if it has expired. - None. + A list of the roles that have been updated, loaded, and are valid. """ # Do the arguments have the correct format? @@ -2093,7 +2096,7 @@ def refresh_targets_metadata_chain(self, rolename): parent_roles.append(roles_added+'/'+next_role) roles_added = roles_added+'/'+next_role - message = 'Minimum metadata to download to set the chain of trust: '+\ + message = 'Minimum metadata to download and set the chain of trust: '+\ repr(parent_roles)+'.' logger.info(message) @@ -2127,7 +2130,8 @@ def refresh_targets_metadata_chain(self, rolename): logger.debug('Roles to update: '+repr(parent_roles)+'.') # Iterate 'parent_roles', load each role's metadata file from disk, and - # update it if it has changed. + # update it if it has changed. + refreshed_chain = [] for rolename in parent_roles: self._load_metadata_from_file('previous', rolename) self._load_metadata_from_file('current', rolename) @@ -2137,9 +2141,12 @@ def refresh_targets_metadata_chain(self, rolename): # Remove the role if it has expired. try: self._ensure_not_expired(rolename) + refreshed_chain.append(rolename) except tuf.ExpiredMetadataError: tuf.roledb.remove_role(rolename) + return refreshed_chain + @@ -2150,6 +2157,7 @@ def _targets_of_role(self, rolename, targets=None, skip_refresh=False): Return the target information for all the targets of 'rolename'. The returned information is a list conformant to 'tuf.formats.TARGETFILES_SCHEMA' and has the form: + [{'filepath': 'a/b/c.txt', 'fileinfo': {'length': 13323, 'hashes': {'sha256': dbfac345..}} @@ -2195,7 +2203,7 @@ def _targets_of_role(self, rolename, targets=None, skip_refresh=False): # Do we have metadata for 'rolename'? if rolename not in self.metadata['current']: - message = 'No metadata for '+rolename+'. Unable to determine targets.' + message = 'No metadata for '+repr(rolename)+'. Unable to determine targets.' logger.debug(message) return targets @@ -2219,13 +2227,11 @@ def targets_of_role(self, rolename='targets'): Return a list of trusted targets directly specified by 'rolename'. The returned information is a list conformant to tuf.formats.TARGETFILES_SCHEMA and has the form: + [{'filepath': 'a/b/c.txt', 'fileinfo': {'length': 13323, 'hashes': {'sha256': dbfac345..}} ...] - - This may be a very slow operation if there is a large number of - delegations and many metadata files aren't already downloaded. rolename: @@ -2253,8 +2259,9 @@ def targets_of_role(self, rolename='targets'): # Raise 'tuf.FormatError' if there is a mismatch. tuf.formats.RELPATH_SCHEMA.check_match(rolename) + self.refresh_targets_metadata_chain(rolename) self._refresh_targets_metadata(rolename) - + return self._targets_of_role(rolename, skip_refresh=True) @@ -2641,6 +2648,7 @@ def updated_targets(self, targets, destination_directory): The returned information is a list conformant to 'tuf.formats.TARGETFILES_SCHEMA' and has the form: + [{'filepath': 'a/b/c.txt', 'fileinfo': {'length': 13323, 'hashes': {'sha256': dbfac345..}} @@ -2648,7 +2656,8 @@ def updated_targets(self, targets, destination_directory): targets: - A list of target files. + A list of target files. Targets that come earlier in the list are + chosen over duplicates that may occur later. destination_directory: The directory containing the target files. @@ -2669,13 +2678,20 @@ def updated_targets(self, targets, destination_directory): tuf.formats.TARGETFILES_SCHEMA.check_match(targets) tuf.formats.PATH_SCHEMA.check_match(destination_directory) + # Keep track of the target objects and filepaths of updated targets. + # Return 'updated_targets' and use 'updated_targetpaths' to avoid + # duplicates. updated_targets = [] + updated_targetpaths = [] for target in targets: # Get the target's filepath located in 'destination_directory'. # We will compare targets against this file. target_filepath = os.path.join(destination_directory, target['filepath']) + if target_filepath in updated_targetpaths: + continue + # Try one of the algorithm/digest combos for a mismatch. We break # as soon as we find a mismatch. for algorithm, digest in target['fileinfo']['hashes'].items(): @@ -2683,13 +2699,17 @@ def updated_targets(self, targets, destination_directory): try: digest_object = tuf.hash.digest_filename(target_filepath, algorithm=algorithm) + # This exception would occur if the target does not exist locally. except IOError: updated_targets.append(target) + updated_targetpaths.append(target_filepath) break + # The file does exist locally, check if its hash differs. if digest_object.hexdigest() != digest: updated_targets.append(target) + updated_targetpaths.append(target_filepath) break return updated_targets diff --git a/tuf/libtuf.py b/tuf/libtuf.py index f36d8712..48142152 100755 --- a/tuf/libtuf.py +++ b/tuf/libtuf.py @@ -2658,7 +2658,7 @@ def load_repository(repository_directory): targets_objects[tuf.roledb.get_parent_rolename(metadata_name)] targets_objects[metadata_name] = new_targets_object - self._delegated_roles[(os.path.basename(metadata_name))] = \ + targets_object._delegated_roles[(os.path.basename(metadata_name))] = \ new_targets_object # Add the keys specified in the delegations field of the Targets role.