Newer
Older
package server
import (
"context"
rbacInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
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
userService rbacInterfaces.UserService
roleService rbacInterfaces.RoleService
}
// NewAuthInterceptor receives a JWTManager and a rbacMand returns a new AuthInterceptor provding gRPC Interceptor functionality.
func NewAuthInterceptor(
jwtManager *rbac.JWTManager,
userService rbacInterfaces.UserService,
roleService rbacInterfaces.RoleService,
) *AuthInterceptor {
return &AuthInterceptor{
jwtManager: jwtManager,
userService: userService,
roleService: roleService,
// 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) {
return handler(ctx, req)
case *apb.LogoutRequest:
return handler(ctx, req)
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
if len(md["authorize"]) > 0 {
claims, err := auth.jwtManager.GetClaimsFromToken(token)
if err != nil {
return err
}
if time.Now().Unix() > claims.ExpiresAt {
return status.Errorf(codes.PermissionDenied, "token expired at %v, please login", time.Unix(claims.ExpiresAt, 0))
}
user, err := auth.userService.Get(store.Query{Name: claims.Username})
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
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 := auth.roleService.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")
}