///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2019, 2020 Oracle and/or its affiliates.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

///////////////////////////////////////////////////////////////////////////////

= gRPC Server Security
:description: Helidon Security gRPC integration
:keywords: helidon, grpc, security

Security integration of the  <<grpc/01_introduction.adoc,gRPC server>>

== Prerequisites

Declare the following dependency in your project:

[source,xml]
.Maven Dependency
----
<dependency>
    <groupId>io.helidon.security.integration</groupId>
    <artifactId>helidon-security-integration-grpc</artifactId>
</dependency>
----

== Bootstrapping

There are two steps to configure security with gRPC server:

1. Create security instance and register it with server
2. Protect gRPC services of server with various security features

[source,java]
.Example using builders
----
// gRPC server's routing
GrpcRouting.builder()
    // This is step 1 - register security instance with gRPC server processing
    // security - instance of security either from config or from a builder
    // securityDefaults - default enforcement for each service that has a security definition
    .intercept(GrpcSecurity.create(security).securityDefaults(GrpcSecurity.authenticate()))
    // this is step 2 - protect a service
    // register and protect this service with authentication (from defaults) and role "user"
    .register(greetService, GrpcSecurity.rolesAllowed("user"))
    .build();
----

[source,java]
.Example using builders for more fine grained method level security
----
// create the service descriptor
ServiceDescriptor greetService = ServiceDescriptor.builder(new GreetService())
        // Add an instance of gRPC security that will apply to all methods of
        // the service - in this case require the "user" role
        .intercept(GrpcSecurity.rolesAllowed("user"))
        // Add an instance of gRPC security that will apply to the "SetGreeting"
        // method of the service - in this case require the "admin" role
        .intercept("SetGreeting", GrpcSecurity.rolesAllowed("admin"))
        .build();

// Create the gRPC server's routing
GrpcRouting.builder()
    // This is step 1 - register security instance with gRPC server processing
    // security - instance of security either from config or from a builder
    // securityDefaults - default enforcement for each service that has a security definition
    .intercept(GrpcSecurity.create(security).securityDefaults(GrpcSecurity.authenticate()))
    // this is step 2 - add the service descriptor
    .register(greetService)
    .build();
----

[source,java]
.Example using configuration
----
GrpcRouting.builder()
    // helper method to load both security and gRPC server security from configuration
    .intercept(GrpcSecurity.create(config))
    // continue with gRPC server route configuration...
    .register(new GreetService())
    .build();
----

[source,conf]
.Example using configuration - configuration (HOCON)
----
# This may change in the future - to align with gRPC server configuration,
# once it is supported
security
  grpc-server:
    # Configuration of integration with gRPC server
    defaults:
        authenticate: true
    # Configuration security for individual services
    services:
    - name: "GreetService"
      defaults:
      roles-allowed: ["user"]
      # Configuration security for individual methods of the service
      methods:
      - name: "SetGreeting"
        roles-allowed: ["admin"]
----

=== Client security
When using the Helidon SE gRPC client API security can be configured for a gRPC service
or at the individual method level. The client API has a custom `CallCredentials` implementation that
integrates with the Helidon security APIs.

[source,java]
.Example configuring client security for a service
----
Security security = Security.builder()  // <1>
        .addProvider(HttpBasicAuthProvider.create(config.get("http-basic-auth")))
        .build();

GrpcClientSecurity clientSecurity = GrpcClientSecurity.builder(security.createContext("test.client")) // <2>
        .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, user)
        .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, password)
        .build();

ClientServiceDescriptor descriptor = ClientServiceDescriptor // <3>
        .builder(StringService.class)
        .unary("Lower")
        .callCredentials(clientSecurity)                     // <4>
        .build();

GrpcServiceClient client = GrpcServiceClient.create(channel, descriptor); // <5>

String response = client.blockingUnary("Lower", "ABCD"); // <6>
----
<1> Create the Helidon `Security` instance (in this case using the basic auth provider)
<2> Create the `GrpcClientSecurity` gRPC `CallCredentials` adding the user and password
property expected by the basic auth provider.
<3> Create the gRPC `ClientServiceDescriptor` for the `StringService` gRPC service.
<4> Set the `GrpcClientSecurity` instance as the call credentials for all methods of the service
<5> Create a `GrpcServiceClient` that will allow methods to be called on the service
<6> Call the "Lower" method which will use the configured basic auth credentials


[source,java]
.Example configuring client security for a specific method
----
GrpcClientSecurity clientSecurity = GrpcClientSecurity.builder(security.createContext("test.client")) // <1>
        .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, user)
        .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, password)
        .build();

ClientServiceDescriptor descriptor = ClientServiceDescriptor // <2>
        .builder(StringService.class)
        .unary("Lower")
        .unary("Upper", rules -> rules.callCredentials(clientSecurity)) // <3>
        .build();
----
<1> Create the `GrpcClientSecurity` call credentials in the same way as above.
<2> Create the `ClientServiceDescriptor`, this time with two unary methods, "Lower" and "Upper".
<3> The "Upper" method is configured to use the `GrpcClientSecurity` call credentials, the "Lower" method
will be called without any credentials.


=== Outbound security
Outbound security covers three scenarios:

* Calling a secure gRPC service from inside a gRPC service method handler
* Calling a secure gRPC service from inside a web server method handler
* Calling a secure web endpoint from inside a gRPC service method handler

Within each scenario credentials can be propagated if the gRPC/http method
handler is executing within a security context or credentials can be overridden
to provide a different set of credentials to use to call the outbound endpoint.

[source,java]
.Example calling a secure gRPC service from inside a gRPC service method handler
----
// Obtain the SecurityContext from the current gRPC call Context
SecurityContext securityContext = GrpcSecurity.SECURITY_CONTEXT.get();

// Create a gRPC CallCredentials that will use the current request's
// security context to configure outbound credentials
GrpcClientSecurity clientSecurity = GrpcClientSecurity.create(securityContext);

// Create the gRPC stub using the CallCredentials
EchoServiceGrpc.EchoServiceBlockingStub stub = noCredsEchoStub.withCallCredentials(clientSecurity);
----

[source,java]
.Example calling a secure gRPC service from inside a web server method handler
----
private static void propagateCredentialsWebRequest(ServerRequest req, ServerResponse res) {
    try {
        // Create a gRPC CallCredentials that will use the current request's
        // security context to configure outbound credentials
        GrpcClientSecurity clientSecurity = GrpcClientSecurity.create(req);

        // Create the gRPC stub using the CallCredentials
        EchoServiceGrpc.EchoServiceBlockingStub stub = noCredsEchoStub.withCallCredentials(clientSecurity);

        String message = req.queryParams().first("message").orElse(null);
        Echo.EchoResponse echoResponse = stub.echo(Echo.EchoRequest.newBuilder().setMessage(message).build());
        res.send(echoResponse.getMessage());
    } catch (StatusRuntimeException e) {
        res.status(GrpcHelper.toHttpResponseStatus(e)).send();
    } catch (Throwable thrown) {
        res.status(Http.ResponseStatus.create(500, thrown.getMessage())).send();
    }
}
----

[source,java]
.Example calling a secure web endpoint from inside a gRPC service method handler
----
// Obtain the SecurityContext from the gRPC call Context
SecurityContext securityContext = GrpcSecurity.SECURITY_CONTEXT.get();

// Use the SecurityContext as normal to make a http request
Response webResponse = client.target(url)
        .path("/test")
        .request()
        .property(ClientSecurityFeature.PROPERTY_CONTEXT, securityContext)
        .get();
----
