Skip to content

Commit 2145b67

Browse files
author
Lukas Pühringer
authored
Merge pull request #2321 from jku/repository-annotations
Repository annotations
2 parents 9d09c42 + ba949d9 commit 2145b67

3 files changed

Lines changed: 101 additions & 30 deletions

File tree

examples/repository/_simplerepo.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def __init__(self) -> None:
7171
)
7272

7373
# setup a basic repository, generate signing key per top-level role
74-
with self.edit("root") as root:
74+
with self.edit_root() as root:
7575
for role in ["root", "timestamp", "snapshot", "targets"]:
7676
key = keys.generate_ed25519_key()
7777
self.signer_cache[role].append(SSlibSigner(key))
@@ -127,14 +127,14 @@ def add_target(self, path: str, content: str) -> None:
127127
self.target_cache[path] = data
128128

129129
# add a target in the targets metadata
130-
with self.edit("targets") as targets:
130+
with self.edit_targets() as targets:
131131
targets.targets[path] = TargetFile.from_data(path, data)
132132

133133
logger.debug("Targets v%d", targets.version)
134134

135135
# update snapshot, timestamp
136-
self.snapshot()
137-
self.timestamp()
136+
self.do_snapshot()
137+
self.do_timestamp()
138138

139139
def submit_delegation(self, rolename: str, data: bytes) -> bool:
140140
"""Add a delegation to a (offline signed) delegated targets metadata"""
@@ -145,10 +145,11 @@ def submit_delegation(self, rolename: str, data: bytes) -> bool:
145145

146146
# add delegation and key
147147
role = DelegatedRole(rolename, [], 1, True, [f"{rolename}/*"])
148-
with self.edit("targets") as targets:
148+
with self.edit_targets() as targets:
149149
if targets.delegations is None:
150150
targets.delegations = Delegations({}, {})
151-
151+
if targets.delegations.roles is None:
152+
targets.delegations.roles = {}
152153
targets.delegations.roles[rolename] = role
153154
targets.add_key(key, rolename)
154155

@@ -159,8 +160,8 @@ def submit_delegation(self, rolename: str, data: bytes) -> bool:
159160
logger.debug("Targets v%d", targets.version)
160161

161162
# update snapshot, timestamp
162-
self.snapshot()
163-
self.timestamp()
163+
self.do_snapshot()
164+
self.do_timestamp()
164165

165166
return True
166167

@@ -176,15 +177,10 @@ def submit_role(self, role: str, data: bytes) -> bool:
176177
if not targetpath.startswith(f"{role}/"):
177178
raise ValueError(f"targets allowed under {role}/ only")
178179

179-
targets_md = self.role_cache["targets"][-1]
180+
targets_md = self.open("targets")
180181
targets_md.verify_delegate(role, md)
181-
if role in self.role_cache:
182-
current_md = self.role_cache[role][-1]
183-
current_ver = current_md.signed.version
184-
else:
185-
current_ver = 0
186182

187-
if md.signed.version != current_ver + 1:
183+
if md.signed.version != self.targets(role).version + 1:
188184
raise ValueError("Invalid version {md.signed.version}")
189185

190186
except (RepositoryError, ValueError) as e:
@@ -201,7 +197,7 @@ def submit_role(self, role: str, data: bytes) -> bool:
201197
self.target_cache[targetpath] = bytes(f"{targetpath}", "utf-8")
202198

203199
# update snapshot, timestamp
204-
self.snapshot()
205-
self.timestamp()
200+
self.do_snapshot()
201+
self.do_timestamp()
206202

207203
return True

examples/uploader/_localrepo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def add_target(self, role: str, targetpath: str) -> bool:
102102
data = bytes(targetpath, "utf-8")
103103
targetfile = TargetFile.from_data(targetpath, data)
104104
try:
105-
with self.edit(role) as delegated:
105+
with self.edit_targets(role) as delegated:
106106
delegated.targets[targetpath] = targetfile
107107

108108
except Exception as e: # pylint: disable=broad-except

tuf/repository/_repository.py

Lines changed: 87 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@
99
from copy import deepcopy
1010
from typing import Dict, Generator, Optional, Tuple
1111

12-
from tuf.api.metadata import Metadata, MetaFile, Signed
12+
from tuf.api.metadata import (
13+
Metadata,
14+
MetaFile,
15+
Root,
16+
Signed,
17+
Snapshot,
18+
Targets,
19+
Timestamp,
20+
)
1321

1422
logger = logging.getLogger(__name__)
1523

@@ -57,8 +65,9 @@ def close(self, role: str, md: Metadata) -> None:
5765
def targets_infos(self) -> Dict[str, MetaFile]:
5866
"""Returns the MetaFiles for current targets metadatas
5967
60-
This property is used by snapshot() to update Snapshot.meta: Repository
61-
implementations should override this property to enable snapshot().
68+
This property is used by do_snapshot() to update Snapshot.meta:
69+
Repository implementations should override this property to enable
70+
do_snapshot().
6271
6372
Note that there is a difference between this return value and
6473
Snapshot.meta: This dictionary reflects the targets metadata that
@@ -71,9 +80,9 @@ def targets_infos(self) -> Dict[str, MetaFile]:
7180
def snapshot_info(self) -> MetaFile:
7281
"""Returns the MetaFile for current snapshot metadata
7382
74-
This property is used by timestamp() to update Timestamp.meta:
83+
This property is used by do_timestamp() to update Timestamp.meta:
7584
Repository implementations should override this property to enable
76-
timestamp().
85+
do_timestamp().
7786
"""
7887
raise NotImplementedError
7988

@@ -94,7 +103,71 @@ def edit(self, role: str) -> Generator[Signed, None, None]:
94103
yield md.signed
95104
self.close(role, md)
96105

97-
def snapshot(self, force: bool = False) -> Tuple[bool, Dict[str, MetaFile]]:
106+
@contextmanager
107+
def edit_root(self) -> Generator[Root, None, None]:
108+
"""Context manager for editing root metadata. See edit()"""
109+
with self.edit(Root.type) as root:
110+
if not isinstance(root, Root):
111+
raise RuntimeError("Unexpected root type")
112+
yield root
113+
114+
@contextmanager
115+
def edit_timestamp(self) -> Generator[Timestamp, None, None]:
116+
"""Context manager for editing timestamp metadata. See edit()"""
117+
with self.edit(Timestamp.type) as timestamp:
118+
if not isinstance(timestamp, Timestamp):
119+
raise RuntimeError("Unexpected timestamp type")
120+
yield timestamp
121+
122+
@contextmanager
123+
def edit_snapshot(self) -> Generator[Snapshot, None, None]:
124+
"""Context manager for editing snapshot metadata. See edit()"""
125+
with self.edit(Snapshot.type) as snapshot:
126+
if not isinstance(snapshot, Snapshot):
127+
raise RuntimeError("Unexpected snapshot type")
128+
yield snapshot
129+
130+
@contextmanager
131+
def edit_targets(
132+
self, rolename: str = Targets.type
133+
) -> Generator[Targets, None, None]:
134+
"""Context manager for editing targets metadata. See edit()"""
135+
with self.edit(rolename) as targets:
136+
if not isinstance(targets, Targets):
137+
raise RuntimeError(f"Unexpected targets ({rolename}) type")
138+
yield targets
139+
140+
def root(self) -> Root:
141+
"""Read current root metadata"""
142+
root = self.open(Root.type).signed
143+
if not isinstance(root, Root):
144+
raise RuntimeError("Unexpected root type")
145+
return root
146+
147+
def timestamp(self) -> Timestamp:
148+
"""Read current timestamp metadata"""
149+
timestamp = self.open(Timestamp.type).signed
150+
if not isinstance(timestamp, Timestamp):
151+
raise RuntimeError("Unexpected timestamp type")
152+
return timestamp
153+
154+
def snapshot(self) -> Snapshot:
155+
"""Read current snapshot metadata"""
156+
snapshot = self.open(Snapshot.type).signed
157+
if not isinstance(snapshot, Snapshot):
158+
raise RuntimeError("Unexpected snapshot type")
159+
return snapshot
160+
161+
def targets(self, rolename: str = Targets.type) -> Targets:
162+
"""Read current targets metadata"""
163+
targets = self.open(rolename).signed
164+
if not isinstance(targets, Targets):
165+
raise RuntimeError("Unexpected targets type")
166+
return targets
167+
168+
def do_snapshot(
169+
self, force: bool = False
170+
) -> Tuple[bool, Dict[str, MetaFile]]:
98171
"""Update snapshot meta information
99172
100173
Updates the snapshot meta information according to current targets
@@ -115,7 +188,7 @@ def snapshot(self, force: bool = False) -> Tuple[bool, Dict[str, MetaFile]]:
115188
update_version = force
116189
removed: Dict[str, MetaFile] = {}
117190

118-
with self.edit("snapshot") as snapshot:
191+
with self.edit_snapshot() as snapshot:
119192
for keyname, new_meta in self.targets_infos.items():
120193
if keyname not in snapshot.meta:
121194
update_version = True
@@ -131,18 +204,20 @@ def snapshot(self, force: bool = False) -> Tuple[bool, Dict[str, MetaFile]]:
131204
removed[keyname] = old_meta
132205

133206
if not update_version:
134-
# prevent edit() from storing a new snapshot version
207+
# prevent edit_snapshot() from storing a new version
135208
raise AbortEdit("Skip snapshot: No targets version changes")
136209

137210
if not update_version:
138-
# this is reachable as edit() handles AbortEdit
211+
# this is reachable as edit_snapshot() handles AbortEdit
139212
logger.debug("Snapshot update not needed") # type: ignore[unreachable]
140213
else:
141214
logger.debug("Snapshot v%d", snapshot.version)
142215

143216
return update_version, removed
144217

145-
def timestamp(self, force: bool = False) -> Tuple[bool, Optional[MetaFile]]:
218+
def do_timestamp(
219+
self, force: bool = False
220+
) -> Tuple[bool, Optional[MetaFile]]:
146221
"""Update timestamp meta information
147222
148223
Updates timestamp according to current snapshot state
@@ -153,7 +228,7 @@ def timestamp(self, force: bool = False) -> Tuple[bool, Optional[MetaFile]]:
153228
"""
154229
update_version = force
155230
removed = None
156-
with self.edit("timestamp") as timestamp:
231+
with self.edit_timestamp() as timestamp:
157232
if self.snapshot_info.version < timestamp.snapshot_meta.version:
158233
raise ValueError("snapshot version rollback")
159234

@@ -166,7 +241,7 @@ def timestamp(self, force: bool = False) -> Tuple[bool, Optional[MetaFile]]:
166241
raise AbortEdit("Skip timestamp: No snapshot version changes")
167242

168243
if not update_version:
169-
# this is reachable as edit() handles AbortEdit
244+
# this is reachable as edit_timestamp() handles AbortEdit
170245
logger.debug("Timestamp update not needed") # type: ignore[unreachable]
171246
else:
172247
logger.debug("Timestamp v%d", timestamp.version)

0 commit comments

Comments
 (0)