| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 | package controllerimport (	"errors"	"context"        "golang.org/x/crypto/bcrypt"        "time"        nodepb "github.com/gravitl/netmaker/grpc"        "github.com/gravitl/netmaker/models"        "github.com/gravitl/netmaker/functions"        "github.com/gravitl/netmaker/mongoconn"        "go.mongodb.org/mongo-driver/bson"	"google.golang.org/grpc"	"google.golang.org/grpc/metadata"	"google.golang.org/grpc/status"	"google.golang.org/grpc/codes")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" {	if info.FullMethod != "/node.NodeService/CreateNode" {		err := grpcAuthorize(ctx)		if err != nil {			return nil, err		}	}	}	// Calls the handler	h, err := handler(ctx, req)	return h, err}func AuthServerStreamInterceptor(		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			}		}		// Calls the handler		return handler(srv, stream)}func grpcAuthorize(ctx context.Context) error {		md, ok := metadata.FromIncomingContext(ctx)		if !ok {			return status.Errorf(codes.InvalidArgument, "Retrieving metadata is failed")		}		authHeader, ok := md["authorization"]		if !ok {			return status.Errorf(codes.Unauthenticated, "Authorization token is not supplied")		}		authToken := authHeader[0]		mac, network, err := functions.VerifyToken(authToken)		if err  != nil { return err }                networkexists, err := functions.NetworkExists(network)		if err != nil {			return status.Errorf(codes.Unauthenticated, "Unauthorized. Network does not exist: " + network)		}		emptynode := models.Node{}		node, err := functions.GetNodeByMacAddress(network, mac)		if err != nil || node == emptynode {                        return status.Errorf(codes.Unauthenticated, "Node does not exist.")		}                //check that the request is for a valid network                //if (networkCheck && !networkexists) || err != nil {                if (!networkexists) {			return status.Errorf(codes.Unauthenticated, "Network does not exist.")                } else {                        return nil                }}//Node authenticates using its password and retrieves a JWT for authorization.func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.LoginRequest) (*nodepb.LoginResponse, error) {	//out := new(LoginResponse)	macaddress := req.GetMacaddress()	network := req.GetNetwork()	password := req.GetPassword()	var result models.NodeAuth	err := errors.New("Generic server error.")        if macaddress == "" {		//TODO: Set Error  response		err = errors.New("Missing Mac Address.")		return nil, err        } else if password == "" {                err = errors.New("Missing Password.")                return nil, err        } else {            //Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API untill approved).            collection := mongoconn.Client.Database("netmaker").Collection("nodes")            ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	    var err = collection.FindOne(ctx, bson.M{ "macaddress": macaddress, "network": network}).Decode(&result)            defer cancel()            if err != nil {                return nil, err            }           //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)		if err != nil {			return nil, err		}                if tokenString == "" {		    err = errors.New("Something went wrong. Could not retrieve token.")                    return nil, err                }		response := &nodepb.LoginResponse{				Accesstoken: tokenString,			}		return response, nil        }    }}
 |