diff --git a/pyproject.toml b/pyproject.toml index 1d3ebb0..90df8a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,9 +22,9 @@ dependencies = [ # direct dependencies "bsdiff4==1.2.*", "packaging==21.*", - "securesystemslib[crypto,pynacl]==0.24.*", + "securesystemslib[crypto,pynacl]>=0.26.0", "setuptools>=65.5.1", - "tuf==2.0.*", + "tuf==3.0.*", # constraints on sub-dependencies "certifi>=2022.12.7", "cryptography>=38.0.3", diff --git a/src/tufup/client.py b/src/tufup/client.py index 4f09845..c08327a 100644 --- a/src/tufup/client.py +++ b/src/tufup/client.py @@ -10,10 +10,7 @@ import requests from requests.auth import AuthBase from tuf.api.exceptions import DownloadError, UnsignedMetadataError -from tuf.api.metadata import TargetFile import tuf.ngclient -# RequestsFetcher is "private", but we'll just have to live with that, for now. -from tuf.ngclient._internal.requests_fetcher import RequestsFetcher # noqa from tufup.common import TargetMeta from tufup.utils.platform_specific import install_update @@ -53,7 +50,7 @@ def __init__( self.current_archive = TargetMeta(name=app_name, version=current_version) self.current_archive_local_path = target_dir / self.current_archive.path self.new_archive_local_path: Optional[pathlib.Path] = None - self.new_archive_info: Optional[TargetFile] = None + self.new_archive_info: Optional[tuf.ngclient.TargetFile] = None self.new_targets: Optional[dict] = None self.downloaded_target_files = {} @@ -77,7 +74,7 @@ def trusted_target_metas(self) -> list: logger.warning('targets metadata not found') return _trusted_target_metas - def get_targetinfo(self, target_path: Union[str, TargetMeta]) -> Optional[TargetFile]: + def get_targetinfo(self, target_path: Union[str, TargetMeta]) -> Optional[tuf.ngclient.TargetFile]: """Extend Updater.get_targetinfo to handle TargetMeta input args.""" if isinstance(target_path, TargetMeta): target_path = target_path.target_path_str @@ -284,7 +281,8 @@ def _apply_updates( # todo: clean up deprecated local archive -class AuthRequestsFetcher(RequestsFetcher): +class AuthRequestsFetcher(tuf.ngclient.RequestsFetcher): + # RequestsFetcher is public as of python-tuf v2.1.0 (see python-tuf #2277) def __init__( self, session_auth: Optional[Dict[str, Union[Tuple[str, str], AuthBase]]] = None, diff --git a/src/tufup/repo/__init__.py b/src/tufup/repo/__init__.py index 34a4d76..f004765 100644 --- a/src/tufup/repo/__init__.py +++ b/src/tufup/repo/__init__.py @@ -19,11 +19,11 @@ import_ed25519_publickey_from_file, import_ed25519_privatekey_from_file, ) -from securesystemslib.signer import SSlibSigner +# SSlibKey see: https://github.com/secure-systems-lab/securesystemslib/pull/456 +from securesystemslib.signer import SSlibKey, SSlibSigner from tuf.api.metadata import ( SPECIFICATION_VERSION, TOP_LEVEL_ROLE_NAMES, - Key, Metadata, MetaFile, Role, @@ -238,7 +238,7 @@ def public_key_path(self, key_name: str) -> pathlib.Path: def public(self): # return a dict that maps key ids to *public* key objects return { - ssl_key['keyid']: Key.from_securesystemslib_key(key_dict=ssl_key) + ssl_key['keyid']: SSlibKey.from_securesystemslib_key(key_dict=ssl_key) for attr_name, ssl_keys in vars(self).items() if attr_name in TOP_LEVEL_ROLE_NAMES for ssl_key in ssl_keys @@ -391,7 +391,7 @@ def add_public_key( # based on python-tuf basic_repo.py ssl_key = import_ed25519_publickey_from_file(filepath=str(public_key_path)) self.root.signed.add_key( - role=role_name, key=Key.from_securesystemslib_key(ssl_key) + role=role_name, key=SSlibKey.from_securesystemslib_key(ssl_key) ) def set_signature_threshold(self, role_name: str, threshold: int): diff --git a/tests/test_client.py b/tests/test_client.py index 3cb3f05..57c1782 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -8,7 +8,7 @@ from requests.auth import HTTPBasicAuth import tuf.api.exceptions -from tuf.api.metadata import TargetFile +from tuf.ngclient import TargetFile from tests import TempDirTestCase, TEST_REPO_DIR from tufup.client import AuthRequestsFetcher, Client @@ -90,7 +90,8 @@ def test_init(self): self.assertTrue(client._trusted_set.root) # other metadata is not available yet for role_name in ['targets', 'snapshot', 'timestamp']: - self.assertIsNone(getattr(client._trusted_set, role_name)) + # see python-tuf #2250 + self.assertNotIn(role_name, client._trusted_set) def test_trusted_target_metas(self): client = self.get_refreshed_client() @@ -249,6 +250,7 @@ def test_fetch_basic_auth(self): scheme_and_server: HTTPBasicAuth(username=user, password=passwd) } fetcher = AuthRequestsFetcher(session_auth=session_auth) + fetcher.socket_timeout = 30 # in case httpbin.org is slow to respond # we don't have direct access to the response, so we'll just check # that RequestsFetcher.fetch() doesn't raise an error, such as a # status "401 Unauthorized" or "403 Forbidden"