Skip to content

Commit 7ecf522

Browse files
committed
Automatically fill _type and spec_version in build_dict_c...
in tuf.formats.build_dict_conforming_to_schema Populate _type with the expected value for the given schema, and populate spec_version with tuf.SPECIFICATION_VERSION. Do this only when the values are not provided, and support overriding them. Also adds testing for the above and takes advantage of the above in repository_lib's _generate metadata functions. Signed-off-by: Sebastien Awwad <sebastien.awwad@gmail.com>
1 parent 491577c commit 7ecf522

3 files changed

Lines changed: 89 additions & 19 deletions

File tree

tests/test_formats.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,38 @@ def test_build_dict_conforming_to_schema(self):
300300
expires = '1985-10-21T13:20:00Z'
301301
filedict = {'snapshot.json': {'length': length, 'hashes': hashes}}
302302

303-
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches(
303+
304+
# Try with and without _type and spec_version, both of which are
305+
# automatically populated if they are not included.
306+
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # both
304307
tuf.formats.build_dict_conforming_to_schema(
305308
tuf.formats.TIMESTAMP_SCHEMA,
306309
_type='timestamp',
307310
spec_version=spec_version,
308311
version=version,
309312
expires=expires,
310313
meta=filedict)))
314+
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # neither
315+
tuf.formats.build_dict_conforming_to_schema(
316+
tuf.formats.TIMESTAMP_SCHEMA,
317+
version=version,
318+
expires=expires,
319+
meta=filedict)))
320+
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # one
321+
tuf.formats.build_dict_conforming_to_schema(
322+
tuf.formats.TIMESTAMP_SCHEMA,
323+
spec_version=spec_version,
324+
version=version,
325+
expires=expires,
326+
meta=filedict)))
327+
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # the other
328+
tuf.formats.build_dict_conforming_to_schema(
329+
tuf.formats.TIMESTAMP_SCHEMA,
330+
_type='timestamp',
331+
version=version,
332+
expires=expires,
333+
meta=filedict)))
334+
311335

312336
# Try test arguments for invalid Timestamp creation.
313337
bad_spec_version = 123

tuf/formats.py

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@
6969
import time
7070
import copy
7171

72-
import tuf
73-
7472
import securesystemslib.formats
7573
import securesystemslib.schema as SCHEMA
7674

75+
import tuf
76+
7777
import six
7878

7979

@@ -98,6 +98,8 @@
9898

9999
# Role object in {'keyids': [keydids..], 'name': 'ABC', 'threshold': 1,
100100
# 'paths':[filepaths..]} format.
101+
# TODO: This is not a role. In further #660-related PRs, fix it, similar to
102+
# the way I did in Uptane's TUF fork.
101103
ROLE_SCHEMA = SCHEMA.Object(
102104
object_name = 'ROLE_SCHEMA',
103105
name = SCHEMA.Optional(securesystemslib.formats.ROLENAME_SCHEMA),
@@ -470,14 +472,27 @@ def make_signable(role_schema):
470472

471473
def build_dict_conforming_to_schema(schema, **kwargs):
472474
"""
473-
Given a schema object (for example, TIMESTAMP_SCHEMA from this module) and
474-
a set of keyword arguments, create a dictionary that conforms to the given
475-
schema, using the keyword arguments to define the elements of the new dict.
475+
<Purpose>
476+
Given a schema object (for example, TIMESTAMP_SCHEMA from this module) and
477+
a set of keyword arguments, create a dictionary that conforms to the given
478+
schema, using the keyword arguments to define the elements of the new dict.
479+
480+
Checks the result to make sure that it conforms to the given schema, raising
481+
an error if not.
476482
477-
Checks the result to make sure that it conforms to the given schema, raising
478-
an error if not.
483+
<Returns>
484+
A dictionary conforming to the given schema. Adds certain required fields
485+
if they are missing and can be deduced from the schema. The data returned
486+
is a deep copy.
487+
488+
<Exceptions>
489+
securesystemslib.exceptions.FormatError
490+
if the provided data does not match the schema when assembled.
491+
492+
<Side Effects>
493+
None. In particular, the provided values are not modified, and the
494+
returned dictionary does not include references to them.
479495
480-
Returns the new dict conforming to the schema if there are no problems.
481496
"""
482497

483498
# Check that schema supports a check_match call.
@@ -501,6 +516,47 @@ def build_dict_conforming_to_schema(schema, **kwargs):
501516

502517

503518

519+
520+
521+
522+
523+
# Automatically provide certain schema properties if they are not already
524+
# provided and are required in objects of class <schema>.
525+
# This includes:
526+
# _type: <securesystemslib.schema.String object>
527+
# spec_version: SPECIFICATION_VERSION_SCHEMA
528+
#
529+
# (Please note that _required is slightly misleading, as it includes both
530+
# required and optional elements. It should probably be called _components.)
531+
#
532+
for schema_element in schema._required:
533+
key = schema_element[0]
534+
element_type = schema_element[1]
535+
536+
if key in dictionary:
537+
# If the field has been provided, proceed normally.
538+
continue
539+
540+
elif isinstance(element_type, SCHEMA.Optional):
541+
# If the field has NOT been provided but IS optional, proceed without it.
542+
continue
543+
544+
else:
545+
# If the field has not been provided and is required, check to see if
546+
# the field is one of the one of the fields we automatically fill.
547+
548+
# Currently, the list is limited to ['_type', 'spec_version'].
549+
550+
if key == '_type' and isinstance(element_type, SCHEMA.String):
551+
# A SCHEMA.String stores its expected value in _string, so use that.
552+
dictionary[key] = element_type._string
553+
554+
elif (key == 'spec_version' and
555+
element_type == SPECIFICATION_VERSION_SCHEMA):
556+
# If not provided, use the specification version in tuf/__init__.py
557+
dictionary[key] = tuf.SPECIFICATION_VERSION
558+
559+
504560
# If what we produce does not match the provided schema, raise a FormatError.
505561
schema.check_match(dictionary)
506562

tuf/repository_lib.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,8 +1330,6 @@ def generate_root_metadata(version, expiration_date, consistent_snapshot,
13301330
# There are very few things that really need to be done differently.
13311331
return tuf.formats.build_dict_conforming_to_schema(
13321332
tuf.formats.ROOT_SCHEMA,
1333-
_type='root',
1334-
spec_version=tuf.SPECIFICATION_VERSION,
13351333
version=version,
13361334
expires=expiration_date,
13371335
keys=keydict,
@@ -1469,17 +1467,13 @@ def generate_targets_metadata(targets_directory, target_files, version,
14691467
if delegations is not None:
14701468
return tuf.formats.build_dict_conforming_to_schema(
14711469
tuf.formats.TARGETS_SCHEMA,
1472-
_type='targets',
1473-
spec_version=tuf.SPECIFICATION_VERSION,
14741470
version=version,
14751471
expires=expiration_date,
14761472
targets=filedict,
14771473
delegations=delegations)
14781474
else:
14791475
return tuf.formats.build_dict_conforming_to_schema(
14801476
tuf.formats.TARGETS_SCHEMA,
1481-
_type='targets',
1482-
spec_version=tuf.SPECIFICATION_VERSION,
14831477
version=version,
14841478
expires=expiration_date,
14851479
targets=filedict)
@@ -1613,8 +1607,6 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date,
16131607
# There are very few things that really need to be done differently.
16141608
return tuf.formats.build_dict_conforming_to_schema(
16151609
tuf.formats.SNAPSHOT_SCHEMA,
1616-
_type='snapshot',
1617-
spec_version=tuf.SPECIFICATION_VERSION,
16181610
version=version,
16191611
expires=expiration_date,
16201612
meta=fileinfodict)
@@ -1692,8 +1684,6 @@ def generate_timestamp_metadata(snapshot_filename, version, expiration_date,
16921684
# There are very few things that really need to be done differently.
16931685
return tuf.formats.build_dict_conforming_to_schema(
16941686
tuf.formats.TIMESTAMP_SCHEMA,
1695-
spec_version=tuf.SPECIFICATION_VERSION,
1696-
_type='timestamp',
16971687
version=version,
16981688
expires=expiration_date,
16991689
meta=snapshot_fileinfo)

0 commit comments

Comments
 (0)