|
| 1 | +#!/usr/bin/env python |
| 2 | +"""Conformance client for python-tuf, part of tuf-conformance""" |
| 3 | + |
| 4 | +# Copyright 2024 tuf-conformance contributors |
| 5 | +# SPDX-License-Identifier: MIT OR Apache-2.0 |
| 6 | + |
| 7 | +import argparse |
| 8 | +import os |
| 9 | +import shutil |
| 10 | +import sys |
| 11 | +from datetime import datetime, timedelta, timezone |
| 12 | + |
| 13 | +from tuf.ngclient import Updater, UpdaterConfig |
| 14 | + |
| 15 | + |
| 16 | +def init(metadata_dir: str, trusted_root: str) -> None: |
| 17 | + """Initialize local trusted metadata""" |
| 18 | + |
| 19 | + # No need to actually run python-tuf code at this point |
| 20 | + shutil.copyfile(trusted_root, os.path.join(metadata_dir, "root.json")) |
| 21 | + print(f"python-tuf test client: Initialized repository in {metadata_dir}") |
| 22 | + |
| 23 | + |
| 24 | +def refresh( |
| 25 | + metadata_url: str, |
| 26 | + metadata_dir: str, |
| 27 | + days_in_future: str, |
| 28 | + max_root_rotations: int, |
| 29 | +) -> None: |
| 30 | + """Refresh local metadata from remote""" |
| 31 | + |
| 32 | + updater = Updater( |
| 33 | + metadata_dir, |
| 34 | + metadata_url, |
| 35 | + config=UpdaterConfig(max_root_rotations=int(max_root_rotations)), |
| 36 | + ) |
| 37 | + if days_in_future != "0": |
| 38 | + day_int = int(days_in_future) |
| 39 | + day_in_future = datetime.now(timezone.utc) + timedelta(days=day_int) |
| 40 | + updater._trusted_set.reference_time = day_in_future # noqa: SLF001 |
| 41 | + updater.refresh() |
| 42 | + print(f"python-tuf test client: Refreshed metadata in {metadata_dir}") |
| 43 | + |
| 44 | + |
| 45 | +def download_target( |
| 46 | + metadata_url: str, |
| 47 | + metadata_dir: str, |
| 48 | + target_name: str, |
| 49 | + download_dir: str, |
| 50 | + target_base_url: str, |
| 51 | +) -> None: |
| 52 | + """Download target.""" |
| 53 | + |
| 54 | + updater = Updater( |
| 55 | + metadata_dir, |
| 56 | + metadata_url, |
| 57 | + download_dir, |
| 58 | + target_base_url, |
| 59 | + config=UpdaterConfig(prefix_targets_with_hash=False), |
| 60 | + ) |
| 61 | + target_info = updater.get_targetinfo(target_name) |
| 62 | + if not target_info: |
| 63 | + raise RuntimeError(f"{target_name} not found in repository") |
| 64 | + updater.download_target(target_info) |
| 65 | + |
| 66 | + |
| 67 | +def main() -> int: |
| 68 | + """Main TUF Client Example function""" |
| 69 | + |
| 70 | + parser = argparse.ArgumentParser(description="TUF Client Example") |
| 71 | + parser.add_argument("--metadata-url", required=False) |
| 72 | + parser.add_argument("--metadata-dir", required=True) |
| 73 | + parser.add_argument("--target-name", required=False) |
| 74 | + parser.add_argument("--target-dir", required=False) |
| 75 | + parser.add_argument("--target-base-url", required=False) |
| 76 | + parser.add_argument("--days-in-future", required=False, default="0") |
| 77 | + parser.add_argument( |
| 78 | + "--max-root-rotations", required=False, default=32, type=int |
| 79 | + ) |
| 80 | + |
| 81 | + sub_command = parser.add_subparsers(dest="sub_command") |
| 82 | + init_parser = sub_command.add_parser( |
| 83 | + "init", |
| 84 | + help="Initialize client with given trusted root", |
| 85 | + ) |
| 86 | + init_parser.add_argument("trusted_root") |
| 87 | + |
| 88 | + sub_command.add_parser( |
| 89 | + "refresh", |
| 90 | + help="Refresh the client metadata", |
| 91 | + ) |
| 92 | + |
| 93 | + sub_command.add_parser( |
| 94 | + "download", |
| 95 | + help="Downloads a target", |
| 96 | + ) |
| 97 | + |
| 98 | + command_args = parser.parse_args() |
| 99 | + |
| 100 | + # initialize the TUF Client Example infrastructure |
| 101 | + if command_args.sub_command == "init": |
| 102 | + init(command_args.metadata_dir, command_args.trusted_root) |
| 103 | + elif command_args.sub_command == "refresh": |
| 104 | + refresh( |
| 105 | + command_args.metadata_url, |
| 106 | + command_args.metadata_dir, |
| 107 | + command_args.days_in_future, |
| 108 | + command_args.max_root_rotations, |
| 109 | + ) |
| 110 | + elif command_args.sub_command == "download": |
| 111 | + download_target( |
| 112 | + command_args.metadata_url, |
| 113 | + command_args.metadata_dir, |
| 114 | + command_args.target_name, |
| 115 | + command_args.target_dir, |
| 116 | + command_args.target_base_url, |
| 117 | + ) |
| 118 | + else: |
| 119 | + parser.print_help() |
| 120 | + |
| 121 | + return 0 |
| 122 | + |
| 123 | + |
| 124 | +if __name__ == "__main__": |
| 125 | + sys.exit(main()) |
0 commit comments