2021-03-26 00:17:52 +08:00
|
|
|
package controller
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-07-22 06:55:19 +08:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2021-08-03 06:06:26 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
"github.com/gravitl/netmaker/database"
|
|
|
|
"github.com/gravitl/netmaker/functions"
|
|
|
|
nodepb "github.com/gravitl/netmaker/grpc"
|
2021-10-27 00:27:29 +08:00
|
|
|
"github.com/gravitl/netmaker/logic"
|
2021-07-22 06:55:19 +08:00
|
|
|
"github.com/gravitl/netmaker/models"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
2021-03-26 00:17:52 +08:00
|
|
|
"google.golang.org/grpc"
|
2021-07-22 06:55:19 +08:00
|
|
|
"google.golang.org/grpc/codes"
|
2021-03-26 00:17:52 +08:00
|
|
|
"google.golang.org/grpc/metadata"
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
)
|
|
|
|
|
2021-12-08 04:51:57 +08:00
|
|
|
// AuthServerUnaryInterceptor - auth unary interceptor logic
|
2021-03-26 00:17:52 +08:00
|
|
|
func AuthServerUnaryInterceptor(ctx context.Context,
|
|
|
|
req interface{},
|
|
|
|
info *grpc.UnaryServerInfo,
|
|
|
|
handler grpc.UnaryHandler) (interface{}, error) {
|
|
|
|
// Skip authorize when GetJWT is requested
|
|
|
|
|
|
|
|
if info.FullMethod != "/node.NodeService/Login" {
|
2021-07-22 06:55:19 +08:00
|
|
|
if info.FullMethod != "/node.NodeService/CreateNode" {
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
err := grpcAuthorize(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-03-26 00:17:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calls the handler
|
|
|
|
h, err := handler(ctx, req)
|
|
|
|
|
|
|
|
return h, err
|
|
|
|
}
|
2021-12-08 04:51:57 +08:00
|
|
|
|
|
|
|
// AuthServerStreamInterceptor - auth stream interceptor
|
2021-03-26 00:17:52 +08:00
|
|
|
func AuthServerStreamInterceptor(
|
2021-07-22 06:55:19 +08:00
|
|
|
srv interface{},
|
|
|
|
stream grpc.ServerStream,
|
|
|
|
info *grpc.StreamServerInfo,
|
|
|
|
handler grpc.StreamHandler,
|
|
|
|
) error {
|
|
|
|
if info.FullMethod == "/node.NodeService/GetPeers" {
|
|
|
|
if err := grpcAuthorize(stream.Context()); err != nil {
|
|
|
|
return err
|
2021-03-26 00:17:52 +08:00
|
|
|
}
|
2021-07-22 06:55:19 +08:00
|
|
|
}
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
// Calls the handler
|
|
|
|
return handler(srv, stream)
|
2021-03-26 00:17:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func grpcAuthorize(ctx context.Context) error {
|
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
md, ok := metadata.FromIncomingContext(ctx)
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
if !ok {
|
|
|
|
return status.Errorf(codes.InvalidArgument, "Retrieving metadata is failed")
|
|
|
|
}
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
authHeader, ok := md["authorization"]
|
|
|
|
if !ok {
|
|
|
|
return status.Errorf(codes.Unauthenticated, "Authorization token is not supplied")
|
|
|
|
}
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
authToken := authHeader[0]
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2022-02-02 10:50:11 +08:00
|
|
|
nodeID, _, network, err := logic.VerifyToken(authToken)
|
2021-07-22 06:55:19 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
networkexists, err := functions.NetworkExists(network)
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
if err != nil {
|
|
|
|
return status.Errorf(codes.Unauthenticated, "Unauthorized. Network does not exist: "+network)
|
|
|
|
}
|
2022-02-02 10:50:11 +08:00
|
|
|
node, err := logic.GetNodeByID(nodeID)
|
2021-08-10 23:30:58 +08:00
|
|
|
if database.IsEmptyRecord(err) {
|
2022-01-11 06:52:21 +08:00
|
|
|
// == DELETE replace logic after 2 major version updates ==
|
|
|
|
if node, err = logic.GetDeletedNodeByID(node.ID); err == nil {
|
2021-08-11 00:52:22 +08:00
|
|
|
if functions.RemoveDeletedNode(node.ID) {
|
|
|
|
return status.Errorf(codes.Unauthenticated, models.NODE_DELETE)
|
2021-08-10 10:31:01 +08:00
|
|
|
}
|
|
|
|
return status.Errorf(codes.Unauthenticated, "Node does not exist.")
|
|
|
|
}
|
2021-08-11 00:52:22 +08:00
|
|
|
return status.Errorf(codes.Unauthenticated, "Empty record")
|
2021-08-10 23:30:58 +08:00
|
|
|
}
|
2022-01-19 23:44:00 +08:00
|
|
|
if err != nil || node.ID == "" {
|
2021-07-22 06:55:19 +08:00
|
|
|
return status.Errorf(codes.Unauthenticated, "Node does not exist.")
|
|
|
|
}
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
if !networkexists {
|
|
|
|
return status.Errorf(codes.Unauthenticated, "Network does not exist.")
|
|
|
|
}
|
2021-08-11 00:52:22 +08:00
|
|
|
return nil
|
2021-03-26 00:17:52 +08:00
|
|
|
}
|
|
|
|
|
2021-12-11 10:09:42 +08:00
|
|
|
// Login - node authenticates using its password and retrieves a JWT for authorization.
|
2021-08-03 06:06:26 +08:00
|
|
|
func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.Object) (*nodepb.Object, error) {
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2022-02-02 11:36:02 +08:00
|
|
|
var reqNode, err = getNodeFromRequestData(req.Data)
|
2022-01-12 08:01:20 +08:00
|
|
|
if err != nil {
|
2021-08-03 06:06:26 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-01-11 06:52:21 +08:00
|
|
|
nodeID := reqNode.ID
|
2021-08-03 06:06:26 +08:00
|
|
|
network := reqNode.Network
|
|
|
|
password := reqNode.Password
|
2022-01-11 06:52:21 +08:00
|
|
|
macaddress := reqNode.MacAddress
|
2021-03-26 00:17:52 +08:00
|
|
|
|
|
|
|
var result models.NodeAuth
|
|
|
|
|
2022-01-11 06:52:21 +08:00
|
|
|
if nodeID == "" {
|
2021-03-26 00:17:52 +08:00
|
|
|
//TODO: Set Error response
|
2022-01-11 06:52:21 +08:00
|
|
|
err = errors.New("missing node ID")
|
2021-03-26 00:17:52 +08:00
|
|
|
return nil, err
|
2021-07-22 06:55:19 +08:00
|
|
|
} else if password == "" {
|
2021-12-11 10:09:42 +08:00
|
|
|
err = errors.New("missing password")
|
2021-07-22 06:55:19 +08:00
|
|
|
return nil, err
|
|
|
|
} else {
|
2022-01-12 08:01:20 +08:00
|
|
|
//Search DB for node with ID. Ignore pending nodes (they should not be able to authenticate with API until approved).
|
2021-07-22 06:55:19 +08:00
|
|
|
collection, err := database.FetchRecords(database.NODES_TABLE_NAME)
|
2021-04-07 21:39:40 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-09 00:01:44 +08:00
|
|
|
var found = false
|
2021-07-22 06:55:19 +08:00
|
|
|
for _, value := range collection {
|
|
|
|
if err = json.Unmarshal([]byte(value), &result); err != nil {
|
|
|
|
continue // finish going through nodes
|
|
|
|
}
|
2022-01-11 06:52:21 +08:00
|
|
|
if result.ID == nodeID && result.Network == network {
|
2022-02-09 00:01:44 +08:00
|
|
|
found = true
|
2021-07-22 06:55:19 +08:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-09 00:01:44 +08:00
|
|
|
if !found {
|
|
|
|
deletedNode, err := database.FetchRecord(database.DELETED_NODES_TABLE_NAME, nodeID)
|
|
|
|
if err != nil {
|
|
|
|
err = errors.New("node not found")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err = json.Unmarshal([]byte(deletedNode), &result); err != nil {
|
|
|
|
err = errors.New("node data corrupted")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
//compare password from request to stored password in database
|
|
|
|
//might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text...
|
|
|
|
//TODO: Consider a way of hashing the password client side before sending, or using certificates
|
|
|
|
err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(password))
|
|
|
|
if err != nil && result.Password != password {
|
|
|
|
return nil, err
|
|
|
|
} else {
|
|
|
|
//Create a new JWT for the node
|
2022-01-11 06:52:21 +08:00
|
|
|
tokenString, err := logic.CreateJWT(result.ID, macaddress, result.Network)
|
2021-03-26 00:17:52 +08:00
|
|
|
|
2021-07-22 06:55:19 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if tokenString == "" {
|
2021-12-11 10:09:42 +08:00
|
|
|
err = errors.New("something went wrong, could not retrieve token")
|
2021-07-22 06:55:19 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-08-03 06:06:26 +08:00
|
|
|
response := &nodepb.Object{
|
|
|
|
Data: tokenString,
|
|
|
|
Type: nodepb.ACCESS_TOKEN,
|
2021-03-26 00:17:52 +08:00
|
|
|
}
|
2021-07-22 06:55:19 +08:00
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
}
|
2021-03-26 00:17:52 +08:00
|
|
|
}
|