-
Notifications
You must be signed in to change notification settings - Fork 281
Fix 736: Rebuild roledb in refresh() even if root unchanged #737
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e18d3db
47dbdba
18a5aa4
43efa42
e92680f
9b6c91a
f493161
621ec3a
6373c26
e9cd01e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -209,11 +209,16 @@ def test_update(self): | |
| self.assertEqual(sorted(['role1', 'root', 'snapshot', 'targets', 'timestamp']), | ||
| sorted(tuf.roledb.get_rolenames('test_repository2'))) | ||
|
|
||
| # Note: refresh() resets the known metadata and updates the latest | ||
| # top-level metadata. | ||
| self.repository_updater.refresh() | ||
|
|
||
| self.assertEqual(sorted(['role1', 'root', 'snapshot', 'targets', 'timestamp']), | ||
| self.assertEqual(sorted(['root', 'snapshot', 'targets', 'timestamp']), | ||
| sorted(tuf.roledb.get_rolenames('test_repository1'))) | ||
| self.assertEqual(sorted(['role1', 'root', 'snapshot', 'targets', 'timestamp']), | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by this change. How did this result from the PR? Edit: ah, probably from the targets_of_role change...?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. refresh() rebuilds the roledb and only refreshes top-level metadata. See line 212. |
||
|
|
||
| # test_repository2 wasn't refreshed and should still know about delegated | ||
| # roles. | ||
| self.assertEqual(sorted(['root', 'role1', 'snapshot', 'targets', 'timestamp']), | ||
| sorted(tuf.roledb.get_rolenames('test_repository2'))) | ||
|
|
||
| # 'role1.json' should be downloaded, because it provides info for the | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -873,8 +873,9 @@ def _rebuild_key_and_role_db(self): | |
| Non-public method that rebuilds the key and role databases from the | ||
| currently trusted 'root' metadata object extracted from 'root.json'. | ||
| This private method is called when a new/updated 'root' metadata file is | ||
| loaded. This method will only store the role information of the | ||
| top-level roles (i.e., 'root', 'targets', 'snapshot', 'timestamp'). | ||
| loaded or when updater.refresh() is called. This method will only store | ||
| the role information of the top-level roles (i.e., 'root', 'targets', | ||
| 'snapshot', 'timestamp'). | ||
|
|
||
| <Arguments> | ||
| None. | ||
|
|
@@ -903,6 +904,7 @@ def _rebuild_key_and_role_db(self): | |
| # reloading delegated roles is not required here. | ||
| tuf.keydb.create_keydb_from_root_metadata(self.metadata['current']['root'], | ||
| self.repository_name) | ||
|
|
||
| tuf.roledb.create_roledb_from_root_metadata(self.metadata['current']['root'], | ||
| self.repository_name) | ||
|
|
||
|
|
@@ -1081,9 +1083,21 @@ def refresh(self, unsafely_update_root_if_necessary=True): | |
| # do we blindly trust the downloaded root metadata here? | ||
| self._update_root_metadata(root_metadata) | ||
|
|
||
| # Ensure that the role and key information of the top-level roles is the | ||
| # latest. We do this whether or not Root needed to be updated, in order to | ||
| # ensure that, e.g., the entries in roledb for top-level roles are | ||
| # populated with expected keyid info so that roles can be validated. In | ||
| # certain circumstances, top-level metadata might be missing because it was | ||
| # marked obsolete and deleted after a failed attempt, and thus we should | ||
| # refresh them here as a protective measure. See Issue #736. | ||
| self._rebuild_key_and_role_db() | ||
| self.consistent_snapshot = \ | ||
| self.metadata['current']['root']['consistent_snapshot'] | ||
|
|
||
| # Use default but sane information for timestamp metadata, and do not | ||
| # require strict checks on its required length. | ||
| self._update_metadata('timestamp', DEFAULT_TIMESTAMP_UPPERLENGTH) | ||
|
|
||
| # TODO: After fetching snapshot.json, we should either verify the root | ||
| # fileinfo referenced there matches what was fetched earlier in | ||
| # _update_root_metadata() or make another attempt to download root.json. | ||
|
|
@@ -1663,10 +1677,12 @@ def _update_metadata(self, metadata_role, upperbound_filelength, version=None): | |
| """ | ||
| <Purpose> | ||
| Non-public method that downloads, verifies, and 'installs' the metadata | ||
| belonging to 'metadata_role'. Calling this method implies the metadata | ||
| has been updated by the repository and thus needs to be re-downloaded. | ||
| The current and previous metadata stores are updated if the newly | ||
| downloaded metadata is successfully downloaded and verified. | ||
| belonging to 'metadata_role'. Calling this method implies that the | ||
| 'metadata_role' on the repository is newer than the client's, and thus | ||
| needs to be re-downloaded. The current and previous metadata stores are | ||
| updated if the newly downloaded metadata is successfully downloaded and | ||
| verified. This method also assumes that the store of top-level metadata | ||
| is the latest and exists. | ||
|
|
||
| <Arguments> | ||
| metadata_role: | ||
|
|
@@ -1773,12 +1789,6 @@ def _update_metadata(self, metadata_role, upperbound_filelength, version=None): | |
| self.metadata['current'][metadata_role] = updated_metadata_object | ||
| self._update_versioninfo(metadata_filename) | ||
|
|
||
| # Ensure the role and key information of the top-level roles is also updated | ||
| # according to the newly-installed Root metadata. | ||
| if metadata_role == 'root': | ||
| self._rebuild_key_and_role_db() | ||
| self.consistent_snapshot = updated_metadata_object['consistent_snapshot'] | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
@@ -2584,11 +2594,11 @@ def targets_of_role(self, rolename='targets'): | |
| # Raise 'securesystemslib.exceptions.FormatError' if there is a mismatch. | ||
| securesystemslib.formats.RELPATH_SCHEMA.check_match(rolename) | ||
|
|
||
| self._refresh_targets_metadata(refresh_all_delegated_roles=True) | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is refresh_all_delegated_roles necessary? This could be quite a few roles. Next point is a bit complex: This more complex point plays into the correct way to run targets_of_role on a delegated role.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thinking is that Community repos will certainly use get_one_valid_targetinfo(), but we might want to provide all_targets() and targets_of_roles() for simpler use cases / adopters.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After an offline discussion, @awwad and I came to the conclusion that it's better to not support |
||
| if not tuf.roledb.role_exists(rolename, self.repository_name): | ||
| raise tuf.exceptions.UnknownRoleError(rolename) | ||
|
|
||
| self._refresh_targets_metadata(rolename) | ||
|
|
||
| return self._targets_of_role(rolename, skip_refresh=True) | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test looks good.