Skip to content

Commit

Permalink
Merge 2b89dc0 into 30ba6e9
Browse files Browse the repository at this point in the history
  • Loading branch information
MVrachev committed Apr 19, 2021
2 parents 30ba6e9 + 2b89dc0 commit 75fbf68
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 9 deletions.
33 changes: 33 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,39 @@ 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.

# TODO: Remove the deepcopy when Metadata.from_dict() doesn't have
# the side effect to destroy the passed dictionary.
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)
Expand Down
52 changes: 43 additions & 9 deletions tuf/api/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,22 @@ class Signed:
spec_version: The TUF specification version number (semver) the
metadata format adheres to.
expires: The metadata expiration datetime object.
unrecognized_fields: An optional dictionary storing all unrecognized
fields. Used for backward compatibility. If None is provided, an
empty dictionary will be created.
"""

# NOTE: Signed is a stupid name, because this might not be signed yet, but
# 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
Expand All @@ -318,6 +326,9 @@ def __init__(
if version < 0:
raise ValueError(f"version must be >= 0, got {version}")
self.version = version
if unrecognized_fields is None:
unrecognized_fields = {}
self.unrecognized_fields = unrecognized_fields

@staticmethod
def _common_fields_from_dict(signed_dict: Mapping[str, Any]) -> list:
Expand Down Expand Up @@ -349,6 +360,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:
Expand Down Expand Up @@ -425,8 +437,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
Expand All @@ -439,7 +454,11 @@ 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 and unrecognized.
unrecognized_fields = root_dict
return cls(
*common_args, consistent_snapshot, keys, roles, unrecognized_fields
)

def to_dict(self) -> Dict[str, Any]:
"""Returns the dict representation of self. """
Expand Down Expand Up @@ -501,8 +520,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

Expand All @@ -511,7 +533,9 @@ 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 and unrecognized.
unrecognized_fields = timestamp_dict
return cls(*common_args, meta, unrecognized_fields)

def to_dict(self) -> Dict[str, Any]:
"""Returns the dict representation of self. """
Expand Down Expand Up @@ -565,8 +589,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

Expand All @@ -575,7 +602,9 @@ 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 and unrecognized.
unrecognized_fields = snapshot_dict
return cls(*common_args, meta, unrecognized_fields)

def to_dict(self) -> Dict[str, Any]:
"""Returns the dict representation of self. """
Expand Down Expand Up @@ -668,8 +697,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
Expand All @@ -680,7 +712,9 @@ 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 and unrecognized.
unrecognized_fields = targets_dict
return cls(*common_args, targets, delegations, unrecognized_fields)

def to_dict(self) -> Dict[str, Any]:
"""Returns the dict representation of self. """
Expand Down

0 comments on commit 75fbf68

Please sign in to comment.