/*
 *
 * Copyright 2020 gRPC authors.
 *
 * 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.
 *
 */

// The server demonstrates how to use the credential reloading feature in
// advancedtls to serve mTLS connections from the client.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"net"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
	"google.golang.org/grpc/keepalive"
	"google.golang.org/grpc/security/advancedtls"
	"google.golang.org/grpc/security/advancedtls/testdata"

	pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

var port = ":50051"

// Intervals that set to monitor the credential updates.
const credRefreshingInterval = 1 * time.Minute

type greeterServer struct {
	pb.UnimplementedGreeterServer
}

// sayHello is a simple implementation of the pb.GreeterServer SayHello method.
func (greeterServer) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
	flag.Parse()
	fmt.Printf("server starting on port %s...\n", port)

	identityOptions := pemfile.Options{
		CertFile:        testdata.Path("server_cert_1.pem"),
		KeyFile:         testdata.Path("server_key_1.pem"),
		RefreshDuration: credRefreshingInterval,
	}
	identityProvider, err := pemfile.NewProvider(identityOptions)
	if err != nil {
		log.Fatalf("pemfile.NewProvider(%v) failed: %v", identityOptions, err)
	}
	defer identityProvider.Close()
	rootOptions := pemfile.Options{
		RootFile:        testdata.Path("server_trust_cert_1.pem"),
		RefreshDuration: credRefreshingInterval,
	}
	rootProvider, err := pemfile.NewProvider(rootOptions)
	if err != nil {
		log.Fatalf("pemfile.NewProvider(%v) failed: %v", rootOptions, err)
	}
	defer rootProvider.Close()

	// Start a server and create a client using advancedtls API with Provider.
	options := &advancedtls.Options{
		IdentityOptions: advancedtls.IdentityCertificateOptions{
			IdentityProvider: identityProvider,
		},
		RootOptions: advancedtls.RootCertificateOptions{
			RootProvider: rootProvider,
		},
		RequireClientCert: true,
		AdditionalPeerVerification: func(params *advancedtls.HandshakeVerificationInfo) (*advancedtls.PostHandshakeVerificationResults, error) {
			// This message is to show the certificate under the hood is actually reloaded.
			fmt.Printf("Client common name: %s.\n", params.Leaf.Subject.CommonName)
			return &advancedtls.PostHandshakeVerificationResults{}, nil
		},
		VerificationType: advancedtls.CertVerification,
	}
	serverTLSCreds, err := advancedtls.NewServerCreds(options)
	if err != nil {
		log.Fatalf("advancedtls.NewServerCreds(%v) failed: %v", options, err)
	}
	s := grpc.NewServer(grpc.Creds(serverTLSCreds), grpc.KeepaliveParams(keepalive.ServerParameters{
		// Set the max connection time to be 0.5 s to force the client to
		// re-establish the connection, and hence re-invoke the verification
		// callback.
		MaxConnectionAge: 500 * time.Millisecond,
	}))
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	pb.RegisterGreeterServer(s, greeterServer{})
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
