Source code for spack.test.cmd.gpg

# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)

import os
import sys

import pytest

import llnl.util.filesystem as fs

import spack.bootstrap
import spack.util.executable
import spack.util.gpg
from spack.main import SpackCommand
from spack.paths import mock_gpg_data_path, mock_gpg_keys_path
from spack.util.executable import ProcessError

#: spack command used by tests below
gpg = SpackCommand('gpg')
bootstrap = SpackCommand('bootstrap')
mirror = SpackCommand('mirror')

pytestmark = pytest.mark.skipif(sys.platform == "win32",
                                reason="does not run on windows")


[docs]@pytest.fixture def tmp_scope(): """Creates a temporary configuration scope""" base_name = 'internal-testing-scope' current_overrides = set( x.name for x in spack.config.config.matching_scopes(r'^{0}'.format(base_name))) num_overrides = 0 scope_name = base_name while scope_name in current_overrides: scope_name = '{0}{1}'.format(base_name, num_overrides) num_overrides += 1 with spack.config.override(spack.config.InternalConfigScope(scope_name)): yield scope_name
# test gpg command detection
[docs]@pytest.mark.parametrize('cmd_name,version', [ ('gpg', 'undetectable'), # undetectable version ('gpg', 'gpg (GnuPG) 1.3.4'), # insufficient version ('gpg', 'gpg (GnuPG) 2.2.19'), # sufficient version ('gpg2', 'gpg (GnuPG) 2.2.19'), # gpg2 command ]) def test_find_gpg(cmd_name, version, tmpdir, mock_gnupghome, monkeypatch): TEMPLATE = ('#!/bin/sh\n' 'echo "{version}"\n') with tmpdir.as_cwd(): for fname in (cmd_name, 'gpgconf'): with open(fname, 'w') as f: f.write(TEMPLATE.format(version=version)) fs.set_executable(fname) monkeypatch.setitem(os.environ, "PATH", str(tmpdir)) if version == 'undetectable' or version.endswith('1.3.4'): with pytest.raises(spack.util.gpg.SpackGPGError): spack.util.gpg.init(force=True) else: spack.util.gpg.init(force=True) assert spack.util.gpg.GPG is not None assert spack.util.gpg.GPGCONF is not None
[docs]def test_no_gpg_in_path(tmpdir, mock_gnupghome, monkeypatch, mutable_config): monkeypatch.setitem(os.environ, "PATH", str(tmpdir)) bootstrap('disable') with pytest.raises(RuntimeError): spack.util.gpg.init(force=True)
[docs]@pytest.mark.maybeslow def test_gpg(tmpdir, tmp_scope, mock_gnupghome): # Verify a file with an empty keyring. with pytest.raises(ProcessError): gpg('verify', os.path.join(mock_gpg_data_path, 'content.txt')) # Import the default key. gpg('init', '--from', mock_gpg_keys_path) # List the keys. # TODO: Test the output here. gpg('list', '--trusted') gpg('list', '--signing') # Verify the file now that the key has been trusted. gpg('verify', os.path.join(mock_gpg_data_path, 'content.txt')) # Untrust the default key. gpg('untrust', 'Spack testing') # Now that the key is untrusted, verification should fail. with pytest.raises(ProcessError): gpg('verify', os.path.join(mock_gpg_data_path, 'content.txt')) # Create a file to test signing. test_path = tmpdir.join('to-sign.txt') with open(str(test_path), 'w+') as fout: fout.write('Test content for signing.\n') # Signing without a private key should fail. with pytest.raises(RuntimeError) as exc_info: gpg('sign', str(test_path)) assert exc_info.value.args[0] == 'no signing keys are available' # Create a key for use in the tests. keypath = tmpdir.join('testing-1.key') gpg('create', '--comment', 'Spack testing key', '--export', str(keypath), 'Spack testing 1', 'spack@googlegroups.com') keyfp = spack.util.gpg.signing_keys()[0] # List the keys. # TODO: Test the output here. gpg('list') gpg('list', '--trusted') gpg('list', '--signing') # Signing with the default (only) key. gpg('sign', str(test_path)) # Verify the file we just verified. gpg('verify', str(test_path)) # Export the key for future use. export_path = tmpdir.join('export.testing.key') gpg('export', str(export_path)) # Test exporting the private key private_export_path = tmpdir.join('export-secret.testing.key') gpg('export', '--secret', str(private_export_path)) # Ensure we exported the right content! with open(str(private_export_path), 'r') as fd: content = fd.read() assert "BEGIN PGP PRIVATE KEY BLOCK" in content # and for the public key with open(str(export_path), 'r') as fd: content = fd.read() assert "BEGIN PGP PUBLIC KEY BLOCK" in content # Create a second key for use in the tests. gpg('create', '--comment', 'Spack testing key', 'Spack testing 2', 'spack@googlegroups.com') # List the keys. # TODO: Test the output here. gpg('list', '--trusted') gpg('list', '--signing') test_path = tmpdir.join('to-sign-2.txt') with open(str(test_path), 'w+') as fout: fout.write('Test content for signing.\n') # Signing with multiple signing keys is ambiguous. with pytest.raises(RuntimeError) as exc_info: gpg('sign', str(test_path)) assert exc_info.value.args[0] == \ 'multiple signing keys are available; please choose one' # Signing with a specified key. gpg('sign', '--key', keyfp, str(test_path)) # Untrusting signing keys needs a flag. with pytest.raises(ProcessError): gpg('untrust', 'Spack testing 1') # Untrust the key we created. gpg('untrust', '--signing', keyfp) # Verification should now fail. with pytest.raises(ProcessError): gpg('verify', str(test_path)) # Trust the exported key. gpg('trust', str(export_path)) # Verification should now succeed again. gpg('verify', str(test_path)) # Publish the keys using a directory path test_path = tmpdir.join('dir_cache') os.makedirs('%s' % test_path) gpg('publish', '--rebuild-index', '-d', str(test_path)) assert os.path.exists('%s/build_cache/_pgp/index.json' % test_path) # Publish the keys using a mirror url test_path = tmpdir.join('url_cache') os.makedirs('%s' % test_path) test_url = 'file://%s' % test_path gpg('publish', '--rebuild-index', '--mirror-url', test_url) assert os.path.exists('%s/build_cache/_pgp/index.json' % test_path) # Publish the keys using a mirror name test_path = tmpdir.join('named_cache') os.makedirs('%s' % test_path) mirror_url = 'file://%s' % test_path mirror('add', '--scope', tmp_scope, 'gpg', mirror_url) gpg('publish', '--rebuild-index', '-m', 'gpg') assert os.path.exists('%s/build_cache/_pgp/index.json' % test_path)