Merge pull request #831 from nathanielmanistaatgoogle/security-progress

Work towards invocation-side security.
pull/780/merge
Masood Malekghassemi 10 years ago
commit e07edc50e1
  1. 4
      src/python/src/grpc/_adapter/_c_test.py
  2. 18
      src/python/src/grpc/_adapter/_channel.c
  3. 1
      src/python/src/grpc/_adapter/_client_credentials.c
  4. 3
      src/python/src/grpc/_adapter/_face_test_case.py
  5. 6
      src/python/src/grpc/_adapter/_links_test.py
  6. 5
      src/python/src/grpc/_adapter/_lonely_rear_link_test.py
  7. 6
      src/python/src/grpc/_adapter/_low_test.py
  8. 71
      src/python/src/grpc/_adapter/rear.py
  9. 11
      src/python/src/grpc/early_adopter/_reexport.py
  10. 22
      src/python/src/grpc/early_adopter/implementations.py
  11. 16
      src/python/src/grpc/framework/assembly/implementations.py

@ -70,7 +70,7 @@ class _CTest(unittest.TestCase):
def testChannel(self):
_c.init()
channel = _c.Channel('test host:12345')
channel = _c.Channel('test host:12345', None)
del channel
_c.shut_down()
@ -81,7 +81,7 @@ class _CTest(unittest.TestCase):
_c.init()
channel = _c.Channel('%s:%d' % (host, 12345))
channel = _c.Channel('%s:%d' % (host, 12345), None)
call = _c.Call(channel, method, host, time.time() + _TIMEOUT)
del call
del channel

@ -35,18 +35,28 @@
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/grpc_security.h>
#include "grpc/_adapter/_client_credentials.h"
static int pygrpc_channel_init(Channel *self, PyObject *args, PyObject *kwds) {
const char *hostport;
static char *kwlist[] = {"hostport", NULL};
PyObject *client_credentials;
static char *kwlist[] = {"hostport", "client_credentials", NULL};
if (!(PyArg_ParseTupleAndKeywords(args, kwds, "s:Channel", kwlist,
&hostport))) {
if (!(PyArg_ParseTupleAndKeywords(args, kwds, "sO:Channel", kwlist,
&hostport, &client_credentials))) {
return -1;
}
if (client_credentials == Py_None) {
self->c_channel = grpc_channel_create(hostport, NULL);
return 0;
} else {
self->c_channel = grpc_secure_channel_create(
((ClientCredentials *)client_credentials)->c_client_credentials,
hostport, NULL);
return 0;
}
}
static void pygrpc_channel_dealloc(Channel *self) {

@ -58,6 +58,7 @@ static int pygrpc_client_credentials_init(ClientCredentials *self,
self->c_client_credentials =
grpc_ssl_credentials_create(root_certificates, NULL);
}
return 0;
}
static void pygrpc_client_credentials_dealloc(ClientCredentials *self) {

@ -85,7 +85,8 @@ class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage):
port = fore_link.port()
rear_link = rear.RearLink(
'localhost', port, pool,
serialization.request_serializers, serialization.response_deserializers)
serialization.request_serializers,
serialization.response_deserializers, False, None, None, None)
rear_link.start()
front = tickets_implementations.front(pool, pool, pool)
back = tickets_implementations.back(

@ -75,7 +75,7 @@ class RoundTripTest(unittest.TestCase):
rear_link = rear.RearLink(
'localhost', port, self.rear_link_pool, {test_method: None},
{test_method: None})
{test_method: None}, False, None, None, None)
rear_link.join_fore_link(test_fore_link)
test_fore_link.join_rear_link(rear_link)
rear_link.start()
@ -129,7 +129,7 @@ class RoundTripTest(unittest.TestCase):
rear_link = rear.RearLink(
'localhost', port, self.rear_link_pool, {test_method: _IDENTITY},
{test_method: _IDENTITY})
{test_method: _IDENTITY}, False, None, None, None)
rear_link.join_fore_link(test_fore_link)
test_fore_link.join_rear_link(rear_link)
rear_link.start()
@ -193,7 +193,7 @@ class RoundTripTest(unittest.TestCase):
rear_link = rear.RearLink(
'localhost', port, self.rear_link_pool,
{test_method: scenario.serialize_request},
{test_method: scenario.deserialize_response})
{test_method: scenario.deserialize_response}, False, None, None, None)
rear_link.join_fore_link(test_fore_link)
test_fore_link.join_rear_link(rear_link)
rear_link.start()

@ -50,7 +50,8 @@ class LonelyRearLinkTest(unittest.TestCase):
self.pool.shutdown(wait=True)
def testUpAndDown(self):
rear_link = rear.RearLink('nonexistent', 54321, self.pool, {}, {})
rear_link = rear.RearLink(
'nonexistent', 54321, self.pool, {}, {}, False, None, None, None)
rear_link.start()
rear_link.stop()
@ -63,7 +64,7 @@ class LonelyRearLinkTest(unittest.TestCase):
rear_link = rear.RearLink(
'nonexistent', 54321, self.pool, {test_method: None},
{test_method: None})
{test_method: None}, False, None, None, None)
rear_link.join_fore_link(fore_link)
rear_link.start()

@ -56,7 +56,7 @@ class LonelyClientTest(unittest.TestCase):
finish_tag = object()
completion_queue = _low.CompletionQueue()
channel = _low.Channel('%s:%d' % (host, port))
channel = _low.Channel('%s:%d' % (host, port), None)
client_call = _low.Call(channel, method, host, deadline)
client_call.invoke(completion_queue, metadata_tag, finish_tag)
@ -87,7 +87,7 @@ class EchoTest(unittest.TestCase):
self.server.start()
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port))
self.channel = _low.Channel('%s:%d' % (self.host, port), None)
def tearDown(self):
self.server.stop()
@ -265,7 +265,7 @@ class CancellationTest(unittest.TestCase):
self.server.start()
self.client_completion_queue = _low.CompletionQueue()
self.channel = _low.Channel('%s:%d' % (self.host, port))
self.channel = _low.Channel('%s:%d' % (self.host, port), None)
def tearDown(self):
self.server.stop()

@ -92,7 +92,8 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
"""An invocation-side bridge between RPC Framework and the C-ish _low code."""
def __init__(
self, host, port, pool, request_serializers, response_deserializers):
self, host, port, pool, request_serializers, response_deserializers,
secure, root_certificates, private_key, certificate_chain):
"""Constructor.
Args:
@ -103,6 +104,13 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
serializer behaviors.
response_deserializers: A dict from RPC method names to response object
deserializer behaviors.
secure: A boolean indicating whether or not to use a secure connection.
root_certificates: The PEM-encoded root certificates or None to ask for
them to be retrieved from a default location.
private_key: The PEM-encoded private key to use or None if no private
key should be used.
certificate_chain: The PEM-encoded certificate chain to use or None if
no certificate chain should be used.
"""
self._condition = threading.Condition()
self._host = host
@ -116,6 +124,14 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
self._channel = None
self._rpc_states = {}
self._spinning = False
if secure:
self._client_credentials = _low.ClientCredentials(
root_certificates, private_key, certificate_chain)
else:
self._client_credentials = None
self._root_certificates = root_certificates
self._private_key = private_key
self._certificate_chain = certificate_chain
def _on_write_event(self, operation_id, event, rpc_state):
if event.write_accepted:
@ -310,7 +326,8 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
"""
with self._condition:
self._completion_queue = _low.CompletionQueue()
self._channel = _low.Channel('%s:%d' % (self._host, self._port))
self._channel = _low.Channel(
'%s:%d' % (self._host, self._port), self._client_credentials)
return self
def _stop(self):
@ -369,11 +386,17 @@ class RearLink(ticket_interfaces.RearLink, activated.Activated):
class _ActivatedRearLink(ticket_interfaces.RearLink, activated.Activated):
def __init__(self, host, port, request_serializers, response_deserializers):
def __init__(
self, host, port, request_serializers, response_deserializers, secure,
root_certificates, private_key, certificate_chain):
self._host = host
self._port = port
self._request_serializers = request_serializers
self._response_deserializers = response_deserializers
self._secure = secure
self._root_certificates = root_certificates
self._private_key = private_key
self._certificate_chain = certificate_chain
self._lock = threading.Lock()
self._pool = None
@ -391,7 +414,8 @@ class _ActivatedRearLink(ticket_interfaces.RearLink, activated.Activated):
self._pool = logging_pool.pool(_THREAD_POOL_SIZE)
self._rear_link = RearLink(
self._host, self._port, self._pool, self._request_serializers,
self._response_deserializers)
self._response_deserializers, self._secure, self._root_certificates,
self._private_key, self._certificate_chain)
self._rear_link.join_fore_link(self._fore_link)
self._rear_link.start()
return self
@ -422,6 +446,7 @@ class _ActivatedRearLink(ticket_interfaces.RearLink, activated.Activated):
self._rear_link.accept_front_to_back_ticket(ticket)
# TODO(issue 726): reconcile these two creation functions.
def activated_rear_link(
host, port, request_serializers, response_deserializers):
"""Creates a RearLink that is also an activated.Activated.
@ -436,6 +461,42 @@ def activated_rear_link(
serializer behavior.
response_deserializers: A dictionary from RPC method name to response
object deserializer behavior.
secure: A boolean indicating whether or not to use a secure connection.
root_certificates: The PEM-encoded root certificates or None to ask for
them to be retrieved from a default location.
private_key: The PEM-encoded private key to use or None if no private key
should be used.
certificate_chain: The PEM-encoded certificate chain to use or None if no
certificate chain should be used.
"""
return _ActivatedRearLink(
host, port, request_serializers, response_deserializers, False, None,
None, None)
def secure_activated_rear_link(
host, port, request_serializers, response_deserializers, root_certificates,
private_key, certificate_chain):
"""Creates a RearLink that is also an activated.Activated.
The returned object is only valid for use between calls to its start and stop
methods (or in context when used as a context manager).
Args:
host: The host to which to connect for RPC service.
port: The port to which to connect for RPC service.
request_serializers: A dictionary from RPC method name to request object
serializer behavior.
response_deserializers: A dictionary from RPC method name to response
object deserializer behavior.
root_certificates: The PEM-encoded root certificates or None to ask for
them to be retrieved from a default location.
private_key: The PEM-encoded private key to use or None if no private key
should be used.
certificate_chain: The PEM-encoded certificate chain to use or None if no
certificate chain should be used.
"""
return _ActivatedRearLink(
host, port, request_serializers, response_deserializers)
host, port, request_serializers, response_deserializers, True,
root_certificates, private_key, certificate_chain)

@ -27,9 +27,6 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import abc
import collections
from grpc.framework.face import exceptions as face_exceptions
from grpc.framework.face import interfaces as face_interfaces
from grpc.framework.foundation import future
@ -186,6 +183,14 @@ class _Stub(interfaces.Stub):
def __getattr__(self, attr):
underlying_attr = self._assembly_stub.__getattr__(attr)
cardinality = self._cardinalities.get(attr)
# TODO(nathaniel): unify this trick with its other occurrence in the code.
if cardinality is None:
for name, cardinality in self._cardinalities.iteritems():
last_slash_index = name.rfind('/')
if 0 <= last_slash_index and name[last_slash_index + 1:] == attr:
break
else:
raise AttributeError(attr)
if cardinality is interfaces.Cardinality.UNARY_UNARY:
return _UnaryUnarySyncAsync(underlying_attr)
elif cardinality is interfaces.Cardinality.UNARY_STREAM:

@ -93,13 +93,7 @@ class _Server(interfaces.Server):
with self._lock:
return self._fore_link.port()
def _build_stub(
methods, host, port, root_certificates, private_key, certificate_chain):
breakdown = _assembly_utilities.break_down_invocation(methods)
# TODO(nathaniel): pass security values.
activated_rear_link = _rear.activated_rear_link(
host, port, breakdown.request_serializers,
breakdown.response_deserializers)
def _build_stub(breakdown, activated_rear_link):
assembly_stub = _assembly_implementations.assemble_dynamic_inline_stub(
breakdown.implementations, activated_rear_link)
return _reexport.stub(assembly_stub, breakdown.cardinalities)
@ -123,7 +117,11 @@ def insecure_stub(methods, host, port):
Returns:
An interfaces.Stub affording RPC invocation.
"""
return _build_stub(methods, host, port, None, None, None)
breakdown = _assembly_utilities.break_down_invocation(methods)
activated_rear_link = _rear.activated_rear_link(
host, port, breakdown.request_serializers,
breakdown.response_deserializers)
return _build_stub(breakdown, activated_rear_link)
def secure_stub(
@ -146,8 +144,12 @@ def secure_stub(
Returns:
An interfaces.Stub affording RPC invocation.
"""
return _build_stub(
methods, host, port, root_certificates, private_key, certificate_chain)
breakdown = _assembly_utilities.break_down_invocation(methods)
activated_rear_link = _rear.secure_activated_rear_link(
host, port, breakdown.request_serializers,
breakdown.response_deserializers, root_certificates, private_key,
certificate_chain)
return _build_stub(breakdown, activated_rear_link)
def insecure_server(methods, port):

@ -31,16 +31,18 @@
import threading
# tickets_interfaces, face_interfaces, and activated are referenced from
# specification in this module.
from grpc.framework.assembly import interfaces
from grpc.framework.base import util as base_utilities
from grpc.framework.base.packets import implementations as tickets_implementations
from grpc.framework.base.packets import interfaces as tickets_interfaces
from grpc.framework.base.packets import interfaces as tickets_interfaces # pylint: disable=unused-import
from grpc.framework.common import cardinality
from grpc.framework.common import style
from grpc.framework.face import implementations as face_implementations
from grpc.framework.face import interfaces as face_interfaces
from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import
from grpc.framework.face import utilities as face_utilities
from grpc.framework.foundation import activated
from grpc.framework.foundation import activated # pylint: disable=unused-import
from grpc.framework.foundation import logging_pool
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
@ -138,7 +140,13 @@ class _DynamicInlineStub(object):
with self._lock:
behavior = self._behaviors.get(attr)
if behavior is None:
raise AttributeError(attr)
for name, behavior in self._behaviors.iteritems():
last_slash_index = name.rfind('/')
if 0 <= last_slash_index and name[last_slash_index + 1:] == attr:
return behavior
else:
raise AttributeError(
'_DynamicInlineStub instance has no attribute "%s"!' % attr)
else:
return behavior

Loading…
Cancel
Save