mirror of
https://github.com/theupdateframework/python-tuf
synced 2026-05-24 10:08:28 +00:00
Replace hard-coded logger names with __name__. For the most part this just uses the standard conventions to create the same logger hierarchy as existed before. The only real difference is that loggers created for printing during tests are no longer part of the 'tuf' hierarchy. Signed-off-by: Joshua Lock <jlock@vmware.com>
427 lines
15 KiB
Python
Executable file
427 lines
15 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
# Copyright 2014 - 2017, New York University and the TUF contributors
|
|
# SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
"""
|
|
<Program Name>
|
|
test_developer_tool.py.
|
|
|
|
<Authors>
|
|
Santiago Torres Arias <torresariass@gmail.com>
|
|
Zane Fisher <zanefisher@gmail.com>
|
|
|
|
<Started>
|
|
January 22, 2014.
|
|
|
|
<Copyright>
|
|
See LICENSE-MIT OR LICENSE for licensing information.
|
|
|
|
<Purpose>
|
|
Unit test for the 'developer_tool.py' module.
|
|
"""
|
|
|
|
import os
|
|
import time
|
|
import datetime
|
|
import unittest
|
|
import logging
|
|
import tempfile
|
|
import shutil
|
|
import unittest
|
|
|
|
import tuf
|
|
import tuf.log
|
|
import tuf.roledb
|
|
import tuf.keydb
|
|
import tuf.developer_tool as developer_tool
|
|
import tuf.exceptions
|
|
|
|
import securesystemslib
|
|
|
|
from tuf.developer_tool import METADATA_DIRECTORY_NAME
|
|
from tuf.developer_tool import TARGETS_DIRECTORY_NAME
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
developer_tool.disable_console_log_messages()
|
|
|
|
class TestProject(unittest.TestCase):
|
|
|
|
tmp_dir = None
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.tmp_dir = tempfile.mkdtemp(dir = os.getcwd())
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
shutil.rmtree(cls.tmp_dir)
|
|
|
|
def setUp(self):
|
|
# called before every test case
|
|
pass
|
|
|
|
def tearDown(self):
|
|
# called after every test case
|
|
tuf.roledb.clear_roledb(clear_all=True)
|
|
tuf.keydb.clear_keydb(clear_all=True)
|
|
|
|
|
|
def test_create_new_project(self):
|
|
# Test cases for the create_new_project function. In this test we will
|
|
# check input, correct file creation and format. We also check
|
|
# that a proper object is generated. We will use the normal layout for this
|
|
# test suite.
|
|
|
|
# Create a local subfolder for this test.
|
|
local_tmp = tempfile.mkdtemp(dir = self.tmp_dir)
|
|
|
|
# These are the usual values we will be throwing to the function, however
|
|
# we will swap these for nulls or malformed values every now and then to
|
|
# test input.
|
|
project_name = 'test_suite'
|
|
metadata_directory = local_tmp
|
|
location_in_repository = '/prefix'
|
|
targets_directory = None
|
|
key = None
|
|
|
|
# Create a blank project.
|
|
project = developer_tool.create_new_project(project_name, metadata_directory,
|
|
location_in_repository)
|
|
|
|
self.assertTrue(isinstance(project, developer_tool.Project))
|
|
self.assertTrue(project.layout_type == 'repo-like')
|
|
self.assertTrue(project.prefix == location_in_repository)
|
|
self.assertTrue(project.project_name == project_name)
|
|
self.assertTrue(project.metadata_directory ==
|
|
os.path.join(metadata_directory,METADATA_DIRECTORY_NAME))
|
|
self.assertTrue(project.targets_directory ==
|
|
os.path.join(metadata_directory,TARGETS_DIRECTORY_NAME))
|
|
|
|
# Create a blank project without a prefix.
|
|
project = developer_tool.create_new_project(project_name, metadata_directory)
|
|
self.assertTrue(isinstance(project, developer_tool.Project))
|
|
self.assertTrue(project.layout_type == 'repo-like')
|
|
self.assertTrue(project.prefix == '')
|
|
self.assertTrue(project.project_name == project_name)
|
|
self.assertTrue(project.metadata_directory ==
|
|
os.path.join(metadata_directory,METADATA_DIRECTORY_NAME))
|
|
self.assertTrue(project.targets_directory ==
|
|
os.path.join(metadata_directory,TARGETS_DIRECTORY_NAME))
|
|
|
|
# Create a blank project without a valid metadata directory.
|
|
self.assertRaises(securesystemslib.exceptions.FormatError, developer_tool.create_new_project,
|
|
0, metadata_directory, location_in_repository)
|
|
self.assertRaises(securesystemslib.exceptions.FormatError, developer_tool.create_new_project,
|
|
project_name, 0, location_in_repository)
|
|
self.assertRaises(securesystemslib.exceptions.FormatError, developer_tool.create_new_project,
|
|
project_name, metadata_directory, 0)
|
|
|
|
|
|
# Create a new project with a flat layout.
|
|
targets_directory = tempfile.mkdtemp(dir = local_tmp)
|
|
metadata_directory = tempfile.mkdtemp(dir = local_tmp)
|
|
project = developer_tool.create_new_project(project_name, metadata_directory,
|
|
location_in_repository, targets_directory)
|
|
self.assertTrue(isinstance(project, developer_tool.Project))
|
|
self.assertTrue(project.layout_type == 'flat')
|
|
self.assertTrue(project.prefix == location_in_repository)
|
|
self.assertTrue(project.project_name == project_name)
|
|
self.assertTrue(project.metadata_directory == metadata_directory)
|
|
self.assertTrue(project.targets_directory == targets_directory)
|
|
|
|
# Finally, check that if targets_directory is set, it is valid.
|
|
self.assertRaises(securesystemslib.exceptions.FormatError, developer_tool.create_new_project,
|
|
project_name, metadata_directory, location_in_repository, 0)
|
|
|
|
# Copy a key to our workspace and create a new project with it.
|
|
keystore_path = os.path.join('repository_data','keystore')
|
|
|
|
# I will use the same key as the one provided in the repository
|
|
# tool tests for the root role, but this is not a root role...
|
|
root_key_path = os.path.join(keystore_path,'root_key.pub')
|
|
project_key = developer_tool.import_rsa_publickey_from_file(root_key_path)
|
|
|
|
# Test create new project with a key added by default.
|
|
project = developer_tool.create_new_project(project_name, metadata_directory,
|
|
location_in_repository, targets_directory, project_key)
|
|
|
|
self.assertTrue(isinstance(project, developer_tool.Project))
|
|
self.assertTrue(project.layout_type == 'flat')
|
|
self.assertTrue(project.prefix == location_in_repository)
|
|
self.assertTrue(project.project_name == project_name)
|
|
self.assertTrue(project.metadata_directory == metadata_directory)
|
|
self.assertTrue(project.targets_directory == targets_directory)
|
|
self.assertTrue(len(project.keys) == 1)
|
|
self.assertTrue(project.keys[0] == project_key['keyid'])
|
|
|
|
# Try to write to an invalid location. The OSError should be re-raised by
|
|
# create_new_project().
|
|
shutil.rmtree(targets_directory)
|
|
tuf.roledb.clear_roledb()
|
|
tuf.keydb.clear_keydb()
|
|
|
|
metadata_directory = '/'
|
|
valid_metadata_directory_name = developer_tool.METADATA_DIRECTORY_NAME
|
|
developer_tool.METADATA_DIRECTORY_NAME = '/'
|
|
|
|
try:
|
|
developer_tool.create_new_project(project_name, metadata_directory,
|
|
location_in_repository, targets_directory, project_key)
|
|
|
|
except (OSError, tuf.exceptions.RepositoryError):
|
|
pass
|
|
|
|
developer_tool.METADATA_DIRECTORY_NAME = valid_metadata_directory_name
|
|
|
|
|
|
|
|
def test_load_project(self):
|
|
# This test case will first try to load an existing project and test for
|
|
# verify the loaded object. It will next try to load a nonexisting project
|
|
# and expect a correct error handler. Finally, it will try to overwrite the
|
|
# existing prefix of the loaded project.
|
|
|
|
# Create a local subfolder for this test.
|
|
local_tmp = tempfile.mkdtemp(dir = self.tmp_dir)
|
|
|
|
# Test non-existent project filepath.
|
|
nonexistent_path = os.path.join(local_tmp, 'nonexistent')
|
|
self.assertRaises(IOError, developer_tool.load_project, nonexistent_path)
|
|
|
|
# Copy the pregenerated metadata.
|
|
project_data_filepath = os.path.join('repository_data', 'project')
|
|
target_project_data_filepath = os.path.join(local_tmp, 'project')
|
|
shutil.copytree('repository_data/project', target_project_data_filepath)
|
|
|
|
# Properly load a project.
|
|
repo_filepath = os.path.join(local_tmp, 'project', 'test-flat')
|
|
new_targets_path = os.path.join(local_tmp, 'project', 'targets')
|
|
project = developer_tool.load_project(repo_filepath,
|
|
new_targets_location = new_targets_path)
|
|
self.assertTrue(project._targets_directory == new_targets_path)
|
|
self.assertTrue(project.layout_type == 'flat')
|
|
|
|
# Load a project overwriting the prefix.
|
|
project = developer_tool.load_project(repo_filepath, prefix='new')
|
|
self.assertTrue(project.prefix == 'new')
|
|
|
|
# Load a project with a file missing.
|
|
file_to_corrupt = os.path.join(repo_filepath, 'test-flat.json')
|
|
with open(file_to_corrupt, 'wt') as fp:
|
|
fp.write('this is not a json file')
|
|
|
|
self.assertRaises(securesystemslib.exceptions.Error, developer_tool.load_project, repo_filepath)
|
|
|
|
|
|
|
|
|
|
def test_add_verification_keys(self):
|
|
# Create a new project instance.
|
|
project = developer_tool.Project('test_verification_keys', 'somepath',
|
|
'someotherpath', 'prefix')
|
|
|
|
# Add invalid verification key.
|
|
self.assertRaises(securesystemslib.exceptions.FormatError, project.add_verification_key, 'invalid')
|
|
|
|
# Add verification key.
|
|
# - load it first
|
|
keystore_path = os.path.join('repository_data', 'keystore')
|
|
first_verification_key_path = os.path.join(keystore_path,'root_key.pub')
|
|
first_verification_key = \
|
|
developer_tool.import_rsa_publickey_from_file(first_verification_key_path)
|
|
|
|
project.add_verification_key(first_verification_key)
|
|
|
|
|
|
# Add another verification key (should expect exception.)
|
|
second_verification_key_path = os.path.join(keystore_path, 'snapshot_key.pub')
|
|
second_verification_key = \
|
|
developer_tool.import_ed25519_publickey_from_file(second_verification_key_path)
|
|
|
|
self.assertRaises(securesystemslib.exceptions.Error,
|
|
project.add_verification_key,(second_verification_key))
|
|
|
|
|
|
|
|
# Add a verification key for the delegation.
|
|
project.delegate('somedelegation', [], [])
|
|
project('somedelegation').add_verification_key(first_verification_key)
|
|
project('somedelegation').add_verification_key(second_verification_key)
|
|
|
|
|
|
# Add another delegation of the delegation.
|
|
project('somedelegation').delegate('somesubdelegation', [], [])
|
|
project('somesubdelegation').add_verification_key(first_verification_key)
|
|
project('somesubdelegation').add_verification_key(second_verification_key)
|
|
|
|
|
|
def test_write(self):
|
|
|
|
# Create tmp directory.
|
|
local_tmp = tempfile.mkdtemp(dir=self.tmp_dir)
|
|
|
|
# Create new project inside tmp directory.
|
|
project = developer_tool.create_new_project('new_project', local_tmp,
|
|
'prefix');
|
|
|
|
# Create some target files inside the tmp directory.
|
|
target_filepath = os.path.join(local_tmp, 'targets', 'test_target')
|
|
with open(target_filepath, 'wt') as fp:
|
|
fp.write('testing file')
|
|
|
|
|
|
# Add the targets.
|
|
project.add_target(os.path.basename(target_filepath))
|
|
|
|
# Add verification keys.
|
|
keystore_path = os.path.join('repository_data', 'keystore')
|
|
project_key_path = os.path.join(keystore_path, 'root_key.pub')
|
|
project_key = \
|
|
developer_tool.import_rsa_publickey_from_file(project_key_path)
|
|
|
|
|
|
# Call status (for the sake of doing it and to improve test coverage by
|
|
# executing its statements.)
|
|
project.status()
|
|
|
|
project.add_verification_key(project_key)
|
|
|
|
|
|
# Add another verification key (should expect exception.)
|
|
delegation_key_path = os.path.join(keystore_path, 'snapshot_key.pub')
|
|
delegation_key = \
|
|
developer_tool.import_ed25519_publickey_from_file(delegation_key_path)
|
|
|
|
# Add a subdelegation.
|
|
subdelegation_key_path = os.path.join(keystore_path, 'timestamp_key.pub')
|
|
subdelegation_key = \
|
|
developer_tool.import_ed25519_publickey_from_file(subdelegation_key_path)
|
|
|
|
# Add a delegation.
|
|
project.delegate('delegation', [delegation_key], [])
|
|
project('delegation').delegate('subdelegation', [subdelegation_key], [])
|
|
|
|
# call write (except)
|
|
self.assertRaises(securesystemslib.exceptions.Error, project.write, ())
|
|
|
|
# Call status (for the sake of doing it and executing its statements.)
|
|
project.status()
|
|
|
|
# Load private keys.
|
|
project_private_key_path = os.path.join(keystore_path, 'root_key')
|
|
project_private_key = \
|
|
developer_tool.import_rsa_privatekey_from_file(project_private_key_path,
|
|
'password')
|
|
|
|
delegation_private_key_path = os.path.join(keystore_path, 'snapshot_key')
|
|
delegation_private_key = \
|
|
developer_tool.import_ed25519_privatekey_from_file(delegation_private_key_path,
|
|
'password')
|
|
|
|
subdelegation_private_key_path = \
|
|
os.path.join(keystore_path, 'timestamp_key')
|
|
subdelegation_private_key = \
|
|
developer_tool.import_ed25519_privatekey_from_file(subdelegation_private_key_path,
|
|
'password')
|
|
|
|
# Test partial write.
|
|
# backup everything (again)
|
|
# + backup targets.
|
|
targets_backup = project.target_files
|
|
|
|
# + backup delegations.
|
|
delegations_backup = \
|
|
tuf.roledb.get_delegated_rolenames(project.project_name)
|
|
|
|
# + backup layout type.
|
|
layout_type_backup = project.layout_type
|
|
|
|
# + backup keyids.
|
|
keys_backup = project.keys
|
|
delegation_keys_backup = project('delegation').keys
|
|
|
|
# + backup the prefix.
|
|
prefix_backup = project.prefix
|
|
|
|
# + backup the name.
|
|
name_backup = project.project_name
|
|
|
|
# Write and reload.
|
|
self.assertRaises(securesystemslib.exceptions.Error, project.write)
|
|
project.write(write_partial=True)
|
|
|
|
project = developer_tool.load_project(local_tmp)
|
|
|
|
# Check against backup.
|
|
self.assertEqual(list(project.target_files.keys()), list(targets_backup.keys()))
|
|
new_delegations = tuf.roledb.get_delegated_rolenames(project.project_name)
|
|
self.assertEqual(new_delegations, delegations_backup)
|
|
self.assertEqual(project.layout_type, layout_type_backup)
|
|
self.assertEqual(project.keys, keys_backup)
|
|
|
|
self.assertEqual(project('delegation').keys, delegation_keys_backup)
|
|
|
|
self.assertEqual(project.prefix, prefix_backup)
|
|
self.assertEqual(project.project_name, name_backup)
|
|
|
|
roleinfo = tuf.roledb.get_roleinfo(project.project_name)
|
|
|
|
self.assertEqual(roleinfo['partial_loaded'], True)
|
|
|
|
|
|
|
|
# Load_signing_keys.
|
|
project('delegation').load_signing_key(delegation_private_key)
|
|
|
|
project.status()
|
|
|
|
project.load_signing_key(project_private_key)
|
|
|
|
# Backup everything.
|
|
# + backup targets.
|
|
targets_backup = project.target_files
|
|
|
|
# + backup delegations.
|
|
delegations_backup = \
|
|
tuf.roledb.get_delegated_rolenames(project.project_name)
|
|
|
|
# + backup layout type.
|
|
layout_type_backup = project.layout_type
|
|
|
|
# + backup keyids
|
|
keys_backup = project.keys
|
|
delegation_keys_backup = project('delegation').keys
|
|
|
|
# + backup the prefix.
|
|
prefix_backup = project.prefix
|
|
|
|
# + backup the name.
|
|
name_backup = project.project_name
|
|
|
|
# Call status (for the sake of doing it.)
|
|
project.status()
|
|
|
|
# Call write.
|
|
project.write()
|
|
|
|
# Call load.
|
|
project = developer_tool.load_project(local_tmp)
|
|
|
|
|
|
# Check against backup.
|
|
self.assertEqual(list(project.target_files.keys()), list(targets_backup.keys()))
|
|
|
|
new_delegations = tuf.roledb.get_delegated_rolenames(project.project_name)
|
|
self.assertEqual(new_delegations, delegations_backup)
|
|
self.assertEqual(project.layout_type, layout_type_backup)
|
|
self.assertEqual(project.keys, keys_backup)
|
|
self.assertEqual(project('delegation').keys, delegation_keys_backup)
|
|
self.assertEqual(project.prefix, prefix_backup)
|
|
self.assertEqual(project.project_name, name_backup)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|