aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Nathaniel Manista <nathaniel@google.com>2015-02-26 16:09:06 +0000
committerGravatar Nathaniel Manista <nathaniel@google.com>2015-02-26 17:11:23 +0000
commit1c37fe62fd99ca9a899002e15342fd3584b83ca0 (patch)
tree6ceb019958264718e17f894b5edf7ffa7f36e3ba /src
parent68acd356764a8fe20a5dbb8fb45d2d9319640715 (diff)
Work towards invocation-side security.
Diffstat (limited to 'src')
-rw-r--r--src/python/src/grpc/_adapter/_c_test.py4
-rw-r--r--src/python/src/grpc/_adapter/_channel.c22
-rw-r--r--src/python/src/grpc/_adapter/_client_credentials.c1
-rw-r--r--src/python/src/grpc/_adapter/_face_test_case.py3
-rw-r--r--src/python/src/grpc/_adapter/_links_test.py6
-rw-r--r--src/python/src/grpc/_adapter/_lonely_rear_link_test.py5
-rw-r--r--src/python/src/grpc/_adapter/_low_test.py6
-rw-r--r--src/python/src/grpc/_adapter/rear.py71
-rw-r--r--src/python/src/grpc/early_adopter/_reexport.py11
-rw-r--r--src/python/src/grpc/early_adopter/implementations.py22
-rw-r--r--src/python/src/grpc/framework/assembly/implementations.py16
11 files changed, 128 insertions, 39 deletions
diff --git a/src/python/src/grpc/_adapter/_c_test.py b/src/python/src/grpc/_adapter/_c_test.py
index 44aff44ffa..d81c63e346 100644
--- a/src/python/src/grpc/_adapter/_c_test.py
+++ b/src/python/src/grpc/_adapter/_c_test.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
diff --git a/src/python/src/grpc/_adapter/_channel.c b/src/python/src/grpc/_adapter/_channel.c
index 3ba943e4b2..9cf580bcfb 100644
--- a/src/python/src/grpc/_adapter/_channel.c
+++ b/src/python/src/grpc/_adapter/_channel.c
@@ -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;
}
-
- self->c_channel = grpc_channel_create(hostport, NULL);
- return 0;
+ 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) {
diff --git a/src/python/src/grpc/_adapter/_client_credentials.c b/src/python/src/grpc/_adapter/_client_credentials.c
index b970c866ef..e8ccff8d17 100644
--- a/src/python/src/grpc/_adapter/_client_credentials.c
+++ b/src/python/src/grpc/_adapter/_client_credentials.c
@@ -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) {
diff --git a/src/python/src/grpc/_adapter/_face_test_case.py b/src/python/src/grpc/_adapter/_face_test_case.py
index 8cce322d30..475d780c95 100644
--- a/src/python/src/grpc/_adapter/_face_test_case.py
+++ b/src/python/src/grpc/_adapter/_face_test_case.py
@@ -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(
diff --git a/src/python/src/grpc/_adapter/_links_test.py b/src/python/src/grpc/_adapter/_links_test.py
index 6b3bcee9fa..5d7e677243 100644
--- a/src/python/src/grpc/_adapter/_links_test.py
+++ b/src/python/src/grpc/_adapter/_links_test.py
@@ -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()
diff --git a/src/python/src/grpc/_adapter/_lonely_rear_link_test.py b/src/python/src/grpc/_adapter/_lonely_rear_link_test.py
index 9a13309a18..77821ba71a 100644
--- a/src/python/src/grpc/_adapter/_lonely_rear_link_test.py
+++ b/src/python/src/grpc/_adapter/_lonely_rear_link_test.py
@@ -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()
diff --git a/src/python/src/grpc/_adapter/_low_test.py b/src/python/src/grpc/_adapter/_low_test.py
index 898c62c002..03e3f473a3 100644
--- a/src/python/src/grpc/_adapter/_low_test.py
+++ b/src/python/src/grpc/_adapter/_low_test.py
@@ -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()
diff --git a/src/python/src/grpc/_adapter/rear.py b/src/python/src/grpc/_adapter/rear.py
index 94ff66ffda..bfde5f5c57 100644
--- a/src/python/src/grpc/_adapter/rear.py
+++ b/src/python/src/grpc/_adapter/rear.py
@@ -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)
diff --git a/src/python/src/grpc/early_adopter/_reexport.py b/src/python/src/grpc/early_adopter/_reexport.py
index 35855bc9c8..35f4e85a72 100644
--- a/src/python/src/grpc/early_adopter/_reexport.py
+++ b/src/python/src/grpc/early_adopter/_reexport.py
@@ -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:
diff --git a/src/python/src/grpc/early_adopter/implementations.py b/src/python/src/grpc/early_adopter/implementations.py
index 241ed7dcdb..6195958624 100644
--- a/src/python/src/grpc/early_adopter/implementations.py
+++ b/src/python/src/grpc/early_adopter/implementations.py
@@ -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):
diff --git a/src/python/src/grpc/framework/assembly/implementations.py b/src/python/src/grpc/framework/assembly/implementations.py
index b9d314844c..f7166ed99d 100644
--- a/src/python/src/grpc/framework/assembly/implementations.py
+++ b/src/python/src/grpc/framework/assembly/implementations.py
@@ -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