From 868afda420309014ff5d9f584ce4dbaba4a6d2f9 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Fri, 17 Sep 2021 19:02:43 +0300 Subject: [PATCH 01/10] docs: Change object attribute doc style Situation before * constructor args are not documented * object attributes are documented * sphinx cannot show object attribute type annotations * attribute docs take a lot of vertical space Now: * constructor args are documented * sphinx can show annotated types of constructor args * class docstring now explains the attributes are the same as constructor args (and attributes are not explicitly documented) Signed-off-by: Jussi Kukkonen --- docs/api/tuf.api.metadata.rst | 1 - docs/api/tuf.ngclient.config.rst | 1 - docs/api/tuf.ngclient.fetcher.rst | 1 - docs/api/tuf.ngclient.updater.rst | 1 - docs/conf.py | 9 +++ tuf/api/metadata.py | 102 +++++++++++++++++++++++------- tuf/ngclient/updater.py | 31 +++++---- 7 files changed, 102 insertions(+), 44 deletions(-) diff --git a/docs/api/tuf.api.metadata.rst b/docs/api/tuf.api.metadata.rst index c4a58bb4..2d961d75 100644 --- a/docs/api/tuf.api.metadata.rst +++ b/docs/api/tuf.api.metadata.rst @@ -4,4 +4,3 @@ Metadata .. automodule:: tuf.api.metadata :members: :undoc-members: - :show-inheritance: diff --git a/docs/api/tuf.ngclient.config.rst b/docs/api/tuf.ngclient.config.rst index 150df082..deb8fe64 100644 --- a/docs/api/tuf.ngclient.config.rst +++ b/docs/api/tuf.ngclient.config.rst @@ -4,4 +4,3 @@ Configuration .. automodule:: tuf.ngclient.config :members: :undoc-members: - :show-inheritance: diff --git a/docs/api/tuf.ngclient.fetcher.rst b/docs/api/tuf.ngclient.fetcher.rst index 1f689d4f..74271f48 100644 --- a/docs/api/tuf.ngclient.fetcher.rst +++ b/docs/api/tuf.ngclient.fetcher.rst @@ -4,4 +4,3 @@ Fetcher .. automodule:: tuf.ngclient.fetcher :members: :undoc-members: - :show-inheritance: diff --git a/docs/api/tuf.ngclient.updater.rst b/docs/api/tuf.ngclient.updater.rst index 0fc46757..9eaa6da5 100644 --- a/docs/api/tuf.ngclient.updater.rst +++ b/docs/api/tuf.ngclient.updater.rst @@ -3,4 +3,3 @@ Updater .. automodule:: tuf.ngclient.updater :members: - :special-members: __init__ diff --git a/docs/conf.py b/docs/conf.py index 2b087620..a8218740 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,5 +52,14 @@ # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] +# -- Autodoc configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html + autodoc_mock_imports = ['securesystemslib'] +# Tone down the "tuf.api.metadata." repetition +add_module_names = False +python_use_unqualified_type_names = True + +# Show typehints in argument doc lines, but not in signatures +autodoc_typehints = "description" diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index 4e3f5a5b..994de6de 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -104,7 +104,10 @@ class Metadata(Generic[T]): "[Root]" is not validated at runtime (as pure annotations are not available then). - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: signed: A subclass of Signed, which has the actual metadata payload, i.e. one of Targets, Snapshot, Timestamp or Root. signatures: An ordered dictionary of keyids to Signature objects, each @@ -390,11 +393,12 @@ class Signed(metaclass=abc.ABCMeta): on the signed attribute. This class provides attributes and methods that are common for all TUF metadata types (roles). - Attributes: - _type: The metadata type string. Also available without underscore. + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: version: The metadata version number. - spec_version: The TUF specification version number (semver) the - metadata format adheres to. + spec_version: The supported TUF specification version number. expires: The metadata expiration datetime object. unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -409,6 +413,7 @@ def _type(self) -> str: @property def type(self) -> str: + """Metadata type as string.""" return self._signed_type # NOTE: Signed is a stupid name, because this might not be signed yet, but @@ -520,7 +525,10 @@ class Key: Please note that "Key" instances are not semanticly validated during initialization: this only happens at signature verification time. - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: keyid: An identifier string that must uniquely identify a key within the metadata it is used in. This implementation does not verify that keyid is the hash of a specific representation of the key. @@ -653,7 +661,10 @@ class Role: Role defines how many keys are required to successfully sign the roles metadata, and which keys are accepted. - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: keyids: A set of strings representing signing keys for this role. threshold: Number of keys required to sign this role's metadata. unrecognized_fields: Dictionary of all unrecognized fields. @@ -697,12 +708,18 @@ def to_dict(self) -> Dict[str, Any]: class Root(Signed): """A container for the signed part of root metadata. - Attributes: - consistent_snapshot: An optional boolean indicating whether the - repository supports consistent snapshots. + Parameters listed below are also instance attributes. + + Args: + version: The metadata version number. + spec_version: The supported TUF specification version number. + expires: The metadata expiration datetime object. keys: Dictionary of keyids to Keys. Defines the keys used in 'roles'. roles: Dictionary of role names to Roles. Defines which keys are required to sign the metadata for a specific role. + consistent_snapshot: An optional boolean indicating whether the + repository supports consistent snapshots. + unrecognized_fields: Dictionary of all unrecognized fields. """ _signed_type = "root" @@ -859,7 +876,10 @@ def _validate_length(length: int) -> None: class MetaFile(BaseFile): """A container with information about a particular metadata file. - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: version: An integer indicating the version of the metadata file. length: An optional integer indicating the length of the metadata file. hashes: An optional dictionary of hash algorithm names to hash values. @@ -934,8 +954,15 @@ class Timestamp(Signed): TUF file format uses a dictionary to contain the snapshot information: this is not the case with Timestamp.snapshot_meta which is a MetaFile. - Attributes: - snapshot_meta: MetaFile instance with the snapshot meta information. + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: + version: The metadata version number. + spec_version: The supported TUF specification version number. + expires: The metadata expiration datetime object. + unrecognized_fields: Dictionary of all unrecognized fields. + snapshot_meta: MetaFile with the snapshot meta information. """ _signed_type = "timestamp" @@ -977,7 +1004,14 @@ class Snapshot(Signed): Snapshot contains information about all target Metadata files. - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: + version: The metadata version number. + spec_version: The supported TUF specification version number. + expires: The metadata expiration datetime object. + unrecognized_fields: Dictionary of all unrecognized fields. meta: A dictionary of target metadata filenames to MetaFile objects. """ @@ -1034,13 +1068,17 @@ class DelegatedRole(Role): paths and path_hash_prefixes are mutually exclusive: both cannot be set, at least one of them must be set. - Attributes: - name: A string giving the name of the delegated role. - terminating: A boolean indicating whether subsequent delegations - should be considered during a target lookup. - paths: An optional list of path pattern strings. See note above. - path_hash_prefixes: An optional list of hash prefixes. See note above. - unrecognized_fields: Dictionary of all unrecognized fields. + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: + name: Name of the delegated role. + keyids: Signing key keyids for this delegated role. + threshold: Number of keys required to sign this role's metadata. + terminating: Does this delegation terminate a target lookup. + paths: Path patterns. See note above. + path_hash_prefixes: Hash prefixes. See note above. + unrecognized_fields: Attributes not managed by TUF Metadata API. """ def __init__( @@ -1152,7 +1190,10 @@ def is_delegated_path(self, target_filepath: str) -> bool: class Delegations: """A container object storing information about all delegations. - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: keys: Dictionary of keyids to Keys. Defines the keys used in 'roles'. roles: Ordered dictionary of role names to DelegatedRoles instances. It defines which keys are required to sign the metadata for a specific @@ -1202,7 +1243,10 @@ def to_dict(self) -> Dict[str, Any]: class TargetFile(BaseFile): """A container with information about a particular target file. - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: length: An integer indicating the length of the target file. hashes: A dictionary of hash algorithm names to hash values. path: A string denoting the path to a target file relative to a base @@ -1255,6 +1299,7 @@ def from_file( hash_algorithms: Optional[List[str]] = None, ) -> "TargetFile": """Creates TargetFile object from a file. + Arguments: target_file_path: The TargetFile path. local_path: The local path to the file to create TargetFile from. @@ -1277,12 +1322,14 @@ def from_data( hash_algorithms: Optional[List[str]] = None, ) -> "TargetFile": """Creates TargetFile object from bytes. + Arguments: target_file_path: The TargetFile path. data: The data to create TargetFile from. hash_algorithms: An optional list of hash algorithms to create the hashes with. If not specified the securesystemslib default hash algorithm is used. + Raises: UnsupportedAlgorithmError: The hash algorithms list contains an unsupported algorithm. @@ -1339,10 +1386,17 @@ class Targets(Signed): Targets contains verifying information about target files and also delegates responsibility to other Targets roles. - Attributes: + *All parameters named below are not just constructor arguments but also + instance attributes.* + + Args: + version: The metadata version number. + spec_version: The supported TUF specification version number. + expires: The metadata expiration datetime object. targets: A dictionary of target filenames to TargetFiles delegations: An optional Delegations that defines how this Targets further delegates responsibility to other Targets Metadata files. + unrecognized_fields: Dictionary of all unrecognized fields. """ _signed_type = "targets" diff --git a/tuf/ngclient/updater.py b/tuf/ngclient/updater.py index c616b7cd..b89073a4 100644 --- a/tuf/ngclient/updater.py +++ b/tuf/ngclient/updater.py @@ -78,7 +78,21 @@ class Updater: - """Implementation of the TUF client workflow.""" + """Creates a new Updater instance and loads trusted root metadata. + + Args: + repository_dir: Local metadata directory. Directory must be + writable and it must contain a trusted root.json file. + metadata_base_url: Base URL for all remote metadata downloads + target_base_url: Optional; Default base URL for all remote target + downloads. Can be individually set in download_target() + fetcher: Optional; FetcherInterface implementation used to download + both metadata and targets. Default is RequestsFetcher + + Raises: + OSError: Local root.json cannot be read + RepositoryError: Local root.json is invalid + """ def __init__( self, @@ -88,21 +102,6 @@ def __init__( fetcher: Optional[FetcherInterface] = None, config: Optional[UpdaterConfig] = None, ): - """Creates a new Updater instance and loads trusted root metadata. - - Args: - repository_dir: Local metadata directory. Directory must be - writable and it must contain a trusted root.json file. - metadata_base_url: Base URL for all remote metadata downloads - target_base_url: Optional; Default base URL for all remote target - downloads. Can be individually set in download_target() - fetcher: Optional; FetcherInterface implementation used to download - both metadata and targets. Default is RequestsFetcher - - Raises: - OSError: Local root.json cannot be read - RepositoryError: Local root.json is invalid - """ self._dir = repository_dir self._metadata_base_url = _ensure_trailing_slash(metadata_base_url) if target_base_url is None: From 217bd9dbc7969789a573afd5ebe9fdef667ca579 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 13:03:28 +0300 Subject: [PATCH 02/10] Separate API documentation pages This makes the individual pages easier to read. Use some autodoc configuration so we can have less config in the automodule/autoclass declarations. Signed-off-by: Jussi Kukkonen --- docs/api/tuf.api.metadata.helpers.rst | 14 ++++++++++++++ docs/api/tuf.api.metadata.metadata.rst | 4 ++++ docs/api/tuf.api.metadata.root.rst | 4 ++++ docs/api/tuf.api.metadata.rst | 6 ------ docs/api/tuf.api.metadata.snapshot.rst | 4 ++++ docs/api/tuf.api.metadata.targets.rst | 4 ++++ docs/api/tuf.api.metadata.timestamp.rst | 4 ++++ docs/api/tuf.api.rst | 19 ++++++++++--------- docs/api/tuf.api.serialization.rst | 1 - docs/api/tuf.ngclient.config.rst | 1 - docs/api/tuf.ngclient.fetcher.rst | 1 - docs/api/tuf.ngclient.updater.rst | 1 - docs/conf.py | 4 ++++ 13 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 docs/api/tuf.api.metadata.helpers.rst create mode 100644 docs/api/tuf.api.metadata.metadata.rst create mode 100644 docs/api/tuf.api.metadata.root.rst delete mode 100644 docs/api/tuf.api.metadata.rst create mode 100644 docs/api/tuf.api.metadata.snapshot.rst create mode 100644 docs/api/tuf.api.metadata.targets.rst create mode 100644 docs/api/tuf.api.metadata.timestamp.rst diff --git a/docs/api/tuf.api.metadata.helpers.rst b/docs/api/tuf.api.metadata.helpers.rst new file mode 100644 index 00000000..a1f7dcfe --- /dev/null +++ b/docs/api/tuf.api.metadata.helpers.rst @@ -0,0 +1,14 @@ +Helper classes +--------------------------------- + +.. autoclass:: tuf.api.metadata.DelegatedRole + +.. autoclass:: tuf.api.metadata.Delegations + +.. autoclass:: tuf.api.metadata.Key + +.. autoclass:: tuf.api.metadata.MetaFile + +.. autoclass:: tuf.api.metadata.Role + +.. autoclass:: tuf.api.metadata.TargetFile diff --git a/docs/api/tuf.api.metadata.metadata.rst b/docs/api/tuf.api.metadata.metadata.rst new file mode 100644 index 00000000..bac11a31 --- /dev/null +++ b/docs/api/tuf.api.metadata.metadata.rst @@ -0,0 +1,4 @@ +Metadata class +--------------------------------- + +.. autoclass:: tuf.api.metadata.Metadata diff --git a/docs/api/tuf.api.metadata.root.rst b/docs/api/tuf.api.metadata.root.rst new file mode 100644 index 00000000..ab6194bc --- /dev/null +++ b/docs/api/tuf.api.metadata.root.rst @@ -0,0 +1,4 @@ +Root class +--------------------------------- + +.. autoclass:: tuf.api.metadata.Root diff --git a/docs/api/tuf.api.metadata.rst b/docs/api/tuf.api.metadata.rst deleted file mode 100644 index 2d961d75..00000000 --- a/docs/api/tuf.api.metadata.rst +++ /dev/null @@ -1,6 +0,0 @@ -Metadata ---------------------------------- - -.. automodule:: tuf.api.metadata - :members: - :undoc-members: diff --git a/docs/api/tuf.api.metadata.snapshot.rst b/docs/api/tuf.api.metadata.snapshot.rst new file mode 100644 index 00000000..1d1c2025 --- /dev/null +++ b/docs/api/tuf.api.metadata.snapshot.rst @@ -0,0 +1,4 @@ +Snapshot class +--------------------------------- + +.. autoclass:: tuf.api.metadata.Snapshot diff --git a/docs/api/tuf.api.metadata.targets.rst b/docs/api/tuf.api.metadata.targets.rst new file mode 100644 index 00000000..a8af3ab3 --- /dev/null +++ b/docs/api/tuf.api.metadata.targets.rst @@ -0,0 +1,4 @@ +Targets class +--------------------------------- + +.. autoclass:: tuf.api.metadata.Targets diff --git a/docs/api/tuf.api.metadata.timestamp.rst b/docs/api/tuf.api.metadata.timestamp.rst new file mode 100644 index 00000000..2d29d37d --- /dev/null +++ b/docs/api/tuf.api.metadata.timestamp.rst @@ -0,0 +1,4 @@ +Timestamp class +--------------------------------- + +.. autoclass:: tuf.api.metadata.Timestamp diff --git a/docs/api/tuf.api.rst b/docs/api/tuf.api.rst index b93902c6..de6eb954 100644 --- a/docs/api/tuf.api.rst +++ b/docs/api/tuf.api.rst @@ -1,18 +1,19 @@ Metadata API =============== -The low-level Metadata API contains two modules: +.. toctree:: -* :doc:`tuf.api.metadata` contains the actual Metadata abstraction - that higher level libraries and application code should use to interact - with TUF metadata. This abstraction provides safe reading and writing to - supported file formats and helper functions for accessing and modifying - the metadata contents. -* :doc:`tuf.api.serialization` covers serializing the metadata into - specific wire formats (like json). + tuf.api.metadata.metadata + tuf.api.metadata.root + tuf.api.metadata.timestamp + tuf.api.metadata.snapshot + tuf.api.metadata.targets .. toctree:: :hidden: - tuf.api.metadata + tuf.api.metadata.helpers tuf.api.serialization + +.. automodule:: tuf.api.metadata + :no-members: \ No newline at end of file diff --git a/docs/api/tuf.api.serialization.rst b/docs/api/tuf.api.serialization.rst index 1603148d..51d6d592 100644 --- a/docs/api/tuf.api.serialization.rst +++ b/docs/api/tuf.api.serialization.rst @@ -5,6 +5,5 @@ JSON serialization ----------------------------- .. automodule:: tuf.api.serialization.json - :members: :undoc-members: :show-inheritance: diff --git a/docs/api/tuf.ngclient.config.rst b/docs/api/tuf.ngclient.config.rst index deb8fe64..b69d7cf4 100644 --- a/docs/api/tuf.ngclient.config.rst +++ b/docs/api/tuf.ngclient.config.rst @@ -2,5 +2,4 @@ Configuration ============= .. automodule:: tuf.ngclient.config - :members: :undoc-members: diff --git a/docs/api/tuf.ngclient.fetcher.rst b/docs/api/tuf.ngclient.fetcher.rst index 74271f48..f37ea14f 100644 --- a/docs/api/tuf.ngclient.fetcher.rst +++ b/docs/api/tuf.ngclient.fetcher.rst @@ -2,5 +2,4 @@ Fetcher ============ .. automodule:: tuf.ngclient.fetcher - :members: :undoc-members: diff --git a/docs/api/tuf.ngclient.updater.rst b/docs/api/tuf.ngclient.updater.rst index 9eaa6da5..3f032c6b 100644 --- a/docs/api/tuf.ngclient.updater.rst +++ b/docs/api/tuf.ngclient.updater.rst @@ -2,4 +2,3 @@ Updater ========= .. automodule:: tuf.ngclient.updater - :members: diff --git a/docs/conf.py b/docs/conf.py index a8218740..b545362a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,3 +63,7 @@ # Show typehints in argument doc lines, but not in signatures autodoc_typehints = "description" + +autodoc_default_options = { + 'members': True, +} \ No newline at end of file From 21ce5e2915e94446c9f281f77549b327e3f67b3c Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 13:08:49 +0300 Subject: [PATCH 03/10] Hide to_dict()/from_dict() These are helpers for serialization implementers, not useful for Metadata API users. Signed-off-by: Jussi Kukkonen --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index b545362a..71f1f137 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -66,4 +66,5 @@ autodoc_default_options = { 'members': True, + 'exclude-members': 'to_dict, from_dict' } \ No newline at end of file From d5743c2312ff726369582735094c2da91e7e3a3e Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 13:48:21 +0300 Subject: [PATCH 04/10] metadata API docs: remove duplication Annotations already include type info and e.g. Optional: remove those from text where useful. Signed-off-by: Jussi Kukkonen --- tuf/api/metadata.py | 94 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index 994de6de..3497943d 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -108,8 +108,8 @@ class Metadata(Generic[T]): instance attributes.* Args: - signed: A subclass of Signed, which has the actual metadata payload, - i.e. one of Targets, Snapshot, Timestamp or Root. + signed: The actual metadata payload, i.e. one of Targets, Snapshot, + Timestamp or Root. signatures: An ordered dictionary of keyids to Signature objects, each signing the canonical serialized representation of 'signed'. """ @@ -208,9 +208,9 @@ def from_bytes( """Loads TUF metadata from raw data. Arguments: - data: metadata content as bytes. - deserializer: Optional; A MetadataDeserializer instance that - implements deserialization. Default is JSONDeserializer. + data: metadata content. + deserializer: MetadataDeserializer implementation to use. Default + is JSONDeserializer. Raises: tuf.api.serialization.DeserializationError: @@ -343,7 +343,7 @@ def verify_delegate( Args: delegated_role: Name of the delegated role to verify delegated_metadata: The Metadata object for the delegated role - signed_serializer: Optional; serializer used for delegate + signed_serializer: serializer used for delegate serialization. Default is CanonicalJSONSerializer. Raises: @@ -399,7 +399,7 @@ class Signed(metaclass=abc.ABCMeta): Args: version: The metadata version number. spec_version: The supported TUF specification version number. - expires: The metadata expiration datetime object. + expires: The metadata expiry date. unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -497,9 +497,8 @@ def is_expired(self, reference_time: Optional[datetime] = None) -> bool: """Checks metadata expiration against a reference time. Args: - reference_time: Optional; The time to check expiration date against. - A naive datetime in UTC expected. - If not provided, checks against the current UTC date and time. + reference_time: The time to check expiration date against. A naive + datetime in UTC expected. Default is current UTC date and time. Returns: True if expiration time is less than the reference time. @@ -522,21 +521,20 @@ def bump_version(self) -> None: class Key: """A container class representing the public portion of a Key. - Please note that "Key" instances are not semanticly validated during - initialization: this only happens at signature verification time. + Supported key content (type, scheme and keyval) is defined in + Securesystemslib. *All parameters named below are not just constructor arguments but also instance attributes.* Args: - keyid: An identifier string that must uniquely identify a key within - the metadata it is used in. This implementation does not verify - that keyid is the hash of a specific representation of the key. - keytype: A string denoting a public key signature system, - such as "rsa", "ed25519", "ecdsa" and "ecdsa-sha2-nistp256". - scheme: A string denoting a corresponding signature scheme. For example: + keyid: Key identifier that is unique within the metadata it is used in. + Keyid is not verified to be the hash of a specific representation + of the key. + keytype: key type, e.g. "rsa", "ed25519" or "ecdsa-sha2-nistp256". + scheme: signature scheme. For example: "rsassa-pss-sha256", "ed25519", and "ecdsa-sha2-nistp256". - keyval: A dictionary containing the public portion of the key. + keyval: Opaque key content unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -613,7 +611,7 @@ def verify_signature( Arguments: metadata: Metadata to verify - signed_serializer: Optional; SignedSerializer to serialize + signed_serializer: SignedSerializer to serialize 'metadata.signed' with. Default is CanonicalJSONSerializer. Raises: @@ -665,7 +663,7 @@ class Role: instance attributes.* Args: - keyids: A set of strings representing signing keys for this role. + keyids: The roles signing key identifiers. threshold: Number of keys required to sign this role's metadata. unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -713,12 +711,11 @@ class Root(Signed): Args: version: The metadata version number. spec_version: The supported TUF specification version number. - expires: The metadata expiration datetime object. + expires: The metadata expiry date. keys: Dictionary of keyids to Keys. Defines the keys used in 'roles'. roles: Dictionary of role names to Roles. Defines which keys are required to sign the metadata for a specific role. - consistent_snapshot: An optional boolean indicating whether the - repository supports consistent snapshots. + consistent_snapshot: Does repository support consistent snapshots. unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -880,9 +877,9 @@ class MetaFile(BaseFile): instance attributes.* Args: - version: An integer indicating the version of the metadata file. - length: An optional integer indicating the length of the metadata file. - hashes: An optional dictionary of hash algorithm names to hash values. + version: Version of the metadata file. + length: Length of the metadata file. + hashes: Dictionary of hash algorithm names to hash values. unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -960,9 +957,9 @@ class Timestamp(Signed): Args: version: The metadata version number. spec_version: The supported TUF specification version number. - expires: The metadata expiration datetime object. + expires: The metadata expiry date. unrecognized_fields: Dictionary of all unrecognized fields. - snapshot_meta: MetaFile with the snapshot meta information. + snapshot_meta: Meta information for snapshot metadata. """ _signed_type = "timestamp" @@ -1010,7 +1007,7 @@ class Snapshot(Signed): Args: version: The metadata version number. spec_version: The supported TUF specification version number. - expires: The metadata expiration datetime object. + expires: The metadata expiry date. unrecognized_fields: Dictionary of all unrecognized fields. meta: A dictionary of target metadata filenames to MetaFile objects. """ @@ -1072,8 +1069,8 @@ class DelegatedRole(Role): instance attributes.* Args: - name: Name of the delegated role. - keyids: Signing key keyids for this delegated role. + name: Delegated role name. + keyids: Delegated role signing key identifiers. threshold: Number of keys required to sign this role's metadata. terminating: Does this delegation terminate a target lookup. paths: Path patterns. See note above. @@ -1198,7 +1195,7 @@ class Delegations: roles: Ordered dictionary of role names to DelegatedRoles instances. It defines which keys are required to sign the metadata for a specific role. The roles order also defines the order that role delegations - are considered in. + are considered during target searches. unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -1247,10 +1244,9 @@ class TargetFile(BaseFile): instance attributes.* Args: - length: An integer indicating the length of the target file. + length: Length in bytes. hashes: A dictionary of hash algorithm names to hash values. - path: A string denoting the path to a target file relative to a base - URL of targets. + path: URL path to a target file, relative to a base targets URL. unrecognized_fields: Dictionary of all unrecognized fields. """ @@ -1301,11 +1297,11 @@ def from_file( """Creates TargetFile object from a file. Arguments: - target_file_path: The TargetFile path. - local_path: The local path to the file to create TargetFile from. - hash_algorithms: An optional list of hash algorithms to create - the hashes with. If not specified the securesystemslib default - hash algorithm is used. + target_file_path: URL path to a target file, relative to a base + targets URL. + local_path: The local path to target file content. + hash_algorithms: hash algorithms to calculate hashes with. If not + specified the securesystemslib default hash algorithm is used. Raises: FileNotFoundError: The file doesn't exist. UnsupportedAlgorithmError: The hash algorithms list @@ -1324,11 +1320,11 @@ def from_data( """Creates TargetFile object from bytes. Arguments: - target_file_path: The TargetFile path. - data: The data to create TargetFile from. - hash_algorithms: An optional list of hash algorithms to create - the hashes with. If not specified the securesystemslib default - hash algorithm is used. + target_file_path: URL path to a target file, relative to a base + targets URL. + data: The target file content. + hash_algorithms: Hash algorithms to create the hashes with. If not + specified the securesystemslib default hash algorithm is used. Raises: UnsupportedAlgorithmError: The hash algorithms list @@ -1392,10 +1388,10 @@ class Targets(Signed): Args: version: The metadata version number. spec_version: The supported TUF specification version number. - expires: The metadata expiration datetime object. + expires: The metadata expiry date. targets: A dictionary of target filenames to TargetFiles - delegations: An optional Delegations that defines how this Targets - further delegates responsibility to other Targets Metadata files. + delegations: Defines how this Targets delegates responsibility to other + Targets Metadata files. unrecognized_fields: Dictionary of all unrecognized fields. """ From 424cc3282bec0a41fb6f84f8381e89adaefcf341 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 14:06:23 +0300 Subject: [PATCH 05/10] docs: Include the Serialization interfaces Signed-off-by: Jussi Kukkonen --- docs/api/tuf.api.serialization.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api/tuf.api.serialization.rst b/docs/api/tuf.api.serialization.rst index 51d6d592..610ab910 100644 --- a/docs/api/tuf.api.serialization.rst +++ b/docs/api/tuf.api.serialization.rst @@ -1,9 +1,10 @@ Serialization ============================= +.. automodule:: tuf.api.serialization + JSON serialization ----------------------------- .. automodule:: tuf.api.serialization.json - :undoc-members: :show-inheritance: From 892aa04cb395b5d6051551d408da46c8bee0e1a4 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 15:25:47 +0300 Subject: [PATCH 06/10] Metadata API: Rewrite module doc Rewrite the module docstring to better fit the current sphinx documentation. Signed-off-by: Jussi Kukkonen --- tuf/api/metadata.py | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index 3497943d..f2c0b862 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -1,38 +1,28 @@ # Copyright New York University and the TUF contributors # SPDX-License-Identifier: MIT OR Apache-2.0 -"""TUF role metadata model. - -This module contains low-level API through container classes for TUF role -metadata. The API aims to provide: +""" +The low-level Metadata API in ``tuf.api.metadata`` module contains: * Safe de/serialization of metadata to and from files. * Access to and modification of signed metadata content. * Signing metadata and verifying signatures. -Each of the top level metadata roles is an instance of the Metadata[T] class -where the "signed" portion of each of the roles (or the "T") is an instance -of one of the classes Root, Timestamp, Snapshot or Targets. -For example, Metadata[Root] represents the TUF root role and that in practice -means that this is a Metadata object with a signed attribute of type Root. +Metadata API implements functionality at the metadata file level, it does +not provide TUF repository or client functionality on its own (but can be used +to implement them). -Additionally, there are helper classes providing abstractions over the complex -metadata fields inside the four top level classes - Root, Timestamp, Snapshot -and Targets. +The API design is based on the file format defined in the `TUF specification +`_ and the object +attributes generally follow the JSON format used in the specification. -Note: the metadata module provides a low-level API and as such it doesn't use -concepts like "repository" or "trusted collection of metadata". -In this file there is no implementation of the repository-side logic or client -update workflows, but instead it provides solid base for other components to do -so. - -The metadata model supports any custom serialization format, defaulting to JSON -as wireline format and Canonical JSON for reproducible signature creation and -verification. -Custom serializers must implement the abstract serialization interface defined -in 'tuf.api.serialization', and may use the [to|from]_dict convenience methods -available in the class model. +The above principle means that a ``Metadata`` object represents a single +metadata file, and has a ``signed`` attribute that is an instance of one of the +four top level signed classes (Root, Timestamp, Snapshot and Targets). To make +Python type annotations useful Metadata can be type constrained: e.g. the +signed attribute of ``Metadata[Root]`` is known to be ``Root``. +Currently Metadata API supports JSON as the file format. """ import abc import fnmatch From a77c0831e7eff75fee2e334d6b9db5cc4bcf5b8e Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 17:06:17 +0300 Subject: [PATCH 07/10] docs: rename "helpers" to "supporting classes" Also add a summary to the page -- unfortunately getting a standard TOC would require creating a rst page for each class. Signed-off-by: Jussi Kukkonen --- docs/api/tuf.api.metadata.helpers.rst | 14 ------------ docs/api/tuf.api.metadata.supporting.rst | 27 ++++++++++++++++++++++++ docs/api/tuf.api.rst | 2 +- docs/conf.py | 2 +- 4 files changed, 29 insertions(+), 16 deletions(-) delete mode 100644 docs/api/tuf.api.metadata.helpers.rst create mode 100644 docs/api/tuf.api.metadata.supporting.rst diff --git a/docs/api/tuf.api.metadata.helpers.rst b/docs/api/tuf.api.metadata.helpers.rst deleted file mode 100644 index a1f7dcfe..00000000 --- a/docs/api/tuf.api.metadata.helpers.rst +++ /dev/null @@ -1,14 +0,0 @@ -Helper classes ---------------------------------- - -.. autoclass:: tuf.api.metadata.DelegatedRole - -.. autoclass:: tuf.api.metadata.Delegations - -.. autoclass:: tuf.api.metadata.Key - -.. autoclass:: tuf.api.metadata.MetaFile - -.. autoclass:: tuf.api.metadata.Role - -.. autoclass:: tuf.api.metadata.TargetFile diff --git a/docs/api/tuf.api.metadata.supporting.rst b/docs/api/tuf.api.metadata.supporting.rst new file mode 100644 index 00000000..906e70e9 --- /dev/null +++ b/docs/api/tuf.api.metadata.supporting.rst @@ -0,0 +1,27 @@ +Supporting classes +--------------------------------- + +The Metadata API includes multiple classes that are used by the top-level +ones (Root, Timestamp, Snapshot, Targets): + +.. autosummary:: + :nosignatures: + + tuf.api.metadata.DelegatedRole + tuf.api.metadata.Delegations + tuf.api.metadata.Key + tuf.api.metadata.MetaFile + tuf.api.metadata.Role + tuf.api.metadata.TargetFile + +.. autoclass:: tuf.api.metadata.DelegatedRole + +.. autoclass:: tuf.api.metadata.Delegations + +.. autoclass:: tuf.api.metadata.Key + +.. autoclass:: tuf.api.metadata.MetaFile + +.. autoclass:: tuf.api.metadata.Role + +.. autoclass:: tuf.api.metadata.TargetFile diff --git a/docs/api/tuf.api.rst b/docs/api/tuf.api.rst index de6eb954..d4309b84 100644 --- a/docs/api/tuf.api.rst +++ b/docs/api/tuf.api.rst @@ -12,7 +12,7 @@ Metadata API .. toctree:: :hidden: - tuf.api.metadata.helpers + tuf.api.metadata.supporting tuf.api.serialization .. automodule:: tuf.api.metadata diff --git a/docs/conf.py b/docs/conf.py index 71f1f137..cb146ed9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,7 +30,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.napoleon'] +extensions = ['sphinx.ext.napoleon', 'sphinx.ext.autosummary'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From ed520ee55d77de4e0a87d6ad4e877c2ef0953ff3 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 17:19:05 +0300 Subject: [PATCH 08/10] Metadata API: Improve serialization docs Signed-off-by: Jussi Kukkonen --- tuf/api/serialization/__init__.py | 18 +++++++++--------- tuf/api/serialization/json.py | 13 ++++++------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/tuf/api/serialization/__init__.py b/tuf/api/serialization/__init__.py index 4ec0a4ae..36fa5d76 100644 --- a/tuf/api/serialization/__init__.py +++ b/tuf/api/serialization/__init__.py @@ -1,19 +1,19 @@ # Copyright New York University and the TUF contributors # SPDX-License-Identifier: MIT OR Apache-2.0 -"""TUF role metadata de/serialization. - -This sub-package provides abstract base classes and concrete implementations to -serialize and deserialize TUF role metadata and metadata parts. +"""``tuf.api.serialization`` module provides abstract base classes and concrete +implementations to serialize and deserialize TUF metadata. Any custom de/serialization implementations should inherit from the abstract -base classes defined in this __init__.py module. +base classes defined in this module. The implementations can use the +``to_dict()``/``from_dict()`` implementations available in the Metadata +API objects. - Metadata de/serializers are used to convert to and from wireline formats. - Signed serializers are used to canonicalize data for cryptographic signatures generation and verification. - """ + import abc from typing import TYPE_CHECKING @@ -36,7 +36,7 @@ class MetadataDeserializer(metaclass=abc.ABCMeta): @abc.abstractmethod def deserialize(self, raw_data: bytes) -> "Metadata": - """Deserialize passed bytes to Metadata object.""" + """Deserialize bytes to Metadata object.""" raise NotImplementedError @@ -45,7 +45,7 @@ class MetadataSerializer(metaclass=abc.ABCMeta): @abc.abstractmethod def serialize(self, metadata_obj: "Metadata") -> bytes: - """Serialize passed Metadata object to bytes.""" + """Serialize Metadata object to bytes.""" raise NotImplementedError @@ -54,5 +54,5 @@ class SignedSerializer(metaclass=abc.ABCMeta): @abc.abstractmethod def serialize(self, signed_obj: "Signed") -> bytes: - """Serialize passed Signed object to bytes.""" + """Serialize Signed object to bytes.""" raise NotImplementedError diff --git a/tuf/api/serialization/json.py b/tuf/api/serialization/json.py index 43814993..07e9a0d3 100644 --- a/tuf/api/serialization/json.py +++ b/tuf/api/serialization/json.py @@ -1,14 +1,13 @@ # Copyright New York University and the TUF contributors # SPDX-License-Identifier: MIT OR Apache-2.0 -"""TUF role metadata JSON serialization and deserialization. - -This module provides concrete implementations to serialize and deserialize TUF -role metadata to and from the JSON wireline format for transportation, and -to serialize the 'signed' part of TUF role metadata to the OLPC Canonical JSON -format for signature generation and verification. - +"""``tuf.api.serialization.json`` module provides concrete implementations to +serialize and deserialize TUF role metadata to and from the JSON wireline +format for transportation, and to serialize the 'signed' part of TUF role +metadata to the OLPC Canonical JSON format for signature generation and +verification. """ + import json from securesystemslib.formats import encode_canonical From 8ed446c14bf34044b0b73008faf4fe7d5619bdb4 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Thu, 23 Sep 2021 17:57:19 +0300 Subject: [PATCH 09/10] Metadata API: Stop annotating __init__() return value This may have been required by a linter at some point, but isn't anymore: Not annotating makes the documentation look better. Signed-off-by: Jussi Kukkonen --- tuf/api/metadata.py | 20 ++++++++++---------- tuf/api/serialization/json.py | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tuf/api/metadata.py b/tuf/api/metadata.py index f2c0b862..0b88f4c0 100644 --- a/tuf/api/metadata.py +++ b/tuf/api/metadata.py @@ -415,7 +415,7 @@ def __init__( spec_version: str, expires: datetime, unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): spec_list = spec_version.split(".") if ( len(spec_list) != 3 @@ -535,7 +535,7 @@ def __init__( scheme: str, keyval: Dict[str, str], unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): if not all( isinstance(at, str) for at in [keyid, keytype, scheme] ) or not isinstance(keyval, Dict): @@ -663,7 +663,7 @@ def __init__( keyids: List[str], threshold: int, unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): keyids_set = set(keyids) if len(keyids_set) != len(keyids): raise ValueError( @@ -722,7 +722,7 @@ def __init__( roles: Dict[str, Role], consistent_snapshot: Optional[bool] = None, unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): super().__init__(version, spec_version, expires, unrecognized_fields) self.consistent_snapshot = consistent_snapshot self.keys = keys @@ -879,7 +879,7 @@ def __init__( length: Optional[int] = None, hashes: Optional[Dict[str, str]] = None, unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): if version <= 0: raise ValueError(f"Metafile version must be > 0, got {version}") @@ -961,7 +961,7 @@ def __init__( expires: datetime, snapshot_meta: MetaFile, unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): super().__init__(version, spec_version, expires, unrecognized_fields) self.snapshot_meta = snapshot_meta @@ -1011,7 +1011,7 @@ def __init__( expires: datetime, meta: Dict[str, MetaFile], unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): super().__init__(version, spec_version, expires, unrecognized_fields) self.meta = meta @@ -1077,7 +1077,7 @@ def __init__( paths: Optional[List[str]] = None, path_hash_prefixes: Optional[List[str]] = None, unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): super().__init__(keyids, threshold, unrecognized_fields) self.name = name self.terminating = terminating @@ -1194,7 +1194,7 @@ def __init__( keys: Dict[str, Key], roles: "OrderedDict[str, DelegatedRole]", unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): self.keys = keys self.roles = roles self.unrecognized_fields = unrecognized_fields or {} @@ -1246,7 +1246,7 @@ def __init__( hashes: Dict[str, str], path: str, unrecognized_fields: Optional[Mapping[str, Any]] = None, - ) -> None: + ): self._validate_length(length) self._validate_hashes(hashes) diff --git a/tuf/api/serialization/json.py b/tuf/api/serialization/json.py index 07e9a0d3..b043310f 100644 --- a/tuf/api/serialization/json.py +++ b/tuf/api/serialization/json.py @@ -50,7 +50,7 @@ class JSONSerializer(MetadataSerializer): """ - def __init__(self, compact: bool = False) -> None: + def __init__(self, compact: bool = False): self.compact = compact def serialize(self, metadata_obj: Metadata) -> bytes: From 4e835855df0cdae68a208f8c45e0edc125b264d3 Mon Sep 17 00:00:00 2001 From: Jussi Kukkonen Date: Fri, 1 Oct 2021 14:42:54 +0300 Subject: [PATCH 10/10] ngclient: Fix docs link to specification Signed-off-by: Jussi Kukkonen --- tuf/ngclient/config.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tuf/ngclient/config.py b/tuf/ngclient/config.py index 177594bf..6182c70d 100644 --- a/tuf/ngclient/config.py +++ b/tuf/ngclient/config.py @@ -18,11 +18,11 @@ class UpdaterConfig: timestamp_max_length: The maximum length of a timestamp metadata file. snapshot_max_length: The maximum length of a snapshot metadata file. targets_max_length: The maximum length of a targets metadata file. - prefix_targets_with_hash: When consistent snapshots are used - (see https://theupdateframework.github.io/specification/latest/#consistent-snapshots), #pylint: disable=line-too-long - target download URLs are formed by prefixing the filename with a - hash digest of file content by default. This can be overridden by - setting prefix_targets_with_hash to False. + prefix_targets_with_hash: When `consistent snapshots + `_ + are used, target download URLs are formed by prefixing the filename + with a hash digest of file content by default. This can be + overridden by setting prefix_targets_with_hash to False. """