diff --git a/tests/test_formats.py b/tests/test_formats.py index 298c36f7a3..541e0c2b06 100755 --- a/tests/test_formats.py +++ b/tests/test_formats.py @@ -273,68 +273,60 @@ def test_schemas(self): - def test_MetaFile(self): - # Test conditions for instantiations of a class that inherits from - # 'tuf.formats.MetaFile'. - class NewMetadataFile(tuf.formats.MetaFile): - def __init__(self, version, expires): - self.info = {} - self.info['version'] = version - self.info['expires'] = expires - - metadata = NewMetadataFile(123, 456) - metadata2 = NewMetadataFile(123, 456) - metadata3 = NewMetadataFile(333, 333) - - # Test the comparison operators. - self.assertTrue(metadata == metadata2) - self.assertFalse(metadata != metadata2) - self.assertFalse(metadata == metadata3) - - # Test the 'getattr' method. - self.assertEqual(123, getattr(metadata, 'version')) - self.assertRaises(AttributeError, getattr, metadata, 'bad') - + def test_build_dict_conforming_to_schema(self): + # Test construction of a few metadata formats using + # build_dict_conforming_to_schema(). - def test_TimestampFile(self): - # Test conditions for valid instances of 'tuf.formats.TimestampFile'. - version = 8 + # Try building Timestamp metadata. + version = 8 length = 88 hashes = {'sha256': '3c7fe3eeded4a34'} expires = '1985-10-21T13:20:00Z' filedict = {'snapshot.json': {'length': length, 'hashes': hashes}} - make_metadata = tuf.formats.TimestampFile.make_metadata - from_metadata = tuf.formats.TimestampFile.from_metadata - TIMESTAMP_SCHEMA = tuf.formats.TIMESTAMP_SCHEMA - - self.assertTrue(TIMESTAMP_SCHEMA.matches(make_metadata(version, expires, - filedict))) - metadata = make_metadata(version, expires, filedict) - self.assertTrue(isinstance(from_metadata(metadata), tuf.formats.TimestampFile)) + self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches( + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TIMESTAMP_SCHEMA, + _type='Timestamp', + version=version, + expires=expires, + meta=filedict))) - # Test conditions for invalid arguments. + # Try test arguments for invalid Timestamp creation. bad_version = 'eight' bad_expires = '2000' bad_filedict = 123 - self.assertRaises(tuf.FormatError, make_metadata, bad_version, - expires, filedict) - self.assertRaises(tuf.FormatError, make_metadata, version, - bad_expires, filedict) - self.assertRaises(tuf.FormatError, make_metadata, version, - expires, bad_filedict) - - self.assertRaises(tuf.FormatError, from_metadata, 123) - - - - - def test_RootFile(self): - # Test conditions for valid instances of 'tuf.formats.RootFile'. - version = 8 + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TIMESTAMP_SCHEMA, + _type='Timestamp', + version=bad_version, + expires=expires, + meta=filedict) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TIMESTAMP_SCHEMA, + _type='Timestamp', + version=version, + expires=bad_expires, + meta=filedict) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TIMESTAMP_SCHEMA, + _type='Timestamp', + version=version, + expires=expires, + meta=bad_filedict) + + with self.assertRaises(ValueError): + tuf.formats.build_dict_conforming_to_schema(123) + + + # Try building Root metadata. consistent_snapshot = False - expires = '1985-10-21T13:20:00Z' keydict = {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', @@ -346,85 +338,149 @@ def test_RootFile(self): compression_algorithms = ['gz'] - make_metadata = tuf.formats.RootFile.make_metadata - from_metadata = tuf.formats.RootFile.from_metadata - ROOT_SCHEMA = tuf.formats.ROOT_SCHEMA - self.assertTrue(ROOT_SCHEMA.matches(make_metadata(version, expires, - keydict, roledict, - consistent_snapshot, - compression_algorithms))) - metadata = make_metadata(version, expires, keydict, roledict, - consistent_snapshot, compression_algorithms) - self.assertTrue(isinstance(from_metadata(metadata), tuf.formats.RootFile)) + self.assertTrue(tuf.formats.ROOT_SCHEMA.matches( + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms))) - # Test conditions for invalid arguments. - bad_version = '8' - bad_expires = 'eight' + + # Additional test arguments for invalid Root creation. bad_keydict = 123 bad_roledict = 123 - bad_compression_algorithms = ['nozip'] - - self.assertRaises(tuf.FormatError, make_metadata, bad_version, - expires, - keydict, roledict, - consistent_snapshot, - compression_algorithms) - self.assertRaises(tuf.FormatError, make_metadata, version, - bad_expires, - keydict, roledict, - consistent_snapshot, - compression_algorithms) - self.assertRaises(tuf.FormatError, make_metadata, version, - expires, - bad_keydict, roledict, - consistent_snapshot, - compression_algorithms) - self.assertRaises(tuf.FormatError, make_metadata, version, - expires, - keydict, bad_roledict, - consistent_snapshot, - compression_algorithms) - - self.assertRaises(tuf.FormatError, from_metadata, 'bad') - - - - def test_SnapshotFile(self): - # Test conditions for valid instances of 'tuf.formats.SnapshotFile'. - version = 8 - expires = '1985-10-21T13:20:00Z' + bad_compression_algorithms = 5 + + # TODO: Later on, write a test looper that takes pairs of key-value args + # to substitute in on each run to shorten this.... There's a lot of + # test code that looks like this, and it'd be easier to use a looper. + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=bad_version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=bad_expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=expires, + keys=bad_keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=expires, + keys=keydict, + roles=bad_roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=bad_compression_algorithms) + + with self.assertRaises(TypeError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, 'bad') + + with self.assertRaises(ValueError): + tuf.formats.build_dict_conforming_to_schema( + 'bad', + _type='Root', + version=version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + + + + # Try building Snapshot metadata. versiondict = {'targets.json' : {'version': version}} - - make_metadata = tuf.formats.SnapshotFile.make_metadata - from_metadata = tuf.formats.SnapshotFile.from_metadata - SNAPSHOT_SCHEMA = tuf.formats.SNAPSHOT_SCHEMA - self.assertTrue(SNAPSHOT_SCHEMA.matches(make_metadata(version, expires, - versiondict))) - metadata = make_metadata(version, expires, versiondict) - self.assertTrue(isinstance(from_metadata(metadata), tuf.formats.SnapshotFile)) + self.assertTrue(tuf.formats.SNAPSHOT_SCHEMA.matches( + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.SNAPSHOT_SCHEMA, + _type='Snapshot', + version=version, + expires=expires, + meta=versiondict))) - # Test conditions for invalid arguments. - bad_version = '8' - bad_expires = '2000' + # Additional test arguments for invalid Snapshot creation. bad_versiondict = 123 - self.assertRaises(tuf.FormatError, make_metadata, version, - expires, bad_versiondict) - self.assertRaises(tuf.FormatError, make_metadata, bad_version, expires, - versiondict) - self.assertRaises(tuf.FormatError, make_metadata, version, bad_expires, - bad_versiondict) - - self.assertRaises(tuf.FormatError, from_metadata, 123) - - - - def test_TargetsFile(self): - # Test conditions for valid instances of 'tuf.formats.TargetsFile'. - version = 8 - expires = '1985-10-21T13:20:00Z' + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.SNAPSHOT_SCHEMA, + _type='Snapshot', + version=version, + expires=expires, + meta=versiondict) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.SNAPSHOT_SCHEMA, + _type='Snapshot', + version=bad_version, + expires=expires, + meta=versiondict) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.SNAPSHOT_SCHEMA, + _type='Snapshot', + version=version, + expires=bad_expires, + meta=versiondict) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.SNAPSHOT_SCHEMA, + _type='Snapshot', + version=version, + expires=expires, + meta=bad_versiondict) + + + + # Try building Targets metadata. filedict = {'metadata/targets.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}} @@ -435,61 +491,67 @@ def test_TargetsFile(self): 'roles': [{'name': 'root', 'keyids': ['123abc'], 'threshold': 1, 'paths': ['path1/', 'path2']}]} - make_metadata = tuf.formats.TargetsFile.make_metadata - from_metadata = tuf.formats.TargetsFile.from_metadata - TARGETS_SCHEMA = tuf.formats.TARGETS_SCHEMA - self.assertTrue(TARGETS_SCHEMA.matches(make_metadata(version, expires, - filedict, delegations))) - self.assertTrue(TARGETS_SCHEMA.matches(make_metadata(version, expires, filedict))) + self.assertTrue(tuf.formats.TARGETS_SCHEMA.matches( + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', + version=version, + expires=expires, + targets=filedict, + delegations=delegations))) - metadata = make_metadata(version, expires, filedict, delegations) - self.assertTrue(isinstance(from_metadata(metadata), tuf.formats.TargetsFile)) - - # Test conditions for different combination of required arguments (i.e., - # a filedict or delegations argument is required.) - metadata = make_metadata(version, expires, filedict) - self.assertTrue(isinstance(from_metadata(metadata), tuf.formats.TargetsFile)) - - metadata = make_metadata(version, expires, delegations=delegations) - self.assertTrue(isinstance(from_metadata(metadata), tuf.formats.TargetsFile)) - - # Directly instantiating a TargetsFile object. - tuf.formats.TargetsFile(version, expires) - tuf.formats.TargetsFile(version, expires, filedict) - tuf.formats.TargetsFile(version, expires, delegations=delegations) + # Try with no delegations included (should work, since they're optional). + self.assertTrue(tuf.formats.TARGETS_SCHEMA.matches( + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', + version=version, + expires=expires, + targets=filedict))) - # Test conditions for invalid arguments. - bad_version = 'eight' - bad_expires = '2000' + + # Additional test arguments for invalid Targets creation. bad_filedict = 123 bad_delegations = 123 - self.assertRaises(tuf.FormatError, make_metadata, bad_version, expires, - filedict, delegations) - self.assertRaises(tuf.FormatError, make_metadata, version, bad_expires, - filedict, delegations) - self.assertRaises(tuf.FormatError, make_metadata, version, expires, - bad_filedict, delegations) - self.assertRaises(tuf.FormatError, make_metadata, version, expires, - filedict, bad_delegations) - self.assertRaises(tuf.Error, make_metadata, version, expires) - - self.assertRaises(tuf.FormatError, from_metadata, 123) - + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', + version=bad_version, + expires=expires, + targets=filedict, + delegations=delegations) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', + version=version, + expires=bad_expires, + targets=filedict, + delegations=delegations) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', + version=version, + expires=expires, + targets=bad_filedict, + delegations=delegations) + + with self.assertRaises(tuf.exceptions.FormatError): + tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', + version=version, + expires=expires, + targets=filedict, + delegations=bad_delegations) - def test_MirrorsFile(self): - # Test normal case. - version = 8 - expires = '1985-10-21T13:20:00Z' - - mirrors_file = tuf.formats.MirrorsFile(version, expires) - - make_metadata = tuf.formats.MirrorsFile.make_metadata - from_metadata = tuf.formats.MirrorsFile.from_metadata - self.assertRaises(NotImplementedError, make_metadata) - self.assertRaises(NotImplementedError, from_metadata, mirrors_file) @@ -657,22 +719,7 @@ def test_make_role_metadata(self): # 'paths' and 'path_hash_prefixes' cannot both be specified. self.assertRaises(tuf.FormatError, make_role, keyids, threshold, name, paths, path_hash_prefixes) - def test_get_role_class(self): - # Test conditions for valid arguments. - get_role_class = tuf.formats.get_role_class - - self.assertEqual(tuf.formats.RootFile, get_role_class('Root')) - self.assertEqual(tuf.formats.TargetsFile, get_role_class('Targets')) - self.assertEqual(tuf.formats.SnapshotFile, get_role_class('Snapshot')) - self.assertEqual(tuf.formats.TimestampFile, get_role_class('Timestamp')) - self.assertEqual(tuf.formats.MirrorsFile, get_role_class('Mirrors')) - # Test conditions for invalid arguments. - self.assertRaises(tuf.FormatError, get_role_class, 'role') - self.assertRaises(tuf.FormatError, get_role_class, 'ROLE') - self.assertRaises(tuf.FormatError, get_role_class, 'abcd') - self.assertRaises(tuf.FormatError, get_role_class, 123) - self.assertRaises(tuf.FormatError, get_role_class, tuf.formats.RootFile) @@ -690,11 +737,13 @@ def test_expected_meta_rolename(self): # Test conditions for invalid arguments. self.assertRaises(tuf.FormatError, expected_rolename, 123) - self.assertRaises(tuf.FormatError, expected_rolename, tuf.formats.RootFile) + self.assertRaises(tuf.FormatError, expected_rolename, tuf.formats.ROOT_SCHEMA) self.assertRaises(tuf.FormatError, expected_rolename, True) + + def test_check_signable_object_format(self): # Test condition for a valid argument. root = {'_type': 'Root', @@ -716,7 +765,7 @@ def test_check_signable_object_format(self): check_signable = tuf.formats.check_signable_object_format self.assertRaises(tuf.FormatError, check_signable, 'Root') self.assertRaises(tuf.FormatError, check_signable, 123) - self.assertRaises(tuf.FormatError, check_signable, tuf.formats.RootFile) + self.assertRaises(tuf.FormatError, check_signable, tuf.formats.ROOT_SCHEMA) self.assertRaises(tuf.FormatError, check_signable, True) saved_type = root['signed']['_type'] @@ -755,7 +804,7 @@ def test_encode_canonical(self): self.assertEqual('[1,2,3]', ''.join(result)) # Test conditions for invalid arguments. - self.assertRaises(tuf.FormatError, encode, tuf.formats.RootFile) + self.assertRaises(tuf.FormatError, encode, tuf.formats.ROOT_SCHEMA) self.assertRaises(tuf.FormatError, encode, 8.0) self.assertRaises(tuf.FormatError, encode, {"x": 8.0}) self.assertRaises(tuf.FormatError, encode, 8.0, output) diff --git a/tests/test_keydb.py b/tests/test_keydb.py index 4743223b18..90fb1b1e54 100755 --- a/tests/test_keydb.py +++ b/tests/test_keydb.py @@ -311,12 +311,17 @@ def test_create_keydb_from_root_metadata(self): consistent_snapshot = False expires = '1985-10-21T01:21:00Z' compression_algorithms = ['gz'] - - root_metadata = tuf.formats.RootFile.make_metadata(version, - expires, - keydict, roledict, - consistent_snapshot, - compression_algorithms) + + root_metadata = tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + self.assertEqual(None, tuf.keydb.create_keydb_from_root_metadata(root_metadata)) tuf.keydb.create_keydb_from_root_metadata(root_metadata) @@ -367,12 +372,18 @@ def test_create_keydb_from_root_metadata(self): version = 8 expires = '1985-10-21T01:21:00Z' compression_algorithms = ['gz'] - - root_metadata = tuf.formats.RootFile.make_metadata(version, - expires, - keydict, roledict, - consistent_snapshot, - compression_algorithms) + + root_metadata = tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + spec_version='1.0', + version=version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + self.assertEqual(None, tuf.keydb.create_keydb_from_root_metadata(root_metadata)) # Ensure only 'keyid2' was added to the keydb database. 'keyid' and diff --git a/tests/test_roledb.py b/tests/test_roledb.py index 06a2aa175c..bde00c1479 100755 --- a/tests/test_roledb.py +++ b/tests/test_roledb.py @@ -539,13 +539,18 @@ def test_create_roledb_from_root_metadata(self): version = 8 consistent_snapshot = False expires = '1985-10-21T01:21:00Z' - compression_algorithms = ['gz'] + compression_algorithms = ['gz'] + + root_metadata = tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) - root_metadata = tuf.formats.RootFile.make_metadata(version, - expires, - keydict, roledict, - consistent_snapshot, - compression_algorithms) self.assertEqual(None, tuf.roledb.create_roledb_from_root_metadata(root_metadata)) @@ -590,11 +595,16 @@ def test_create_roledb_from_root_metadata(self): # Generate 'root_metadata' to verify that 'release' and 'root' are added # to the role database. - root_metadata = tuf.formats.RootFile.make_metadata(version, - expires, - keydict, roledict, - consistent_snapshot, - compression_algorithms) + root_metadata = tuf.formats.build_dict_conforming_to_schema( + tuf.formats.ROOT_SCHEMA, + _type='Root', + version=version, + expires=expires, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) + self.assertEqual(None, tuf.roledb.create_roledb_from_root_metadata(root_metadata)) diff --git a/tuf/formats.py b/tuf/formats.py index baf54b3875..214902777a 100755 --- a/tuf/formats.py +++ b/tuf/formats.py @@ -20,8 +20,7 @@ module should be read and understood before tackling this module. 'formats.py' can be broken down into three sections. (1) Schemas and object - matching. (2) Classes that represent Role Metadata and help produce correctly - formatted files. (3) Functions that help produce or verify TUF objects. + matching. (2) Functions that help produce or verify TUF objects. The first section deals with schemas and object matching based on format. There are two ways of checking the format of objects. The first method @@ -45,17 +44,7 @@ the match fails. There are numerous variations of object checking provided by 'formats.py' and 'schema.py'. - The second section deals with the role metadata classes. There are - multiple top-level roles, each with differing metadata formats. - Example: - - root_object = tuf.formats.RootFile.from_metadata(root_metadata_file) - targets_metadata = tuf.formats.TargetsFile.make_metadata(...) - - The input and output of these classes are checked against their respective - schema to ensure correctly formatted metadata. - - The last section contains miscellaneous functions related to the format of + The second section contains miscellaneous functions related to the format of TUF objects. Example: @@ -684,161 +673,41 @@ def make_metadata(version, expiration_date, filedict): +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. -class RootFile(MetaFile): - def __init__(self, version, expires, keys, roles, consistent_snapshot, - compression_algorithms): - self.info = {} - self.info['version'] = version - self.info['expires'] = expires - self.info['keys'] = keys - self.info['roles'] = roles - self.info['consistent_snapshot'] = consistent_snapshot - self.info['compression_algorithms'] = compression_algorithms - - - @staticmethod - def from_metadata(object): - # Is 'object' a Root metadata file? - # Raise 'tuf.FormatError' if not. - ROOT_SCHEMA.check_match(object) - - version = object['version'] - expires = object['expires'] - keys = object['keys'] - roles = object['roles'] - consistent_snapshot = object['consistent_snapshot'] - compression_algorithms = object['compression_algorithms'] - - return RootFile(version, expires, keys, roles, consistent_snapshot, - compression_algorithms) - - - @staticmethod - def make_metadata(version, expiration_date, keydict, roledict, - consistent_snapshot, compression_algorithms): - result = {'_type' : 'Root'} - result['version'] = version - result['expires'] = expiration_date - result['keys'] = keydict - result['roles'] = roledict - result['consistent_snapshot'] = consistent_snapshot - result['compression_algorithms'] = compression_algorithms - - # Is 'result' a Root metadata file? - # Raise 'tuf.FormatError' if not. - ROOT_SCHEMA.check_match(result) - - return result - - - - - -class SnapshotFile(MetaFile): - def __init__(self, version, expires, versiondict): - self.info = {} - self.info['version'] = version - self.info['expires'] = expires - self.info['meta'] = versiondict - - - @staticmethod - def from_metadata(object): - # Is 'object' a Snapshot metadata file? - # Raise 'tuf.FormatError' if not. - SNAPSHOT_SCHEMA.check_match(object) - - version = object['version'] - expires = object['expires'] - versiondict = object['meta'] - - return SnapshotFile(version, expires, versiondict) - - - @staticmethod - def make_metadata(version, expiration_date, versiondict): - result = {'_type' : 'Snapshot'} - result['version'] = version - result['expires'] = expiration_date - result['meta'] = versiondict - - # Is 'result' a Snapshot metadata file? - # Raise 'tuf.FormatError' if not. - SNAPSHOT_SCHEMA.check_match(result) - - return result - - - - - -class TargetsFile(MetaFile): - def __init__(self, version, expires, filedict=None, delegations=None): - if filedict is None: - filedict = {} - if delegations is None: - delegations = {} - self.info = {} - self.info['version'] = version - self.info['expires'] = expires - self.info['targets'] = filedict - self.info['delegations'] = delegations - - - @staticmethod - def from_metadata(object): - # Is 'object' a Targets metadata file? - # Raise tuf.FormatError if not. - TARGETS_SCHEMA.check_match(object) - - version = object['version'] - expires = object['expires'] - filedict = object.get('targets') - delegations = object.get('delegations') - - return TargetsFile(version, expires, filedict, delegations) - - - @staticmethod - def make_metadata(version, expiration_date, filedict=None, delegations=None): - if filedict is None and delegations is None: - raise tuf.Error('We don\'t allow completely empty targets metadata.') - - result = {'_type' : 'Targets'} - result['version'] = version - result['expires'] = expiration_date - result['targets'] = {} - if filedict is not None: - result['targets'] = filedict - if delegations is not None: - result['delegations'] = delegations - - # Is 'result' a Targets metadata file? - # Raise 'tuf.FormatError' if not. - TARGETS_SCHEMA.check_match(result) - - return result - - + Checks the result to make sure that it conforms to the given schema, raising + an error if not. + Returns the new dict conforming to the schema if there are no problems. + """ + # Check that schema supports a check_match call. + # Duck typing version of this check: + if not hasattr(schema, 'check_match'): + raise ValueError( + 'The given "schema" does not seem to be a schema. It has no ' + '"check_match" method. Given schema: ' + repr(schema)) -class MirrorsFile(MetaFile): - def __init__(self, version, expires): - self.info = {} - self.info['version'] = version - self.info['expires'] = expires + # # Strict typing version of this check: + # # Check that schema_name is a SCHEMA.Object. + # if not isinstance(schema, schema.Schema): + # raise ValueError( + # 'The first argument must be a schema.Schema object, but is not. ' + # 'Given schema: ' + repr(schema)) + # The return value. + d = {} - @staticmethod - def from_metadata(object): - raise NotImplementedError + for key, value in kwargs.items(): + d[key] = value + schema.check_match(d) - @staticmethod - def make_metadata(): - raise NotImplementedError + return d @@ -852,15 +721,6 @@ def make_metadata(): 'Timestamp' : TIMESTAMP_SCHEMA, 'Mirrors' : MIRRORLIST_SCHEMA} -# A dict holding the recognized class names for the top-level roles. -# That is, the role classes listed in this module (e.g., class TargetsFile()). -ROLE_CLASSES_BY_TYPE = { - 'Root' : RootFile, - 'Targets' : TargetsFile, - 'Snapshot' : SnapshotFile, - 'Timestamp' : TimestampFile, - 'Mirrors' : MirrorsFile} - @@ -1220,53 +1080,6 @@ def make_role_metadata(keyids, threshold, name=None, paths=None, -def get_role_class(expected_rolename): - """ - - Return the role class corresponding to - 'expected_rolename'. The role name returned - by expected_meta_rolename() should be the name - passed as an argument to this function. If - 'expected_rolename' is 'Root', the class - RootFile is returned. - - - expected_rolename: - The role name used to determine which role class - to return. - - - tuf.FormatError, if 'expected_rolename' is not a - supported role. - - - None. - - - The class corresponding to 'expected_rolename'. - E.g., 'Snapshot' as an argument to this function causes - SnapshotFile' to be returned. - """ - - # Does 'expected_rolename' have the correct type? - # This check ensures 'expected_rolename' conforms to - # 'tuf.formats.NAME_SCHEMA'. - # Raise 'tuf.FormatError' if there is a mismatch. - NAME_SCHEMA.check_match(expected_rolename) - - try: - role_class = ROLE_CLASSES_BY_TYPE[expected_rolename] - - except KeyError: - raise tuf.FormatError(repr(expected_rolename) + ' not supported.') - - else: - return role_class - - - - - def expected_meta_rolename(meta_rolename): """ diff --git a/tuf/repository_lib.py b/tuf/repository_lib.py index 0ec8269213..4c1dbc8a82 100755 --- a/tuf/repository_lib.py +++ b/tuf/repository_lib.py @@ -1485,12 +1485,23 @@ def generate_root_metadata(version, expiration_date, consistent_snapshot, roledict[rolename] = role_metadata # Generate the root metadata object. - root_metadata = tuf.formats.RootFile.make_metadata(version, expiration_date, - keydict, roledict, - consistent_snapshot, - compression_algorithms) - - return root_metadata + # Use generalized build_dict_conforming_to_schema func to produce a dict that + # contains all the appropriate information for this type of metadata, + # checking that the result conforms to the appropriate schema. + # TODO: Later, probably after the rewrite for TUF Issue #660, generalize + # further, upward, by replacing generate_targets_metadata, + # generate_root_metadata, etc. with one function that generates + # metadata, possibly rolling that upwards into the calling function. + # 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', # TODO: Does this have to be capitalized? -.- + version=version, + expires=expiration_date, + keys=keydict, + roles=roledict, + consistent_snapshot=consistent_snapshot, + compression_algorithms=compression_algorithms) @@ -1608,14 +1619,36 @@ def generate_targets_metadata(targets_directory, target_files, version, if not os.path.exists(digest_target): logger.warning('Hard linking target file to ' + repr(digest_target)) os.link(target_path, digest_target) - - # Generate the targets metadata object. - targets_metadata = tuf.formats.TargetsFile.make_metadata(version, - expiration_date, - filedict, - delegations) - return targets_metadata + # Generate the targets metadata object. + # Use generalized build_dict_conforming_to_schema func to produce a dict that + # contains all the appropriate information for targets metadata, + # checking that the result conforms to the appropriate schema. + # TODO: Later, probably after the rewrite for TUF Issue #660, generalize + # further, upward, by replacing generate_targets_metadata, + # generate_root_metadata, etc. with one function that generates + # metadata, possibly rolling that upwards into the calling function. + # There are very few things that really need to be done differently. + if delegations is not None: + return tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', # TODO: Does this have to be capitalized? -.- + version=version, + expires=expiration_date, + targets=filedict, + delegations=delegations) + else: + return tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TARGETS_SCHEMA, + _type='Targets', # TODO: Does this have to be capitalized? -.- + version=version, + expires=expiration_date, + targets=filedict) + # TODO: As an alternative to the odd if/else above where we decide whether or + # not to include the delegations argument based on whether or not it is + # None, consider instead adding a check in + # build_dict_conforming_to_schema that skips a keyword if that keyword + # is optional in the schema and the value passed in is set to None.... @@ -1727,11 +1760,20 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, rolename, repository_name) # Generate the Snapshot metadata object. - snapshot_metadata = tuf.formats.SnapshotFile.make_metadata(version, - expiration_date, - fileinfodict) - - return snapshot_metadata + # Use generalized build_dict_conforming_to_schema func to produce a dict that + # contains all the appropriate information for snapshot metadata, + # checking that the result conforms to the appropriate schema. + # TODO: Later, probably after the rewrite for TUF Issue #660, generalize + # further, upward, by replacing generate_targets_metadata, + # generate_root_metadata, etc. with one function that generates + # metadata, possibly rolling that upwards into the calling function. + # 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', # TODO: Normalize rolename capitalization across the code. -.- + version=version, + expires=expiration_date, + meta=fileinfodict) @@ -1790,11 +1832,20 @@ def generate_timestamp_metadata(snapshot_filename, version, expiration_date, # excluded. # Generate the timestamp metadata object. - timestamp_metadata = tuf.formats.TimestampFile.make_metadata(version, - expiration_date, - snapshot_fileinfo) - - return timestamp_metadata + # Use generalized build_dict_conforming_to_schema func to produce a dict that + # contains all the appropriate information for timestamp metadata, + # checking that the result conforms to the appropriate schema. + # TODO: Later, probably after the rewrite for TUF Issue #660, generalize + # further, upward, by replacing generate_targets_metadata, + # generate_root_metadata, etc. with one function that generates + # metadata, possibly rolling that upwards into the calling function. + # There are very few things that really need to be done differently. + return tuf.formats.build_dict_conforming_to_schema( + tuf.formats.TIMESTAMP_SCHEMA, + _type='Timestamp', # TODO: Normalize rolename capitalization across the code. -.- + version=version, + expires=expiration_date, + meta=snapshot_fileinfo)