Skip to content

Commit 4524ca1

Browse files
author
Ivana Atanasova
committed
Create constants for top-level rolenames
This is a change in the metadata API to remove hardcoded rolenames and use constants instead. Fixes theupdateframework#1648 Signed-off-by: Ivana Atanasova <[email protected]>
1 parent 1b5df4c commit 4524ca1

File tree

3 files changed

+63
-59
lines changed

3 files changed

+63
-59
lines changed

tuf/api/metadata.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,19 @@
6161
SignedSerializer,
6262
)
6363

64+
_ROOT: str = "root"
65+
_SNAPSHOT: str = "snapshot"
66+
_TARGETS: str = "targets"
67+
_TIMESTAMP: str = "timestamp"
68+
6469
# pylint: disable=too-many-lines
6570

6671
logger = logging.getLogger(__name__)
6772

6873
# We aim to support SPECIFICATION_VERSION and require the input metadata
6974
# files to have the same major version (the first number) as ours.
7075
SPECIFICATION_VERSION = ["1", "0", "19"]
71-
TOP_LEVEL_ROLE_NAMES = {"root", "timestamp", "snapshot", "targets"}
76+
TOP_LEVEL_ROLE_NAMES = {_ROOT, _TIMESTAMP, _SNAPSHOT, _TARGETS}
7277

7378
# T is a Generic type constraint for Metadata.signed
7479
T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets")
@@ -130,13 +135,13 @@ def from_dict(cls, metadata: Dict[str, Any]) -> "Metadata[T]":
130135
# Dispatch to contained metadata class on metadata _type field.
131136
_type = metadata["signed"]["_type"]
132137

133-
if _type == "targets":
138+
if _type == _TARGETS:
134139
inner_cls: Type[Signed] = Targets
135-
elif _type == "snapshot":
140+
elif _type == _SNAPSHOT:
136141
inner_cls = Snapshot
137-
elif _type == "timestamp":
142+
elif _type == _TIMESTAMP:
138143
inner_cls = Timestamp
139-
elif _type == "root":
144+
elif _type == _ROOT:
140145
inner_cls = Root
141146
else:
142147
raise ValueError(f'unrecognized metadata type "{_type}"')
@@ -394,18 +399,13 @@ class Signed(metaclass=abc.ABCMeta):
394399
unrecognized_fields: Dictionary of all unrecognized fields.
395400
"""
396401

397-
# Signed implementations are expected to override this
398-
_signed_type: ClassVar[str] = "signed"
402+
# type is required for static reference without changing the API
403+
type: ClassVar[str] = "signed"
399404

400405
# _type and type are identical: 1st replicates file format, 2nd passes lint
401406
@property
402407
def _type(self) -> str:
403-
return self._signed_type
404-
405-
@property
406-
def type(self) -> str:
407-
"""Metadata type as string."""
408-
return self._signed_type
408+
return self.type
409409

410410
# NOTE: Signed is a stupid name, because this might not be signed yet, but
411411
# we keep it to match spec terminology (I often refer to this as "payload",
@@ -458,8 +458,8 @@ def _common_fields_from_dict(
458458
459459
"""
460460
_type = signed_dict.pop("_type")
461-
if _type != cls._signed_type:
462-
raise ValueError(f"Expected type {cls._signed_type}, got {_type}")
461+
if _type != cls.type:
462+
raise ValueError(f"Expected type {cls.type}, got {_type}")
463463

464464
version = signed_dict.pop("version")
465465
spec_version = signed_dict.pop("spec_version")
@@ -712,7 +712,7 @@ class Root(Signed):
712712
unrecognized_fields: Dictionary of all unrecognized fields.
713713
"""
714714

715-
_signed_type = "root"
715+
type = _ROOT
716716

717717
# TODO: determine an appropriate value for max-args
718718
# pylint: disable=too-many-arguments
@@ -965,7 +965,7 @@ class Timestamp(Signed):
965965
snapshot_meta: Meta information for snapshot metadata.
966966
"""
967967

968-
_signed_type = "timestamp"
968+
type = _TIMESTAMP
969969

970970
def __init__(
971971
self,
@@ -1015,7 +1015,7 @@ class Snapshot(Signed):
10151015
meta: A dictionary of target metadata filenames to MetaFile objects.
10161016
"""
10171017

1018-
_signed_type = "snapshot"
1018+
type = _SNAPSHOT
10191019

10201020
def __init__(
10211021
self,
@@ -1402,7 +1402,7 @@ class Targets(Signed):
14021402
unrecognized_fields: Dictionary of all unrecognized fields.
14031403
"""
14041404

1405-
_signed_type = "targets"
1405+
type = _TARGETS
14061406

14071407
# TODO: determine an appropriate value for max-args
14081408
# pylint: disable=too-many-arguments
@@ -1423,7 +1423,7 @@ def __init__(
14231423
def from_dict(cls, signed_dict: Dict[str, Any]) -> "Targets":
14241424
"""Creates Targets object from its dict representation."""
14251425
common_args = cls._common_fields_from_dict(signed_dict)
1426-
targets = signed_dict.pop("targets")
1426+
targets = signed_dict.pop(_TARGETS)
14271427
try:
14281428
delegations_dict = signed_dict.pop("delegations")
14291429
except KeyError:
@@ -1444,7 +1444,7 @@ def to_dict(self) -> Dict[str, Any]:
14441444
targets = {}
14451445
for target_path, target_file_obj in self.targets.items():
14461446
targets[target_path] = target_file_obj.to_dict()
1447-
targets_dict["targets"] = targets
1447+
targets_dict[_TARGETS] = targets
14481448
if self.delegations is not None:
14491449
targets_dict["delegations"] = self.delegations.to_dict()
14501450
return targets_dict

tuf/ngclient/_internal/trusted_metadata_set.py

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
network IO, which are not handled here.
1111
1212
Loaded metadata can be accessed via index access with rolename as key
13-
(trusted_set["root"]) or, in the case of top-level metadata, using the helper
13+
(trusted_set[Root.type]) or, in the case of top-level metadata, using the helper
1414
properties (trusted_set.root).
1515
1616
The rules that TrustedMetadataSet follows for top-level metadata are
@@ -35,7 +35,7 @@
3535
>>> trusted_set = TrustedMetadataSet(f.read())
3636
>>>
3737
>>> # update root from remote until no more are available
38-
>>> with download("root", trusted_set.root.signed.version + 1) as f:
38+
>>> with download(Root.type, trusted_set.root.signed.version + 1) as f:
3939
>>> trusted_set.update_root(f.read())
4040
>>>
4141
>>> # load local timestamp, then update from remote
@@ -45,7 +45,7 @@
4545
>>> except (RepositoryError, OSError):
4646
>>> pass # failure to load a local file is ok
4747
>>>
48-
>>> with download("timestamp") as f:
48+
>>> with download(Timestamp.type) as f:
4949
>>> trusted_set.update_timestamp(f.read())
5050
>>>
5151
>>> # load local snapshot, then update from remote if needed
@@ -55,7 +55,7 @@
5555
>>> except (RepositoryError, OSError):
5656
>>> # local snapshot is not valid, load from remote
5757
>>> # (RepositoryErrors here stop the update)
58-
>>> with download("snapshot", version) as f:
58+
>>> with download(Snapshot.type, version) as f:
5959
>>> trusted_set.update_snapshot(f.read())
6060
6161
TODO:
@@ -123,22 +123,22 @@ def __iter__(self) -> Iterator[Metadata]:
123123
@property
124124
def root(self) -> Metadata[Root]:
125125
"""Current root Metadata"""
126-
return self._trusted_set["root"]
126+
return self._trusted_set[Root.type]
127127

128128
@property
129129
def timestamp(self) -> Optional[Metadata[Timestamp]]:
130130
"""Current timestamp Metadata or None"""
131-
return self._trusted_set.get("timestamp")
131+
return self._trusted_set.get(Timestamp.type)
132132

133133
@property
134134
def snapshot(self) -> Optional[Metadata[Snapshot]]:
135135
"""Current snapshot Metadata or None"""
136-
return self._trusted_set.get("snapshot")
136+
return self._trusted_set.get(Snapshot.type)
137137

138138
@property
139139
def targets(self) -> Optional[Metadata[Targets]]:
140140
"""Current targets Metadata or None"""
141-
return self._trusted_set.get("targets")
141+
return self._trusted_set.get(Targets.type)
142142

143143
# Methods for updating metadata
144144
def update_root(self, data: bytes) -> None:
@@ -163,23 +163,25 @@ def update_root(self, data: bytes) -> None:
163163
except DeserializationError as e:
164164
raise exceptions.RepositoryError("Failed to load root") from e
165165

166-
if new_root.signed.type != "root":
166+
if new_root.signed.type != Root.type:
167167
raise exceptions.RepositoryError(
168168
f"Expected 'root', got '{new_root.signed.type}'"
169169
)
170170

171171
# Verify that new root is signed by trusted root
172-
self.root.verify_delegate("root", new_root)
172+
self.root.verify_delegate(Root.type, new_root)
173173

174174
if new_root.signed.version != self.root.signed.version + 1:
175175
raise exceptions.ReplayedMetadataError(
176-
"root", new_root.signed.version, self.root.signed.version
176+
Root.type,
177+
new_root.signed.version,
178+
self.root.signed.version,
177179
)
178180

179181
# Verify that new root is signed by itself
180-
new_root.verify_delegate("root", new_root)
182+
new_root.verify_delegate(Root.type, new_root)
181183

182-
self._trusted_set["root"] = new_root
184+
self._trusted_set[Root.type] = new_root
183185
logger.info("Updated root v%d", new_root.signed.version)
184186

185187
def update_timestamp(self, data: bytes) -> None:
@@ -214,20 +216,20 @@ def update_timestamp(self, data: bytes) -> None:
214216
except DeserializationError as e:
215217
raise exceptions.RepositoryError("Failed to load timestamp") from e
216218

217-
if new_timestamp.signed.type != "timestamp":
219+
if new_timestamp.signed.type != Timestamp.type:
218220
raise exceptions.RepositoryError(
219221
f"Expected 'timestamp', got '{new_timestamp.signed.type}'"
220222
)
221223

222-
self.root.verify_delegate("timestamp", new_timestamp)
224+
self.root.verify_delegate(Timestamp.type, new_timestamp)
223225

224226
# If an existing trusted timestamp is updated,
225227
# check for a rollback attack
226228
if self.timestamp is not None:
227229
# Prevent rolling back timestamp version
228230
if new_timestamp.signed.version < self.timestamp.signed.version:
229231
raise exceptions.ReplayedMetadataError(
230-
"timestamp",
232+
Timestamp.type,
231233
new_timestamp.signed.version,
232234
self.timestamp.signed.version,
233235
)
@@ -237,15 +239,15 @@ def update_timestamp(self, data: bytes) -> None:
237239
< self.timestamp.signed.snapshot_meta.version
238240
):
239241
raise exceptions.ReplayedMetadataError(
240-
"snapshot",
242+
Snapshot.type,
241243
new_timestamp.signed.snapshot_meta.version,
242244
self.timestamp.signed.snapshot_meta.version,
243245
)
244246

245247
# expiry not checked to allow old timestamp to be used for rollback
246248
# protection of new timestamp: expiry is checked in update_snapshot()
247249

248-
self._trusted_set["timestamp"] = new_timestamp
250+
self._trusted_set[Timestamp.type] = new_timestamp
249251
logger.info("Updated timestamp v%d", new_timestamp.signed.version)
250252

251253
# timestamp is loaded: raise if it is not valid _final_ timestamp
@@ -310,12 +312,12 @@ def update_snapshot(
310312
except DeserializationError as e:
311313
raise exceptions.RepositoryError("Failed to load snapshot") from e
312314

313-
if new_snapshot.signed.type != "snapshot":
315+
if new_snapshot.signed.type != Snapshot.type:
314316
raise exceptions.RepositoryError(
315317
f"Expected 'snapshot', got '{new_snapshot.signed.type}'"
316318
)
317319

318-
self.root.verify_delegate("snapshot", new_snapshot)
320+
self.root.verify_delegate(Snapshot.type, new_snapshot)
319321

320322
# version not checked against meta version to allow old snapshot to be
321323
# used in rollback protection: it is checked when targets is updated
@@ -341,7 +343,7 @@ def update_snapshot(
341343
# expiry not checked to allow old snapshot to be used for rollback
342344
# protection of new snapshot: it is checked when targets is updated
343345

344-
self._trusted_set["snapshot"] = new_snapshot
346+
self._trusted_set[Snapshot.type] = new_snapshot
345347
logger.info("Updated snapshot v%d", new_snapshot.signed.version)
346348

347349
# snapshot is loaded, but we raise if it's not valid _final_ snapshot
@@ -371,7 +373,7 @@ def update_targets(self, data: bytes) -> None:
371373
RepositoryError: Metadata failed to load or verify. The actual
372374
error type and content will contain more details.
373375
"""
374-
self.update_delegated_targets(data, "targets", "root")
376+
self.update_delegated_targets(data, Targets.type, Root.type)
375377

376378
def update_delegated_targets(
377379
self, data: bytes, role_name: str, delegator_name: str
@@ -419,7 +421,7 @@ def update_delegated_targets(
419421
except DeserializationError as e:
420422
raise exceptions.RepositoryError("Failed to load snapshot") from e
421423

422-
if new_delegate.signed.type != "targets":
424+
if new_delegate.signed.type != Targets.type:
423425
raise exceptions.RepositoryError(
424426
f"Expected 'targets', got '{new_delegate.signed.type}'"
425427
)
@@ -449,12 +451,12 @@ def _load_trusted_root(self, data: bytes) -> None:
449451
except DeserializationError as e:
450452
raise exceptions.RepositoryError("Failed to load root") from e
451453

452-
if new_root.signed.type != "root":
454+
if new_root.signed.type != Root.type:
453455
raise exceptions.RepositoryError(
454456
f"Expected 'root', got '{new_root.signed.type}'"
455457
)
456458

457-
new_root.verify_delegate("root", new_root)
459+
new_root.verify_delegate(Root.type, new_root)
458460

459-
self._trusted_set["root"] = new_root
461+
self._trusted_set[Root.type] = new_root
460462
logger.info("Loaded trusted root v%d", new_root.signed.version)

0 commit comments

Comments
 (0)