Skip to content

Commit d06193e

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. Adresses theupdateframework#1648 Signed-off-by: Ivana Atanasova <[email protected]>
1 parent 5a9b3fa commit d06193e

File tree

4 files changed

+70
-63
lines changed

4 files changed

+70
-63
lines changed

tests/repository_simulator.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
Timestamp,
7474
)
7575
from tuf.api.serialization.json import JSONSerializer
76+
from tuf.api.metadata import Rolename
7677
from tuf.exceptions import FetcherHTTPError, RepositoryError
7778
from tuf.ngclient.fetcher import FetcherInterface
7879

@@ -136,7 +137,7 @@ def targets(self) -> Targets:
136137
return self.md_targets.signed
137138

138139
def all_targets(self) -> Iterator[Tuple[str, Targets]]:
139-
yield "targets", self.md_targets.signed
140+
yield Rolename.targets, self.md_targets.signed
140141
for role, md in self.md_delegates.items():
141142
yield role, md.signed
142143

@@ -178,7 +179,7 @@ def _initialize(self):
178179
def publish_root(self):
179180
"""Sign and store a new serialized version of root"""
180181
self.md_root.signatures.clear()
181-
for signer in self.signers["root"].values():
182+
for signer in self.signers[Rolename.root].values():
182183
self.md_root.sign(signer, append=True)
183184

184185
self.signed_roots.append(self.md_root.to_bytes(JSONSerializer()))
@@ -191,9 +192,9 @@ def fetch(self, url: str) -> Iterator[bytes]:
191192
if path.startswith("/metadata/") and path.endswith(".json"):
192193
ver_and_name = path[len("/metadata/") :][: -len(".json")]
193194
# only consistent_snapshot supported ATM: timestamp is special case
194-
if ver_and_name == "timestamp":
195+
if ver_and_name == Rolename.timestamp:
195196
version = None
196-
role = "timestamp"
197+
role = Rolename.timestamp
197198
else:
198199
version, _, role = ver_and_name.partition(".")
199200
version = int(version)
@@ -230,19 +231,19 @@ def _fetch_metadata(
230231
231232
If version is None, non-versioned metadata is being requested
232233
"""
233-
if role == "root":
234+
if role == Rolename.root:
234235
# return a version previously serialized in publish_root()
235236
if version is None or version > len(self.signed_roots):
236237
raise FetcherHTTPError(f"Unknown root version {version}", 404)
237238
logger.debug("fetched root version %d", role, version)
238239
return self.signed_roots[version - 1]
239240
else:
240241
# sign and serialize the requested metadata
241-
if role == "timestamp":
242+
if role == Rolename.timestamp:
242243
md: Metadata = self.md_timestamp
243-
elif role == "snapshot":
244+
elif role == Rolename.snapshot:
244245
md = self.md_snapshot
245-
elif role == "targets":
246+
elif role == Rolename.targets:
246247
md = self.md_targets
247248
else:
248249
md = self.md_delegates[role]
@@ -275,7 +276,7 @@ def update_timestamp(self):
275276
self.timestamp.snapshot_meta.version = self.snapshot.version
276277

277278
if self.compute_metafile_hashes_length:
278-
hashes, length = self._compute_hashes_and_length("snapshot")
279+
hashes, length = self._compute_hashes_and_length(Rolename.snapshot)
279280
self.timestamp.snapshot_meta.hashes = hashes
280281
self.timestamp.snapshot_meta.length = length
281282

@@ -296,7 +297,7 @@ def update_snapshot(self):
296297
self.update_timestamp()
297298

298299
def add_target(self, role: str, data: bytes, path: str):
299-
if role == "targets":
300+
if role == Rolename.targets:
300301
targets = self.targets
301302
else:
302303
targets = self.md_delegates[role].signed
@@ -314,7 +315,7 @@ def add_delegation(
314315
paths: Optional[List[str]],
315316
hash_prefixes: Optional[List[str]],
316317
):
317-
if delegator_name == "targets":
318+
if delegator_name == Rolename.targets:
318319
delegator = self.targets
319320
else:
320321
delegator = self.md_delegates[delegator_name].signed
@@ -350,9 +351,9 @@ def write(self):
350351

351352
for ver in range(1, len(self.signed_roots) + 1):
352353
with open(os.path.join(dir, f"{ver}.root.json"), "wb") as f:
353-
f.write(self._fetch_metadata("root", ver))
354+
f.write(self._fetch_metadata(Rolename.root, ver))
354355

355-
for role in ["timestamp", "snapshot", "targets"]:
356+
for role in [Rolename.timestamp, Rolename.snapshot, Rolename.targets]:
356357
with open(os.path.join(dir, f"{role}.json"), "wb") as f:
357358
f.write(self._fetch_metadata(role))
358359

tuf/api/metadata.py

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

64+
class Rolename:
65+
root = "root"
66+
timestamp = "timestamp"
67+
snapshot = "snapshot"
68+
targets = "targets"
69+
6470
# pylint: disable=too-many-lines
6571

6672
logger = logging.getLogger(__name__)
6773

6874
# We aim to support SPECIFICATION_VERSION and require the input metadata
6975
# files to have the same major version (the first number) as ours.
7076
SPECIFICATION_VERSION = ["1", "0", "19"]
71-
TOP_LEVEL_ROLE_NAMES = {"root", "timestamp", "snapshot", "targets"}
77+
TOP_LEVEL_ROLE_NAMES = {Rolename.root, Rolename.timestamp, Rolename.snapshot, Rolename.targets}
7278

7379
# T is a Generic type constraint for Metadata.signed
7480
T = TypeVar("T", "Root", "Timestamp", "Snapshot", "Targets")
@@ -130,13 +136,13 @@ def from_dict(cls, metadata: Dict[str, Any]) -> "Metadata[T]":
130136
# Dispatch to contained metadata class on metadata _type field.
131137
_type = metadata["signed"]["_type"]
132138

133-
if _type == "targets":
139+
if _type == Rolename.targets:
134140
inner_cls: Type[Signed] = Targets
135-
elif _type == "snapshot":
141+
elif _type == Rolename.snapshot:
136142
inner_cls = Snapshot
137-
elif _type == "timestamp":
143+
elif _type == Rolename.timestamp:
138144
inner_cls = Timestamp
139-
elif _type == "root":
145+
elif _type == Rolename.root:
140146
inner_cls = Root
141147
else:
142148
raise ValueError(f'unrecognized metadata type "{_type}"')
@@ -712,7 +718,7 @@ class Root(Signed):
712718
unrecognized_fields: Dictionary of all unrecognized fields.
713719
"""
714720

715-
_signed_type = "root"
721+
_signed_type = Rolename.root
716722

717723
# TODO: determine an appropriate value for max-args
718724
# pylint: disable=too-many-arguments
@@ -965,7 +971,7 @@ class Timestamp(Signed):
965971
snapshot_meta: Meta information for snapshot metadata.
966972
"""
967973

968-
_signed_type = "timestamp"
974+
_signed_type = Rolename.timestamp
969975

970976
def __init__(
971977
self,
@@ -1015,7 +1021,7 @@ class Snapshot(Signed):
10151021
meta: A dictionary of target metadata filenames to MetaFile objects.
10161022
"""
10171023

1018-
_signed_type = "snapshot"
1024+
_signed_type = Rolename.snapshot
10191025

10201026
def __init__(
10211027
self,
@@ -1402,7 +1408,7 @@ class Targets(Signed):
14021408
unrecognized_fields: Dictionary of all unrecognized fields.
14031409
"""
14041410

1405-
_signed_type = "targets"
1411+
_signed_type = Rolename.targets
14061412

14071413
# TODO: determine an appropriate value for max-args
14081414
# pylint: disable=too-many-arguments
@@ -1423,7 +1429,7 @@ def __init__(
14231429
def from_dict(cls, signed_dict: Dict[str, Any]) -> "Targets":
14241430
"""Creates Targets object from its dict representation."""
14251431
common_args = cls._common_fields_from_dict(signed_dict)
1426-
targets = signed_dict.pop("targets")
1432+
targets = signed_dict.pop(Rolename.targets)
14271433
try:
14281434
delegations_dict = signed_dict.pop("delegations")
14291435
except KeyError:
@@ -1444,7 +1450,7 @@ def to_dict(self) -> Dict[str, Any]:
14441450
targets = {}
14451451
for target_path, target_file_obj in self.targets.items():
14461452
targets[target_path] = target_file_obj.to_dict()
1447-
targets_dict["targets"] = targets
1453+
targets_dict[Rolename.targets] = targets
14481454
if self.delegations is not None:
14491455
targets_dict["delegations"] = self.delegations.to_dict()
14501456
return targets_dict

tuf/ngclient/_internal/trusted_metadata_set.py

Lines changed: 27 additions & 27 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[Rolename.root]) 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(Rolename.root, 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(Rolename.timestamp) 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(Rolename.snapshot, version) as f:
5959
>>> trusted_set.update_snapshot(f.read())
6060
6161
TODO:
@@ -73,7 +73,7 @@
7373
from typing import Dict, Iterator, Optional
7474

7575
from tuf import exceptions
76-
from tuf.api.metadata import Metadata, Root, Snapshot, Targets, Timestamp
76+
from tuf.api.metadata import Metadata, Root, Snapshot, Targets, Timestamp, Rolename
7777
from tuf.api.serialization import DeserializationError
7878

7979
logger = logging.getLogger(__name__)
@@ -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[Rolename.root]
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(Rolename.timestamp)
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(Rolename.snapshot)
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(Rolename.targets)
142142

143143
# Methods for updating metadata
144144
def update_root(self, data: bytes) -> None:
@@ -163,23 +163,23 @@ 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 != Rolename.root:
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(Rolename.root, 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+
Rolename.root, new_root.signed.version, self.root.signed.version
177177
)
178178

179179
# Verify that new root is signed by itself
180-
new_root.verify_delegate("root", new_root)
180+
new_root.verify_delegate(Rolename.root, new_root)
181181

182-
self._trusted_set["root"] = new_root
182+
self._trusted_set[Rolename.root] = new_root
183183
logger.info("Updated root v%d", new_root.signed.version)
184184

185185
def update_timestamp(self, data: bytes) -> None:
@@ -214,20 +214,20 @@ def update_timestamp(self, data: bytes) -> None:
214214
except DeserializationError as e:
215215
raise exceptions.RepositoryError("Failed to load timestamp") from e
216216

217-
if new_timestamp.signed.type != "timestamp":
217+
if new_timestamp.signed.type != Rolename.timestamp:
218218
raise exceptions.RepositoryError(
219219
f"Expected 'timestamp', got '{new_timestamp.signed.type}'"
220220
)
221221

222-
self.root.verify_delegate("timestamp", new_timestamp)
222+
self.root.verify_delegate(Rolename.timestamp, new_timestamp)
223223

224224
# If an existing trusted timestamp is updated,
225225
# check for a rollback attack
226226
if self.timestamp is not None:
227227
# Prevent rolling back timestamp version
228228
if new_timestamp.signed.version < self.timestamp.signed.version:
229229
raise exceptions.ReplayedMetadataError(
230-
"timestamp",
230+
Rolename.timestamp,
231231
new_timestamp.signed.version,
232232
self.timestamp.signed.version,
233233
)
@@ -237,15 +237,15 @@ def update_timestamp(self, data: bytes) -> None:
237237
< self.timestamp.signed.snapshot_meta.version
238238
):
239239
raise exceptions.ReplayedMetadataError(
240-
"snapshot",
240+
Rolename.snapshot,
241241
new_timestamp.signed.snapshot_meta.version,
242242
self.timestamp.signed.snapshot_meta.version,
243243
)
244244

245245
# expiry not checked to allow old timestamp to be used for rollback
246246
# protection of new timestamp: expiry is checked in update_snapshot()
247247

248-
self._trusted_set["timestamp"] = new_timestamp
248+
self._trusted_set[Rolename.timestamp] = new_timestamp
249249
logger.info("Updated timestamp v%d", new_timestamp.signed.version)
250250

251251
# timestamp is loaded: raise if it is not valid _final_ timestamp
@@ -310,12 +310,12 @@ def update_snapshot(
310310
except DeserializationError as e:
311311
raise exceptions.RepositoryError("Failed to load snapshot") from e
312312

313-
if new_snapshot.signed.type != "snapshot":
313+
if new_snapshot.signed.type != Rolename.snapshot:
314314
raise exceptions.RepositoryError(
315315
f"Expected 'snapshot', got '{new_snapshot.signed.type}'"
316316
)
317317

318-
self.root.verify_delegate("snapshot", new_snapshot)
318+
self.root.verify_delegate(Rolename.snapshot, new_snapshot)
319319

320320
# version not checked against meta version to allow old snapshot to be
321321
# used in rollback protection: it is checked when targets is updated
@@ -341,7 +341,7 @@ def update_snapshot(
341341
# expiry not checked to allow old snapshot to be used for rollback
342342
# protection of new snapshot: it is checked when targets is updated
343343

344-
self._trusted_set["snapshot"] = new_snapshot
344+
self._trusted_set[Rolename.snapshot] = new_snapshot
345345
logger.info("Updated snapshot v%d", new_snapshot.signed.version)
346346

347347
# snapshot is loaded, but we raise if it's not valid _final_ snapshot
@@ -371,7 +371,7 @@ def update_targets(self, data: bytes) -> None:
371371
RepositoryError: Metadata failed to load or verify. The actual
372372
error type and content will contain more details.
373373
"""
374-
self.update_delegated_targets(data, "targets", "root")
374+
self.update_delegated_targets(data, Rolename.targets, Rolename.root)
375375

376376
def update_delegated_targets(
377377
self, data: bytes, role_name: str, delegator_name: str
@@ -419,7 +419,7 @@ def update_delegated_targets(
419419
except DeserializationError as e:
420420
raise exceptions.RepositoryError("Failed to load snapshot") from e
421421

422-
if new_delegate.signed.type != "targets":
422+
if new_delegate.signed.type != Rolename.targets:
423423
raise exceptions.RepositoryError(
424424
f"Expected 'targets', got '{new_delegate.signed.type}'"
425425
)
@@ -449,12 +449,12 @@ def _load_trusted_root(self, data: bytes) -> None:
449449
except DeserializationError as e:
450450
raise exceptions.RepositoryError("Failed to load root") from e
451451

452-
if new_root.signed.type != "root":
452+
if new_root.signed.type != Rolename.root:
453453
raise exceptions.RepositoryError(
454454
f"Expected 'root', got '{new_root.signed.type}'"
455455
)
456456

457-
new_root.verify_delegate("root", new_root)
457+
new_root.verify_delegate(Rolename.root, new_root)
458458

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

0 commit comments

Comments
 (0)