import flask import gssapi import requests from requests_gssapi import HTTPSPNEGOAuth from zope import component from zope.interface import implementer from ceo_common.interfaces import IConfig, IHTTPClient @implementer(IHTTPClient) class HTTPClient: def __init__(self): # Determine how to connect to other ceod instances cfg = component.getUtility(IConfig) if cfg.get('ceod_use_https'): self.scheme = 'https' else: self.scheme = 'http' self.ceod_port = cfg.get('ceod_port') self.base_domain = cfg.get('base_domain') def request(self, host: str, api_path: str, method: str, delegate: bool, **kwargs): # always use the FQDN if '.' not in host: host = host + '.' + self.base_domain # SPNEGO spnego_kwargs = { 'opportunistic_auth': True, 'target_name': gssapi.Name('ceod/' + host), } if flask.has_request_context() and 'client_token' in flask.g: # This is reached when we are the server and the client has forwarded # their credentials to us. spnego_kwargs['creds'] = gssapi.Credentials(token=flask.g.client_token) if delegate: # This is reached when we are the client and we want to forward our # credentials to the server. spnego_kwargs['delegate'] = True auth = HTTPSPNEGOAuth(**spnego_kwargs) return requests.request( method, f'{self.scheme}://{host}:{self.ceod_port}{api_path}', auth=auth, **kwargs, ) def get(self, host: str, api_path: str, delegate: bool = True, **kwargs): return self.request(host, api_path, 'GET', delegate, **kwargs) def post(self, host: str, api_path: str, delegate: bool = True, **kwargs): return self.request(host, api_path, 'POST', delegate, **kwargs) def patch(self, host: str, api_path: str, delegate: bool = True, **kwargs): return self.request(host, api_path, 'PATCH', delegate, **kwargs) def delete(self, host: str, api_path: str, delegate: bool = True, **kwargs): return self.request(host, api_path, 'DELETE', delegate, **kwargs)