Skip to content

Commit

Permalink
Make Metadata a container class (WIP)
Browse files Browse the repository at this point in the history
This commit performs restructuring on the recently added metadata
class model architecture, which shall be part of a new simple TUF
API.

The key change is that the Metadata class is now used as container
for inner TUF metadata (Root, Timestamp, Snapshot, Targets) instead
of serving as base class for these, that means we use 'composition'
instead of 'inheritance'. Still, in order to aggregate common
attributes of the inner Metadata (expires, version, spec_version),
we use a new baseclass 'Signed', which also corresponds to the
signed field of the outer metadata container.

Based on prior observations in TUF's sister project in-toto, this
architecture seems to more closely represent the metadata model as
it is defined in the specification (see in-toto/in-toto#98 and
in-toto/in-toto#142 for related discussions).

Note that the proposed changes require us to now access some
attributes/methods via the signed attribute of a Metadata object
and not directly on the Metadata object, but it would be possible
to add short-cuts. (see todo notes in doc header).

Further changes include:
 - Add minimal doc header with TODO notes

 - Make attributes that correspond to fields in TUF JSON metadata
public again. There doesn't seem to be a good reason to protect
them with leading underscores and use setters/getters instead, it
just adds more code.

 - Generally try to reduce code.

 - Remove keyring and consistent_snapshot attributes from metadata
   class. As discussed in #1060 they are a better fit for extra
   management code (also see #660)

- Remove sslib schema checks (see TODO notes about validation in
  doc header)

 - Drop usage of build_dict_conforming_to_schema, it seems a lot
   simpler and more explicit to just code this here.

 - ... same goes for make_metadata_fileinfo

 - Adapt tests accordingly

TODO: Document!!!
Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
  • Loading branch information
lukpueh committed Jul 10, 2020
1 parent 76cb560 commit 833a58a
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 201 deletions.
81 changes: 37 additions & 44 deletions tests/test_tuf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,15 @@ def setUpClass(cls):
# files. 'temporary_directory' must be deleted in TearDownClass() so that
# temporary files are always removed, even when exceptions occur.
cls.temporary_directory = tempfile.mkdtemp(dir=os.getcwd())
test_repo_data = os.path.join('repository_data', 'repository')

test_repo_data = os.path.join(
os.path.dirname(os.path.realpath(__file__)), 'repository_data')

cls.repo_dir = os.path.join(cls.temporary_directory, 'repository')
shutil.copytree(test_repo_data, cls.repo_dir)
test_repo_keys = os.path.join('repository_data', 'keystore')
shutil.copytree(os.path.join(test_repo_data, 'repository'), cls.repo_dir)

cls.keystore_dir = os.path.join(cls.temporary_directory, 'keystore')
shutil.copytree(test_repo_keys, cls.keystore_dir)
shutil.copytree(os.path.join(test_repo_data, 'keystore'), cls.keystore_dir)



Expand Down Expand Up @@ -100,37 +103,32 @@ def test_metadata_base(self):
snapshot_path = os.path.join(self.repo_dir, 'metadata', 'snapshot.json')
md = metadata.Snapshot.read_from_json(snapshot_path)

self.assertEqual(md.version, 1)
md.bump_version()
self.assertEqual(md.version, 2)

self.assertEqual(md.expiration,
iso8601.parse_date("2030-01-01").replace(tzinfo=None))
md.bump_expiration()
self.assertEqual(md.expiration,
iso8601.parse_date("2030-01-02").replace(tzinfo=None))
md.bump_expiration(timedelta(days=365))
self.assertEqual(md.expiration,
iso8601.parse_date("2031-01-02").replace(tzinfo=None))
self.assertEqual(md.signed.version, 1)
md.signed.bump_version()
self.assertEqual(md.signed.version, 2)
self.assertEqual(md.signed.expires, '2030-01-01T00:00:00Z')
md.signed.bump_expiration()
self.assertEqual(md.signed.expires, '2030-01-02T00:00:00Z')
md.signed.bump_expiration(timedelta(days=365))
self.assertEqual(md.signed.expires, '2031-01-02T00:00:00Z')


def test_metadata_snapshot(self):
snapshot_path = os.path.join(self.repo_dir, 'metadata', 'snapshot.json')
snapshot = metadata.Snapshot.read_from_json(snapshot_path)

key_ring = self._load_key_ring()
snapshot.keyring = key_ring
snapshot.verify()
snapshot.verify(key_ring)

# Create a dict representing what we expect the updated data to be
fileinfo = snapshot.signed['meta']
fileinfo = snapshot.signed.meta
hashes = {'sha256': 'c2986576f5fdfd43944e2b19e775453b96748ec4fe2638a6d2f32f1310967095'}
fileinfo['role1.json']['version'] = 2
fileinfo['role1.json']['hashes'] = hashes
fileinfo['role1.json']['length'] = 123

snapshot.update('role1', 2, 123, hashes)
self.assertEqual(snapshot.signed['meta'], fileinfo)
snapshot.signed.update('role1', 2, 123, hashes)
self.assertEqual(snapshot.signed.meta, fileinfo)

# snapshot.signable()

Expand All @@ -146,39 +144,34 @@ def test_metadata_timestamp(self):
timestamp = metadata.Timestamp.read_from_json(timestamp_path)

key_ring = self._load_key_ring()
timestamp.keyring = key_ring
timestamp.verify()

self.assertEqual(timestamp.version, 1)
timestamp.bump_version()
self.assertEqual(timestamp.version, 2)

self.assertEqual(timestamp.expiration,
iso8601.parse_date("2030-01-01").replace(tzinfo=None))
timestamp.bump_expiration()
self.assertEqual(timestamp.expiration,
iso8601.parse_date("2030-01-02").replace(tzinfo=None))
timestamp.bump_expiration(timedelta(days=365))
self.assertEqual(timestamp.expiration,
iso8601.parse_date("2031-01-02").replace(tzinfo=None))
timestamp.verify(key_ring)

self.assertEqual(timestamp.signed.version, 1)
timestamp.signed.bump_version()
self.assertEqual(timestamp.signed.version, 2)

self.assertEqual(timestamp.signed.expires, '2030-01-01T00:00:00Z')
timestamp.signed.bump_expiration()
self.assertEqual(timestamp.signed.expires, '2030-01-02T00:00:00Z')
timestamp.signed.bump_expiration(timedelta(days=365))
self.assertEqual(timestamp.signed.expires, '2031-01-02T00:00:00Z')

# Test whether dateutil.relativedelta works, this provides a much easier to
# use interface for callers
saved_expiration = timestamp.expiration
delta = relativedelta(days=1)
timestamp.bump_expiration(delta)
self.assertEqual(timestamp.expires, "2031-01-03T00:00:00Z")
timestamp.signed.bump_expiration(delta)
self.assertEqual(timestamp.signed.expires, '2031-01-03T00:00:00Z')
delta = relativedelta(years=5)
timestamp.bump_expiration(delta)
self.assertEqual(timestamp.expires, "2036-01-03T00:00:00Z")
timestamp.signed.bump_expiration(delta)
self.assertEqual(timestamp.signed.expires, '2036-01-03T00:00:00Z')

hashes = {'sha256': '0ae9664468150a9aa1e7f11feecb32341658eb84292851367fea2da88e8a58dc'}
fileinfo = timestamp.signed['meta']['snapshot.json']
fileinfo = timestamp.signed.meta['snapshot.json']
fileinfo['hashes'] = hashes
fileinfo['version'] = 2
fileinfo['length'] = 520
timestamp.update(2, 520, hashes)
self.assertEqual(timestamp.signed['meta']['snapshot.json'], fileinfo)
timestamp.signed.update(2, 520, hashes)
self.assertEqual(timestamp.signed.meta['snapshot.json'], fileinfo)

# timestamp.sign()

Expand Down
Loading

0 comments on commit 833a58a

Please sign in to comment.