import flask from flask import g 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, IKerberosService @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, method: str, host: str, path: str, **kwargs): # always use the FQDN if '.' not in host: host = host + '.' + self.base_domain if method == 'GET': need_auth = path.startswith('/api/members') or \ path.startswith('/api/cloud') delegate = False else: need_auth = True delegate = True # SPNEGO if need_auth: spnego_kwargs = { 'opportunistic_auth': True, 'target_name': gssapi.Name('ceod/' + host), } if flask.has_request_context(): # This is reached when we are the server and the client has # forwarded their credentials to us. token = None if g.get('need_admin_creds', False): # Some Kerberos bindings in some programming languages can't # perform delegation, so use the admin creds here. token = component.getUtility(IKerberosService).get_admin_creds_token() elif 'client_token' in g: token = g.client_token if token is not None: spnego_kwargs['creds'] = gssapi.Credentials(token=token) elif 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) else: auth = None return requests.request( method, f'{self.scheme}://{host}:{self.ceod_port}{path}', auth=auth, **kwargs, ) def get(self, host: str, path: str, **kwargs): return self.request('GET', host, path, **kwargs) def post(self, host: str, path: str, **kwargs): return self.request('POST', host, path, **kwargs) def patch(self, host: str, path: str, **kwargs): return self.request('PATCH', host, path, **kwargs) def delete(self, host: str, path: str, **kwargs): return self.request('DELETE', host, path, **kwargs)