Merge pull request #1654 from jku/make-refresh-optional

ngclient: Implicitly call refresh()
This commit is contained in:
Teodora Sechkova 2021-11-17 10:50:34 +02:00 committed by GitHub
commit 67ff0424e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 35 deletions

View file

@ -282,6 +282,20 @@ def test_refresh_with_only_local_root(self):
expected_files = ["role1", "root", "snapshot", "targets", "timestamp"]
self._assert_files(expected_files)
def test_implicit_refresh_with_only_local_root(self):
os.remove(os.path.join(self.client_directory, "timestamp.json"))
os.remove(os.path.join(self.client_directory, "snapshot.json"))
os.remove(os.path.join(self.client_directory, "targets.json"))
os.remove(os.path.join(self.client_directory, "role1.json"))
os.remove(os.path.join(self.client_directory, "role2.json"))
os.remove(os.path.join(self.client_directory, "1.root.json"))
self._assert_files(["root"])
# Get targetinfo for 'file3.txt' listed in the delegated role1
targetinfo3 = self.updater.get_targetinfo("file3.txt")
expected_files = ["role1", "root", "snapshot", "targets", "timestamp"]
self._assert_files(expected_files)
def test_both_target_urls_not_set(self):
# target_base_url = None and Updater._target_base_url = None
updater = ngclient.Updater(

View file

@ -10,24 +10,23 @@
secure manner: All downloaded files are verified by signed metadata.
High-level description of Updater functionality:
* Initializing an :class:`~tuf.ngclient.updater.Updater` loads and validates
the trusted local root metadata: This root metadata is used as the source
of trust for all other metadata.
* Calling :func:`~tuf.ngclient.updater.Updater.refresh()` will update root
metadata and load all other top-level metadata as described in the
specification, using both locally cached metadata and metadata downloaded
from the remote repository.
* When metadata is up-to-date, targets can be dowloaded. The repository
snapshot is consistent so multiple targets can be downloaded without
fear of repository content changing. For each target:
* Initializing an ``Updater`` loads and validates the trusted local root
metadata: This root metadata is used as the source of trust for all other
metadata.
* ``refresh()`` can optionally be called to update and load all top-level
metadata as described in the specification, using both locally cached
metadata and metadata downloaded from the remote repository. If refresh is
not done explicitly, it will happen automatically during the first target
info lookup.
* Updater can be used to download targets. For each target:
* :func:`~tuf.ngclient.updater.Updater.get_targetinfo()` is
used to find information about a specific target. This will load new
targets metadata as needed (from local cache or remote repository).
* :func:`~tuf.ngclient.updater.Updater.find_cached_target()` can be used
to check if a target file is already locally cached.
* :func:`~tuf.ngclient.updater.Updater.download_target()` downloads a
target file and ensures it is verified correct by the metadata.
* ``Updater.get_targetinfo()`` is first used to find information about a
specific target. This will load new targets metadata as needed (from
local cache or remote repository).
* ``Updater.find_cached_target()`` can optionally be used to check if a
target file is already locally cached.
* ``Updater.download_target()`` downloads a target file and ensures it is
verified correct by the metadata.
Below is a simple example of using the Updater to download and verify
"file.txt" from a remote repository. The required environment for this example
@ -52,9 +51,6 @@
target_base_url="http://localhost:8000/targets/",
)
# Update top-level metadata from remote
updater.refresh()
# Update metadata, then download target if needed
info = updater.get_targetinfo("file.txt")
path = updater.find_cached_target(info)
@ -130,11 +126,16 @@ def refresh(self) -> None:
specified order (root -> timestamp -> snapshot -> targets) implementing
all the checks required in the TUF client workflow.
The metadata for delegated roles are not refreshed by this method as
that happens on demand during get_targetinfo().
A ``refresh()`` can be done only once during the lifetime of an Updater.
If ``refresh()`` has not been explicitly called before the first
``get_targetinfo()`` call, it will be done implicitly at that time.
The refresh() method should be called by the client before any other
method calls.
The metadata for delegated roles is not updated by ``refresh()``:
that happens on demand during ``get_targetinfo()``. However, if the
repository uses `consistent_snapshot
<https://theupdateframework.github.io/specification/latest/#consistent-snapshots>`_,
then all metadata downloaded downloaded by the Updater will use the same
consistent repository state.
Raises:
OSError: New metadata could not be written to disk
@ -159,22 +160,18 @@ def get_targetinfo(self, target_path: str) -> Optional[TargetFile]:
"""Returns TargetFile instance with information for 'target_path'.
The return value can be used as an argument to
:func:`download_target()` and :func:`find_cached_target()`.
:func:`refresh()` must be called before calling
`get_targetinfo()`. Subsequent calls to
`get_targetinfo()` will use the same consistent repository
state: Changes that happen in the repository between calling
:func:`refresh()` and `get_targetinfo()` will not be
seen by the updater.
``download_target()`` and ``find_cached_target()``.
If ``refresh()`` has not been called before calling
``get_targetinfo()``, the refresh will be done implicitly.
As a side-effect this method downloads all the additional (delegated
targets) metadata it needs to return the target information.
Args:
target_path: A target identifier that is a path-relative-URL string
(https://url.spec.whatwg.org/#path-relative-url-string).
Typically this is also the unix file path of the eventually
downloaded file.
target_path: A `path-relative-URL string
<https://url.spec.whatwg.org/#path-relative-url-string>`_
that uniquely identifies the target within the repository.
Raises:
OSError: New metadata could not be written to disk
@ -184,6 +181,9 @@ def get_targetinfo(self, target_path: str) -> Optional[TargetFile]:
Returns:
A TargetFile instance or None.
"""
if self._trusted_set.targets is None:
self.refresh()
return self._preorder_depth_first_walk(target_path)
def find_cached_target(