github.com/koneal2013/storymetadatagenerator@v0.0.0-20230222212341-b170f254daa5/internal/server/grpc.go (about) 1 package server 2 3 import ( 4 "context" 5 "encoding/json" 6 "time" 7 8 grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware" 9 grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/auth" 10 grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" 11 grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" 12 "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" 13 "go.opentelemetry.io/otel" 14 otel_codes "go.opentelemetry.io/otel/codes" 15 "go.opentelemetry.io/otel/trace" 16 "go.uber.org/zap" 17 "go.uber.org/zap/zapcore" 18 "google.golang.org/grpc" 19 "google.golang.org/grpc/codes" 20 "google.golang.org/grpc/credentials" 21 peer2 "google.golang.org/grpc/peer" 22 "google.golang.org/grpc/status" 23 24 metadata_api_v1 "github.com/koneal2013/storymetadatagenerator/api/v1" 25 grpc_api "github.com/koneal2013/storymetadatagenerator/api/v1/grpc" 26 ) 27 28 const ( 29 objectWildCard = "*" 30 getStoryMetadataAction = "GetStoryMetadata" 31 ) 32 33 type Authorizer interface { 34 Authorize(subject, object, action string) error 35 } 36 37 type GrpcConfig struct { 38 Authorizer 39 } 40 41 func NewGRPCServer(config *GrpcConfig, opts ...grpc.ServerOption) (*grpc.Server, error) { 42 logger := zap.L().Named("grpc_server") 43 zapOpts := []grpc_zap.Option{ 44 grpc_zap.WithDurationField( 45 func(duration time.Duration) zapcore.Field { 46 return zap.Int64("grpc.time_ns", duration.Nanoseconds()) 47 }), 48 } 49 opts = append(opts, grpc.StreamInterceptor( 50 grpcmiddleware.ChainStreamServer( 51 grpc_ctxtags.StreamServerInterceptor(), 52 grpc_zap.StreamServerInterceptor(logger, zapOpts...), 53 grpcauth.StreamServerInterceptor(authenticate), 54 otelgrpc.StreamServerInterceptor(), 55 )), grpc.UnaryInterceptor(grpcmiddleware.ChainUnaryServer( 56 grpc_zap.UnaryServerInterceptor(logger, zapOpts...), 57 grpcauth.UnaryServerInterceptor(authenticate), 58 otelgrpc.UnaryServerInterceptor(), 59 ))) 60 gsrv := grpc.NewServer(opts...) 61 if srv, err := newGrpcServer(config); err != nil { 62 return nil, err 63 } else { 64 grpc_api.RegisterStorymetadataServer(gsrv, srv) 65 return gsrv, nil 66 } 67 } 68 69 type grpcServer struct { 70 grpc_api.UnimplementedStorymetadataServer 71 *GrpcConfig 72 grpcTracer trace.Tracer 73 } 74 75 func (g grpcServer) GetMetadata(ctx context.Context, request *grpc_api.GetStoryMetadataRequest) (*grpc_api.GetStoryMetadataResponse, error) { 76 _, span := g.grpcTracer.Start(ctx, "GetMetadata") 77 defer span.End() 78 if err := g.Authorizer.Authorize(subject(ctx), objectWildCard, getStoryMetadataAction); err != nil { 79 span.RecordError(err) 80 span.SetStatus(otel_codes.Error, err.Error()) 81 return nil, err 82 } 83 storyMetadata := metadata_api_v1.New(int(request.NumberOfPages)) 84 storyMetadata.LoadStories(ctx) 85 storiesBytes, err := json.Marshal(storyMetadata.Stories) 86 if err != nil { 87 return nil, err 88 } 89 errsBytes, err := json.Marshal(storyMetadata.Errs) 90 if err != nil { 91 return nil, err 92 } 93 return &grpc_api.GetStoryMetadataResponse{ 94 Stories: storiesBytes, 95 Errs: errsBytes, 96 }, nil 97 } 98 99 func newGrpcServer(config *GrpcConfig) (srv *grpcServer, err error) { 100 srv = &grpcServer{ 101 GrpcConfig: config, 102 grpcTracer: otel.GetTracerProvider().Tracer("GrpcTracer"), 103 } 104 return srv, nil 105 } 106 107 func authenticate(ctx context.Context) (context.Context, error) { 108 if peer, ok := peer2.FromContext(ctx); !ok { 109 return ctx, status.New(codes.Unknown, "couldn't find peer info").Err() 110 } else if peer.AuthInfo == nil { 111 return context.WithValue(ctx, subjectContextKey{}, ""), nil 112 } else { 113 tlsInfo := peer.AuthInfo.(credentials.TLSInfo) 114 subject := tlsInfo.State.VerifiedChains[0][0].Subject.CommonName 115 ctx = context.WithValue(ctx, subjectContextKey{}, subject) 116 return ctx, nil 117 } 118 } 119 120 type subjectContextKey struct{} 121 122 func subject(ctx context.Context) string { 123 return ctx.Value(subjectContextKey{}).(string) 124 }