From 42771cc3c91fad2f7db8b8e1574d7a715ccdf3ee Mon Sep 17 00:00:00 2001 From: Renata Date: Sat, 4 Jan 2025 18:13:51 -0500 Subject: [PATCH] feat: add an option to update certain metadata files when signing target files, even if no targets were modified --- taf/api/targets.py | 17 +++++++++++++++-- taf/tuf/repository.py | 32 +++++++++++++++++++------------- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/taf/api/targets.py b/taf/api/targets.py index de65b6d9..7a30051c 100644 --- a/taf/api/targets.py +++ b/taf/api/targets.py @@ -329,6 +329,7 @@ def register_target_files( no_commit_warning: Optional[bool] = True, reset_updated_targets_on_error: Optional[bool] = False, commit_msg: Optional[str] = None, + force_update_of_roles: Optional[str] = None, ): """ Register all files found in the target directory as targets - update the targets @@ -338,12 +339,14 @@ def register_target_files( path: Authentication repository's path. keystore: Location of the keystore files. roles_key_infos: A dictionary whose keys are role names, while values contain information about the keys. - scheme (optional): Signing scheme. Set to rsa-pkcs1v15-sha256 by default. + scheme (optional): Signing scheme. Set to rsa-pkcs1v15-sha256 by default. auth_repo (optional): If auth repository is already initialized, it can be passed and used. write (optional): Write metadata updates to disk if set to True commit (optional): Indicates if the changes should be committed and pushed automatically. prompt_for_keys (optional): Whether to ask the user to enter their key if it is not located inside the keystore directory. push (optional): Flag specifying whether to push to remote + force_update_of_roles (optional): A list of roles whose version should be updated, even + if no other changes are made Side Effects: Updates metadata files, writes changes to disk and optionally commits changes. @@ -376,9 +379,14 @@ def register_target_files( roles_key_infos, keystore, enter_info=False ) + roles_to_load = list(roles_and_targets.keys()) + if force_update_of_roles: + for role in force_update_of_roles: + if role not in roles_to_load: + roles_to_load.append(role) with manage_repo_and_signers( auth_repo, - list(roles_and_targets.keys()), + roles_to_load, keystore, scheme, prompt_for_keys, @@ -394,6 +402,11 @@ def register_target_files( ): for role, targets in roles_and_targets.items(): auth_repo.update_target_role(role, targets) + if force_update_of_roles: + for role in force_update_of_roles: + if role not in roles_and_targets: + auth_repo.update_target_role(role, None, True) + if update_snapshot_and_timestamp: auth_repo.update_snapshot_and_timestamp() diff --git a/taf/tuf/repository.py b/taf/tuf/repository.py index 9086792a..e8de2153 100644 --- a/taf/tuf/repository.py +++ b/taf/tuf/repository.py @@ -1608,29 +1608,35 @@ def sync_snapshot_with_roles(self, roles: List[str]) -> None: for role in roles: sn.meta[f"{role}.json"].version = sn.meta[f"{role}.json"].version + 1 - def update_target_role(self, role: str, target_paths: Dict): + def update_target_role(self, role: str, target_paths: Dict, force=False): """ Update the specified target role by adding or removing target files and target objects for the specified target paths + If false is True, update the metadata files even if no target + paths are specified """ if not self.check_if_role_exists(role): raise TAFError(f"Role {role} does not exist") self.verify_signers_loaded([role]) removed_paths = [] target_files = [] - for target_path in target_paths: - full_path = self.path / TARGETS_DIRECTORY_NAME / target_path - # file removed, removed from te role - if not full_path.is_file(): - removed_paths.append(target_path) - else: - custom_data = self.get_target_file_custom_data(target_path) - target_file = self._create_target_object( - full_path, target_path, custom_data - ) - target_files.append(target_file) + if target_paths: + for target_path in target_paths: + full_path = self.path / TARGETS_DIRECTORY_NAME / target_path + # file removed, removed from te role + if not full_path.is_file(): + removed_paths.append(target_path) + else: + custom_data = self.get_target_file_custom_data(target_path) + target_file = self._create_target_object( + full_path, target_path, custom_data + ) + target_files.append(target_file) - self._modify_targets_role(target_files, removed_paths, role) + self._modify_targets_role(target_files, removed_paths, role) + elif force: + with self.edit(role) as _: + pass def update_snapshot_and_timestamp(self, force: Optional[bool] = True): """