Skip to content

Commit

Permalink
Automatically fill _type and spec_version in build_dict_c...
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
awwad committed Mar 29, 2019
1 parent 491577c commit 7ecf522
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 19 deletions.
26 changes: 25 additions & 1 deletion tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,38 @@ def test_build_dict_conforming_to_schema(self):
expires = '1985-10-21T13:20:00Z'
filedict = {'snapshot.json': {'length': length, 'hashes': hashes}}

self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches(

# Try with and without _type and spec_version, both of which are
# automatically populated if they are not included.
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # both
tuf.formats.build_dict_conforming_to_schema(
tuf.formats.TIMESTAMP_SCHEMA,
_type='timestamp',
spec_version=spec_version,
version=version,
expires=expires,
meta=filedict)))
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # neither
tuf.formats.build_dict_conforming_to_schema(
tuf.formats.TIMESTAMP_SCHEMA,
version=version,
expires=expires,
meta=filedict)))
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # one
tuf.formats.build_dict_conforming_to_schema(
tuf.formats.TIMESTAMP_SCHEMA,
spec_version=spec_version,
version=version,
expires=expires,
meta=filedict)))
self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( # the other
tuf.formats.build_dict_conforming_to_schema(
tuf.formats.TIMESTAMP_SCHEMA,
_type='timestamp',
version=version,
expires=expires,
meta=filedict)))


# Try test arguments for invalid Timestamp creation.
bad_spec_version = 123
Expand Down
72 changes: 64 additions & 8 deletions tuf/formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@
import time
import copy

import tuf

import securesystemslib.formats
import securesystemslib.schema as SCHEMA

import tuf

import six


Expand All @@ -98,6 +98,8 @@

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

def build_dict_conforming_to_schema(schema, **kwargs):
"""
Given a schema object (for example, TIMESTAMP_SCHEMA from this module) and
a set of keyword arguments, create a dictionary that conforms to the given
schema, using the keyword arguments to define the elements of the new dict.
<Purpose>
Given a schema object (for example, TIMESTAMP_SCHEMA from this module) and
a set of keyword arguments, create a dictionary that conforms to the given
schema, using the keyword arguments to define the elements of the new dict.
Checks the result to make sure that it conforms to the given schema, raising
an error if not.
Checks the result to make sure that it conforms to the given schema, raising
an error if not.
<Returns>
A dictionary conforming to the given schema. Adds certain required fields
if they are missing and can be deduced from the schema. The data returned
is a deep copy.
<Exceptions>
securesystemslib.exceptions.FormatError
if the provided data does not match the schema when assembled.
<Side Effects>
None. In particular, the provided values are not modified, and the
returned dictionary does not include references to them.
Returns the new dict conforming to the schema if there are no problems.
"""

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







# Automatically provide certain schema properties if they are not already
# provided and are required in objects of class <schema>.
# This includes:
# _type: <securesystemslib.schema.String object>
# spec_version: SPECIFICATION_VERSION_SCHEMA
#
# (Please note that _required is slightly misleading, as it includes both
# required and optional elements. It should probably be called _components.)
#
for schema_element in schema._required:
key = schema_element[0]
element_type = schema_element[1]

if key in dictionary:
# If the field has been provided, proceed normally.
continue

elif isinstance(element_type, SCHEMA.Optional):
# If the field has NOT been provided but IS optional, proceed without it.
continue

else:
# If the field has not been provided and is required, check to see if
# the field is one of the one of the fields we automatically fill.

# Currently, the list is limited to ['_type', 'spec_version'].

if key == '_type' and isinstance(element_type, SCHEMA.String):
# A SCHEMA.String stores its expected value in _string, so use that.
dictionary[key] = element_type._string

elif (key == 'spec_version' and
element_type == SPECIFICATION_VERSION_SCHEMA):
# If not provided, use the specification version in tuf/__init__.py
dictionary[key] = tuf.SPECIFICATION_VERSION


# If what we produce does not match the provided schema, raise a FormatError.
schema.check_match(dictionary)

Expand Down
10 changes: 0 additions & 10 deletions tuf/repository_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1330,8 +1330,6 @@ def generate_root_metadata(version, expiration_date, consistent_snapshot,
# There are very few things that really need to be done differently.
return tuf.formats.build_dict_conforming_to_schema(
tuf.formats.ROOT_SCHEMA,
_type='root',
spec_version=tuf.SPECIFICATION_VERSION,
version=version,
expires=expiration_date,
keys=keydict,
Expand Down Expand Up @@ -1469,17 +1467,13 @@ def generate_targets_metadata(targets_directory, target_files, version,
if delegations is not None:
return tuf.formats.build_dict_conforming_to_schema(
tuf.formats.TARGETS_SCHEMA,
_type='targets',
spec_version=tuf.SPECIFICATION_VERSION,
version=version,
expires=expiration_date,
targets=filedict,
delegations=delegations)
else:
return tuf.formats.build_dict_conforming_to_schema(
tuf.formats.TARGETS_SCHEMA,
_type='targets',
spec_version=tuf.SPECIFICATION_VERSION,
version=version,
expires=expiration_date,
targets=filedict)
Expand Down Expand Up @@ -1613,8 +1607,6 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date,
# There are very few things that really need to be done differently.
return tuf.formats.build_dict_conforming_to_schema(
tuf.formats.SNAPSHOT_SCHEMA,
_type='snapshot',
spec_version=tuf.SPECIFICATION_VERSION,
version=version,
expires=expiration_date,
meta=fileinfodict)
Expand Down Expand Up @@ -1692,8 +1684,6 @@ def generate_timestamp_metadata(snapshot_filename, version, expiration_date,
# There are very few things that really need to be done differently.
return tuf.formats.build_dict_conforming_to_schema(
tuf.formats.TIMESTAMP_SCHEMA,
spec_version=tuf.SPECIFICATION_VERSION,
_type='timestamp',
version=version,
expires=expiration_date,
meta=snapshot_fileinfo)
Expand Down

0 comments on commit 7ecf522

Please sign in to comment.