diff --git a/examples/python/auth/BUILD.bazel b/examples/python/auth/BUILD.bazel index 6e748d55abe..7bb4203f93f 100644 --- a/examples/python/auth/BUILD.bazel +++ b/examples/python/auth/BUILD.bazel @@ -13,7 +13,7 @@ # limitations under the License. filegroup( - name = "_credentails_files", + name = "_credentials_files", testonly = 1, srcs = [ "credentials/localhost.key", @@ -26,13 +26,13 @@ py_library( name = "_credentials", testonly = 1, srcs = ["_credentials.py"], - data = [":_credentails_files"], + data = [":_credentials_files"], ) py_binary( - name = "customize_auth_client", + name = "customized_auth_client", testonly = 1, - srcs = ["customize_auth_client.py"], + srcs = ["customized_auth_client.py"], deps = [ ":_credentials", "//src/python/grpcio/grpc:grpcio", @@ -41,9 +41,9 @@ py_binary( ) py_binary( - name = "customize_auth_server", + name = "customized_auth_server", testonly = 1, - srcs = ["customize_auth_server.py"], + srcs = ["customized_auth_server.py"], deps = [ ":_credentials", "//src/python/grpcio/grpc:grpcio", @@ -58,8 +58,8 @@ py_test( deps = [ "//src/python/grpcio/grpc:grpcio", "//examples:py_helloworld", - ":customize_auth_client", - ":customize_auth_server", + ":customized_auth_client", + ":customized_auth_server", ":_credentials", ], ) diff --git a/examples/python/auth/README.md b/examples/python/auth/README.md index a13f829b68a..2fd044b8a30 100644 --- a/examples/python/auth/README.md +++ b/examples/python/auth/README.md @@ -2,7 +2,9 @@ ## Check Our Guide First -For most common usage of authentication in gRPC Python, please see our [Authentication](https://grpc.io/docs/guides/auth/) guide's Python section, it includes: +For most common usage of authentication in gRPC Python, please see our +[Authentication](https://grpc.io/docs/guides/auth/) guide's Python section. The +Guide includes following scenarios: 1. Server SSL credential setup 2. Client SSL credential setup @@ -13,11 +15,14 @@ Also, the guide talks about gRPC specific credential types. ### Channel credentials -Channel credentials are attached to a `Channel` object, the most common use case are SSL credentials. +Channel credentials are attached to a `Channel` object, the most common use case +are SSL credentials. ### Call credentials -Call credentials are attached to a `Call` object (corresponding to an RPC). Under the hood, the call credentials is a function that takes in information of the RPC and modify metadata through callback. +Call credentials are attached to a `Call` object (corresponding to an RPC). +Under the hood, the call credentials is a function that takes in information of +the RPC and modify metadata through callback. ## About This Example @@ -28,7 +33,16 @@ This example focuses on extending gRPC authentication mechanism: ## AuthMetadataPlugin: Manipulate metadata for each call -Unlike TLS/SSL based authentication, the authentication extension in gRPC Python lives in a much higher level of abstraction. It relies on the transmit of metadata (HTTP Header) between client and server. gRPC Python provides a way to intercept an RPC and append authentication related metadata through [`AuthMetadataPlugin`](https://grpc.github.io/grpc/python/grpc.html#grpc.AuthMetadataPlugin). +Unlike TLS/SSL based authentication, the authentication extension in gRPC Python +lives at a much higher level of networking. It relies on the transmission of +metadata (HTTP Header) between client and server, instead of alternating the +transport protocol. + +gRPC Python provides a way to intercept an RPC and append authentication related +metadata through +[`AuthMetadataPlugin`](https://grpc.github.io/grpc/python/grpc.html#grpc.AuthMetadataPlugin). +Those in need of a custom authentication method may simply provide a concrete +implementation of the following interface: ```Python class AuthMetadataPlugin: @@ -46,3 +60,53 @@ class AuthMetadataPlugin: synchronously or asynchronously. """ ``` + +Then pass the instance of the concrete implementation to +`grpc.metadata_call_credentials` function to be converted into a +`CallCredentials` object. Please NOTE that it is possible to pass a Python +function object directly, but we recommend to inherit from the base class to +ensure implementation correctness. + + +```Python +def metadata_call_credentials(metadata_plugin, name=None): + """Construct CallCredentials from an AuthMetadataPlugin. + + Args: + metadata_plugin: An AuthMetadataPlugin to use for authentication. + name: An optional name for the plugin. + + Returns: + A CallCredentials. + """ +``` + +The `CallCredentials` object can be passed directly into an RPC like: + +```Python +call_credentials = grpc.metadata_call_credentials(my_foo_plugin) +stub.FooRpc(request, credentials=call_credentials) +``` + +Or you can use `ChannelCredentials` and `CallCredentials` at the same time by +combining them: + +```Python +channel_credentials = ... +call_credentials = ... +composite_credentials = grpc.composite_channel_credentials( + channel_credential, + call_credentials) +channel = grpc.secure_channel(server_address, composite_credentials) +``` + +It is also possible to apply multiple `CallCredentials` to a single RPC: + +```Python +call_credentials_foo = ... +call_credentials_bar = ... +call_credentials = grpc.composite_call_credentials( + call_credentials_foo, + call_credentials_bar) +stub.FooRpc(request, credentials=call_credentials) +``` diff --git a/examples/python/auth/customize_auth_client.py b/examples/python/auth/customized_auth_client.py similarity index 98% rename from examples/python/auth/customize_auth_client.py rename to examples/python/auth/customized_auth_client.py index 324f629caf1..c9ba7c70074 100644 --- a/examples/python/auth/customize_auth_client.py +++ b/examples/python/auth/customized_auth_client.py @@ -32,7 +32,7 @@ _LOGGER.setLevel(logging.INFO) _ONE_DAY_IN_SECONDS = 60 * 60 * 24 _SERVER_ADDR_TEMPLATE = 'localhost:%d' -_SIGNATURE_HEADER_KEY = 'x-signautre' +_SIGNATURE_HEADER_KEY = 'x-signature' class AuthGateway(grpc.AuthMetadataPlugin): @@ -96,7 +96,7 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument( '--port', - nargs=1, + nargs='?', type=int, default=50051, help='the address of server') diff --git a/examples/python/auth/customize_auth_server.py b/examples/python/auth/customized_auth_server.py similarity index 92% rename from examples/python/auth/customize_auth_server.py rename to examples/python/auth/customized_auth_server.py index 23805ae7c3e..915c74a7510 100644 --- a/examples/python/auth/customize_auth_server.py +++ b/examples/python/auth/customized_auth_server.py @@ -34,7 +34,7 @@ _LOGGER.setLevel(logging.INFO) _ONE_DAY_IN_SECONDS = 60 * 60 * 24 _LISTEN_ADDRESS_TEMPLATE = 'localhost:%d' -_SIGNATURE_HEADER_KEY = 'x-signautre' +_SIGNATURE_HEADER_KEY = 'x-signature' class SignatureValidationInterceptor(grpc.ServerInterceptor): @@ -61,8 +61,7 @@ class SignatureValidationInterceptor(grpc.ServerInterceptor): class SimpleGreeter(helloworld_pb2_grpc.GreeterServicer): - @staticmethod - def SayHello(request, unused_context): + def SayHello(self, request, unused_context): return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name) @@ -86,19 +85,21 @@ def run_server(port): _credentials.SERVER_CERTIFICATE, ),)) - # Pass down credentails + # Pass down credentials port = server.add_secure_port(_LISTEN_ADDRESS_TEMPLATE % port, server_credentials) server.start() - yield port - server.stop(0) + try: + yield port + finally: + server.stop(0) def main(): parser = argparse.ArgumentParser() parser.add_argument( - '--port', nargs=1, type=int, default=50051, help='the listening port') + '--port', nargs='?', type=int, default=50051, help='the listening port') args = parser.parse_args() with run_server(args.port) as port: