Add support for client certs to the Python API.
This adds support for passing a client cert (in the format expected by the `requests` library) to the `Client` constructor, as well as for specifying one on the command line or in .zuliprc (through new `client_cert` and `client_cert_key` options).
This commit is contained in:
parent
6034ae7b9a
commit
ad94b57e2f
|
@ -134,6 +134,17 @@ def generate_option_group(parser, prefix=''):
|
||||||
CA certificates. This will be used to
|
CA certificates. This will be used to
|
||||||
verify the server's identity. All
|
verify the server's identity. All
|
||||||
certificates should be PEM encoded.''')
|
certificates should be PEM encoded.''')
|
||||||
|
group.add_option('--client-cert',
|
||||||
|
action='store',
|
||||||
|
dest='client_cert',
|
||||||
|
help='''Specify a file containing a client
|
||||||
|
certificate (not needed for most deployments).''')
|
||||||
|
group.add_option('--client-cert-key',
|
||||||
|
action='store',
|
||||||
|
dest='client_cert_key',
|
||||||
|
help='''Specify a file containing the client
|
||||||
|
certificate's key (if it is in a separate
|
||||||
|
file).''')
|
||||||
return group
|
return group
|
||||||
|
|
||||||
def init_from_options(options, client=None):
|
def init_from_options(options, client=None):
|
||||||
|
@ -144,7 +155,9 @@ def init_from_options(options, client=None):
|
||||||
return Client(email=options.zulip_email, api_key=options.zulip_api_key,
|
return Client(email=options.zulip_email, api_key=options.zulip_api_key,
|
||||||
config_file=options.zulip_config_file, verbose=options.verbose,
|
config_file=options.zulip_config_file, verbose=options.verbose,
|
||||||
site=options.zulip_site, client=client,
|
site=options.zulip_site, client=client,
|
||||||
cert_bundle=options.cert_bundle, insecure=options.insecure)
|
cert_bundle=options.cert_bundle, insecure=options.insecure,
|
||||||
|
client_cert=options.client_cert,
|
||||||
|
client_cert_key=options.client_cert_key)
|
||||||
|
|
||||||
def get_default_config_filename():
|
def get_default_config_filename():
|
||||||
config_file = os.path.join(os.environ["HOME"], ".zuliprc")
|
config_file = os.path.join(os.environ["HOME"], ".zuliprc")
|
||||||
|
@ -158,7 +171,8 @@ class Client(object):
|
||||||
def __init__(self, email=None, api_key=None, config_file=None,
|
def __init__(self, email=None, api_key=None, config_file=None,
|
||||||
verbose=False, retry_on_errors=True,
|
verbose=False, retry_on_errors=True,
|
||||||
site=None, client=None,
|
site=None, client=None,
|
||||||
cert_bundle=None, insecure=None):
|
cert_bundle=None, insecure=None,
|
||||||
|
client_cert=None, client_cert_key=None):
|
||||||
if client is None:
|
if client is None:
|
||||||
client = _default_client()
|
client = _default_client()
|
||||||
|
|
||||||
|
@ -174,6 +188,10 @@ class Client(object):
|
||||||
email = config.get("api", "email")
|
email = config.get("api", "email")
|
||||||
if site is None and config.has_option("api", "site"):
|
if site is None and config.has_option("api", "site"):
|
||||||
site = config.get("api", "site")
|
site = config.get("api", "site")
|
||||||
|
if client_cert is None and config.has_option("api", "client_cert"):
|
||||||
|
client_cert = config.get("api", "client_cert")
|
||||||
|
if client_cert_key is None and config.has_option("api", "client_cert_key"):
|
||||||
|
client_cert_key = config.get("api", "client_cert_key")
|
||||||
if cert_bundle is None and config.has_option("api", "cert_bundle"):
|
if cert_bundle is None and config.has_option("api", "cert_bundle"):
|
||||||
cert_bundle = config.get("api", "cert_bundle")
|
cert_bundle = config.get("api", "cert_bundle")
|
||||||
if insecure is None and config.has_option("api", "insecure"):
|
if insecure is None and config.has_option("api", "insecure"):
|
||||||
|
@ -220,6 +238,21 @@ class Client(object):
|
||||||
# Default behavior: verify against system CA certificates
|
# Default behavior: verify against system CA certificates
|
||||||
self.tls_verification=True
|
self.tls_verification=True
|
||||||
|
|
||||||
|
if client_cert is None:
|
||||||
|
if client_cert_key is not None:
|
||||||
|
raise RuntimeError("client cert key '%s' specified, but no client cert public part provided"
|
||||||
|
%(client_cert_key,))
|
||||||
|
else: # we have a client cert
|
||||||
|
if not os.path.isfile(client_cert):
|
||||||
|
raise RuntimeError("client cert '%s' does not exist"
|
||||||
|
%(client_cert,))
|
||||||
|
if client_cert_key is not None:
|
||||||
|
if not os.path.isfile(client_cert_key):
|
||||||
|
raise RuntimeError("client cert key '%s' does not exist"
|
||||||
|
%(client_cert_key,))
|
||||||
|
self.client_cert = client_cert
|
||||||
|
self.client_cert_key = client_cert_key
|
||||||
|
|
||||||
def get_user_agent(self):
|
def get_user_agent(self):
|
||||||
vendor = ''
|
vendor = ''
|
||||||
vendor_version = ''
|
vendor_version = ''
|
||||||
|
@ -289,12 +322,21 @@ class Client(object):
|
||||||
else:
|
else:
|
||||||
kwarg = "data"
|
kwarg = "data"
|
||||||
kwargs = {kwarg: query_state["request"]}
|
kwargs = {kwarg: query_state["request"]}
|
||||||
|
|
||||||
|
# Build a client cert object for requests
|
||||||
|
if self.client_cert_key is not None:
|
||||||
|
client_cert = (self.client_cert, self.client_cert_key)
|
||||||
|
else:
|
||||||
|
client_cert = self.client_cert
|
||||||
|
|
||||||
res = requests.request(
|
res = requests.request(
|
||||||
method,
|
method,
|
||||||
urllib.parse.urljoin(self.base_url, url),
|
urllib.parse.urljoin(self.base_url, url),
|
||||||
auth=requests.auth.HTTPBasicAuth(self.email,
|
auth=requests.auth.HTTPBasicAuth(self.email,
|
||||||
self.api_key),
|
self.api_key),
|
||||||
verify=self.tls_verification, timeout=90,
|
verify=self.tls_verification,
|
||||||
|
cert=client_cert,
|
||||||
|
timeout=90,
|
||||||
headers={"User-agent": self.get_user_agent()},
|
headers={"User-agent": self.get_user_agent()},
|
||||||
**kwargs)
|
**kwargs)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue