#!/usr/bin/env python """ test_developer_tool.py. Santiago Torres Arias Zane Fisher See LICENSE for licensing inforation. Unit test for the 'developer_tool.py' module. """ import os import time import datetime import unittest import logging import tempfile import shutil import tuf import tuf.log import tuf.formats import tuf.roledb import tuf.keydb import tuf.developer_tool as developer_tool from tuf.developer_tool import METADATA_DIRECTORY_NAME from tuf.developer_tool import TARGETS_DIRECTORY_NAME logger = logging.getLogger('tuf.test_developer_tool') 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() tuf.keydb.clear_keydb() pass 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(tuf.FormatError, developer_tool.create_new_project, 0, metadata_directory, location_in_repository) self.assertRaises(tuf.FormatError, developer_tool.create_new_project, project_name, 0, location_in_repository) self.assertRaises(tuf.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(tuf.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']) # Set as readonly and try to write a repo. shutil.rmtree(targets_directory) os.chmod(local_tmp, 0o0555) tuf.roledb.clear_roledb() tuf.keydb.clear_keydb() self.assertRaises(OSError, developer_tool.create_new_project ,project_name, metadata_directory, location_in_repository, targets_directory, project_key) os.chmod(local_tmp, 0o0777) shutil.rmtree(metadata_directory) os.chmod(local_tmp, 0o0555) tuf.roledb.clear_roledb() tuf.keydb.clear_keydb() self.assertRaises(OSError, developer_tool.create_new_project ,project_name, metadata_directory, location_in_repository, targets_directory, project_key) os.chmod(local_tmp, 0o0777) shutil.rmtree(local_tmp) 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(tuf.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(tuf.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_rsa_publickey_from_file(second_verification_key_path) self.assertRaises(tuf.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(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_rsa_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_rsa_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(tuf.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_rsa_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_rsa_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 # Set the compressions. We will be checking this part here too. project.compressions = ['gz'] project('delegation').compressions = project.compressions # Write and reload. self.assertRaises(tuf.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()