netmaker/controllers/authGrpc.go

167 lines
4.4 KiB
Go
Raw Normal View History

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"
"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"
)
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
}
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
2021-07-22 06:55:19 +08:00
mac, network, err := functions.VerifyToken(authToken)
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)
}
emptynode := models.Node{}
node, err := functions.GetNodeByMacAddress(network, mac)
2021-08-10 23:30:58 +08:00
if database.IsEmptyRecord(err) {
2021-08-11 00:52:22 +08:00
if node, err = functions.GetDeletedNodeByMacAddress(network, mac); err == nil {
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
}
if err != nil || node.MacAddress == emptynode.MacAddress {
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
}
//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
//out := new(LoginResponse)
2021-08-03 06:06:26 +08:00
var reqNode models.Node
if err := json.Unmarshal([]byte(req.Data), &reqNode); err != nil {
return nil, err
}
macaddress := reqNode.MacAddress
network := reqNode.Network
password := reqNode.Password
2021-03-26 00:17:52 +08:00
var result models.NodeAuth
err := errors.New("Generic server error.")
2021-07-22 06:55:19 +08:00
if macaddress == "" {
2021-03-26 00:17:52 +08:00
//TODO: Set Error response
err = errors.New("Missing Mac Address.")
return nil, err
2021-07-22 06:55:19 +08:00
} else if password == "" {
err = errors.New("Missing Password.")
return nil, err
} else {
2021-10-09 03:07:12 +08:00
//Search DB for node with Mac Address. 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
}
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
}
if result.MacAddress == macaddress && result.Network == network {
break
}
}
//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
tokenString, err := functions.CreateJWT(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 == "" {
err = errors.New("Something went wrong. Could not retrieve token.")
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
}