Merge "encrypt: add --public-key argument"

This commit is contained in:
Zuul 2021-01-29 01:39:13 +00:00 committed by Gerrit Code Review
commit 83075295b9

View File

@ -17,6 +17,7 @@ import configparser
import logging import logging
import os import os
import prettytable import prettytable
import shutil
import sys import sys
import tempfile import tempfile
import textwrap import textwrap
@ -27,6 +28,10 @@ from zuulclient.utils import get_default
from zuulclient.utils import encrypt_with_openssl from zuulclient.utils import encrypt_with_openssl
class ArgumentException(Exception):
pass
class ZuulClient(): class ZuulClient():
app_name = 'zuul-client' app_name = 'zuul-client'
app_description = 'Zuul User CLI' app_description = 'Zuul User CLI'
@ -100,7 +105,7 @@ class ZuulClient():
if (self.args.oldrev is not None) or \ if (self.args.oldrev is not None) or \
(self.args.newrev is not None): (self.args.newrev is not None):
if self.args.oldrev == self.args.newrev: if self.args.oldrev == self.args.newrev:
raise Exception( raise ArgumentException(
"The old and new revisions must not be the same.") "The old and new revisions must not be the same.")
# if they're not set, we pad them out to zero # if they're not set, we pad them out to zero
if self.args.oldrev is None: if self.args.oldrev is None:
@ -109,9 +114,9 @@ class ZuulClient():
self.args.newrev = '0000000000000000000000000000000000000000' self.args.newrev = '0000000000000000000000000000000000000000'
if self.args.func == self.dequeue: if self.args.func == self.dequeue:
if self.args.change is None and self.args.ref is None: if self.args.change is None and self.args.ref is None:
raise Exception("Change or ref needed.") raise ArgumentException("Change or ref needed.")
if self.args.change is not None and self.args.ref is not None: if self.args.change is not None and self.args.ref is not None:
raise Exception( raise ArgumentException(
"The 'change' and 'ref' arguments are mutually exclusive.") "The 'change' and 'ref' arguments are mutually exclusive.")
def readConfig(self): def readConfig(self):
@ -128,7 +133,8 @@ class ZuulClient():
if os.path.exists(os.path.expanduser(fp)): if os.path.exists(os.path.expanduser(fp)):
self.config.read(os.path.expanduser(fp)) self.config.read(os.path.expanduser(fp))
return return
raise Exception("Unable to locate config file in %s" % locations) raise ArgumentException(
"Unable to locate config file in %s" % locations)
def setup_logging(self): def setup_logging(self):
"""Client logging does not rely on conf file""" """Client logging does not rely on conf file"""
@ -157,12 +163,12 @@ class ZuulClient():
tenant_scope = client.info.get('tenant', None) tenant_scope = client.info.get('tenant', None)
if self.args.tenant != '': if self.args.tenant != '':
if tenant_scope is not None and tenant_scope != self.args.tenant: if tenant_scope is not None and tenant_scope != self.args.tenant:
raise Exception( raise ArgumentException(
'Error: Zuul API URL %s is ' 'Error: Zuul API URL %s is '
'scoped to tenant "%s"' % (client.base_url, tenant_scope)) 'scoped to tenant "%s"' % (client.base_url, tenant_scope))
else: else:
if tenant_scope is None: if tenant_scope is None:
raise Exception( raise ArgumentException(
"Error: the --tenant argument is required" "Error: the --tenant argument is required"
) )
@ -440,10 +446,15 @@ class ZuulClient():
def add_encrypt_subparser(self, subparsers): def add_encrypt_subparser(self, subparsers):
cmd_encrypt = subparsers.add_parser( cmd_encrypt = subparsers.add_parser(
'encrypt', help='Encrypt a secret to be used in a project\'s jobs') 'encrypt', help='Encrypt a secret to be used in a project\'s jobs')
cmd_encrypt.add_argument('--public-key',
help='path to project public key '
'(bypass API call)',
metavar='/path/to/pubkey',
required=False, default=None)
cmd_encrypt.add_argument('--tenant', help='tenant name', cmd_encrypt.add_argument('--tenant', help='tenant name',
required=False, default='') required=False, default='')
cmd_encrypt.add_argument('--project', help='project name', cmd_encrypt.add_argument('--project', help='project name',
required=True) required=False, default=None)
cmd_encrypt.add_argument('--no-strip', action='store_true', cmd_encrypt.add_argument('--no-strip', action='store_true',
help='Do not strip whitespace from beginning ' help='Do not strip whitespace from beginning '
'or end of input.', 'or end of input.',
@ -474,6 +485,10 @@ class ZuulClient():
cmd_encrypt.set_defaults(func=self.encrypt) cmd_encrypt.set_defaults(func=self.encrypt)
def encrypt(self): def encrypt(self):
if self.args.project is None and self.args.public_key is None:
raise ArgumentException(
'Either provide a public key or a project to continue'
)
if self.args.infile: if self.args.infile:
try: try:
with open(self.args.infile) as f: with open(self.args.infile) as f:
@ -489,13 +504,17 @@ class ZuulClient():
plaintext = plaintext.strip() plaintext = plaintext.strip()
pubkey_file = tempfile.NamedTemporaryFile(delete=False) pubkey_file = tempfile.NamedTemporaryFile(delete=False)
self.log.debug('Creating temporary key file %s' % pubkey_file.name) self.log.debug('Creating temporary key file %s' % pubkey_file.name)
try:
if self.args.public_key is not None:
self.log.debug('Using local public key')
shutil.copy(self.args.public_key, pubkey_file.name)
else:
client = self.get_client() client = self.get_client()
self._check_tenant_scope(client) self._check_tenant_scope(client)
try:
key = client.get_key(self.args.tenant, self.args.project) key = client.get_key(self.args.tenant, self.args.project)
pubkey_file.write(str.encode(key)) pubkey_file.write(str.encode(key))
pubkey_file.close() pubkey_file.close()
self.log.debug('Calling openssl') self.log.debug('Calling openssl')
ciphertext_chunks = encrypt_with_openssl(pubkey_file.name, ciphertext_chunks = encrypt_with_openssl(pubkey_file.name,
plaintext, plaintext,
@ -522,6 +541,9 @@ class ZuulClient():
else: else:
print(output) print(output)
return_code = True return_code = True
except ArgumentException as e:
# do not log and re-raise, caught later
raise e
except Exception as e: except Exception as e:
self.log.exception(e) self.log.exception(e)
return_code = False return_code = False