|
|
|
@ -16,47 +16,123 @@ cimport cpython |
|
|
|
|
|
|
|
|
|
import grpc |
|
|
|
|
import threading |
|
|
|
|
import traceback |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class ChannelCredentials: |
|
|
|
|
cdef class CallCredentials: |
|
|
|
|
|
|
|
|
|
def __cinit__(self): |
|
|
|
|
grpc_init() |
|
|
|
|
self.c_credentials = NULL |
|
|
|
|
self.c_ssl_pem_key_cert_pair.private_key = NULL |
|
|
|
|
self.c_ssl_pem_key_cert_pair.certificate_chain = NULL |
|
|
|
|
self.references = [] |
|
|
|
|
cdef grpc_call_credentials *c(self): |
|
|
|
|
raise NotImplementedError() |
|
|
|
|
|
|
|
|
|
# The object *can* be invalid in Python if we fail to make the credentials |
|
|
|
|
# (and the core thus returns NULL credentials). Used primarily for debugging. |
|
|
|
|
@property |
|
|
|
|
def is_valid(self): |
|
|
|
|
return self.c_credentials != NULL |
|
|
|
|
|
|
|
|
|
def __dealloc__(self): |
|
|
|
|
if self.c_credentials != NULL: |
|
|
|
|
grpc_channel_credentials_release(self.c_credentials) |
|
|
|
|
grpc_shutdown() |
|
|
|
|
cdef int _get_metadata( |
|
|
|
|
void *state, grpc_auth_metadata_context context, |
|
|
|
|
grpc_credentials_plugin_metadata_cb cb, void *user_data, |
|
|
|
|
grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], |
|
|
|
|
size_t *num_creds_md, grpc_status_code *status, |
|
|
|
|
const char **error_details) with gil: |
|
|
|
|
def callback(Metadata metadata, grpc_status_code status, bytes error_details): |
|
|
|
|
if status is StatusCode.ok: |
|
|
|
|
cb(user_data, metadata.c_metadata, metadata.c_count, status, NULL) |
|
|
|
|
else: |
|
|
|
|
cb(user_data, NULL, 0, status, error_details) |
|
|
|
|
args = context.service_url, context.method_name, callback, |
|
|
|
|
threading.Thread(target=<object>state, args=args).start() |
|
|
|
|
return 0 # Asynchronous return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class CallCredentials: |
|
|
|
|
cdef void _destroy(void *state) with gil: |
|
|
|
|
cpython.Py_DECREF(<object>state) |
|
|
|
|
|
|
|
|
|
def __cinit__(self): |
|
|
|
|
grpc_init() |
|
|
|
|
self.c_credentials = NULL |
|
|
|
|
self.references = [] |
|
|
|
|
|
|
|
|
|
# The object *can* be invalid in Python if we fail to make the credentials |
|
|
|
|
# (and the core thus returns NULL credentials). Used primarily for debugging. |
|
|
|
|
@property |
|
|
|
|
def is_valid(self): |
|
|
|
|
return self.c_credentials != NULL |
|
|
|
|
cdef class MetadataPluginCallCredentials(CallCredentials): |
|
|
|
|
|
|
|
|
|
def __dealloc__(self): |
|
|
|
|
if self.c_credentials != NULL: |
|
|
|
|
grpc_call_credentials_release(self.c_credentials) |
|
|
|
|
grpc_shutdown() |
|
|
|
|
def __cinit__(self, metadata_plugin, name): |
|
|
|
|
self._metadata_plugin = metadata_plugin |
|
|
|
|
self._name = name |
|
|
|
|
|
|
|
|
|
cdef grpc_call_credentials *c(self): |
|
|
|
|
cdef grpc_metadata_credentials_plugin c_metadata_plugin |
|
|
|
|
c_metadata_plugin.get_metadata = _get_metadata |
|
|
|
|
c_metadata_plugin.destroy = _destroy |
|
|
|
|
c_metadata_plugin.state = <void *>self._metadata_plugin |
|
|
|
|
c_metadata_plugin.type = self._name |
|
|
|
|
cpython.Py_INCREF(self._metadata_plugin) |
|
|
|
|
return grpc_metadata_credentials_create_from_plugin(c_metadata_plugin, NULL) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef grpc_call_credentials *_composition(call_credentialses): |
|
|
|
|
call_credentials_iterator = iter(call_credentialses) |
|
|
|
|
cdef CallCredentials composition = next(call_credentials_iterator) |
|
|
|
|
cdef grpc_call_credentials *c_composition = composition.c() |
|
|
|
|
cdef CallCredentials additional_call_credentials |
|
|
|
|
cdef grpc_call_credentials *c_additional_call_credentials |
|
|
|
|
cdef grpc_call_credentials *c_next_composition |
|
|
|
|
for additional_call_credentials in call_credentials_iterator: |
|
|
|
|
c_additional_call_credentials = additional_call_credentials.c() |
|
|
|
|
c_next_composition = grpc_composite_call_credentials_create( |
|
|
|
|
c_composition, c_additional_call_credentials, NULL) |
|
|
|
|
grpc_call_credentials_release(c_composition) |
|
|
|
|
grpc_call_credentials_release(c_additional_call_credentials) |
|
|
|
|
c_composition = c_next_composition |
|
|
|
|
return c_composition |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class CompositeCallCredentials(CallCredentials): |
|
|
|
|
|
|
|
|
|
def __cinit__(self, call_credentialses): |
|
|
|
|
self._call_credentialses = call_credentialses |
|
|
|
|
|
|
|
|
|
cdef grpc_call_credentials *c(self): |
|
|
|
|
return _composition(self._call_credentialses) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class ChannelCredentials: |
|
|
|
|
|
|
|
|
|
cdef grpc_channel_credentials *c(self): |
|
|
|
|
raise NotImplementedError() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class SSLChannelCredentials(ChannelCredentials): |
|
|
|
|
|
|
|
|
|
def __cinit__(self, pem_root_certificates, private_key, certificate_chain): |
|
|
|
|
self._pem_root_certificates = pem_root_certificates |
|
|
|
|
self._private_key = private_key |
|
|
|
|
self._certificate_chain = certificate_chain |
|
|
|
|
|
|
|
|
|
cdef grpc_channel_credentials *c(self): |
|
|
|
|
cdef const char *c_pem_root_certificates |
|
|
|
|
cdef grpc_ssl_pem_key_cert_pair c_pem_key_certificate_pair |
|
|
|
|
if self._pem_root_certificates is None: |
|
|
|
|
c_pem_root_certificates = NULL |
|
|
|
|
else: |
|
|
|
|
c_pem_root_certificates = self._pem_root_certificates |
|
|
|
|
if self._private_key is None and self._certificate_chain is None: |
|
|
|
|
return grpc_ssl_credentials_create( |
|
|
|
|
c_pem_root_certificates, NULL, NULL) |
|
|
|
|
else: |
|
|
|
|
c_pem_key_certificate_pair.private_key = self._private_key |
|
|
|
|
c_pem_key_certificate_pair.certificate_chain = self._certificate_chain |
|
|
|
|
return grpc_ssl_credentials_create( |
|
|
|
|
c_pem_root_certificates, &c_pem_key_certificate_pair, NULL) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class CompositeChannelCredentials(ChannelCredentials): |
|
|
|
|
|
|
|
|
|
def __cinit__(self, call_credentialses, channel_credentials): |
|
|
|
|
self._call_credentialses = call_credentialses |
|
|
|
|
self._channel_credentials = channel_credentials |
|
|
|
|
|
|
|
|
|
cdef grpc_channel_credentials *c(self): |
|
|
|
|
cdef grpc_channel_credentials *c_channel_credentials |
|
|
|
|
c_channel_credentials = self._channel_credentials.c() |
|
|
|
|
cdef grpc_call_credentials *c_call_credentials_composition = _composition( |
|
|
|
|
self._call_credentialses) |
|
|
|
|
cdef grpc_channel_credentials *composition |
|
|
|
|
c_composition = grpc_composite_channel_credentials_create( |
|
|
|
|
c_channel_credentials, c_call_credentials_composition, NULL) |
|
|
|
|
grpc_channel_credentials_release(c_channel_credentials) |
|
|
|
|
grpc_call_credentials_release(c_call_credentials_composition) |
|
|
|
|
return c_composition |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class ServerCertificateConfig: |
|
|
|
@ -89,190 +165,6 @@ cdef class ServerCredentials: |
|
|
|
|
grpc_server_credentials_release(self.c_credentials) |
|
|
|
|
grpc_shutdown() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class CredentialsMetadataPlugin: |
|
|
|
|
|
|
|
|
|
def __cinit__(self, object plugin_callback, bytes name): |
|
|
|
|
""" |
|
|
|
|
Args: |
|
|
|
|
plugin_callback (callable): Callback accepting a service URL (str/bytes) |
|
|
|
|
and callback object (accepting a MetadataArray, |
|
|
|
|
grpc_status_code, and a str/bytes error message). This argument |
|
|
|
|
when called should be non-blocking and eventually call the callback |
|
|
|
|
object with the appropriate status code/details and metadata (if |
|
|
|
|
successful). |
|
|
|
|
name (bytes): Plugin name. |
|
|
|
|
""" |
|
|
|
|
grpc_init() |
|
|
|
|
if not callable(plugin_callback): |
|
|
|
|
raise ValueError('expected callable plugin_callback') |
|
|
|
|
self.plugin_callback = plugin_callback |
|
|
|
|
self.plugin_name = name |
|
|
|
|
|
|
|
|
|
def __dealloc__(self): |
|
|
|
|
grpc_shutdown() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef grpc_metadata_credentials_plugin _c_plugin(CredentialsMetadataPlugin plugin): |
|
|
|
|
cdef grpc_metadata_credentials_plugin c_plugin |
|
|
|
|
c_plugin.get_metadata = plugin_get_metadata |
|
|
|
|
c_plugin.destroy = plugin_destroy_c_plugin_state |
|
|
|
|
c_plugin.state = <void *>plugin |
|
|
|
|
c_plugin.type = plugin.plugin_name |
|
|
|
|
cpython.Py_INCREF(plugin) |
|
|
|
|
return c_plugin |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef class AuthMetadataContext: |
|
|
|
|
|
|
|
|
|
def __cinit__(self): |
|
|
|
|
grpc_init() |
|
|
|
|
self.context.service_url = NULL |
|
|
|
|
self.context.method_name = NULL |
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
def service_url(self): |
|
|
|
|
return self.context.service_url |
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
def method_name(self): |
|
|
|
|
return self.context.method_name |
|
|
|
|
|
|
|
|
|
def __dealloc__(self): |
|
|
|
|
grpc_shutdown() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cdef int plugin_get_metadata( |
|
|
|
|
void *state, grpc_auth_metadata_context context, |
|
|
|
|
grpc_credentials_plugin_metadata_cb cb, void *user_data, |
|
|
|
|
grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], |
|
|
|
|
size_t *num_creds_md, grpc_status_code *status, |
|
|
|
|
const char **error_details) with gil: |
|
|
|
|
called_flag = [False] |
|
|
|
|
def python_callback( |
|
|
|
|
Metadata metadata, grpc_status_code status, |
|
|
|
|
bytes error_details): |
|
|
|
|
cb(user_data, metadata.c_metadata, metadata.c_count, status, error_details) |
|
|
|
|
called_flag[0] = True |
|
|
|
|
cdef CredentialsMetadataPlugin self = <CredentialsMetadataPlugin>state |
|
|
|
|
cdef AuthMetadataContext cy_context = AuthMetadataContext() |
|
|
|
|
cy_context.context = context |
|
|
|
|
def async_callback(): |
|
|
|
|
try: |
|
|
|
|
self.plugin_callback(cy_context, python_callback) |
|
|
|
|
except Exception as error: |
|
|
|
|
if not called_flag[0]: |
|
|
|
|
cb(user_data, NULL, 0, StatusCode.unknown, |
|
|
|
|
traceback.format_exc().encode()) |
|
|
|
|
threading.Thread(group=None, target=async_callback).start() |
|
|
|
|
return 0 # Asynchronous return |
|
|
|
|
|
|
|
|
|
cdef void plugin_destroy_c_plugin_state(void *state) with gil: |
|
|
|
|
cpython.Py_DECREF(<CredentialsMetadataPlugin>state) |
|
|
|
|
|
|
|
|
|
def channel_credentials_google_default(): |
|
|
|
|
cdef ChannelCredentials credentials = ChannelCredentials(); |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = grpc_google_default_credentials_create() |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def channel_credentials_ssl(pem_root_certificates, |
|
|
|
|
SslPemKeyCertPair ssl_pem_key_cert_pair): |
|
|
|
|
pem_root_certificates = str_to_bytes(pem_root_certificates) |
|
|
|
|
cdef ChannelCredentials credentials = ChannelCredentials() |
|
|
|
|
cdef const char *c_pem_root_certificates = NULL |
|
|
|
|
if pem_root_certificates is not None: |
|
|
|
|
c_pem_root_certificates = pem_root_certificates |
|
|
|
|
credentials.references.append(pem_root_certificates) |
|
|
|
|
if ssl_pem_key_cert_pair is not None: |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = grpc_ssl_credentials_create( |
|
|
|
|
c_pem_root_certificates, &ssl_pem_key_cert_pair.c_pair, NULL) |
|
|
|
|
credentials.references.append(ssl_pem_key_cert_pair) |
|
|
|
|
else: |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = grpc_ssl_credentials_create( |
|
|
|
|
c_pem_root_certificates, NULL, NULL) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def channel_credentials_composite( |
|
|
|
|
ChannelCredentials credentials_1 not None, |
|
|
|
|
CallCredentials credentials_2 not None): |
|
|
|
|
if not credentials_1.is_valid or not credentials_2.is_valid: |
|
|
|
|
raise ValueError("passed credentials must both be valid") |
|
|
|
|
cdef ChannelCredentials credentials = ChannelCredentials() |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = grpc_composite_channel_credentials_create( |
|
|
|
|
credentials_1.c_credentials, credentials_2.c_credentials, NULL) |
|
|
|
|
credentials.references.append(credentials_1) |
|
|
|
|
credentials.references.append(credentials_2) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def call_credentials_composite( |
|
|
|
|
CallCredentials credentials_1 not None, |
|
|
|
|
CallCredentials credentials_2 not None): |
|
|
|
|
if not credentials_1.is_valid or not credentials_2.is_valid: |
|
|
|
|
raise ValueError("passed credentials must both be valid") |
|
|
|
|
cdef CallCredentials credentials = CallCredentials() |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = grpc_composite_call_credentials_create( |
|
|
|
|
credentials_1.c_credentials, credentials_2.c_credentials, NULL) |
|
|
|
|
credentials.references.append(credentials_1) |
|
|
|
|
credentials.references.append(credentials_2) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def call_credentials_google_compute_engine(): |
|
|
|
|
cdef CallCredentials credentials = CallCredentials() |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = ( |
|
|
|
|
grpc_google_compute_engine_credentials_create(NULL)) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def call_credentials_service_account_jwt_access( |
|
|
|
|
json_key, Timespec token_lifetime not None): |
|
|
|
|
json_key = str_to_bytes(json_key) |
|
|
|
|
cdef CallCredentials credentials = CallCredentials() |
|
|
|
|
cdef char *json_key_c_string = json_key |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = ( |
|
|
|
|
grpc_service_account_jwt_access_credentials_create( |
|
|
|
|
json_key_c_string, token_lifetime.c_time, NULL)) |
|
|
|
|
credentials.references.append(json_key) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def call_credentials_google_refresh_token(json_refresh_token): |
|
|
|
|
json_refresh_token = str_to_bytes(json_refresh_token) |
|
|
|
|
cdef CallCredentials credentials = CallCredentials() |
|
|
|
|
cdef char *json_refresh_token_c_string = json_refresh_token |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = grpc_google_refresh_token_credentials_create( |
|
|
|
|
json_refresh_token_c_string, NULL) |
|
|
|
|
credentials.references.append(json_refresh_token) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def call_credentials_google_iam(authorization_token, authority_selector): |
|
|
|
|
authorization_token = str_to_bytes(authorization_token) |
|
|
|
|
authority_selector = str_to_bytes(authority_selector) |
|
|
|
|
cdef CallCredentials credentials = CallCredentials() |
|
|
|
|
cdef char *authorization_token_c_string = authorization_token |
|
|
|
|
cdef char *authority_selector_c_string = authority_selector |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = grpc_google_iam_credentials_create( |
|
|
|
|
authorization_token_c_string, authority_selector_c_string, NULL) |
|
|
|
|
credentials.references.append(authorization_token) |
|
|
|
|
credentials.references.append(authority_selector) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
def call_credentials_metadata_plugin(CredentialsMetadataPlugin plugin): |
|
|
|
|
cdef CallCredentials credentials = CallCredentials() |
|
|
|
|
cdef grpc_metadata_credentials_plugin c_plugin = _c_plugin(plugin) |
|
|
|
|
with nogil: |
|
|
|
|
credentials.c_credentials = ( |
|
|
|
|
grpc_metadata_credentials_create_from_plugin(c_plugin, NULL)) |
|
|
|
|
# TODO(atash): the following held reference is *probably* never necessary |
|
|
|
|
credentials.references.append(plugin) |
|
|
|
|
return credentials |
|
|
|
|
|
|
|
|
|
cdef const char* _get_c_pem_root_certs(pem_root_certs): |
|
|
|
|
if pem_root_certs is None: |
|
|
|
|
return NULL |
|
|
|
|