grpc/interceptors/auth/interceptors.go

100 lines
2.4 KiB
Go

package auth
import (
"context"
"crypto/subtle"
"strings"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.linka.cloud/grpc/interceptors"
)
func ChainedAuthFuncs(fn ...grpc_auth.AuthFunc) grpc_auth.AuthFunc {
return func(ctx context.Context) (context.Context, error) {
code := codes.Unauthenticated
for _, v := range fn {
ctx2, err := v(ctx)
if err == nil {
return ctx2, nil
}
s, ok := status.FromError(err)
if !ok {
return ctx2, err
}
if s.Code() == codes.PermissionDenied {
code = codes.PermissionDenied
}
}
return ctx, status.Error(code, code.String())
}
}
func NewServerInterceptors(opts ...Option) interceptors.ServerInterceptors {
o := options{}
for _, v := range opts {
v(&o)
}
return &interceptor{o: o, authFn: ChainedAuthFuncs(o.authFns...)}
}
type interceptor struct {
o options
authFn grpc_auth.AuthFunc
}
func (i *interceptor) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
a := grpc_auth.UnaryServerInterceptor(i.authFn)
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
if i.isNotProtected(info.FullMethod) {
return handler(ctx, req)
}
return a(ctx, req, info, handler)
}
}
func (i *interceptor) StreamServerInterceptor() grpc.StreamServerInterceptor {
a := grpc_auth.StreamServerInterceptor(i.authFn)
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
if i.isNotProtected(info.FullMethod) {
return handler(srv, ss)
}
return a(srv, ss, info, handler)
}
}
func (i *interceptor) isNotProtected(endpoint string) bool {
// default to not ignored
if len(i.o.ignoredMethods) == 0 && len(i.o.methods) == 0 {
return false
}
// endpoint is like /helloworld.Greeter/SayHello
parts := strings.Split(strings.TrimPrefix(endpoint, "/"), "/")
// invalid endpoint format
if len(parts) != 2 {
return false
}
method := parts[1]
for _, v := range i.o.ignoredMethods {
if v == method {
return true
}
}
if len(i.o.methods) == 0 {
return false
}
for _, v := range i.o.methods {
if v == method {
return false
}
}
return true
}
func Equals(s1, s2 string) bool {
return subtle.ConstantTimeCompare([]byte(s1), []byte(s2)) == 1
}