mirror of https://github.com/grpc/grpc.git
parent
fcbe7daf83
commit
60a83c744b
10 changed files with 268 additions and 30 deletions
@ -0,0 +1,73 @@ |
||||
# Copyright 2016, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""GRPCAuthMetadataPlugins for standard authentication.""" |
||||
|
||||
from concurrent import futures |
||||
|
||||
from grpc.beta import interfaces |
||||
|
||||
|
||||
def _sign_request(callback, token, error): |
||||
metadata = (('authorization', 'Bearer {}'.format(token)),) |
||||
callback(metadata, error) |
||||
|
||||
|
||||
class GoogleCallCredentials(interfaces.GRPCAuthMetadataPlugin): |
||||
"""Metadata wrapper for GoogleCredentials from the oauth2client library.""" |
||||
|
||||
def __init__(self, credentials): |
||||
self._credentials = credentials |
||||
self._pool = futures.ThreadPoolExecutor(max_workers=1) |
||||
|
||||
def __call__(self, context, callback): |
||||
# MetadataPlugins cannot block (see grpc.beta.interfaces.py) |
||||
future = self._pool.submit(self._credentials.get_access_token) |
||||
future.add_done_callback(lambda x: self._get_token_callback(callback, x)) |
||||
|
||||
def _get_token_callback(self, callback, future): |
||||
try: |
||||
access_token = future.result().access_token |
||||
except Exception as e: |
||||
_sign_request(callback, None, e) |
||||
else: |
||||
_sign_request(callback, access_token, None) |
||||
|
||||
def __del__(self): |
||||
self._pool.shutdown(wait=False) |
||||
|
||||
|
||||
class AccessTokenCallCredentials(interfaces.GRPCAuthMetadataPlugin): |
||||
"""Metadata wrapper for raw access token credentials.""" |
||||
|
||||
def __init__(self, access_token): |
||||
self._access_token = access_token |
||||
|
||||
def __call__(self, context, callback): |
||||
_sign_request(callback, self._access_token, None) |
@ -0,0 +1,96 @@ |
||||
# Copyright 2016, Google Inc. |
||||
# All rights reserved. |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
"""Tests of standard AuthMetadataPlugins.""" |
||||
|
||||
import collections |
||||
import threading |
||||
import unittest |
||||
|
||||
from grpc.beta import _auth |
||||
|
||||
|
||||
class MockGoogleCreds(object): |
||||
|
||||
def get_access_token(self): |
||||
token = collections.namedtuple('MockAccessTokenInfo', |
||||
('access_token', 'expires_in')) |
||||
token.access_token = 'token' |
||||
return token |
||||
|
||||
|
||||
class MockExceptionGoogleCreds(object): |
||||
|
||||
def get_access_token(self): |
||||
raise Exception() |
||||
|
||||
|
||||
class GoogleCallCredentialsTest(unittest.TestCase): |
||||
|
||||
def test_google_call_credentials_success(self): |
||||
callback_event = threading.Event() |
||||
|
||||
def mock_callback(metadata, error): |
||||
self.assertEqual(metadata, (('authorization', 'Bearer token'),)) |
||||
self.assertIsNone(error) |
||||
callback_event.set() |
||||
|
||||
call_creds = _auth.GoogleCallCredentials(MockGoogleCreds()) |
||||
call_creds(None, mock_callback) |
||||
self.assertTrue(callback_event.wait(1.0)) |
||||
|
||||
def test_google_call_credentials_error(self): |
||||
callback_event = threading.Event() |
||||
|
||||
def mock_callback(metadata, error): |
||||
self.assertIsNotNone(error) |
||||
callback_event.set() |
||||
|
||||
call_creds = _auth.GoogleCallCredentials(MockExceptionGoogleCreds()) |
||||
call_creds(None, mock_callback) |
||||
self.assertTrue(callback_event.wait(1.0)) |
||||
|
||||
|
||||
class AccessTokenCallCredentialsTest(unittest.TestCase): |
||||
|
||||
def test_google_call_credentials_success(self): |
||||
callback_event = threading.Event() |
||||
|
||||
def mock_callback(metadata, error): |
||||
self.assertEqual(metadata, (('authorization', 'Bearer token'),)) |
||||
self.assertIsNone(error) |
||||
callback_event.set() |
||||
|
||||
call_creds = _auth.AccessTokenCallCredentials('token') |
||||
call_creds(None, mock_callback) |
||||
self.assertTrue(callback_event.wait(1.0)) |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
unittest.main(verbosity=2) |
Loading…
Reference in new issue