mirror of
https://github.com/theupdateframework/python-tuf
synced 2026-05-24 10:08:28 +00:00
Merge pull request #1345 from MVrachev/implement-adr-8
New metadata API: add support for ADR 0008
This commit is contained in:
commit
feb340f8da
2 changed files with 64 additions and 9 deletions
|
|
@ -349,6 +349,37 @@ def test_metadata_targets(self):
|
|||
# Verify that data is updated
|
||||
self.assertEqual(targets.signed.targets[filename], fileinfo)
|
||||
|
||||
def setup_dict_with_unrecognized_field(self, file_path, field, value):
|
||||
json_dict = {}
|
||||
with open(file_path) as f:
|
||||
json_dict = json.loads(f.read())
|
||||
# We are changing the json dict without changing the signature.
|
||||
# This could be a problem if we want to do verification on this dict.
|
||||
json_dict["signed"][field] = value
|
||||
return json_dict
|
||||
|
||||
def test_support_for_unrecognized_fields(self):
|
||||
for metadata in ["root", "timestamp", "snapshot", "targets"]:
|
||||
path = os.path.join(self.repo_dir, "metadata", metadata + ".json")
|
||||
dict1 = self.setup_dict_with_unrecognized_field(path, "f", "b")
|
||||
# Test that the metadata classes store unrecognized fields when
|
||||
# initializing and passes them when casting the instance to a dict.
|
||||
|
||||
temp_copy = copy.deepcopy(dict1)
|
||||
metadata_obj = Metadata.from_dict(temp_copy)
|
||||
|
||||
self.assertEqual(dict1["signed"], metadata_obj.signed.to_dict())
|
||||
|
||||
# Test that two instances of the same class could have different
|
||||
# unrecognized fields.
|
||||
dict2 = self.setup_dict_with_unrecognized_field(path, "f2", "b2")
|
||||
temp_copy2 = copy.deepcopy(dict2)
|
||||
metadata_obj2 = Metadata.from_dict(temp_copy2)
|
||||
self.assertNotEqual(
|
||||
metadata_obj.signed.to_dict(), metadata_obj2.signed.to_dict()
|
||||
)
|
||||
|
||||
|
||||
# Run unit test.
|
||||
if __name__ == '__main__':
|
||||
utils.configure_test_logging(sys.argv)
|
||||
|
|
|
|||
|
|
@ -320,6 +320,7 @@ class Signed:
|
|||
spec_version: The TUF specification version number (semver) the
|
||||
metadata format adheres to.
|
||||
expires: The metadata expiration datetime object.
|
||||
unrecognized_fields: Dictionary of all unrecognized fields.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -327,7 +328,12 @@ class Signed:
|
|||
# we keep it to match spec terminology (I often refer to this as "payload",
|
||||
# or "inner metadata")
|
||||
def __init__(
|
||||
self, _type: str, version: int, spec_version: str, expires: datetime
|
||||
self,
|
||||
_type: str,
|
||||
version: int,
|
||||
spec_version: str,
|
||||
expires: datetime,
|
||||
unrecognized_fields: Optional[Mapping[str, Any]] = None,
|
||||
) -> None:
|
||||
|
||||
self._type = _type
|
||||
|
|
@ -338,6 +344,7 @@ def __init__(
|
|||
if version < 0:
|
||||
raise ValueError(f"version must be >= 0, got {version}")
|
||||
self.version = version
|
||||
self.unrecognized_fields = unrecognized_fields or {}
|
||||
|
||||
@staticmethod
|
||||
def _common_fields_from_dict(signed_dict: Mapping[str, Any]) -> list:
|
||||
|
|
@ -369,6 +376,7 @@ def _common_fields_to_dict(self) -> Dict[str, Any]:
|
|||
"version": self.version,
|
||||
"spec_version": self.spec_version,
|
||||
"expires": self.expires.isoformat() + "Z",
|
||||
**self.unrecognized_fields,
|
||||
}
|
||||
|
||||
def is_expired(self, reference_time: datetime = None) -> bool:
|
||||
|
|
@ -445,8 +453,11 @@ def __init__(
|
|||
consistent_snapshot: bool,
|
||||
keys: Mapping[str, Any],
|
||||
roles: Mapping[str, Any],
|
||||
unrecognized_fields: Optional[Mapping[str, Any]] = None,
|
||||
) -> None:
|
||||
super().__init__(_type, version, spec_version, expires)
|
||||
super().__init__(
|
||||
_type, version, spec_version, expires, unrecognized_fields
|
||||
)
|
||||
# TODO: Add classes for keys and roles
|
||||
self.consistent_snapshot = consistent_snapshot
|
||||
self.keys = keys
|
||||
|
|
@ -459,7 +470,8 @@ def from_dict(cls, root_dict: Mapping[str, Any]) -> "Root":
|
|||
consistent_snapshot = root_dict.pop("consistent_snapshot")
|
||||
keys = root_dict.pop("keys")
|
||||
roles = root_dict.pop("roles")
|
||||
return cls(*common_args, consistent_snapshot, keys, roles)
|
||||
# All fields left in the root_dict are unrecognized.
|
||||
return cls(*common_args, consistent_snapshot, keys, roles, root_dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Returns the dict representation of self. """
|
||||
|
|
@ -521,8 +533,11 @@ def __init__(
|
|||
spec_version: str,
|
||||
expires: datetime,
|
||||
meta: Mapping[str, Any],
|
||||
unrecognized_fields: Optional[Mapping[str, Any]] = None,
|
||||
) -> None:
|
||||
super().__init__(_type, version, spec_version, expires)
|
||||
super().__init__(
|
||||
_type, version, spec_version, expires, unrecognized_fields
|
||||
)
|
||||
# TODO: Add class for meta
|
||||
self.meta = meta
|
||||
|
||||
|
|
@ -531,7 +546,8 @@ def from_dict(cls, timestamp_dict: Mapping[str, Any]) -> "Timestamp":
|
|||
"""Creates Timestamp object from its dict representation. """
|
||||
common_args = cls._common_fields_from_dict(timestamp_dict)
|
||||
meta = timestamp_dict.pop("meta")
|
||||
return cls(*common_args, meta)
|
||||
# All fields left in the timestamp_dict are unrecognized.
|
||||
return cls(*common_args, meta, timestamp_dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Returns the dict representation of self. """
|
||||
|
|
@ -585,8 +601,11 @@ def __init__(
|
|||
spec_version: str,
|
||||
expires: datetime,
|
||||
meta: Mapping[str, Any],
|
||||
unrecognized_fields: Optional[Mapping[str, Any]] = None,
|
||||
) -> None:
|
||||
super().__init__(_type, version, spec_version, expires)
|
||||
super().__init__(
|
||||
_type, version, spec_version, expires, unrecognized_fields
|
||||
)
|
||||
# TODO: Add class for meta
|
||||
self.meta = meta
|
||||
|
||||
|
|
@ -595,7 +614,8 @@ def from_dict(cls, snapshot_dict: Mapping[str, Any]) -> "Snapshot":
|
|||
"""Creates Snapshot object from its dict representation. """
|
||||
common_args = cls._common_fields_from_dict(snapshot_dict)
|
||||
meta = snapshot_dict.pop("meta")
|
||||
return cls(*common_args, meta)
|
||||
# All fields left in the snapshot_dict are unrecognized.
|
||||
return cls(*common_args, meta, snapshot_dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Returns the dict representation of self. """
|
||||
|
|
@ -688,8 +708,11 @@ def __init__(
|
|||
expires: datetime,
|
||||
targets: Mapping[str, Any],
|
||||
delegations: Mapping[str, Any],
|
||||
unrecognized_fields: Optional[Mapping[str, Any]] = None,
|
||||
) -> None:
|
||||
super().__init__(_type, version, spec_version, expires)
|
||||
super().__init__(
|
||||
_type, version, spec_version, expires, unrecognized_fields
|
||||
)
|
||||
# TODO: Add class for meta
|
||||
self.targets = targets
|
||||
self.delegations = delegations
|
||||
|
|
@ -700,7 +723,8 @@ def from_dict(cls, targets_dict: Mapping[str, Any]) -> "Targets":
|
|||
common_args = cls._common_fields_from_dict(targets_dict)
|
||||
targets = targets_dict.pop("targets")
|
||||
delegations = targets_dict.pop("delegations")
|
||||
return cls(*common_args, targets, delegations)
|
||||
# All fields left in the targets_dict are unrecognized.
|
||||
return cls(*common_args, targets, delegations, targets_dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Returns the dict representation of self. """
|
||||
|
|
|
|||
Loading…
Reference in a new issue