1# Copyright 2019 The gRPC Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Server of the Python example of customizing authentication mechanism.""" 15 16from __future__ import absolute_import 17from __future__ import division 18from __future__ import print_function 19 20import argparse 21import contextlib 22import logging 23from concurrent import futures 24 25import grpc 26from examples import helloworld_pb2 27from examples import helloworld_pb2_grpc 28from examples.python.auth import _credentials 29 30_LOGGER = logging.getLogger(__name__) 31_LOGGER.setLevel(logging.INFO) 32 33_LISTEN_ADDRESS_TEMPLATE = 'localhost:%d' 34_SIGNATURE_HEADER_KEY = 'x-signature' 35 36 37class SignatureValidationInterceptor(grpc.ServerInterceptor): 38 39 def __init__(self): 40 41 def abort(ignored_request, context): 42 context.abort(grpc.StatusCode.UNAUTHENTICATED, 'Invalid signature') 43 44 self._abortion = grpc.unary_unary_rpc_method_handler(abort) 45 46 def intercept_service(self, continuation, handler_call_details): 47 # Example HandlerCallDetails object: 48 # _HandlerCallDetails( 49 # method=u'/helloworld.Greeter/SayHello', 50 # invocation_metadata=...) 51 method_name = handler_call_details.method.split('/')[-1] 52 expected_metadata = (_SIGNATURE_HEADER_KEY, method_name[::-1]) 53 if expected_metadata in handler_call_details.invocation_metadata: 54 return continuation(handler_call_details) 55 else: 56 return self._abortion 57 58 59class SimpleGreeter(helloworld_pb2_grpc.GreeterServicer): 60 61 def SayHello(self, request, unused_context): 62 return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) 63 64 65@contextlib.contextmanager 66def run_server(port): 67 # Bind interceptor to server 68 server = grpc.server(futures.ThreadPoolExecutor(), 69 interceptors=(SignatureValidationInterceptor(),)) 70 helloworld_pb2_grpc.add_GreeterServicer_to_server(SimpleGreeter(), server) 71 72 # Loading credentials 73 server_credentials = grpc.ssl_server_credentials((( 74 _credentials.SERVER_CERTIFICATE_KEY, 75 _credentials.SERVER_CERTIFICATE, 76 ),)) 77 78 # Pass down credentials 79 port = server.add_secure_port(_LISTEN_ADDRESS_TEMPLATE % port, 80 server_credentials) 81 82 server.start() 83 try: 84 yield server, port 85 finally: 86 server.stop(0) 87 88 89def main(): 90 parser = argparse.ArgumentParser() 91 parser.add_argument('--port', 92 nargs='?', 93 type=int, 94 default=50051, 95 help='the listening port') 96 args = parser.parse_args() 97 98 with run_server(args.port) as (server, port): 99 logging.info('Server is listening at port :%d', port) 100 server.wait_for_termination() 101 102 103if __name__ == '__main__': 104 logging.basicConfig(level=logging.INFO) 105 main() 106