Skip to content
Snippets Groups Projects
auth_interceptor.go 3.88 KiB
Newer Older
  • Learn to ignore specific revisions
  • package server
    
    import (
    	"context"
    
    
    	csbipb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi"
    
    	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
    	"code.fbi.h-da.de/danet/gosdn/controller/rbac"
    
    	"code.fbi.h-da.de/danet/gosdn/controller/store"
    
    	"google.golang.org/grpc"
    
    	"google.golang.org/grpc/codes"
    	"google.golang.org/grpc/metadata"
    	"google.golang.org/grpc/status"
    
    // AuthInterceptor provides an AuthInterceptor
    
    type AuthInterceptor struct {
    
    	jwtManager *rbac.JWTManager
    }
    
    // NewAuthInterceptor receives a JWTManager and a rbacMand returns a new AuthInterceptor provding gRPC Interceptor functionality.
    func NewAuthInterceptor(jwtManager *rbac.JWTManager) *AuthInterceptor {
    	return &AuthInterceptor{
    		jwtManager: jwtManager,
    	}
    
    // Unary returns a unary interceptor function to authenticate and authorize unary RPC calls
    func (auth *AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
    
    	return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
    
    		switch r := req.(type) {
    		case *apb.LoginRequest:
    
    			return handler(ctx, req)
    
    		case *apb.LogoutRequest:
    			return handler(ctx, req)
    		case *apb.CreateUsersRequest:
    			if len(r.User) < 2 {
    				return handler(ctx, req)
    			}
    
    			err := auth.authorize(ctx, info.FullMethod)
    			if err != nil {
    				return nil, err
    			}
    
    		case *csbipb.Syn:
    			return handler(ctx, req)
    
    		default:
    			err := auth.authorize(ctx, info.FullMethod)
    			if err != nil {
    				return nil, err
    			}
    		}
    
    
    		return handler(ctx, req)
    	}
    }
    
    
    // Stream returns a server interceptor function to authorize stream RPC calls
    func (auth *AuthInterceptor) Stream() grpc.StreamServerInterceptor {
    	return func(
    		srv interface{},
    		stream grpc.ServerStream,
    		info *grpc.StreamServerInfo,
    		handler grpc.StreamHandler,
    	) error {
    
    		err := auth.authorize(stream.Context(), info.FullMethod)
    		if err != nil {
    			return err
    		}
    
    		return handler(srv, stream)
    	}
    }
    
    func (auth *AuthInterceptor) authorize(ctx context.Context, method string) error {
    	md, ok := metadata.FromIncomingContext(ctx)
    	if !ok {
    		return status.Errorf(codes.Unauthenticated, "metadata is not provided")
    	}
    
    	// validate token and check permission here
    	token := ""
    	if len(md["authorize"]) > 0 {
    		token = md["authorize"][0]
    
    		claims, err := auth.jwtManager.VerifyToken(token)
    		if err != nil {
    			return err
    		}
    
    		user, err := userc.Get(store.Query{Name: claims.Username})
    		if err != nil {
    			return err
    		}
    
    		if user.GetToken() != token {
    			return status.Errorf(codes.PermissionDenied, "invalid token")
    		}
    
    		err = auth.verifyPermisisonForRequestedCall(user.GetRoles(), method)
    		if err != nil {
    			return err
    		}
    	} else {
    		return status.Errorf(codes.PermissionDenied, "no auth token provided")
    	}
    
    	return nil
    }
    
    func (auth *AuthInterceptor) verifyPermisisonForRequestedCall(userRoles map[string]string, requestedMethod string) error {
    	for _, userRole := range userRoles {
    		err := auth.verifyUserRoleAndRequestedCall(userRole, requestedMethod)
    		if err != nil {
    			return err
    		}
    	}
    
    	return nil
    }
    
    func (auth *AuthInterceptor) verifyUserRoleAndRequestedCall(userRole, requestedMethod string) error {
    	storedRoles, err := rolec.GetAll()
    	if err != nil {
    		return err
    	}
    
    
    	for _, storedRole := range storedRoles {
    		if userRole == storedRole.Name() {
    
    			err := auth.compareRequestedPermissionWithRolePermissions(requestedMethod, storedRole.GetPermissions())
    			if err != nil {
    				return err
    			}
    		}
    
    	return status.Errorf(codes.PermissionDenied, "wrong permissions")
    
    }
    
    func (auth *AuthInterceptor) compareRequestedPermissionWithRolePermissions(requestedMethod string, storedRolePermissions []string) error {
    	for _, permission := range storedRolePermissions {
    		if permission == requestedMethod {
    			return nil
    		}
    	}
    
    	return status.Errorf(codes.PermissionDenied, "user not authorized for this call")
    }