|
19 | 19 | * Metadata must be loaded/updated in order:
|
20 | 20 | root -> timestamp -> snapshot -> targets -> (other delegated targets)
|
21 | 21 |
|
| 22 | +
|
22 | 23 | Exceptions are raised if metadata fails to load in any way.
|
23 | 24 |
|
24 | 25 | Example of loading root, timestamp and snapshot:
|
@@ -121,54 +122,79 @@ def verify_with_threshold(
|
121 | 122 | class MetadataBundle(abc.Mapping):
|
122 | 123 | """Internal class to keep track of valid metadata in Updater
|
123 | 124 |
|
124 |
| - MetadataBundle ensures that metadata is valid. It provides easy ways to |
125 |
| - update the metadata with the caller making decisions on what is updated. |
| 125 | + MetadataBundle ensures that the collection of metadata in the bundle is |
| 126 | + valid. It provides easy ways to update the metadata with the caller making |
| 127 | + decisions on what is updated. |
126 | 128 | """
|
127 | 129 |
|
128 |
| - def __init__(self, data: bytes): |
129 |
| - """Initialize by loading trusted root metadata""" |
| 130 | + def __init__(self, root_data: bytes): |
| 131 | + """Initialize bundle by loading trusted root metadata |
| 132 | +
|
| 133 | + Args: |
| 134 | + root_data: Trusted root metadata as bytes. Note that this metadata |
| 135 | + will only be verified by itself: it is the source of trust for |
| 136 | + all metadata in the bundle. |
| 137 | +
|
| 138 | + Raises: |
| 139 | + RepositoryError: Metadata failed to load or verify. The actual |
| 140 | + error type and content will contain more details. |
| 141 | + """ |
130 | 142 | self._bundle = {} # type: Dict[str: Metadata]
|
131 | 143 | self.reference_time = datetime.utcnow()
|
132 | 144 | self._root_update_finished = False
|
133 | 145 |
|
134 |
| - # Load and validate the local root metadata |
135 |
| - # Valid root metadata is required |
| 146 | + # Load and validate the local root metadata. Valid initial trusted root |
| 147 | + # metadata is required |
136 | 148 | logger.debug("Updating initial trusted root")
|
137 |
| - self.update_root(data) |
| 149 | + self.update_root(root_data) |
138 | 150 |
|
139 |
| - # Implement Mapping |
140 |
| - def __getitem__(self, key: str) -> Metadata: |
141 |
| - return self._bundle[key] |
| 151 | + def __getitem__(self, role: str) -> Metadata: |
| 152 | + """Returns current Metadata for 'role'""" |
| 153 | + return self._bundle[role] |
142 | 154 |
|
143 | 155 | def __len__(self) -> int:
|
| 156 | + """Returns number of Metadata objects in bundle""" |
144 | 157 | return len(self._bundle)
|
145 | 158 |
|
146 | 159 | def __iter__(self) -> Iterator[Metadata]:
|
| 160 | + """Returns iterator over all Metadata objects in bundle""" |
147 | 161 | return iter(self._bundle)
|
148 | 162 |
|
149 | 163 | # Helper properties for top level metadata
|
150 | 164 | @property
|
151 | 165 | def root(self) -> Optional[Metadata]:
|
| 166 | + """Current root Metadata or None""" |
152 | 167 | return self._bundle.get("root")
|
153 | 168 |
|
154 | 169 | @property
|
155 | 170 | def timestamp(self) -> Optional[Metadata]:
|
| 171 | + """Current timestamp Metadata or None""" |
156 | 172 | return self._bundle.get("timestamp")
|
157 | 173 |
|
158 | 174 | @property
|
159 | 175 | def snapshot(self) -> Optional[Metadata]:
|
| 176 | + """Current snapshot Metadata or None""" |
160 | 177 | return self._bundle.get("snapshot")
|
161 | 178 |
|
162 | 179 | @property
|
163 | 180 | def targets(self) -> Optional[Metadata]:
|
| 181 | + """Current targets Metadata or None""" |
164 | 182 | return self._bundle.get("targets")
|
165 | 183 |
|
166 | 184 | # Methods for updating metadata
|
167 | 185 | def update_root(self, data: bytes):
|
168 | 186 | """Verifies and loads 'data' as new root metadata.
|
169 | 187 |
|
170 | 188 | Note that an expired intermediate root is considered valid: expiry is
|
171 |
| - only checked for the final root in root_update_finished().""" |
| 189 | + only checked for the final root in root_update_finished(). |
| 190 | +
|
| 191 | + Args: |
| 192 | + data: unverified new root metadata as bytes |
| 193 | +
|
| 194 | + Raises: |
| 195 | + RepositoryError: Metadata failed to load or verify. The actual |
| 196 | + error type and content will contain more details. |
| 197 | + """ |
172 | 198 | if self._root_update_finished:
|
173 | 199 | raise RuntimeError(
|
174 | 200 | "Cannot update root after root update is finished"
|
@@ -206,21 +232,30 @@ def update_root(self, data: bytes):
|
206 | 232 | logger.debug("Updated root")
|
207 | 233 |
|
208 | 234 | def root_update_finished(self):
|
209 |
| - """Mark root metadata as final.""" |
| 235 | + """Marks root metadata as final and verifies it is not expired |
| 236 | +
|
| 237 | + Raises: |
| 238 | + ExpiredMetadataError: The final root metadata is expired. |
| 239 | + """ |
210 | 240 | if self._root_update_finished:
|
211 | 241 | raise RuntimeError("Root update is already finished")
|
212 | 242 |
|
213 | 243 | if self.root.signed.is_expired(self.reference_time):
|
214 | 244 | raise exceptions.ExpiredMetadataError("New root.json is expired")
|
215 | 245 |
|
216 |
| - # We skip specification step 5.3.11: deleting timestamp and snapshot |
217 |
| - # with rotated keys is not needed as they will be invalid, are not |
218 |
| - # loaded and cannot be loaded |
219 | 246 | self._root_update_finished = True
|
220 | 247 | logger.debug("Verified final root.json")
|
221 | 248 |
|
222 | 249 | def update_timestamp(self, data: bytes):
|
223 |
| - """Verifies and loads 'data' as new timestamp metadata.""" |
| 250 | + """Verifies and loads 'data' as new timestamp metadata. |
| 251 | +
|
| 252 | + Args: |
| 253 | + data: unverified new timestamp metadata as bytes |
| 254 | +
|
| 255 | + Raises: |
| 256 | + RepositoryError: Metadata failed to load or verify. The actual |
| 257 | + error type and content will contain more details. |
| 258 | + """ |
224 | 259 | if not self._root_update_finished:
|
225 | 260 | # root_update_finished() not called
|
226 | 261 | raise RuntimeError("Cannot update timestamp before root")
|
@@ -270,7 +305,15 @@ def update_timestamp(self, data: bytes):
|
270 | 305 |
|
271 | 306 | # TODO: remove pylint disable once the hash verification is in metadata.py
|
272 | 307 | def update_snapshot(self, data: bytes): # pylint: disable=too-many-branches
|
273 |
| - """Verifies and loads 'data' as new snapshot metadata.""" |
| 308 | + """Verifies and loads 'data' as new snapshot metadata. |
| 309 | +
|
| 310 | + Args: |
| 311 | + data: unverified new snapshot metadata as bytes |
| 312 | +
|
| 313 | + Raises: |
| 314 | + RepositoryError: Metadata failed to load or verify. The actual |
| 315 | + error type and content will contain more details. |
| 316 | + """ |
274 | 317 |
|
275 | 318 | if self.timestamp is None:
|
276 | 319 | raise RuntimeError("Cannot update snapshot before timestamp")
|
@@ -339,15 +382,30 @@ def update_snapshot(self, data: bytes): # pylint: disable=too-many-branches
|
339 | 382 | logger.debug("Updated snapshot")
|
340 | 383 |
|
341 | 384 | def update_targets(self, data: bytes):
|
342 |
| - """Verifies and loads 'data' as new top-level targets metadata.""" |
| 385 | + """Verifies and loads 'data' as new top-level targets metadata. |
| 386 | +
|
| 387 | + Args: |
| 388 | + data: unverified new targets metadata as bytes |
| 389 | +
|
| 390 | + Raises: |
| 391 | + RepositoryError: Metadata failed to load or verify. The actual |
| 392 | + error type and content will contain more details. |
| 393 | + """ |
343 | 394 | self.update_delegated_targets(data, "targets", "root")
|
344 | 395 |
|
345 | 396 | def update_delegated_targets(
|
346 | 397 | self, data: bytes, role_name: str, delegator_name: str
|
347 | 398 | ):
|
348 | 399 | """Verifies and loads 'data' as new metadata for target 'role_name'.
|
349 | 400 |
|
350 |
| - Raises if verification fails |
| 401 | + Args: |
| 402 | + data: unverified new metadata as bytes |
| 403 | + role_name: The role name of the new metadata |
| 404 | + delegator_name: The name of the role delegating the new metadata |
| 405 | +
|
| 406 | + Raises: |
| 407 | + RepositoryError: Metadata failed to load or verify. The actual |
| 408 | + error type and content will contain more details. |
351 | 409 | """
|
352 | 410 | if self.snapshot is None:
|
353 | 411 | raise RuntimeError("Cannot load targets before snapshot")
|
|
0 commit comments