| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 | package controllerimport (	"context"	"fmt"	"log"	"time"	"github.com/go-playground/validator/v10"	"github.com/gravitl/netmaker/functions"	"github.com/gravitl/netmaker/models"	"github.com/gravitl/netmaker/mongoconn"	"github.com/gravitl/netmaker/servercfg"	"github.com/gravitl/netmaker/serverctl"	"go.mongodb.org/mongo-driver/bson"	"go.mongodb.org/mongo-driver/mongo/options"	"golang.org/x/crypto/bcrypt")func GetPeersList(networkName string) ([]models.PeersResponse, error) {	var peers []models.PeersResponse	//Connection mongoDB with mongoconn class	collection := mongoconn.Client.Database("netmaker").Collection("nodes")	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	//Get all nodes in the relevant network which are NOT in pending state	filter := bson.M{"network": networkName, "ispending": false}	cur, err := collection.Find(ctx, filter)	if err != nil {		return peers, err	}	// Close the cursor once finished and cancel if it takes too long	defer cancel()	for cur.Next(context.TODO()) {		var peer models.PeersResponse		err := cur.Decode(&peer)		if err != nil {			log.Fatal(err)		}		// add the node to our node array		//maybe better to just return this? But then that's just GetNodes...		peers = append(peers, peer)	}	//Uh oh, fatal error! This needs some better error handling	//TODO: needs appropriate error handling so the server doesnt shut down.	if err := cur.Err(); err != nil {		log.Fatal(err)	}	return peers, err}func GetExtPeersList(networkName string, macaddress string) ([]models.ExtPeersResponse, error) {        var peers []models.ExtPeersResponse        //Connection mongoDB with mongoconn class        collection := mongoconn.Client.Database("netmaker").Collection("extclients")        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)        //Get all nodes in the relevant network which are NOT in pending state	filter := bson.M{"network": networkName, "ingressgatewayid": macaddress}        cur, err := collection.Find(ctx, filter)        if err != nil {                return peers, err        }        // Close the cursor once finished and cancel if it takes too long        defer cancel()        for cur.Next(context.TODO()) {                var peer models.ExtPeersResponse                err := cur.Decode(&peer)                if err != nil {                        log.Fatal(err)                }                // add the node to our node array                //maybe better to just return this? But then that's just GetNodes...                peers = append(peers, peer)        }        //Uh oh, fatal error! This needs some better error handling        //TODO: needs appropriate error handling so the server doesnt shut down.        if err := cur.Err(); err != nil {                log.Fatal(err)        }        return peers, err}func ValidateNodeCreate(networkName string, node models.Node) error {	v := validator.New()	_ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {		var isFieldUnique bool = functions.IsFieldUnique(networkName, "macaddress", node.MacAddress)		return isFieldUnique	})	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {		_, err := node.GetNetwork()		return err == nil	})        _ = v.RegisterValidation("in_charset", func(fl validator.FieldLevel) bool {                isgood := functions.NameInNodeCharSet(node.Name)                return isgood        })	err := v.Struct(node)	if err != nil {		for _, e := range err.(validator.ValidationErrors) {			fmt.Println(e)		}	}	return err}func ValidateNodeUpdate(networkName string, node models.NodeUpdate) error {	v := validator.New()	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {		_, err := node.GetNetwork()		return err == nil	})        _ = v.RegisterValidation("in_charset", func(fl validator.FieldLevel) bool {                isgood := functions.NameInNodeCharSet(node.Name)                return isgood        })	err := v.Struct(node)	if err != nil {		for _, e := range err.(validator.ValidationErrors) {			fmt.Println(e)		}	}	return err}func UpdateNode(nodechange models.NodeUpdate, node models.Node) (models.Node, error) {	//Question: Is there a better way  of doing  this than a bunch of "if" statements? probably...	//Eventually, lets have a better way to check if any of the fields are filled out...	queryMac := node.MacAddress	queryNetwork := node.Network	notifynetwork := false	if nodechange.Address != "" {		node.Address = nodechange.Address		notifynetwork = true	}	if nodechange.Address6 != "" {		node.Address6 = nodechange.Address6		notifynetwork = true	}	if nodechange.Name != "" {		node.Name = nodechange.Name	}	if nodechange.LocalAddress != "" {		node.LocalAddress = nodechange.LocalAddress	}	if nodechange.ListenPort != 0 {		node.ListenPort = nodechange.ListenPort	}	if nodechange.ExpirationDateTime != 0 {		node.ExpirationDateTime = nodechange.ExpirationDateTime	}	if nodechange.PostDown != "" {		node.PostDown = nodechange.PostDown	}	if nodechange.Interface != "" {		node.Interface = nodechange.Interface	}	if nodechange.PostUp != "" {		node.PostUp = nodechange.PostUp	}	if nodechange.AccessKey != "" {		node.AccessKey = nodechange.AccessKey	}	if nodechange.Endpoint != "" {		node.Endpoint = nodechange.Endpoint		notifynetwork = true	}	if nodechange.SaveConfig != nil {		node.SaveConfig = nodechange.SaveConfig	}	if nodechange.PersistentKeepalive != 0 {		node.PersistentKeepalive = nodechange.PersistentKeepalive	}	if nodechange.Password != "" {		err := bcrypt.CompareHashAndPassword([]byte(nodechange.Password), []byte(node.Password))		if err != nil && nodechange.Password != node.Password {			hash, err := bcrypt.GenerateFromPassword([]byte(nodechange.Password), 5)			if err != nil {				return node, err			}			nodechange.Password = string(hash)			node.Password = nodechange.Password		}	}	if nodechange.MacAddress != "" {		node.MacAddress = nodechange.MacAddress	}	if nodechange.PublicKey != "" {		node.PublicKey = nodechange.PublicKey		node.KeyUpdateTimeStamp = time.Now().Unix()		notifynetwork = true	}	//collection := mongoconn.ConnectDB()	collection := mongoconn.Client.Database("netmaker").Collection("nodes")	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	// Create filter	filter := bson.M{"macaddress": queryMac, "network": queryNetwork}	node.SetLastModified()	// prepare update model.	update := bson.D{		{"$set", bson.D{			{"address", node.Address},			{"address6", node.Address6},			{"name", node.Name},			{"password", node.Password},			{"listenport", node.ListenPort},			{"publickey", node.PublicKey},			{"keyupdatetimestamp", node.KeyUpdateTimeStamp},			{"expdatetime", node.ExpirationDateTime},			{"endpoint", node.Endpoint},			{"postup", node.PostUp},			{"postdown", node.PostDown},			{"macaddress", node.MacAddress},			{"localaddress", node.LocalAddress},			{"persistentkeepalive", node.PersistentKeepalive},			{"saveconfig", node.SaveConfig},			{"accesskey", node.AccessKey},			{"interface", node.Interface},			{"lastmodified", node.LastModified},		}},	}	var nodeupdate models.Node	errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)	if errN != nil {		return nodeupdate, errN	}	if nodechange.MacAddress != "" {		queryMac = nodechange.MacAddress	}	returnnode, errN := GetNode(queryMac, queryNetwork)	defer cancel()	if notifynetwork {		errN = SetNetworkNodesLastModified(queryNetwork)	}	if servercfg.IsDNSMode() {		errN = SetDNS()	}	return returnnode, errN}func DeleteNode(macaddress string, network string) (bool, error) {	deleted := false	collection := mongoconn.Client.Database("netmaker").Collection("nodes")	filter := bson.M{"macaddress": macaddress, "network": network}	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	result, err := collection.DeleteOne(ctx, filter)	deletecount := result.DeletedCount	if deletecount > 0 {		deleted = true	}	defer cancel()	err = SetNetworkNodesLastModified(network)	fmt.Println("Deleted node " + macaddress + " from network " + network)	if servercfg.IsDNSMode() {		err = SetDNS()	}	return deleted, err}func DeleteIntClient(clientid string) (bool, error) {        deleted := false        collection := mongoconn.Client.Database("netmaker").Collection("intclients")        filter := bson.M{"clientid": clientid}        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)        result, err := collection.DeleteOne(ctx, filter)        deletecount := result.DeletedCount        if deletecount > 0 {                deleted = true        }        defer cancel()	err = serverctl.ReconfigureServerWireGuard()        return deleted, err}func GetNode(macaddress string, network string) (models.Node, error) {	var node models.Node	collection := mongoconn.Client.Database("netmaker").Collection("nodes")	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	filter := bson.M{"macaddress": macaddress, "network": network}	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&node)	defer cancel()	return node, err}func GetIntClient(clientid string) (models.IntClient, error) {        var client models.IntClient        collection := mongoconn.Client.Database("netmaker").Collection("intclients")        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)        filter := bson.M{"clientid": clientid}        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&clientid)        defer cancel()        return client, err}func CreateNode(node models.Node, networkName string) (models.Node, error) {	//encrypt that password so we never see it again	hash, err := bcrypt.GenerateFromPassword([]byte(node.Password), 5)	if err != nil {		return node, err	}	//set password to encrypted password	node.Password = string(hash)	node.Network = networkName	//node.SetDefaults()	//Umm, why am I doing this again?	//TODO: Why am I using a local function instead of the struct function? I really dont know.	//I think I thought it didn't work but uhhh...idk	node.SetDefaults()	//Another DB call here...Inefficient	//Anyways, this scrolls through all the IP Addresses in the network range and checks against nodes	//until one is open and then returns it	node.Address, err = functions.UniqueAddress(networkName)	if err != nil {		return node, err	}	node.Address6, err = functions.UniqueAddress6(networkName)	if err != nil {		return node, err	}	//IDK why these aren't a part of "set defaults. Pretty dumb.	//TODO: This is dumb. Consolidate and fix.	node.SetLastModified()	node.SetDefaultName()	node.SetLastCheckIn()	node.SetLastPeerUpdate()	node.KeyUpdateTimeStamp = time.Now().Unix()	//Create a JWT for the node	tokenString, _ := functions.CreateJWT(node.MacAddress, networkName)	if tokenString == "" {		//returnErrorResponse(w, r, errorResponse)		return node, err	}	// connect db	collection := mongoconn.Client.Database("netmaker").Collection("nodes")	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	// insert our node to the node db.	result, err := collection.InsertOne(ctx, node)	_ = result	defer cancel()	if err != nil {		return node, err	}	//return response for if node  is pending	if !node.IsPending {		functions.DecrimentKey(node.Network, node.AccessKey)	}	SetNetworkNodesLastModified(node.Network)	if servercfg.IsDNSMode() {		err = SetDNS()	}	return node, err}func NodeCheckIn(node models.Node, networkName string) (models.CheckInResponse, error) {	var response models.CheckInResponse	parentnetwork, err := functions.GetParentNetwork(networkName)	if err != nil {		err = fmt.Errorf("%w; Couldnt retrieve Network "+networkName+": ", err)		return response, err	}	parentnode, err := functions.GetNodeByMacAddress(networkName, node.MacAddress)	if err != nil {		err = fmt.Errorf("%w; Couldnt Get Node "+node.MacAddress, err)		return response, err	}	if parentnode.IsPending {		err = fmt.Errorf("%w; Node checking in is still pending: "+node.MacAddress, err)		response.IsPending = true		return response, err	}	networklm := parentnetwork.NetworkLastModified	peerslm := parentnetwork.NodesLastModified	gkeyupdate := parentnetwork.KeyUpdateTimeStamp	nkeyupdate := parentnode.KeyUpdateTimeStamp	peerlistlm := parentnode.LastPeerUpdate	parentnodelm := parentnode.LastModified	parentnodelastcheckin := parentnode.LastCheckIn	if parentnodelastcheckin < parentnodelm {		response.NeedConfigUpdate = true	}	if parentnodelm < networklm {		response.NeedConfigUpdate = true	}	if peerlistlm < peerslm {		response.NeedPeerUpdate = true	}	if nkeyupdate < gkeyupdate {		response.NeedKeyUpdate = true	}	if time.Now().Unix() > parentnode.ExpirationDateTime {		response.NeedDelete = true		_, err = DeleteNode(node.MacAddress, networkName)	} else {		err = TimestampNode(parentnode, true, false, false)		if err != nil {			err = fmt.Errorf("%w; Couldnt Timestamp Node: ", err)			return response, err		}	}	response.Success = true	return response, err}func SetNetworkNodesLastModified(networkName string) error {	timestamp := time.Now().Unix()	collection := mongoconn.Client.Database("netmaker").Collection("networks")	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	// Create filter	filter := bson.M{"netid": networkName}	// prepare update model.	update := bson.D{		{"$set", bson.D{			{"nodeslastmodified", timestamp},		}},	}	result := collection.FindOneAndUpdate(ctx, filter, update)	defer cancel()	if result.Err() != nil {		return result.Err()	}	return nil}func TimestampNode(node models.Node, updatecheckin bool, updatepeers bool, updatelm bool) error {	if updatelm {		node.SetLastModified()	}	if updatecheckin {		node.SetLastCheckIn()	}	if updatepeers {		node.SetLastPeerUpdate()	}	collection := mongoconn.Client.Database("netmaker").Collection("nodes")	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)	// Create filter	filter := bson.M{"macaddress": node.MacAddress, "network": node.Network}	// prepare update model.	update := bson.D{		{"$set", bson.D{			{"lastmodified", node.LastModified},			{"lastpeerupdate", node.LastPeerUpdate},			{"lastcheckin", node.LastCheckIn},		}},	}	var nodeupdate models.Node	err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)	defer cancel()	return err}
 |