Newer
Older
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/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
// AuthInterceptor provides an AuthInterceptor
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 {
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
}
foundRoleInStorage := false
for _, storedRole := range storedRoles {
if userRole == storedRole.Name() {
foundRoleInStorage = true
err := auth.compareRequestedPermissionWithRolePermissions(requestedMethod, storedRole.GetPermissions())
if err != nil {
return err
}
}
if foundRoleInStorage {
return nil
}
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")
}