Browse Source

Merge branch 'develop' of https://github.com/gravitl/netmaker into develop

afeiszli 4 years ago
parent
commit
896784192c

+ 1 - 2
.github/workflows/test.yml

@@ -19,5 +19,4 @@ jobs:
         uses: actions/checkout@v2
         uses: actions/checkout@v2
       - name: run tests
       - name: run tests
         run: |
         run: |
-            cd test
-            go test . -v
+            go test -p 1 ./... -v

+ 28 - 123
controllers/common.go

@@ -4,17 +4,16 @@ import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
 	"log"
 	"log"
-	"net"
 	"time"
 	"time"
 
 
+	"github.com/go-playground/validator/v10"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/functions"
-	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mongoconn"
 	"github.com/gravitl/netmaker/mongoconn"
+	"github.com/gravitl/netmaker/servercfg"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/mongo/options"
 	"go.mongodb.org/mongo-driver/mongo/options"
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/bcrypt"
-	"gopkg.in/go-playground/validator.v9"
 )
 )
 
 
 func GetPeersList(networkName string) ([]models.PeersResponse, error) {
 func GetPeersList(networkName string) ([]models.PeersResponse, error) {
@@ -60,63 +59,32 @@ func GetPeersList(networkName string) ([]models.PeersResponse, error) {
 }
 }
 
 
 func ValidateNodeCreate(networkName string, node models.Node) error {
 func ValidateNodeCreate(networkName string, node models.Node) error {
-
 	v := validator.New()
 	v := validator.New()
-	_ = v.RegisterValidation("address_check", func(fl validator.FieldLevel) bool {
-		isIpv4 := functions.IsIpNet(node.Address)
-		empty := node.Address == ""
-		return (empty || isIpv4)
-	})
-        _ = v.RegisterValidation("address6_check", func(fl validator.FieldLevel) bool {
-                isIpv6 := functions.IsIpNet(node.Address6)
-                empty := node.Address6 == ""
-                return (empty || isIpv6)
-        })
-	_ = v.RegisterValidation("endpoint_check", func(fl validator.FieldLevel) bool {
-		//var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
-		isIp := functions.IsIpNet(node.Endpoint)
-		notEmptyCheck := node.Endpoint != ""
-		return (notEmptyCheck && isIp)
-	})
-	_ = v.RegisterValidation("localaddress_check", func(fl validator.FieldLevel) bool {
-		//var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
-		isIp := functions.IsIpNet(node.LocalAddress)
-		empty := node.LocalAddress == ""
-		return (empty || isIp)
-	})
-
 	_ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
 	_ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
 		var isFieldUnique bool = functions.IsFieldUnique(networkName, "macaddress", node.MacAddress)
 		var isFieldUnique bool = functions.IsFieldUnique(networkName, "macaddress", node.MacAddress)
 		return isFieldUnique
 		return isFieldUnique
 	})
 	})
-
-	_ = v.RegisterValidation("macaddress_valid", func(fl validator.FieldLevel) bool {
-		_, err := net.ParseMAC(node.MacAddress)
+	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
+		_, err := node.GetNetwork()
 		return err == nil
 		return err == nil
 	})
 	})
+	err := v.Struct(node)
 
 
-	_ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
-		isvalid := functions.NameInNodeCharSet(node.Name)
-		return isvalid
-	})
+	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 {
 	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
 		_, err := node.GetNetwork()
 		_, err := node.GetNetwork()
 		return err == nil
 		return err == nil
 	})
 	})
-	_ = v.RegisterValidation("pubkey_check", func(fl validator.FieldLevel) bool {
-		notEmptyCheck := node.PublicKey != ""
-		isBase64 := functions.IsBase64(node.PublicKey)
-		return (notEmptyCheck && isBase64)
-	})
-	_ = v.RegisterValidation("password_check", func(fl validator.FieldLevel) bool {
-		notEmptyCheck := node.Password != ""
-		goodLength := len(node.Password) > 5
-		return (notEmptyCheck && goodLength)
-	})
-
 	err := v.Struct(node)
 	err := v.Struct(node)
-
 	if err != nil {
 	if err != nil {
 		for _, e := range err.(validator.ValidationErrors) {
 		for _, e := range err.(validator.ValidationErrors) {
 			fmt.Println(e)
 			fmt.Println(e)
@@ -125,71 +93,7 @@ func ValidateNodeCreate(networkName string, node models.Node) error {
 	return err
 	return err
 }
 }
 
 
-func ValidateNodeUpdate(networkName string, node models.Node) error {
-
-        v := validator.New()
-        _ = v.RegisterValidation("address_check", func(fl validator.FieldLevel) bool {
-                isIpv4 := functions.IsIpNet(node.Address)
-                empty := node.Address == ""
-                return (empty || isIpv4)
-        })
-        _ = v.RegisterValidation("address6_check", func(fl validator.FieldLevel) bool {
-                isIpv6 := functions.IsIpNet(node.Address6)
-                empty := node.Address6 == ""
-                return (empty || isIpv6)
-        })
-        _ = v.RegisterValidation("endpoint_check", func(fl validator.FieldLevel) bool {
-                //var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
-                isIp := functions.IsIpNet(node.Address)
-		empty := node.Endpoint == ""
-                return (empty || isIp)
-        })
-        _ = v.RegisterValidation("localaddress_check", func(fl validator.FieldLevel) bool {
-                //var isFieldUnique bool = functions.IsFieldUnique(networkName, "endpoint", node.Endpoint)
-                isIp := functions.IsIpNet(node.LocalAddress)
-                empty := node.LocalAddress == ""
-                return (empty || isIp )
-        })
-        _ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
-                return true
-        })
-
-        _ = v.RegisterValidation("macaddress_valid", func(fl validator.FieldLevel) bool {
-                _, err := net.ParseMAC(node.MacAddress)
-                return err == nil
-        })
-
-        _ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
-                isvalid := functions.NameInNodeCharSet(node.Name)
-                return isvalid
-        })
-
-        _ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
-                _, err := node.GetNetwork()
-                return err == nil
-        })
-        _ = v.RegisterValidation("pubkey_check", func(fl validator.FieldLevel) bool {
-                empty := node.PublicKey == ""
-                isBase64 := functions.IsBase64(node.PublicKey)
-                return (empty || isBase64)
-        })
-        _ = v.RegisterValidation("password_check", func(fl validator.FieldLevel) bool {
-                empty := node.Password == ""
-                goodLength := len(node.Password) > 5
-                return (empty || goodLength)
-        })
-
-        err := v.Struct(node)
-
-        if err != nil {
-                for _, e := range err.(validator.ValidationErrors) {
-                        fmt.Println(e)
-                }
-        }
-        return err
-}
-
-func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
+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...
 	//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...
 	//Eventually, lets have a better way to check if any of the fields are filled out...
 	queryMac := node.MacAddress
 	queryMac := node.MacAddress
@@ -200,10 +104,10 @@ func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
 		node.Address = nodechange.Address
 		node.Address = nodechange.Address
 		notifynetwork = true
 		notifynetwork = true
 	}
 	}
-        if nodechange.Address6 != "" {
-                node.Address6 = nodechange.Address6
-                notifynetwork = true
-        }
+	if nodechange.Address6 != "" {
+		node.Address6 = nodechange.Address6
+		notifynetwork = true
+	}
 	if nodechange.Name != "" {
 	if nodechange.Name != "" {
 		node.Name = nodechange.Name
 		node.Name = nodechange.Name
 	}
 	}
@@ -307,9 +211,9 @@ func UpdateNode(nodechange models.Node, node models.Node) (models.Node, error) {
 	if notifynetwork {
 	if notifynetwork {
 		errN = SetNetworkNodesLastModified(queryNetwork)
 		errN = SetNetworkNodesLastModified(queryNetwork)
 	}
 	}
-        if servercfg.IsDNSMode() {
+	if servercfg.IsDNSMode() {
 		errN = SetDNS()
 		errN = SetDNS()
-        }
+	}
 
 
 	return returnnode, errN
 	return returnnode, errN
 }
 }
@@ -336,9 +240,9 @@ func DeleteNode(macaddress string, network string) (bool, error) {
 
 
 	err = SetNetworkNodesLastModified(network)
 	err = SetNetworkNodesLastModified(network)
 	fmt.Println("Deleted node " + macaddress + " from network " + network)
 	fmt.Println("Deleted node " + macaddress + " from network " + network)
-        if servercfg.IsDNSMode() {
-                err = SetDNS()
-        }
+	if servercfg.IsDNSMode() {
+		err = SetDNS()
+	}
 
 
 	return deleted, err
 	return deleted, err
 }
 }
@@ -386,10 +290,11 @@ func CreateNode(node models.Node, networkName string) (models.Node, error) {
 		return node, err
 		return node, err
 	}
 	}
 
 
-        node.Address6, err = functions.UniqueAddress6(networkName)
+	node.Address6, err = functions.UniqueAddress6(networkName)
+
 	if err != nil {
 	if err != nil {
-                return node, err
-        }
+		return node, err
+	}
 
 
 	//IDK why these aren't a part of "set defaults. Pretty dumb.
 	//IDK why these aren't a part of "set defaults. Pretty dumb.
 	//TODO: This is dumb. Consolidate and fix.
 	//TODO: This is dumb. Consolidate and fix.

+ 534 - 0
controllers/common_test.go

@@ -0,0 +1,534 @@
+package controller
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"github.com/stretchr/testify/assert"
+	"go.mongodb.org/mongo-driver/bson"
+)
+
+type NodeValidationTC struct {
+	testname     string
+	node         models.Node
+	errorMessage string
+}
+
+type NodeValidationUpdateTC struct {
+	testname     string
+	node         models.NodeUpdate
+	errorMessage string
+}
+
+func createTestNode(t *testing.T) models.Node {
+	createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"}
+	node, err := CreateNode(createnode, "skynet")
+	assert.Nil(t, err)
+	return node
+}
+
+func TestCreateNode(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	createnode := models.Node{PublicKey: "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.1", MacAddress: "01:02:03:04:05:06", Password: "password", Network: "skynet"}
+	err := ValidateNodeCreate("skynet", createnode)
+	assert.Nil(t, err)
+	node, err := CreateNode(createnode, "skynet")
+	assert.Nil(t, err)
+	assert.Equal(t, "10.0.0.1", node.Endpoint)
+	assert.Equal(t, "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", node.PublicKey)
+	assert.Equal(t, "01:02:03:04:05:06", node.MacAddress)
+	assert.Equal(t, int32(51821), node.ListenPort)
+	assert.NotNil(t, node.Name)
+	assert.Equal(t, "skynet", node.Network)
+	assert.Equal(t, "nm-skynet", node.Interface)
+}
+func TestDeleteNode(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	node := createTestNode(t)
+	t.Run("NodeExists", func(t *testing.T) {
+		deleted, err := DeleteNode(node.MacAddress, node.Network)
+		assert.Nil(t, err)
+		assert.True(t, deleted)
+	})
+	t.Run("NonExistantNode", func(t *testing.T) {
+		deleted, err := DeleteNode(node.MacAddress, node.Network)
+		assert.Nil(t, err)
+		assert.False(t, deleted)
+	})
+}
+func TestGetNode(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	node := createTestNode(t)
+	t.Run("NodeExists", func(t *testing.T) {
+		response, err := GetNode(node.MacAddress, node.Network)
+		assert.Nil(t, err)
+		assert.Equal(t, "10.0.0.1", response.Endpoint)
+		assert.Equal(t, "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", response.PublicKey)
+		assert.Equal(t, "01:02:03:04:05:06", response.MacAddress)
+		assert.Equal(t, int32(51821), response.ListenPort)
+		assert.NotNil(t, response.Name)
+		assert.Equal(t, "skynet", response.Network)
+		assert.Equal(t, "nm-skynet", response.Interface)
+	})
+	t.Run("BadMac", func(t *testing.T) {
+		response, err := GetNode("01:02:03:04:05:07", node.Network)
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, response)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("BadNetwork", func(t *testing.T) {
+		response, err := GetNode(node.MacAddress, "badnet")
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, response)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("NoNode", func(t *testing.T) {
+		_, _ = DeleteNode("01:02:03:04:05:06", "skynet")
+		response, err := GetNode(node.MacAddress, node.Network)
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, response)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+
+}
+func TestGetPeerList(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	_ = createTestNode(t)
+	//createnode := models.Node{PublicKey: "RM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.2", MacAddress: "02:02:03:04:05:06", Password: "password", Network: "skynet"}
+	//_, _ = CreateNode(createnode, "skynet")
+	t.Run("PeerExist", func(t *testing.T) {
+		peers, err := GetPeersList("skynet")
+		assert.Nil(t, err)
+		assert.NotEqual(t, []models.PeersResponse(nil), peers)
+		t.Log(peers)
+	})
+	t.Run("NoNodes", func(t *testing.T) {
+		_, _ = DeleteNode("01:02:03:04:05:06", "skynet")
+		peers, err := GetPeersList("skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, []models.PeersResponse(nil), peers)
+		t.Log(peers)
+	})
+}
+func TestNodeCheckIn(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	node := createTestNode(t)
+	time.Sleep(time.Second * 1)
+	expectedResponse := models.CheckInResponse{false, false, false, false, false, "", false}
+	t.Run("BadNet", func(t *testing.T) {
+		response, err := NodeCheckIn(node, "badnet")
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Couldnt retrieve Network badnet: ")
+		assert.Equal(t, expectedResponse, response)
+	})
+	t.Run("BadNode", func(t *testing.T) {
+		badnode := models.Node{PublicKey: "RM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.2", MacAddress: "02:02:03:04:05:06", Password: "password", Network: "skynet"}
+		response, err := NodeCheckIn(badnode, "skynet")
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Couldnt Get Node 02:02:03:04:05:06")
+		assert.Equal(t, expectedResponse, response)
+	})
+	t.Run("NoUpdatesNeeded", func(t *testing.T) {
+		expectedResponse := models.CheckInResponse{true, false, false, false, false, "", false}
+		response, err := NodeCheckIn(node, node.Network)
+		assert.Nil(t, err)
+		assert.Equal(t, expectedResponse, response)
+	})
+	t.Run("NodePending", func(t *testing.T) {
+		//		create Pending Node
+		createnode := models.Node{PublicKey: "RM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=", Endpoint: "10.0.0.2", MacAddress: "01:02:03:04:05:07", Password: "password", Network: "skynet", IsPending: true}
+		pendingNode, _ := CreateNode(createnode, "skynet")
+		expectedResponse.IsPending = true
+		response, err := NodeCheckIn(pendingNode, "skynet")
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Node checking in is still pending: 01:02:03:04:05:07")
+		assert.Equal(t, expectedResponse, response)
+	})
+	t.Run("ConfigUpdateRequired", func(t *testing.T) {
+		err := TimestampNode(node, false, false, true)
+		assert.Nil(t, err)
+		expectedResponse.NeedConfigUpdate = true
+		expectedResponse.Success = true
+		response, err := NodeCheckIn(node, "skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, true, response.Success)
+		assert.Equal(t, true, response.NeedConfigUpdate)
+	})
+	t.Run("PeerUpdateRequired", func(t *testing.T) {
+		var nodeUpdate models.NodeUpdate
+		newtime := time.Now().Add(time.Hour * -24).Unix()
+		nodeUpdate.LastPeerUpdate = newtime
+		_, err := UpdateNode(nodeUpdate, node)
+		assert.Nil(t, err)
+		response, err := NodeCheckIn(node, "skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, true, response.Success)
+		assert.Equal(t, true, response.NeedPeerUpdate)
+	})
+	t.Run("KeyUpdateRequired", func(t *testing.T) {
+		var network models.Network
+		newtime := time.Now().Add(time.Hour * 24).Unix()
+		t.Log(newtime, time.Now().Unix())
+		//this is cheating; but can't find away to update timestamp through existing api
+		collection := mongoconn.Client.Database("netmaker").Collection("networks")
+		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+		filter := bson.M{"netid": "skynet"}
+		update := bson.D{
+			{"$set", bson.D{
+				{"keyupdatetimestamp", newtime},
+			}},
+		}
+		defer cancel()
+		err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
+		assert.Nil(t, err)
+		response, err := NodeCheckIn(node, "skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, true, response.Success)
+		assert.Equal(t, true, response.NeedKeyUpdate)
+	})
+	t.Run("DeleteNeeded", func(t *testing.T) {
+		var nodeUpdate models.NodeUpdate
+		newtime := time.Now().Add(time.Hour * -24).Unix()
+		nodeUpdate.ExpirationDateTime = newtime
+		_, err := UpdateNode(nodeUpdate, node)
+		assert.Nil(t, err)
+		response, err := NodeCheckIn(node, "skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, true, response.Success)
+		assert.Equal(t, true, response.NeedDelete)
+	})
+}
+
+func TestSetNetworkNodesLastModified(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	t.Run("InvalidNetwork", func(t *testing.T) {
+		err := SetNetworkNodesLastModified("badnet")
+		assert.NotNil(t, err)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("NetworkExists", func(t *testing.T) {
+		err := SetNetworkNodesLastModified("skynet")
+		assert.Nil(t, err)
+	})
+}
+func TestTimestampNode(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	node := createTestNode(t)
+	time.Sleep(time.Second * 1)
+	before, err := GetNode(node.MacAddress, node.Network)
+	assert.Nil(t, err)
+	t.Run("UpdateCheckIn", func(t *testing.T) {
+		err = TimestampNode(node, true, false, false)
+		assert.Nil(t, err)
+		after, err := GetNode(node.MacAddress, node.Network)
+		assert.Nil(t, err)
+		assert.Greater(t, after.LastCheckIn, before.LastCheckIn)
+	})
+	t.Run("UpdatePeers", func(t *testing.T) {
+		err = TimestampNode(node, false, true, false)
+		assert.Nil(t, err)
+		after, err := GetNode(node.MacAddress, node.Network)
+		assert.Nil(t, err)
+		assert.Greater(t, after.LastPeerUpdate, before.LastPeerUpdate)
+	})
+	t.Run("UpdateLastModified", func(t *testing.T) {
+		err = TimestampNode(node, false, false, true)
+		assert.Nil(t, err)
+		after, err := GetNode(node.MacAddress, node.Network)
+		assert.Nil(t, err)
+		assert.Greater(t, after.LastModified, before.LastModified)
+	})
+	t.Run("InvalidNode", func(t *testing.T) {
+		node.MacAddress = "01:02:03:04:05:08"
+		err = TimestampNode(node, true, true, true)
+		assert.NotNil(t, err)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+
+}
+func TestUpdateNode(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	node := createTestNode(t)
+	var update models.NodeUpdate
+	update.MacAddress = "01:02:03:04:05:06"
+	update.Name = "helloworld"
+	newnode, err := UpdateNode(update, node)
+	assert.Nil(t, err)
+	assert.Equal(t, update.Name, newnode.Name)
+}
+
+func TestValidateNodeCreate(t *testing.T) {
+	cases := []NodeValidationTC{
+		//		NodeValidationTC{
+		//			testname: "EmptyAddress",
+		//			node: models.Node{
+		//				Address: "",
+		//			},
+		//			errorMessage: "Field validation for 'Endpoint' failed on the 'address_check' tag",
+		//		},
+		NodeValidationTC{
+			testname: "BadAddress",
+			node: models.Node{
+				Address: "256.0.0.1",
+			},
+			errorMessage: "Field validation for 'Address' failed on the 'ipv4' tag",
+		},
+		NodeValidationTC{
+			testname: "BadAddress6",
+			node: models.Node{
+				Address6: "2607::abcd:efgh::1",
+			},
+			errorMessage: "Field validation for 'Address6' failed on the 'ipv6' tag",
+		},
+		NodeValidationTC{
+			testname: "BadLocalAddress",
+			node: models.Node{
+				LocalAddress: "10.0.200.300",
+			},
+			errorMessage: "Field validation for 'LocalAddress' failed on the 'ip' tag",
+		},
+		NodeValidationTC{
+			testname: "InvalidName",
+			node: models.Node{
+				Name: "mynode*",
+			},
+			errorMessage: "Field validation for 'Name' failed on the 'alphanum' tag",
+		},
+		NodeValidationTC{
+			testname: "NameTooLong",
+			node: models.Node{
+				Name: "mynodexmynode",
+			},
+			errorMessage: "Field validation for 'Name' failed on the 'max' tag",
+		},
+		NodeValidationTC{
+			testname: "ListenPortMin",
+			node: models.Node{
+				ListenPort: 1023,
+			},
+			errorMessage: "Field validation for 'ListenPort' failed on the 'min' tag",
+		},
+		NodeValidationTC{
+			testname: "ListenPortMax",
+			node: models.Node{
+				ListenPort: 65536,
+			},
+			errorMessage: "Field validation for 'ListenPort' failed on the 'max' tag",
+		},
+		NodeValidationTC{
+			testname: "PublicKeyEmpty",
+			node: models.Node{
+				PublicKey: "",
+			},
+			errorMessage: "Field validation for 'PublicKey' failed on the 'required' tag",
+		},
+		NodeValidationTC{
+			testname: "PublicKeyInvalid",
+			node: models.Node{
+				PublicKey: "junk%key",
+			},
+			errorMessage: "Field validation for 'PublicKey' failed on the 'base64' tag",
+		},
+		NodeValidationTC{
+			testname: "EndpointInvalid",
+			node: models.Node{
+				Endpoint: "10.2.0.300",
+			},
+			errorMessage: "Field validation for 'Endpoint' failed on the 'ip' tag",
+		},
+		NodeValidationTC{
+			testname: "EndpointEmpty",
+			node: models.Node{
+				Endpoint: "",
+			},
+			errorMessage: "Field validation for 'Endpoint' failed on the 'required' tag",
+		},
+		NodeValidationTC{
+			testname: "PersistentKeepaliveMax",
+			node: models.Node{
+				PersistentKeepalive: 1001,
+			},
+			errorMessage: "Field validation for 'PersistentKeepalive' failed on the 'max' tag",
+		},
+		NodeValidationTC{
+			testname: "MacAddressInvalid",
+			node: models.Node{
+				MacAddress: "01:02:03:04:05",
+			},
+			errorMessage: "Field validation for 'MacAddress' failed on the 'mac' tag",
+		},
+		NodeValidationTC{
+			testname: "MacAddressMissing",
+			node: models.Node{
+				MacAddress: "",
+			},
+			errorMessage: "Field validation for 'MacAddress' failed on the 'required' tag",
+		},
+		NodeValidationTC{
+			testname: "EmptyPassword",
+			node: models.Node{
+				Password: "",
+			},
+			errorMessage: "Field validation for 'Password' failed on the 'required' tag",
+		},
+		NodeValidationTC{
+			testname: "ShortPassword",
+			node: models.Node{
+				Password: "1234",
+			},
+			errorMessage: "Field validation for 'Password' failed on the 'min' tag",
+		},
+		NodeValidationTC{
+			testname: "NoNetwork",
+			node: models.Node{
+				Network: "badnet",
+			},
+			errorMessage: "Field validation for 'Network' failed on the 'network_exists' tag",
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.testname, func(t *testing.T) {
+			err := ValidateNodeCreate("skynet", tc.node)
+			assert.NotNil(t, err)
+			assert.Contains(t, err.Error(), tc.errorMessage)
+		})
+	}
+	t.Run("MacAddresUnique", func(t *testing.T) {
+		createNet()
+		node := models.Node{MacAddress: "01:02:03:04:05:06", Network: "skynet"}
+		_, err := CreateNode(node, "skynet")
+		assert.Nil(t, err)
+		err = ValidateNodeCreate("skynet", node)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'MacAddress' failed on the 'macaddress_unique' tag")
+	})
+	t.Run("EmptyAddress", func(t *testing.T) {
+		node := models.Node{Address: ""}
+		err := ValidateNodeCreate("skynet", node)
+		assert.NotNil(t, err)
+		assert.NotContains(t, err.Error(), "Field validation for 'Address' failed on the 'ipv4' tag")
+	})
+}
+func TestValidateNodeUpdate(t *testing.T) {
+	//cases
+	cases := []NodeValidationUpdateTC{
+		NodeValidationUpdateTC{
+			testname: "BadAddress",
+			node: models.NodeUpdate{
+				Address: "256.0.0.1",
+			},
+			errorMessage: "Field validation for 'Address' failed on the 'ip' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "BadAddress6",
+			node: models.NodeUpdate{
+				Address6: "2607::abcd:efgh::1",
+			},
+			errorMessage: "Field validation for 'Address6' failed on the 'ipv6' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "BadLocalAddress",
+			node: models.NodeUpdate{
+				LocalAddress: "10.0.200.300",
+			},
+			errorMessage: "Field validation for 'LocalAddress' failed on the 'ip' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "InvalidName",
+			node: models.NodeUpdate{
+				Name: "mynode*",
+			},
+			errorMessage: "Field validation for 'Name' failed on the 'alphanum' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "NameTooLong",
+			node: models.NodeUpdate{
+				Name: "mynodexmynode",
+			},
+			errorMessage: "Field validation for 'Name' failed on the 'max' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "ListenPortMin",
+			node: models.NodeUpdate{
+				ListenPort: 1023,
+			},
+			errorMessage: "Field validation for 'ListenPort' failed on the 'min' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "ListenPortMax",
+			node: models.NodeUpdate{
+				ListenPort: 65536,
+			},
+			errorMessage: "Field validation for 'ListenPort' failed on the 'max' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "PublicKeyInvalid",
+			node: models.NodeUpdate{
+				PublicKey: "bad&key",
+			},
+			errorMessage: "Field validation for 'PublicKey' failed on the 'base64' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "EndpointInvalid",
+			node: models.NodeUpdate{
+				Endpoint: "10.2.0.300",
+			},
+			errorMessage: "Field validation for 'Endpoint' failed on the 'ip' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "PersistentKeepaliveMax",
+			node: models.NodeUpdate{
+				PersistentKeepalive: 1001,
+			},
+			errorMessage: "Field validation for 'PersistentKeepalive' failed on the 'max' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "MacAddressInvalid",
+			node: models.NodeUpdate{
+				MacAddress: "01:02:03:04:05",
+			},
+			errorMessage: "Field validation for 'MacAddress' failed on the 'mac' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "MacAddressMissing",
+			node: models.NodeUpdate{
+				MacAddress: "",
+			},
+			errorMessage: "Field validation for 'MacAddress' failed on the 'required' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "ShortPassword",
+			node: models.NodeUpdate{
+				Password: "1234",
+			},
+			errorMessage: "Field validation for 'Password' failed on the 'min' tag",
+		},
+		NodeValidationUpdateTC{
+			testname: "NoNetwork",
+			node: models.NodeUpdate{
+				Network: "badnet",
+			},
+			errorMessage: "Field validation for 'Network' failed on the 'network_exists' tag",
+		},
+	}
+	for _, tc := range cases {
+		t.Run(tc.testname, func(t *testing.T) {
+			err := ValidateNodeUpdate("skynet", tc.node)
+			assert.NotNil(t, err)
+			assert.Contains(t, err.Error(), tc.errorMessage)
+		})
+	}
+
+}

+ 8 - 0
controllers/config/dnsconfig/Corefile

@@ -0,0 +1,8 @@
+skynet  {
+    reload 15s
+    hosts /root/dnsconfig/netmaker.hosts {
+	fallthrough	
+    }
+    forward . 8.8.8.8 8.8.4.4
+    log
+}

+ 1 - 0
controllers/config/dnsconfig/netmaker.hosts

@@ -0,0 +1 @@
+10.0.0.2         myhost.skynet

+ 17 - 0
controllers/config/environments/dev.yaml

@@ -0,0 +1,17 @@
+server:
+  host: "localhost"
+  apiport: "8081"
+  grpcport: "50051"
+  masterkey: "secretkey"
+  allowedorigin: "*"
+  restbackend: true            
+  agentbackend: true
+  defaultnetname: "default"
+  defaultnetrange: "10.10.10.0/24"
+  createdefault: true
+mongoconn:
+  user: "mongoadmin"
+  pass: "mongopass"
+  host: "localhost"
+  port: "27017"
+  opts: '/?authSource=admin'

+ 288 - 288
controllers/dnsHttpController.go

@@ -7,14 +7,15 @@ import (
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
 	"time"
 	"time"
+
+	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
-	"github.com/txn2/txeh"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mongoconn"
 	"github.com/gravitl/netmaker/mongoconn"
+	"github.com/txn2/txeh"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/mongo/options"
 	"go.mongodb.org/mongo-driver/mongo/options"
-	"gopkg.in/go-playground/validator.v9"
 )
 )
 
 
 func dnsHandlers(r *mux.Router) {
 func dnsHandlers(r *mux.Router) {
@@ -32,85 +33,86 @@ func dnsHandlers(r *mux.Router) {
 //Gets all nodes associated with network, including pending nodes
 //Gets all nodes associated with network, including pending nodes
 func getNodeDNS(w http.ResponseWriter, r *http.Request) {
 func getNodeDNS(w http.ResponseWriter, r *http.Request) {
 
 
-        w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Type", "application/json")
 
 
-        var dns []models.DNSEntry
-        var params = mux.Vars(r)
+	var dns []models.DNSEntry
+	var params = mux.Vars(r)
 
 
 	dns, err := GetNodeDNS(params["network"])
 	dns, err := GetNodeDNS(params["network"])
-        if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
-        }
-
-        //Returns all the nodes in JSON format
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(dns)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+
+	//Returns all the nodes in JSON format
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(dns)
 }
 }
 
 
 //Gets all nodes associated with network, including pending nodes
 //Gets all nodes associated with network, including pending nodes
 func getAllDNS(w http.ResponseWriter, r *http.Request) {
 func getAllDNS(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	dns, err := GetAllDNS()
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	//Returns all the nodes in JSON format
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(dns)
+}
 
 
-        w.Header().Set("Content-Type", "application/json")
-
-        var dns []models.DNSEntry
-
+func GetAllDNS() ([]models.DNSEntry, error) {
+	var dns []models.DNSEntry
 	networks, err := functions.ListNetworks()
 	networks, err := functions.ListNetworks()
-        if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
-        }
-
-        for _, net := range networks {
-                netdns, err := GetDNS(net.NetID)
-                if err != nil {
-			returnErrorResponse(w, r, formatError(err, "internal"))
-                        return
-                }
-	        dns = append(dns, netdns...)
-        }
-
-        //Returns all the nodes in JSON format
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(dns)
+	if err != nil {
+		return []models.DNSEntry{}, err
+	}
+	for _, net := range networks {
+		netdns, err := GetDNS(net.NetID)
+		if err != nil {
+			return []models.DNSEntry{}, nil
+		}
+		dns = append(dns, netdns...)
+	}
+	return dns, nil
 }
 }
 
 
+func GetNodeDNS(network string) ([]models.DNSEntry, error) {
 
 
-func GetNodeDNS(network string) ([]models.DNSEntry, error){
+	var dns []models.DNSEntry
 
 
-        var dns []models.DNSEntry
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 
 
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
-        filter := bson.M{"network": network}
+	filter := bson.M{"network": network}
 
 
-        cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
+	cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
 
 
-        if err != nil {
-                return dns, err
-        }
+	if err != nil {
+		return dns, err
+	}
 
 
-        defer cancel()
+	defer cancel()
 
 
-        for cur.Next(context.TODO()) {
+	for cur.Next(context.TODO()) {
 
 
-                var entry models.DNSEntry
+		var entry models.DNSEntry
 
 
-                err := cur.Decode(&entry)
-                if err != nil {
-                        return dns, err
-                }
+		err := cur.Decode(&entry)
+		if err != nil {
+			return dns, err
+		}
 
 
-                // add item our array of nodes
-                dns = append(dns, entry)
-        }
+		// add item our array of nodes
+		dns = append(dns, entry)
+	}
 
 
-        //TODO: Another fatal error we should take care of.
-        if err := cur.Err(); err != nil {
-                return dns, err
-        }
+	//TODO: Another fatal error we should take care of.
+	if err := cur.Err(); err != nil {
+		return dns, err
+	}
 
 
 	return dns, err
 	return dns, err
 }
 }
@@ -118,144 +120,144 @@ func GetNodeDNS(network string) ([]models.DNSEntry, error){
 //Gets all nodes associated with network, including pending nodes
 //Gets all nodes associated with network, including pending nodes
 func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 func getCustomDNS(w http.ResponseWriter, r *http.Request) {
 
 
-        w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Type", "application/json")
 
 
-        var dns []models.DNSEntry
-        var params = mux.Vars(r)
+	var dns []models.DNSEntry
+	var params = mux.Vars(r)
 
 
-        dns, err := GetCustomDNS(params["network"])
-        if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
-        }
+	dns, err := GetCustomDNS(params["network"])
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
 
-        //Returns all the nodes in JSON format
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(dns)
+	//Returns all the nodes in JSON format
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(dns)
 }
 }
 
 
-func GetCustomDNS(network string) ([]models.DNSEntry, error){
+func GetCustomDNS(network string) ([]models.DNSEntry, error) {
 
 
-        var dns []models.DNSEntry
+	var dns []models.DNSEntry
 
 
-        collection := mongoconn.Client.Database("netmaker").Collection("dns")
+	collection := mongoconn.Client.Database("netmaker").Collection("dns")
 
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
-        filter := bson.M{"network": network}
+	filter := bson.M{"network": network}
 
 
-        cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
+	cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
 
 
-        if err != nil {
-                return dns, err
-        }
+	if err != nil {
+		return dns, err
+	}
 
 
-        defer cancel()
+	defer cancel()
 
 
-        for cur.Next(context.TODO()) {
+	for cur.Next(context.TODO()) {
 
 
-                var entry models.DNSEntry
+		var entry models.DNSEntry
 
 
-                err := cur.Decode(&entry)
-                if err != nil {
-                        return dns, err
-                }
+		err := cur.Decode(&entry)
+		if err != nil {
+			return dns, err
+		}
 
 
-                // add item our array of nodes
-                dns = append(dns, entry)
-        }
+		// add item our array of nodes
+		dns = append(dns, entry)
+	}
 
 
-        //TODO: Another fatal error we should take care of.
-        if err := cur.Err(); err != nil {
-                return dns, err
-        }
+	//TODO: Another fatal error we should take care of.
+	if err := cur.Err(); err != nil {
+		return dns, err
+	}
 
 
-        return dns, err
+	return dns, err
 }
 }
 
 
 func SetDNS() error {
 func SetDNS() error {
-        hostfile := txeh.Hosts{}
-        var corefilestring string
-        networks, err := functions.ListNetworks()
-        if err != nil {
-                return err
-        }
-
-        for _, net := range networks {
-                corefilestring = corefilestring + net.NetID + " "
-                dns, err := GetDNS(net.NetID)
-                if err != nil {
-                        return err
-                }
-                for _, entry := range dns {
-                        hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
-                        if err != nil {
-                                return err
-                        }
-                }
-        }
-        if corefilestring == "" {
-                corefilestring = "example.com"
-        }
-
-        err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
-        if err != nil {
-                return err
-        }
-        err = functions.SetCorefile(corefilestring)
-
-        return err
+	hostfile := txeh.Hosts{}
+	var corefilestring string
+	networks, err := functions.ListNetworks()
+	if err != nil {
+		return err
+	}
+
+	for _, net := range networks {
+		corefilestring = corefilestring + net.NetID + " "
+		dns, err := GetDNS(net.NetID)
+		if err != nil {
+			return err
+		}
+		for _, entry := range dns {
+			hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	if corefilestring == "" {
+		corefilestring = "example.com"
+	}
+
+	err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
+	if err != nil {
+		return err
+	}
+	err = functions.SetCorefile(corefilestring)
+
+	return err
 }
 }
 
 
-func GetDNSEntryNum(domain string, network string) (int, error){
+func GetDNSEntryNum(domain string, network string) (int, error) {
 
 
-        num := 0
+	num := 0
 
 
-        entries, err := GetDNS(network)
-        if err != nil {
-                return 0, err
-        }
+	entries, err := GetDNS(network)
+	if err != nil {
+		return 0, err
+	}
 
 
-        for i := 0; i < len(entries); i++ {
+	for i := 0; i < len(entries); i++ {
 
 
-                if domain == entries[i].Name {
-                        num++
-                }
-        }
+		if domain == entries[i].Name {
+			num++
+		}
+	}
 
 
-        return num, nil
+	return num, nil
 }
 }
 
 
 //Gets all nodes associated with network, including pending nodes
 //Gets all nodes associated with network, including pending nodes
 func getDNS(w http.ResponseWriter, r *http.Request) {
 func getDNS(w http.ResponseWriter, r *http.Request) {
 
 
-        w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Type", "application/json")
 
 
-        var dns []models.DNSEntry
-        var params = mux.Vars(r)
+	var dns []models.DNSEntry
+	var params = mux.Vars(r)
 
 
 	dns, err := GetDNS(params["network"])
 	dns, err := GetDNS(params["network"])
-        if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
-        }
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(dns)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(dns)
 }
 }
 
 
 func GetDNS(network string) ([]models.DNSEntry, error) {
 func GetDNS(network string) ([]models.DNSEntry, error) {
 
 
-        var dns []models.DNSEntry
-        dns, err := GetNodeDNS(network)
-        if err != nil {
-                return dns, err
-        }
-        customdns, err := GetCustomDNS(network)
-        if err != nil {
-                return dns, err
-        }
-
-        dns = append(dns, customdns...)
+	var dns []models.DNSEntry
+	dns, err := GetNodeDNS(network)
+	if err != nil {
+		return dns, err
+	}
+	customdns, err := GetCustomDNS(network)
+	if err != nil {
+		return dns, err
+	}
+
+	dns = append(dns, customdns...)
 	return dns, err
 	return dns, err
 }
 }
 
 
@@ -263,7 +265,7 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
 	var entry models.DNSEntry
 	var entry models.DNSEntry
-        var params = mux.Vars(r)
+	var params = mux.Vars(r)
 
 
 	//get node from body of request
 	//get node from body of request
 	_ = json.NewDecoder(r.Body).Decode(&entry)
 	_ = json.NewDecoder(r.Body).Decode(&entry)
@@ -277,10 +279,10 @@ func createDNS(w http.ResponseWriter, r *http.Request) {
 
 
 	entry, err = CreateDNS(entry)
 	entry, err = CreateDNS(entry)
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
+		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
-        w.WriteHeader(http.StatusOK)
+	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(entry)
 	json.NewEncoder(w).Encode(entry)
 }
 }
 
 
@@ -292,9 +294,9 @@ func updateDNS(w http.ResponseWriter, r *http.Request) {
 	var entry models.DNSEntry
 	var entry models.DNSEntry
 
 
 	//start here
 	//start here
-	entry, err := GetDNSEntry(params["domain"],params["network"])
+	entry, err := GetDNSEntry(params["domain"], params["network"])
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "badrequest"))
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
 
 
@@ -303,21 +305,31 @@ func updateDNS(w http.ResponseWriter, r *http.Request) {
 	// we decode our body request params
 	// we decode our body request params
 	err = json.NewDecoder(r.Body).Decode(&dnschange)
 	err = json.NewDecoder(r.Body).Decode(&dnschange)
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "badrequest"))
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
+	//fill in any missing fields
+	if dnschange.Name == "" {
+		dnschange.Name = entry.Name
+	}
+	if dnschange.Network == "" {
+		dnschange.Network = entry.Network
+	}
+	if dnschange.Address == "" {
+		dnschange.Address = entry.Address
+	}
 
 
 	err = ValidateDNSUpdate(dnschange, entry)
 	err = ValidateDNSUpdate(dnschange, entry)
 
 
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "badrequest"))
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
 
 
 	entry, err = UpdateDNS(dnschange, entry)
 	entry, err = UpdateDNS(dnschange, entry)
 
 
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "badrequest"))
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
 
 
@@ -325,101 +337,101 @@ func updateDNS(w http.ResponseWriter, r *http.Request) {
 }
 }
 
 
 func deleteDNS(w http.ResponseWriter, r *http.Request) {
 func deleteDNS(w http.ResponseWriter, r *http.Request) {
-        // Set header
-        w.Header().Set("Content-Type", "application/json")
+	// Set header
+	w.Header().Set("Content-Type", "application/json")
 
 
-        // get params
-        var params = mux.Vars(r)
+	// get params
+	var params = mux.Vars(r)
 
 
-        success, err := DeleteDNS(params["domain"], params["network"])
+	success, err := DeleteDNS(params["domain"], params["network"])
 
 
-        if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
-        } else if !success {
-                returnErrorResponse(w, r, formatError(errors.New("Delete unsuccessful."), "badrequest"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	} else if !success {
+		returnErrorResponse(w, r, formatError(errors.New("Delete unsuccessful."), "badrequest"))
+		return
+	}
 
 
-        json.NewEncoder(w).Encode(params["domain"] + " deleted.")
+	json.NewEncoder(w).Encode(params["domain"] + " deleted.")
 }
 }
 
 
 func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
 func CreateDNS(entry models.DNSEntry) (models.DNSEntry, error) {
 
 
-        // connect db
-        collection := mongoconn.Client.Database("netmaker").Collection("dns")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	// connect db
+	collection := mongoconn.Client.Database("netmaker").Collection("dns")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
-        // insert our node to the node db.
-        _, err := collection.InsertOne(ctx, entry)
+	// insert our node to the node db.
+	_, err := collection.InsertOne(ctx, entry)
 
 
-        defer cancel()
+	defer cancel()
 
 
-        return entry, err
+	return entry, err
 }
 }
 
 
 func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
 func GetDNSEntry(domain string, network string) (models.DNSEntry, error) {
-        var entry models.DNSEntry
+	var entry models.DNSEntry
 
 
-        collection := mongoconn.Client.Database("netmaker").Collection("dns")
+	collection := mongoconn.Client.Database("netmaker").Collection("dns")
 
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
-        filter := bson.M{"name": domain, "network": network}
-        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&entry)
+	filter := bson.M{"name": domain, "network": network}
+	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&entry)
 
 
-        defer cancel()
+	defer cancel()
 
 
-        return entry, err
+	return entry, err
 }
 }
 
 
 func UpdateDNS(dnschange models.DNSEntry, entry models.DNSEntry) (models.DNSEntry, error) {
 func UpdateDNS(dnschange models.DNSEntry, entry models.DNSEntry) (models.DNSEntry, error) {
 
 
-        queryDNS := entry.Name
-
-        if dnschange.Name != "" {
-                entry.Name = dnschange.Name
-        }
-        if dnschange.Address != "" {
-                entry.Address = dnschange.Address
-        }
-        //collection := mongoconn.ConnectDB()
-        collection := mongoconn.Client.Database("netmaker").Collection("dns")
-
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-        // Create filter
-        filter := bson.M{"name": queryDNS}
-
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"name", entry.Name},
-                        {"address", entry.Address},
-                }},
-        }
-        var dnsupdate models.DNSEntry
-
-        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&dnsupdate)
-        if errN != nil {
-                fmt.Println("Could not update: ")
-                fmt.Println(errN)
-        } else {
-                fmt.Println("DNS Entry updated successfully.")
-        }
-
-        defer cancel()
-
-        return dnsupdate, errN
+	queryDNS := entry.Name
+
+	if dnschange.Name != "" {
+		entry.Name = dnschange.Name
+	}
+	if dnschange.Address != "" {
+		entry.Address = dnschange.Address
+	}
+	//collection := mongoconn.ConnectDB()
+	collection := mongoconn.Client.Database("netmaker").Collection("dns")
+
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+	// Create filter
+	filter := bson.M{"name": queryDNS}
+
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"name", entry.Name},
+			{"address", entry.Address},
+		}},
+	}
+	var dnsupdate models.DNSEntry
+
+	errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&dnsupdate)
+	if errN != nil {
+		fmt.Println("Could not update: ")
+		fmt.Println(errN)
+	} else {
+		fmt.Println("DNS Entry updated successfully.")
+	}
+
+	defer cancel()
+
+	return dnsupdate, errN
 }
 }
 
 
 func DeleteDNS(domain string, network string) (bool, error) {
 func DeleteDNS(domain string, network string) (bool, error) {
-
+	fmt.Println("delete dns entry ", domain, network)
 	deleted := false
 	deleted := false
 
 
 	collection := mongoconn.Client.Database("netmaker").Collection("dns")
 	collection := mongoconn.Client.Database("netmaker").Collection("dns")
 
 
-	filter := bson.M{"name": domain,  "network": network}
+	filter := bson.M{"name": domain, "network": network}
 
 
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
@@ -437,49 +449,36 @@ func DeleteDNS(domain string, network string) (bool, error) {
 }
 }
 
 
 func pushDNS(w http.ResponseWriter, r *http.Request) {
 func pushDNS(w http.ResponseWriter, r *http.Request) {
-        // Set header
-        w.Header().Set("Content-Type", "application/json")
+	// Set header
+	w.Header().Set("Content-Type", "application/json")
 
 
-        err := SetDNS()
+	err := SetDNS()
 
 
-        if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
 	}
 	}
-
-        json.NewEncoder(w).Encode("DNS Pushed to CoreDNS")
+	json.NewEncoder(w).Encode("DNS Pushed to CoreDNS")
 }
 }
 
 
 func ValidateDNSCreate(entry models.DNSEntry) error {
 func ValidateDNSCreate(entry models.DNSEntry) error {
 
 
 	v := validator.New()
 	v := validator.New()
-        fmt.Println("Validating DNS: " + entry.Name)
-        fmt.Println("       Address: " + entry.Address)
-        fmt.Println("       Network: " + entry.Network)
+	fmt.Println("Validating DNS: " + entry.Name)
+	fmt.Println("       Address: " + entry.Address)
+	fmt.Println("       Network: " + entry.Network)
 
 
 	_ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
 	_ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
 		num, err := GetDNSEntryNum(entry.Name, entry.Network)
 		num, err := GetDNSEntryNum(entry.Name, entry.Network)
 		return err == nil && num == 0
 		return err == nil && num == 0
 	})
 	})
 
 
-	_ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
-		isvalid := functions.NameInDNSCharSet(entry.Name)
-                notEmptyCheck := len(entry.Name) > 0
-		return isvalid && notEmptyCheck
-	})
-
-	_ = v.RegisterValidation("address_valid", func(fl validator.FieldLevel) bool {
-		notEmptyCheck := len(entry.Address) > 0
-                isIp := functions.IsIpNet(entry.Address)
-		return notEmptyCheck && isIp
+	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
+		_, err := functions.GetParentNetwork(entry.Network)
+		return err == nil
 	})
 	})
-        _ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
-                _, err := functions.GetParentNetwork(entry.Network)
-                return err == nil
-        })
 
 
 	err := v.Struct(entry)
 	err := v.Struct(entry)
-
 	if err != nil {
 	if err != nil {
 		for _, e := range err.(validator.ValidationErrors) {
 		for _, e := range err.(validator.ValidationErrors) {
 			fmt.Println(e)
 			fmt.Println(e)
@@ -490,41 +489,42 @@ func ValidateDNSCreate(entry models.DNSEntry) error {
 
 
 func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 
 
-        v := validator.New()
+	v := validator.New()
 
 
-        _ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
-		goodNum := false
-                num, err := GetDNSEntryNum(entry.Name, entry.Network)
-		if change.Name != entry.Name {
-			goodNum = num == 0
-		} else {
-                        goodNum = num == 1
-		}
-		return err == nil && goodNum
-        })
-
-        _ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
-                isvalid := functions.NameInDNSCharSet(entry.Name)
-                notEmptyCheck := entry.Name != ""
-                return isvalid && notEmptyCheck
-        })
-
-        _ = v.RegisterValidation("address_valid", func(fl validator.FieldLevel) bool {
-		isValid := true
-		if entry.Address != "" {
-			isValid = functions.IsIpNet(entry.Address)
+	_ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
+		//if name & net not changing name we are good
+		if change.Name == entry.Name && change.Network == entry.Network {
+			return true
 		}
 		}
-		return isValid
-        })
+		num, err := GetDNSEntryNum(change.Name, change.Network)
+		return err == nil && num == 0
+	})
+	_ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
+		_, err := functions.GetParentNetwork(change.Network)
+		fmt.Println(err, entry.Network)
+		return err == nil
+	})
 
 
-        err := v.Struct(entry)
+	//	_ = v.RegisterValidation("name_valid", func(fl validator.FieldLevel) bool {
+	//		isvalid := functions.NameInDNSCharSet(entry.Name)
+	//		notEmptyCheck := entry.Name != ""
+	//		return isvalid && notEmptyCheck
+	//	})
+	//
+	//	_ = v.RegisterValidation("address_valid", func(fl validator.FieldLevel) bool {
+	//		isValid := true
+	//		if entry.Address != "" {
+	//			isValid = functions.IsIpNet(entry.Address)
+	//		}
+	//		return isValid
+	//	})
+
+	err := v.Struct(change)
 
 
-        if err != nil {
-                for _, e := range err.(validator.ValidationErrors) {
-                        fmt.Println(e)
-                }
-        }
-        return err
+	if err != nil {
+		for _, e := range err.(validator.ValidationErrors) {
+			fmt.Println(e)
+		}
+	}
+	return err
 }
 }
-
-

+ 180 - 0
controllers/dnsHttpController_test.go

@@ -0,0 +1,180 @@
+package controller
+
+import (
+	"testing"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetNodeDNS(t *testing.T) {
+	dns, err := GetNodeDNS("skynet")
+	assert.Nil(t, err)
+	t.Log(dns)
+}
+func TestGetCustomDNS(t *testing.T) {
+	dns, err := GetCustomDNS("skynet")
+	assert.Nil(t, err)
+	t.Log(dns)
+}
+func TestGetDNSEntryNum(t *testing.T) {
+	num, err := GetDNSEntryNum("myhost", "skynet")
+	assert.Nil(t, err)
+	t.Log(num)
+}
+func TestGetDNS(t *testing.T) {
+	dns, err := GetDNS("skynet")
+	assert.Nil(t, err)
+	t.Log(dns)
+}
+func TestCreateDNS(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	//dns, err := GetDNS("skynet")
+	//assert.Nil(t, err)
+	//for _, entry := range dns {
+	//	_, _ = DeleteDNS(entry.Name, "skynet")
+	//}
+	entry := models.DNSEntry{"10.0.0.2", "myhost", "skynet"}
+	err := ValidateDNSCreate(entry)
+	assert.Nil(t, err)
+	if err != nil {
+		return
+	}
+	dns, err := CreateDNS(entry)
+	assert.Nil(t, err)
+	t.Log(dns)
+}
+func TestGetDNSEntry(t *testing.T) {
+	entry, err := GetDNSEntry("myhost", "skynet")
+	assert.Nil(t, err)
+	t.Log(entry)
+}
+func TestUpdateDNS(t *testing.T) {
+}
+func TestDeleteDNS(t *testing.T) {
+	t.Run("EntryExists", func(t *testing.T) {
+		success, err := DeleteDNS("myhost", "skynet")
+		assert.Nil(t, err)
+		assert.True(t, success)
+	})
+	t.Run("NoEntry", func(t *testing.T) {
+		success, err := DeleteDNS("myhost", "skynet")
+		assert.Nil(t, err)
+		assert.False(t, success)
+	})
+
+}
+
+func TestValidateDNSUpdate(t *testing.T) {
+	entry := models.DNSEntry{"10.0.0.2", "myhost", "skynet"}
+	_, _ = DeleteDNS("mynode", "skynet")
+	t.Run("BadNetwork", func(t *testing.T) {
+		change := models.DNSEntry{"10.0.0.2", "myhost", "badnet"}
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Network' failed on the 'network_exists' tag")
+	})
+	t.Run("EmptyNetwork", func(t *testing.T) {
+		//this can't actually happen as change.Network is populated if is blank
+		change := models.DNSEntry{"10.0.0.2", "myhost", ""}
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Network' failed on the 'network_exists' tag")
+	})
+	t.Run("EmptyAddress", func(t *testing.T) {
+		//this can't actually happen as change.Address is populated if is blank
+		change := models.DNSEntry{"", "myhost", "skynet"}
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Address' failed on the 'required' tag")
+	})
+	t.Run("BadAddress", func(t *testing.T) {
+		change := models.DNSEntry{"10.0.256.1", "myhost", "skynet"}
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Address' failed on the 'ip' tag")
+	})
+	t.Run("BadName", func(t *testing.T) {
+		change := models.DNSEntry{"10.0.0.2", "myhostr*", "skynet"}
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'alphanum' tag")
+	})
+	t.Run("EmptyName", func(t *testing.T) {
+		//this can't actually happen as change.Name is populated if is blank
+		change := models.DNSEntry{"10.0.0.2", "", "skynet"}
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'required' tag")
+	})
+	t.Run("NameTooLong", func(t *testing.T) {
+		name := ""
+		for i := 1; i < 122; i++ {
+			name = name + "a"
+		}
+		change := models.DNSEntry{"10.0.0.2", name, "skynet"}
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'max' tag")
+	})
+	t.Run("NameUnique", func(t *testing.T) {
+		change := models.DNSEntry{"10.0.0.2", "myhost", "wirecat"}
+		_, _ = CreateDNS(entry)
+		_, _ = CreateDNS(change)
+		err := ValidateDNSUpdate(change, entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'name_unique' tag")
+	})
+
+}
+func TestValidateDNSCreate(t *testing.T) {
+	_, _ = DeleteDNS("mynode", "skynet")
+	t.Run("NoNetwork", func(t *testing.T) {
+		entry := models.DNSEntry{"10.0.0.2", "myhost", "badnet"}
+		err := ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Network' failed on the 'network_exists' tag")
+	})
+	t.Run("EmptyAddress", func(t *testing.T) {
+		entry := models.DNSEntry{"", "myhost", "skynet"}
+		err := ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Address' failed on the 'required' tag")
+	})
+	t.Run("BadAddress", func(t *testing.T) {
+		entry := models.DNSEntry{"10.0.256.1", "myhost", "skynet"}
+		err := ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Address' failed on the 'ip' tag")
+	})
+	t.Run("BadName", func(t *testing.T) {
+		entry := models.DNSEntry{"10.0.0.2", "myhostr*", "skynet"}
+		err := ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'alphanum' tag")
+	})
+	t.Run("EmptyName", func(t *testing.T) {
+		entry := models.DNSEntry{"10.0.0.2", "", "skynet"}
+		err := ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'required' tag")
+	})
+	t.Run("NameTooLong", func(t *testing.T) {
+		name := ""
+		for i := 1; i < 122; i++ {
+			name = name + "a"
+		}
+		entry := models.DNSEntry{"10.0.0.2", name, "skynet"}
+		err := ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'max' tag")
+	})
+	t.Run("NameUnique", func(t *testing.T) {
+		entry := models.DNSEntry{"10.0.0.2", "myhost", "skynet"}
+		_, _ = CreateDNS(entry)
+		err := ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'name_unique' tag")
+	})
+}

+ 277 - 305
controllers/networkHttpController.go

@@ -9,14 +9,16 @@ import (
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 	"time"
 	"time"
+
+	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mongoconn"
 	"github.com/gravitl/netmaker/mongoconn"
+	"github.com/gravitl/netmaker/servercfg"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo/options"
 	"go.mongodb.org/mongo-driver/mongo/options"
-	"gopkg.in/go-playground/validator.v9"
 )
 )
 
 
 func networkHandlers(r *mux.Router) {
 func networkHandlers(r *mux.Router) {
@@ -37,48 +39,54 @@ func networkHandlers(r *mux.Router) {
 func securityCheck(next http.Handler) http.HandlerFunc {
 func securityCheck(next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
 		var errorResponse = models.ErrorResponse{
-			Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
+			Code: http.StatusUnauthorized, Message: "W1R3: It's not you it's me.",
 		}
 		}
 
 
 		var params = mux.Vars(r)
 		var params = mux.Vars(r)
-		hasnetwork := params["networkname"] != ""
-		networkexists, err := functions.NetworkExists(params["networkname"])
+		bearerToken := r.Header.Get("Authorization")
+		err := SecurityCheck(params["networkname"], bearerToken)
 		if err != nil {
 		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
-		} else if hasnetwork && !networkexists {
-			errorResponse = models.ErrorResponse{
-				Code: http.StatusNotFound, Message: "W1R3: This network does not exist.",
-			}
+			errorResponse.Message = err.Error()
 			returnErrorResponse(w, r, errorResponse)
 			returnErrorResponse(w, r, errorResponse)
 			return
 			return
-		} else {
-
-			bearerToken := r.Header.Get("Authorization")
-
-			var hasBearer = true
-			var tokenSplit = strings.Split(bearerToken, " ")
-			var authToken = ""
-
-			if len(tokenSplit) < 2 {
-				hasBearer = false
-			} else {
-				authToken = tokenSplit[1]
-			}
-			//all endpoints here require master so not as complicated
-			//still might not be a good  way of doing this
-			if !hasBearer || !authenticateMaster(authToken) {
-				errorResponse = models.ErrorResponse{
-					Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
-				}
-				returnErrorResponse(w, r, errorResponse)
-				return
-			} else {
-				next.ServeHTTP(w, r)
-			}
 		}
 		}
+		next.ServeHTTP(w, r)
 	}
 	}
 }
 }
+func SecurityCheck(netname, token string) error {
+	hasnetwork := netname != ""
+	networkexists, err := functions.NetworkExists(netname)
+	if err != nil {
+		return err
+	}
+	if hasnetwork && !networkexists {
+		//errorResponse = models.ErrorResponse{
+		//	Code: http.StatusNotFound, Message: "W1R3: This network does not exist.",
+		//}
+		//returnErrorResponse(w, r, errorResponse)
+		return errors.New("This network does not exist")
+	}
+
+	var hasBearer = true
+	var tokenSplit = strings.Split(token, " ")
+	var authToken = ""
+
+	if len(tokenSplit) < 2 {
+		hasBearer = false
+	} else {
+		authToken = tokenSplit[1]
+	}
+	//all endpoints here require master so not as complicated
+	//still might not be a good  way of doing this
+	if !hasBearer || !authenticateMaster(authToken) {
+		//errorResponse = models.ErrorResponse{
+		//	Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
+		//	}
+		//	returnErrorResponse(w, r, errorResponse)
+		return errors.New("You are unauthorized to access this endpoint")
+	} //else {
+	return nil
+}
 
 
 //Consider a more secure way of setting master key
 //Consider a more secure way of setting master key
 func authenticateMaster(tokenString string) bool {
 func authenticateMaster(tokenString string) bool {
@@ -103,31 +111,30 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 }
 }
 
 
-func validateNetworkUpdate(network models.Network) error {
-
+func ValidateNetworkUpdate(network models.NetworkUpdate) error {
 	v := validator.New()
 	v := validator.New()
 
 
-	_ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
-		isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
-		return isvalid
-	})
-        _ = v.RegisterValidation("addressrange6_valid", func(fl validator.FieldLevel) bool {
-                isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
-                return isvalid
-        })
+	//	_ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
+	//		isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
+	//		return isvalid
+	//	})
+	//_ = v.RegisterValidation("addressrange6_valid", func(fl validator.FieldLevel) bool {
+	//		isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
+	//		return isvalid
+	//	})
 
 
-	_ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
-		isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
-		return isvalid
-	})
+	//	_ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
+	//		isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
+	//		return isvalid
+	//	})
 
 
-	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
-		return true
-	})
+	//	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
+	//		return true
+	//	})
 
 
-	_ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
-		return true
-	})
+	//	_ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
+	//		return true
+	//	})
 
 
 	err := v.Struct(network)
 	err := v.Struct(network)
 
 
@@ -139,34 +146,33 @@ func validateNetworkUpdate(network models.Network) error {
 	return err
 	return err
 }
 }
 
 
-func validateNetworkCreate(network models.Network) error {
+func ValidateNetworkCreate(network models.Network) error {
 
 
 	v := validator.New()
 	v := validator.New()
 
 
-	_ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
-		isvalid := functions.IsIpCIDR(fl.Field().String())
-		return isvalid
-	})
-        _ = v.RegisterValidation("addressrange6_valid", func(fl validator.FieldLevel) bool {
+	//	_ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
+	//		isvalid := functions.IsIpCIDR(fl.Field().String())
+	//		return isvalid
+	//	})
+	_ = v.RegisterValidation("addressrange6_valid", func(fl validator.FieldLevel) bool {
 		isvalid := true
 		isvalid := true
 		if *network.IsDualStack {
 		if *network.IsDualStack {
 			isvalid = functions.IsIpCIDR(fl.Field().String())
 			isvalid = functions.IsIpCIDR(fl.Field().String())
 		}
 		}
 		return isvalid
 		return isvalid
-        })
-
-
-	_ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
-		isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
-		return isvalid
 	})
 	})
-
+	//
+	//	_ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
+	//		isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String())
+	//		return isvalid
+	//	})
+	//
 	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
 	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
 		isFieldUnique, _ := functions.IsNetworkNameUnique(fl.Field().String())
 		isFieldUnique, _ := functions.IsNetworkNameUnique(fl.Field().String())
-		inCharSet := functions.NameInNetworkCharSet(fl.Field().String())
-		return isFieldUnique && inCharSet
+		//		inCharSet := functions.NameInNetworkCharSet(fl.Field().String())
+		return isFieldUnique
 	})
 	})
-
+	//
 	_ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
 	_ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
 		isFieldUnique, _ := functions.IsNetworkDisplayNameUnique(fl.Field().String())
 		isFieldUnique, _ := functions.IsNetworkDisplayNameUnique(fl.Field().String())
 		return isFieldUnique
 		return isFieldUnique
@@ -184,61 +190,63 @@ func validateNetworkCreate(network models.Network) error {
 
 
 //Simple get network function
 //Simple get network function
 func getNetwork(w http.ResponseWriter, r *http.Request) {
 func getNetwork(w http.ResponseWriter, r *http.Request) {
-
 	// set header.
 	// set header.
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
+	netname := params["networkname"]
+	network, err := GetNetwork(netname)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(network)
+}
 
 
+func GetNetwork(name string) (models.Network, error) {
 	var network models.Network
 	var network models.Network
-
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-	filter := bson.M{"netid": params["networkname"]}
+	filter := bson.M{"netid": name}
 	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
 	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Network{}, err
 	}
 	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(network)
+	return network, nil
 }
 }
 
 
 func keyUpdate(w http.ResponseWriter, r *http.Request) {
 func keyUpdate(w http.ResponseWriter, r *http.Request) {
-
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
-	var network models.Network
-
-	network, err := functions.GetParentNetwork(params["networkname"])
+	netname := params["networkname"]
+	network, err := KeyUpdate(netname)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(network)
+}
 
 
+func KeyUpdate(netname string) (models.Network, error) {
+	network, err := functions.GetParentNetwork(netname)
+	if err != nil {
+		return models.Network{}, err
+	}
 	network.KeyUpdateTimeStamp = time.Now().Unix()
 	network.KeyUpdateTimeStamp = time.Now().Unix()
-
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-        filter := bson.M{"netid": params["networkname"]}
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"addressrange", network.AddressRange},
-                        {"addressrange6", network.AddressRange6},
-                        {"displayname", network.DisplayName},
-                        {"defaultlistenport", network.DefaultListenPort},
-                        {"defaultpostup", network.DefaultPostUp},
-                        {"defaultpostdown", network.DefaultPostDown},
+	filter := bson.M{"netid": netname}
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"addressrange", network.AddressRange},
+			{"addressrange6", network.AddressRange6},
+			{"displayname", network.DisplayName},
+			{"defaultlistenport", network.DefaultListenPort},
+			{"defaultpostup", network.DefaultPostUp},
+			{"defaultpostdown", network.DefaultPostDown},
 			{"defaultkeepalive", network.DefaultKeepalive},
 			{"defaultkeepalive", network.DefaultKeepalive},
 			{"keyupdatetimestamp", network.KeyUpdateTimeStamp},
 			{"keyupdatetimestamp", network.KeyUpdateTimeStamp},
 			{"defaultsaveconfig", network.DefaultSaveConfig},
 			{"defaultsaveconfig", network.DefaultSaveConfig},
@@ -247,20 +255,14 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
 			{"networklastmodified", network.NetworkLastModified},
 			{"networklastmodified", network.NetworkLastModified},
 			{"allowmanualsignup", network.AllowManualSignUp},
 			{"allowmanualsignup", network.AllowManualSignUp},
 			{"checkininterval", network.DefaultCheckInInterval},
 			{"checkininterval", network.DefaultCheckInInterval},
-		        }},
-	      }
-
+		}},
+	}
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Network{}, err
 	}
 	}
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(network)
+	return network, nil
 }
 }
 
 
 //Update a network
 //Update a network
@@ -292,82 +294,70 @@ func AlertNetwork(netid string) error {
 
 
 //Update a network
 //Update a network
 func updateNetwork(w http.ResponseWriter, r *http.Request) {
 func updateNetwork(w http.ResponseWriter, r *http.Request) {
-
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
 	var network models.Network
 	var network models.Network
-
 	network, err := functions.GetParentNetwork(params["networkname"])
 	network, err := functions.GetParentNetwork(params["networkname"])
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
 
 
-	var networkChange models.Network
-
-	haschange := false
-	hasrangeupdate := false
-	haslocalrangeupdate := false
+	var networkChange models.NetworkUpdate
 
 
 	_ = json.NewDecoder(r.Body).Decode(&networkChange)
 	_ = json.NewDecoder(r.Body).Decode(&networkChange)
-
 	if networkChange.AddressRange == "" {
 	if networkChange.AddressRange == "" {
 		networkChange.AddressRange = network.AddressRange
 		networkChange.AddressRange = network.AddressRange
 	}
 	}
-        if networkChange.AddressRange6 == "" {
-                networkChange.AddressRange6 = network.AddressRange6
-        }
+	if networkChange.AddressRange6 == "" {
+		networkChange.AddressRange6 = network.AddressRange6
+	}
 	if networkChange.NetID == "" {
 	if networkChange.NetID == "" {
 		networkChange.NetID = network.NetID
 		networkChange.NetID = network.NetID
 	}
 	}
 
 
-	err = validateNetworkUpdate(networkChange)
+	err = ValidateNetworkUpdate(networkChange)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
+	}
+	returnednetwork, err := UpdateNetwork(networkChange, network)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
 
 
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(returnednetwork)
+}
+
+func UpdateNetwork(networkChange models.NetworkUpdate, network models.Network) (models.Network, error) {
 	//NOTE: Network.NetID is intentionally NOT editable. It acts as a static ID for the network.
 	//NOTE: Network.NetID is intentionally NOT editable. It acts as a static ID for the network.
 	//DisplayName can be changed instead, which is what shows on the front end
 	//DisplayName can be changed instead, which is what shows on the front end
 	if networkChange.NetID != network.NetID {
 	if networkChange.NetID != network.NetID {
-		returnErrorResponse(w, r, formatError(errors.New("NetID is not editable"), "badrequest"))
-		return
+		return models.Network{}, errors.New("NetID is not editable")
 	}
 	}
-	//MRK:  I think this code block is redundant.  valdiateNetworkUpdate(networkChange) covers this
-	if networkChange.AddressRange != "" {
 
 
-		network.AddressRange = networkChange.AddressRange
+	haschange := false
+	hasrangeupdate := false
+	haslocalrangeupdate := false
 
 
-		var isAddressOK bool = functions.IsIpCIDR(networkChange.AddressRange)
-		if !isAddressOK {
-			err := errors.New("Invalid Range of " + networkChange.AddressRange + " for addresses.")
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
-		}
+	if networkChange.AddressRange != "" {
 		haschange = true
 		haschange = true
 		hasrangeupdate = true
 		hasrangeupdate = true
-
+		network.AddressRange = networkChange.AddressRange
 	}
 	}
 	if networkChange.LocalRange != "" {
 	if networkChange.LocalRange != "" {
-		network.LocalRange = networkChange.LocalRange
-
-		var isAddressOK bool = functions.IsIpCIDR(networkChange.LocalRange)
-		if !isAddressOK {
-			err := errors.New("Invalid Range of " + networkChange.LocalRange + " for internal addresses.")
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
-		}
 		haschange = true
 		haschange = true
 		haslocalrangeupdate = true
 		haslocalrangeupdate = true
+		network.LocalRange = networkChange.LocalRange
 	}
 	}
 	if networkChange.IsLocal != nil {
 	if networkChange.IsLocal != nil {
 		network.IsLocal = networkChange.IsLocal
 		network.IsLocal = networkChange.IsLocal
 	}
 	}
-        if networkChange.IsDualStack != nil {
-                network.IsDualStack = networkChange.IsDualStack
-        }
+	if networkChange.IsDualStack != nil {
+		network.IsDualStack = networkChange.IsDualStack
+	}
 	if networkChange.DefaultListenPort != 0 {
 	if networkChange.DefaultListenPort != 0 {
 		network.DefaultListenPort = networkChange.DefaultListenPort
 		network.DefaultListenPort = networkChange.DefaultListenPort
 		haschange = true
 		haschange = true
@@ -403,65 +393,59 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 
 
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-	filter := bson.M{"netid": params["networkname"]}
+	filter := bson.M{"netid": network.NetID}
 
 
 	if haschange {
 	if haschange {
 		network.SetNetworkLastModified()
 		network.SetNetworkLastModified()
 	}
 	}
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"addressrange", network.AddressRange},
-                        {"addressrange6", network.AddressRange6},
-                        {"displayname", network.DisplayName},
-                        {"defaultlistenport", network.DefaultListenPort},
-                        {"defaultpostup", network.DefaultPostUp},
-                        {"defaultpostdown", network.DefaultPostDown},
-                        {"defaultkeepalive", network.DefaultKeepalive},
-                        {"defaultsaveconfig", network.DefaultSaveConfig},
-                        {"defaultinterface", network.DefaultInterface},
-                        {"nodeslastmodified", network.NodesLastModified},
-                        {"networklastmodified", network.NetworkLastModified},
-                        {"allowmanualsignup", network.AllowManualSignUp},
-                        {"localrange", network.LocalRange},
-                        {"islocal", network.IsLocal},
-                        {"isdualstack", network.IsDualStack},
-                        {"checkininterval", network.DefaultCheckInInterval},
-		              }},
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"addressrange", network.AddressRange},
+			{"addressrange6", network.AddressRange6},
+			{"displayname", network.DisplayName},
+			{"defaultlistenport", network.DefaultListenPort},
+			{"defaultpostup", network.DefaultPostUp},
+			{"defaultpostdown", network.DefaultPostDown},
+			{"defaultkeepalive", network.DefaultKeepalive},
+			{"defaultsaveconfig", network.DefaultSaveConfig},
+			{"defaultinterface", network.DefaultInterface},
+			{"nodeslastmodified", network.NodesLastModified},
+			{"networklastmodified", network.NetworkLastModified},
+			{"allowmanualsignup", network.AllowManualSignUp},
+			{"localrange", network.LocalRange},
+			{"islocal", network.IsLocal},
+			{"isdualstack", network.IsDualStack},
+			{"checkininterval", network.DefaultCheckInInterval},
+		}},
 	}
 	}
 
 
-	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
+	err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
 	defer cancel()
 	defer cancel()
 
 
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Network{}, err
 	}
 	}
 
 
 	//Cycles through nodes and gives them new IP's based on the new range
 	//Cycles through nodes and gives them new IP's based on the new range
 	//Pretty cool, but also pretty inefficient currently
 	//Pretty cool, but also pretty inefficient currently
 	if hasrangeupdate {
 	if hasrangeupdate {
-		err = functions.UpdateNetworkNodeAddresses(params["networkname"])
+		err = functions.UpdateNetworkNodeAddresses(network.NetID)
 		if err != nil {
 		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
+			return models.Network{}, err
 		}
 		}
 	}
 	}
 	if haslocalrangeupdate {
 	if haslocalrangeupdate {
-		err = functions.UpdateNetworkPrivateAddresses(params["networkname"])
+		err = functions.UpdateNetworkPrivateAddresses(network.NetID)
 		if err != nil {
 		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
+			return models.Network{}, err
 		}
 		}
 	}
 	}
 	returnnetwork, err := functions.GetParentNetwork(network.NetID)
 	returnnetwork, err := functions.GetParentNetwork(network.NetID)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Network{}, err
 	}
 	}
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(returnnetwork)
+	return returnnetwork, nil
 }
 }
 
 
 //Delete a network
 //Delete a network
@@ -471,36 +455,42 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
+	network := params["networkname"]
+	count, err := DeleteNetwork(network)
 
 
-	nodecount, err := functions.GetNetworkNodeNumber(params["networkname"])
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	} else if nodecount > 0 {
-		errorResponse := models.ErrorResponse{
-			Code: http.StatusForbidden, Message: "W1R3: Node check failed. All nodes must be deleted before deleting network.",
-		}
-		returnErrorResponse(w, r, errorResponse)
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(count)
+}
 
 
-	collection := mongoconn.Client.Database("netmaker").Collection("networks")
+func DeleteNetwork(network string) (*mongo.DeleteResult, error) {
+	none := &mongo.DeleteResult{}
 
 
-	filter := bson.M{"netid": params["networkname"]}
+	nodecount, err := functions.GetNetworkNodeNumber(network)
+	if err != nil {
+		//returnErrorResponse(w, r, formatError(err, "internal"))
+		return none, err
+	} else if nodecount > 0 {
+		//errorResponse := models.ErrorResponse{
+		//	Code: http.StatusForbidden, Message: "W1R3: Node check failed. All nodes must be deleted before deleting network.",
+		//}
+		//returnErrorResponse(w, r, errorResponse)
+		return none, errors.New("Node check failed. All nodes must be deleted before deleting network")
+	}
 
 
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	filter := bson.M{"netid": network}
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
 	deleteResult, err := collection.DeleteOne(ctx, filter)
 	deleteResult, err := collection.DeleteOne(ctx, filter)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		//returnErrorResponse(w, r, formatError(err, "internal"))
+		return none, err
 	}
 	}
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(deleteResult)
+	return deleteResult, nil
 }
 }
 
 
 //Create a network
 //Create a network
@@ -518,6 +508,16 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
+	err = CreateNetwork(network)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	//json.NewEncoder(w).Encode(result)
+}
+
+func CreateNetwork(network models.Network) error {
 	//TODO: Not really doing good validation here. Same as createNode, updateNode, and updateNetwork
 	//TODO: Not really doing good validation here. Same as createNode, updateNode, and updateNetwork
 	//Need to implement some better validation across the board
 	//Need to implement some better validation across the board
 
 
@@ -525,15 +525,15 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 		falsevar := false
 		falsevar := false
 		network.IsLocal = &falsevar
 		network.IsLocal = &falsevar
 	}
 	}
-        if network.IsDualStack == nil {
-                falsevar := false
-                network.IsDualStack = &falsevar
-        }
+	if network.IsDualStack == nil {
+		falsevar := false
+		network.IsDualStack = &falsevar
+	}
 
 
-	err = validateNetworkCreate(network)
+	err := ValidateNetworkCreate(network)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "badrequest"))
-		return
+		//returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return err
 	}
 	}
 	network.SetDefaults()
 	network.SetDefaults()
 	network.SetNodesLastModified()
 	network.SetNodesLastModified()
@@ -544,16 +544,12 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
 	// insert our network into the network table
 	// insert our network into the network table
-	result, err := collection.InsertOne(ctx, network)
-
+	_, err = collection.InsertOne(ctx, network)
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return err
 	}
 	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(result)
+	return nil
 }
 }
 
 
 // BEGIN KEY MANAGEMENT SECTION
 // BEGIN KEY MANAGEMENT SECTION
@@ -561,187 +557,163 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 //TODO: Very little error handling
 //TODO: Very little error handling
 //accesskey is created as a json string inside the Network collection item in mongo
 //accesskey is created as a json string inside the Network collection item in mongo
 func createAccessKey(w http.ResponseWriter, r *http.Request) {
 func createAccessKey(w http.ResponseWriter, r *http.Request) {
-
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
-	var network models.Network
 	var accesskey models.AccessKey
 	var accesskey models.AccessKey
-
 	//start here
 	//start here
 	network, err := functions.GetParentNetwork(params["networkname"])
 	network, err := functions.GetParentNetwork(params["networkname"])
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
-
 	err = json.NewDecoder(r.Body).Decode(&accesskey)
 	err = json.NewDecoder(r.Body).Decode(&accesskey)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
+	key, err := CreateAccessKey(accesskey, network)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(key)
+	//w.Write([]byte(accesskey.AccessString))
+}
 
 
+func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models.AccessKey, error) {
+	fmt.Println(accesskey)
 	if accesskey.Name == "" {
 	if accesskey.Name == "" {
 		accesskey.Name = functions.GenKeyName()
 		accesskey.Name = functions.GenKeyName()
 	}
 	}
 	if accesskey.Value == "" {
 	if accesskey.Value == "" {
 		accesskey.Value = functions.GenKey()
 		accesskey.Value = functions.GenKey()
 	}
 	}
-
 	if accesskey.Uses == 0 {
 	if accesskey.Uses == 0 {
 		accesskey.Uses = 1
 		accesskey.Uses = 1
 	}
 	}
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+	for _, key := range network.AccessKeys {
+		if key.Name == accesskey.Name {
+			return models.AccessKey{}, errors.New("Duplicate AccessKey Name")
+		}
 	}
 	}
-
 	privAddr := ""
 	privAddr := ""
-	if *network.IsLocal {
-		privAddr = network.LocalRange
+	if network.IsLocal != nil {
+		if *network.IsLocal {
+			privAddr = network.LocalRange
+		}
 	}
 	}
 
 
-	netID := params["networkname"]
+	netID := network.NetID
 	address := servercfg.GetGRPCHost() + ":" + servercfg.GetGRPCPort()
 	address := servercfg.GetGRPCHost() + ":" + servercfg.GetGRPCPort()
 
 
 	accessstringdec := address + "|" + netID + "|" + accesskey.Value + "|" + privAddr
 	accessstringdec := address + "|" + netID + "|" + accesskey.Value + "|" + privAddr
 	accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
 	accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
-
+	//validate accesskey
+	v := validator.New()
+	err := v.Struct(accesskey)
+	if err != nil {
+		for _, e := range err.(validator.ValidationErrors) {
+			fmt.Println(e)
+		}
+		return models.AccessKey{}, err
+	}
 	network.AccessKeys = append(network.AccessKeys, accesskey)
 	network.AccessKeys = append(network.AccessKeys, accesskey)
-
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
 	// Create filter
 	// Create filter
-	filter := bson.M{"netid": params["networkname"]}
-
+	filter := bson.M{"netid": network.NetID}
 	// Read update model from body request
 	// Read update model from body request
 	fmt.Println("Adding key to " + network.NetID)
 	fmt.Println("Adding key to " + network.NetID)
-
 	// prepare update model.
 	// prepare update model.
 	update := bson.D{
 	update := bson.D{
 		{"$set", bson.D{
 		{"$set", bson.D{
 			{"accesskeys", network.AccessKeys},
 			{"accesskeys", network.AccessKeys},
 		}},
 		}},
 	}
 	}
-
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		//returnErrorResponse(w, r, formatError(err, "internal"))
+		return models.AccessKey{}, err
 	}
 	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(accesskey)
-	//w.Write([]byte(accesskey.AccessString))
+	return accesskey, nil
 }
 }
 
 
 //pretty simple get
 //pretty simple get
 func getAccessKeys(w http.ResponseWriter, r *http.Request) {
 func getAccessKeys(w http.ResponseWriter, r *http.Request) {
-
-	// set header.
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
+	network := params["networkname"]
+	keys, err := GetKeys(network)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(keys)
+}
+func GetKeys(net string) ([]models.AccessKey, error) {
 
 
 	var network models.Network
 	var network models.Network
-	//var keys []models.DisplayKey
-	var keys []models.AccessKey
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-	filter := bson.M{"netid": params["networkname"]}
+	filter := bson.M{"netid": net}
 	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
 	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return []models.AccessKey{}, err
 	}
 	}
-	keydata, err := json.Marshal(network.AccessKeys)
-
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-
-	json.Unmarshal(keydata, &keys)
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(keys)
+	return network.AccessKeys, nil
 }
 }
 
 
 //delete key. Has to do a little funky logic since it's not a collection item
 //delete key. Has to do a little funky logic since it's not a collection item
 func deleteAccessKey(w http.ResponseWriter, r *http.Request) {
 func deleteAccessKey(w http.ResponseWriter, r *http.Request) {
-
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
-	var network models.Network
 	keyname := params["name"]
 	keyname := params["name"]
-
-	//start here
-	network, err := functions.GetParentNetwork(params["networkname"])
+	netname := params["networkname"]
+	err := DeleteKey(keyname, netname)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
+	w.WriteHeader(http.StatusOK)
+}
+func DeleteKey(keyname, netname string) error {
+	network, err := functions.GetParentNetwork(netname)
+	if err != nil {
+		return err
+	}
 	//basically, turn the list of access keys into the list of access keys before and after the item
 	//basically, turn the list of access keys into the list of access keys before and after the item
 	//have not done any error handling for if there's like...1 item. I think it works? need to test.
 	//have not done any error handling for if there's like...1 item. I think it works? need to test.
 	found := false
 	found := false
-	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
-
-		currentkey := network.AccessKeys[i]
+	var updatedKeys []models.AccessKey
+	for _, currentkey := range network.AccessKeys {
 		if currentkey.Name == keyname {
 		if currentkey.Name == keyname {
-			network.AccessKeys = append(network.AccessKeys[:i],
-				network.AccessKeys[i+1:]...)
 			found = true
 			found = true
+		} else {
+			updatedKeys = append(updatedKeys, currentkey)
 		}
 		}
 	}
 	}
 	if !found {
 	if !found {
-		err = errors.New("key " + keyname + " does not exist")
-		returnErrorResponse(w, r, formatError(err, "badrequest"))
-		return
+		return errors.New("key " + keyname + " does not exist")
 	}
 	}
 
 
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 	collection := mongoconn.Client.Database("netmaker").Collection("networks")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
 	// Create filter
 	// Create filter
-	filter := bson.M{"netid": params["networkname"]}
-
+	filter := bson.M{"netid": netname}
 	// prepare update model.
 	// prepare update model.
 	update := bson.D{
 	update := bson.D{
 		{"$set", bson.D{
 		{"$set", bson.D{
-			{"accesskeys", network.AccessKeys},
+			{"accesskeys", updatedKeys},
 		}},
 		}},
 	}
 	}
-
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
-
 	defer cancel()
 	defer cancel()
-
-	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-	var keys []models.AccessKey
-	keydata, err := json.Marshal(network.AccessKeys)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return err
 	}
 	}
-
-	json.Unmarshal(keydata, &keys)
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(keys)
+	return nil
 }
 }

+ 572 - 0
controllers/networkHttpController_test.go

@@ -0,0 +1,572 @@
+package controller
+
+import (
+	"testing"
+	"time"
+
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/stretchr/testify/assert"
+)
+
+type NetworkValidationTestCase struct {
+	testname   string
+	network    models.Network
+	errMessage string
+}
+
+func deleteNet(t *testing.T) {
+	nodes, err := functions.GetAllNodes()
+	assert.Nil(t, err)
+	for _, node := range nodes {
+		t.Log("deleting node", node.Name)
+		result, err := DeleteNode(node.MacAddress, node.Network)
+		assert.Nil(t, err)
+		assert.True(t, result)
+	}
+	dns, err := GetAllDNS()
+	assert.Nil(t, err)
+	for _, entry := range dns {
+		t.Log("deleting dns enty", entry.Name, entry.Network)
+		success, err := DeleteDNS(entry.Name, entry.Network)
+		assert.Nil(t, err)
+		assert.True(t, success)
+	}
+	networks, _ := functions.ListNetworks()
+	for _, network := range networks {
+		t.Log("deleting network", network.NetID)
+		success, err := DeleteNetwork(network.NetID)
+		t.Log(success, err)
+	}
+}
+
+func createNet() {
+	var network models.Network
+	network.NetID = "skynet"
+	network.AddressRange = "10.0.0.1/24"
+	network.DisplayName = "mynetwork"
+	_, err := GetNetwork("skynet")
+	if err != nil {
+		CreateNetwork(network)
+	}
+}
+func getNet() models.Network {
+	network, _ := GetNetwork("skynet")
+	return network
+}
+
+func TestGetNetworks(t *testing.T) {
+	//calls functions.ListNetworks --- nothing to be done
+}
+func TestCreateNetwork(t *testing.T) {
+	deleteNet(t)
+	var network models.Network
+	network.NetID = "skynet"
+	network.AddressRange = "10.0.0.1/24"
+	network.DisplayName = "mynetwork"
+	err := CreateNetwork(network)
+	assert.Nil(t, err)
+}
+func TestGetDeleteNetwork(t *testing.T) {
+	createNet()
+	//create nodes
+	t.Run("NetworkwithNodes", func(t *testing.T) {
+	})
+	t.Run("GetExistingNetwork", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, "skynet", network.NetID)
+	})
+	t.Run("DeleteExistingNetwork", func(t *testing.T) {
+		result, err := DeleteNetwork("skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, int64(1), result.DeletedCount)
+		t.Log(result.DeletedCount)
+	})
+	t.Run("GetNonExistantNetwork", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.NotNil(t, err)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+		assert.Equal(t, "", network.NetID)
+	})
+	t.Run("NonExistantNetwork", func(t *testing.T) {
+		result, err := DeleteNetwork("skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, int64(0), result.DeletedCount)
+		t.Log(result.DeletedCount)
+	})
+}
+func TestGetNetwork(t *testing.T) {
+	createNet()
+	t.Run("NoNetwork", func(t *testing.T) {
+		network, err := GetNetwork("badnet")
+		assert.NotNil(t, err)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+		assert.Equal(t, models.Network{}, network)
+	})
+	t.Run("Valid", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, "skynet", network.NetID)
+	})
+}
+func TestUpdateNetwork(t *testing.T) {
+	createNet()
+	network := getNet()
+	t.Run("NetID", func(t *testing.T) {
+		var networkupdate models.NetworkUpdate
+		networkupdate.NetID = "wirecat"
+		_, err := UpdateNetwork(networkupdate, network)
+		assert.NotNil(t, err)
+		assert.Equal(t, "NetID is not editable", err.Error())
+	})
+	t.Run("LocalRange", func(t *testing.T) {
+		var networkupdate models.NetworkUpdate
+		//NetID needs to be set as it will be in updateNetwork
+		networkupdate.NetID = "skynet"
+		networkupdate.LocalRange = "192.168.0.1/24"
+		update, err := UpdateNetwork(networkupdate, network)
+		assert.Nil(t, err)
+		t.Log(err, update)
+	})
+}
+
+func TestKeyUpdate(t *testing.T) {
+	createNet()
+	existing, err := GetNetwork("skynet")
+	assert.Nil(t, err)
+	time.Sleep(time.Second * 1)
+	network, err := KeyUpdate("skynet")
+	assert.Nil(t, err)
+	network, err = GetNetwork("skynet")
+	assert.Nil(t, err)
+	assert.Greater(t, network.KeyUpdateTimeStamp, existing.KeyUpdateTimeStamp)
+}
+
+func TestCreateKey(t *testing.T) {
+	createNet()
+	var accesskey models.AccessKey
+	var network models.Network
+	network.NetID = "skynet"
+	t.Run("InvalidName", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Name = "bad-name"
+		_, err = CreateAccessKey(accesskey, network)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'alphanum' tag")
+	})
+	t.Run("NameTooLong", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Name = "Thisisareallylongkeynamethatwillfail"
+		_, err = CreateAccessKey(accesskey, network)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'max' tag")
+	})
+	t.Run("BlankName", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Name = ""
+		key, err := CreateAccessKey(accesskey, network)
+		assert.Nil(t, err)
+		assert.NotEqual(t, "", key.Name)
+	})
+	t.Run("InvalidValue", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Value = "bad-value"
+		_, err = CreateAccessKey(accesskey, network)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Value' failed on the 'alphanum' tag")
+	})
+	t.Run("BlankValue", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Name = "mykey"
+		accesskey.Value = ""
+		key, err := CreateAccessKey(accesskey, network)
+		assert.Nil(t, err)
+		assert.NotEqual(t, "", key.Value)
+		assert.Equal(t, accesskey.Name, key.Name)
+	})
+	t.Run("ValueTooLong", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Name = "keyname"
+		accesskey.Value = "AccessKeyValuethatistoolong"
+		_, err = CreateAccessKey(accesskey, network)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Value' failed on the 'max' tag")
+	})
+	t.Run("BlankUses", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Uses = 0
+		accesskey.Value = ""
+		key, err := CreateAccessKey(accesskey, network)
+		assert.Nil(t, err)
+		assert.Equal(t, 1, key.Uses)
+	})
+	t.Run("DuplicateKey", func(t *testing.T) {
+		network, err := GetNetwork("skynet")
+		assert.Nil(t, err)
+		accesskey.Name = "mykey"
+		_, err = CreateAccessKey(accesskey, network)
+		assert.NotNil(t, err)
+		assert.Equal(t, "Duplicate AccessKey Name", err.Error())
+	})
+}
+func TestGetKeys(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	network, err := GetNetwork("skynet")
+	assert.Nil(t, err)
+	var key models.AccessKey
+	key.Name = "mykey"
+	_, err = CreateAccessKey(key, network)
+	assert.Nil(t, err)
+	t.Run("KeyExists", func(t *testing.T) {
+		keys, err := GetKeys(network.NetID)
+		assert.Nil(t, err)
+		assert.NotEqual(t, models.AccessKey{}, keys)
+	})
+	t.Run("NonExistantKey", func(t *testing.T) {
+		err := DeleteKey("mykey", "skynet")
+		assert.Nil(t, err)
+		keys, err := GetKeys(network.NetID)
+		assert.Nil(t, err)
+		assert.Equal(t, []models.AccessKey(nil), keys)
+	})
+}
+func TestDeleteKey(t *testing.T) {
+	createNet()
+	network, err := GetNetwork("skynet")
+	assert.Nil(t, err)
+	var key models.AccessKey
+	key.Name = "mykey"
+	_, err = CreateAccessKey(key, network)
+	assert.Nil(t, err)
+	t.Run("ExistingKey", func(t *testing.T) {
+		err := DeleteKey("mykey", "skynet")
+		assert.Nil(t, err)
+	})
+	t.Run("NonExistantKey", func(t *testing.T) {
+		err := DeleteKey("mykey", "skynet")
+		assert.NotNil(t, err)
+		assert.Equal(t, "key mykey does not exist", err.Error())
+	})
+}
+func TestSecurityCheck(t *testing.T) {
+	t.Run("NoNetwork", func(t *testing.T) {
+		err := SecurityCheck("", "Bearer secretkey")
+		assert.Nil(t, err)
+	})
+	t.Run("WithNetwork", func(t *testing.T) {
+		err := SecurityCheck("skynet", "Bearer secretkey")
+		assert.Nil(t, err)
+	})
+	t.Run("BadNet", func(t *testing.T) {
+		err := SecurityCheck("badnet", "Bearer secretkey")
+		assert.NotNil(t, err)
+		t.Log(err)
+	})
+	t.Run("BadToken", func(t *testing.T) {
+		err := SecurityCheck("skynet", "Bearer badkey")
+		assert.NotNil(t, err)
+		t.Log(err)
+	})
+}
+func TestValidateNetworkUpdate(t *testing.T) {
+	//yes := true
+	//no := false
+	deleteNet(t)
+	//DeleteNetworks
+	cases := []NetworkValidationTestCase{
+		NetworkValidationTestCase{
+			testname: "InvalidAddress",
+			network: models.Network{
+				AddressRange: "10.0.0.256",
+			},
+			errMessage: "Field validation for 'AddressRange' failed on the 'cidr' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "InvalidAddress6",
+			network: models.Network{
+				AddressRange6: "2607::ag",
+			},
+			errMessage: "Field validation for 'AddressRange6' failed on the 'cidr' tag",
+		},
+
+		NetworkValidationTestCase{
+			testname: "BadDisplayName",
+			network: models.Network{
+				DisplayName: "skynet*",
+			},
+			errMessage: "Field validation for 'DisplayName' failed on the 'alphanum' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "DisplayNameTooLong",
+			network: models.Network{
+				DisplayName: "Thisisareallylongdisplaynamethatistoolong",
+			},
+			errMessage: "Field validation for 'DisplayName' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "DisplayNameTooShort",
+			network: models.Network{
+				DisplayName: "1",
+			},
+			errMessage: "Field validation for 'DisplayName' failed on the 'min' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "InvalidNetID",
+			network: models.Network{
+				NetID: "contains spaces",
+			},
+			errMessage: "Field validation for 'NetID' failed on the 'alphanum' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "NetIDTooLong",
+			network: models.Network{
+				NetID: "LongNetIDName",
+			},
+			errMessage: "Field validation for 'NetID' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "ListenPortTooLow",
+			network: models.Network{
+				DefaultListenPort: 1023,
+			},
+			errMessage: "Field validation for 'DefaultListenPort' failed on the 'min' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "ListenPortTooHigh",
+			network: models.Network{
+				DefaultListenPort: 65536,
+			},
+			errMessage: "Field validation for 'DefaultListenPort' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "KeepAliveTooBig",
+			network: models.Network{
+				DefaultKeepalive: 1010,
+			},
+			errMessage: "Field validation for 'DefaultKeepalive' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "InvalidLocalRange",
+			network: models.Network{
+				LocalRange: "192.168.0.1",
+			},
+			errMessage: "Field validation for 'LocalRange' failed on the 'cidr' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "CheckInIntervalTooBig",
+			network: models.Network{
+				DefaultCheckInInterval: 100001,
+			},
+			errMessage: "Field validation for 'DefaultCheckInInterval' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "CheckInIntervalTooSmall",
+			network: models.Network{
+				DefaultCheckInInterval: 1,
+			},
+			errMessage: "Field validation for 'DefaultCheckInInterval' failed on the 'min' tag",
+		},
+	}
+	for _, tc := range cases {
+		t.Run(tc.testname, func(t *testing.T) {
+			network := models.NetworkUpdate(tc.network)
+			err := ValidateNetworkUpdate(network)
+			assert.NotNil(t, err)
+			assert.Contains(t, err.Error(), tc.errMessage)
+		})
+	}
+}
+func TestValidateNetworkCreate(t *testing.T) {
+	yes := true
+	no := false
+	deleteNet(t)
+	//DeleteNetworks
+	cases := []NetworkValidationTestCase{
+		NetworkValidationTestCase{
+			testname: "InvalidAddress",
+			network: models.Network{
+				AddressRange: "10.0.0.256",
+				NetID:        "skynet",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'AddressRange' failed on the 'cidr' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "BadDisplayName",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "skynet",
+				DisplayName:  "skynet*",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'DisplayName' failed on the 'alphanum' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "DisplayNameTooLong",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "skynet",
+				DisplayName:  "Thisisareallylongdisplaynamethatistoolong",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'DisplayName' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "DisplayNameTooShort",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "skynet",
+				DisplayName:  "1",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'DisplayName' failed on the 'min' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "NetIDMissing",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'NetID' failed on the 'required' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "InvalidNetID",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "contains spaces",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'NetID' failed on the 'alphanum' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "NetIDTooShort",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'NetID' failed on the 'required' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "NetIDTooLong",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "LongNetIDName",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'NetID' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "ListenPortTooLow",
+			network: models.Network{
+				AddressRange:      "10.0.0.1/24",
+				NetID:             "skynet",
+				DefaultListenPort: 1023,
+				IsDualStack:       &no,
+			},
+			errMessage: "Field validation for 'DefaultListenPort' failed on the 'min' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "ListenPortTooHigh",
+			network: models.Network{
+				AddressRange:      "10.0.0.1/24",
+				NetID:             "skynet",
+				DefaultListenPort: 65536,
+				IsDualStack:       &no,
+			},
+			errMessage: "Field validation for 'DefaultListenPort' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "KeepAliveTooBig",
+			network: models.Network{
+				AddressRange:     "10.0.0.1/24",
+				NetID:            "skynet",
+				DefaultKeepalive: 1010,
+				IsDualStack:      &no,
+			},
+			errMessage: "Field validation for 'DefaultKeepalive' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "InvalidLocalRange",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "skynet",
+				LocalRange:   "192.168.0.1",
+				IsDualStack:  &no,
+			},
+			errMessage: "Field validation for 'LocalRange' failed on the 'cidr' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "DualStackWithoutIPv6",
+			network: models.Network{
+				AddressRange: "10.0.0.1/24",
+				NetID:        "skynet",
+				IsDualStack:  &yes,
+			},
+			errMessage: "Field validation for 'AddressRange6' failed on the 'addressrange6_valid' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "CheckInIntervalTooBig",
+			network: models.Network{
+				AddressRange:           "10.0.0.1/24",
+				NetID:                  "skynet",
+				IsDualStack:            &no,
+				DefaultCheckInInterval: 100001,
+			},
+			errMessage: "Field validation for 'DefaultCheckInInterval' failed on the 'max' tag",
+		},
+		NetworkValidationTestCase{
+			testname: "CheckInIntervalTooSmall",
+			network: models.Network{
+				AddressRange:           "10.0.0.1/24",
+				NetID:                  "skynet",
+				IsDualStack:            &no,
+				DefaultCheckInInterval: 1,
+			},
+			errMessage: "Field validation for 'DefaultCheckInInterval' failed on the 'min' tag",
+		},
+	}
+	for _, tc := range cases {
+		t.Run(tc.testname, func(t *testing.T) {
+			err := ValidateNetworkCreate(tc.network)
+			assert.NotNil(t, err)
+			assert.Contains(t, err.Error(), tc.errMessage)
+		})
+	}
+	t.Run("DuplicateNetID", func(t *testing.T) {
+		deleteNet(t)
+		var net1, net2 models.Network
+		net1.NetID = "skynet"
+		net1.AddressRange = "10.0.0.1/24"
+		net1.DisplayName = "mynetwork"
+		net2.NetID = "skynet"
+		net2.AddressRange = "10.0.1.1/24"
+		net2.IsDualStack = &no
+
+		err := CreateNetwork(net1)
+		assert.Nil(t, err)
+		err = ValidateNetworkCreate(net2)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'NetID' failed on the 'netid_valid' tag")
+	})
+	t.Run("DuplicateDisplayName", func(t *testing.T) {
+		var network models.Network
+		network.NetID = "wirecat"
+		network.AddressRange = "10.0.100.1/24"
+		network.IsDualStack = &no
+		network.DisplayName = "mynetwork"
+		err := ValidateNetworkCreate(network)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'DisplayName' failed on the 'displayname_unique' tag")
+	})
+
+}

+ 208 - 220
controllers/nodeGrpcController.go

@@ -1,11 +1,12 @@
 package controller
 package controller
 
 
 import (
 import (
-        "context"
+	"context"
 	"fmt"
 	"fmt"
+
+	"github.com/gravitl/netmaker/functions"
 	nodepb "github.com/gravitl/netmaker/grpc"
 	nodepb "github.com/gravitl/netmaker/grpc"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/codes"
@@ -15,12 +16,12 @@ import (
 type NodeServiceServer struct {
 type NodeServiceServer struct {
 	NodeDB *mongo.Collection
 	NodeDB *mongo.Collection
 	nodepb.UnimplementedNodeServiceServer
 	nodepb.UnimplementedNodeServiceServer
-
 }
 }
+
 func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeReq) (*nodepb.ReadNodeRes, error) {
 func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeReq) (*nodepb.ReadNodeRes, error) {
 	// convert string id (from proto) to mongoDB ObjectId
 	// convert string id (from proto) to mongoDB ObjectId
 	macaddress := req.GetMacaddress()
 	macaddress := req.GetMacaddress()
-        networkName := req.GetNetwork()
+	networkName := req.GetNetwork()
 	network, _ := functions.GetParentNetwork(networkName)
 	network, _ := functions.GetParentNetwork(networkName)
 
 
 	node, err := GetNode(macaddress, networkName)
 	node, err := GetNode(macaddress, networkName)
@@ -30,45 +31,42 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeRe
 	}
 	}
 
 
 	/*
 	/*
-	if node == nil {
-		return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not find node with Mac Address %s: %v", req.GetMacaddress(), err))
-	}
+		if node == nil {
+			return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not find node with Mac Address %s: %v", req.GetMacaddress(), err))
+		}
 	*/
 	*/
 	// Cast to ReadNodeRes type
 	// Cast to ReadNodeRes type
 	dualvar := false
 	dualvar := false
-        if network.IsDualStack != nil {
-                dualvar = *network.IsDualStack
-        }
+	if network.IsDualStack != nil {
+		dualvar = *network.IsDualStack
+	}
 	localvar := false
 	localvar := false
-        if network.IsLocal != nil {
-                localvar = *network.IsLocal
-        }
-
-
+	if network.IsLocal != nil {
+		localvar = *network.IsLocal
+	}
 
 
 	response := &nodepb.ReadNodeRes{
 	response := &nodepb.ReadNodeRes{
 		Node: &nodepb.Node{
 		Node: &nodepb.Node{
-			Macaddress: node.MacAddress,
-			Name:    node.Name,
-			Address:  node.Address,
-			Address6:  node.Address6,
-			Endpoint:  node.Endpoint,
-			Password:  node.Password,
-			Nodenetwork:  node.Network,
-			Interface:  node.Interface,
-			Localaddress:  node.LocalAddress,
-			Postdown:  node.PostDown,
-			Postup:  node.PostUp,
-			Checkininterval:  node.CheckInInterval,
-			Dnsoff:  !servercfg.IsDNSMode(),
-			Ispending:  node.IsPending,
-			Publickey:  node.PublicKey,
-			Listenport:  node.ListenPort,
-			Keepalive:  node.PersistentKeepalive,
-                        Islocal:  localvar,
-                        Isdualstack:  dualvar,
-                        Localrange:  network.LocalRange,
-
+			Macaddress:      node.MacAddress,
+			Name:            node.Name,
+			Address:         node.Address,
+			Address6:        node.Address6,
+			Endpoint:        node.Endpoint,
+			Password:        node.Password,
+			Nodenetwork:     node.Network,
+			Interface:       node.Interface,
+			Localaddress:    node.LocalAddress,
+			Postdown:        node.PostDown,
+			Postup:          node.PostUp,
+			Checkininterval: node.CheckInInterval,
+			Dnsoff:          !servercfg.IsDNSMode(),
+			Ispending:       node.IsPending,
+			Publickey:       node.PublicKey,
+			Listenport:      node.ListenPort,
+			Keepalive:       node.PersistentKeepalive,
+			Islocal:         localvar,
+			Isdualstack:     dualvar,
+			Localrange:      network.LocalRange,
 		},
 		},
 	}
 	}
 	return response, nil
 	return response, nil
@@ -81,53 +79,51 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
 	// Now we have to convert this into a NodeItem type to convert into BSON
 	// Now we have to convert this into a NodeItem type to convert into BSON
 	node := models.Node{
 	node := models.Node{
 		// ID:       primitive.NilObjectID,
 		// ID:       primitive.NilObjectID,
-                        MacAddress: data.GetMacaddress(),
-                        LocalAddress: data.GetLocaladdress(),
-                        Name:    data.GetName(),
-                        Address:  data.GetAddress(),
-                        Address6:  data.GetAddress6(),
-                        AccessKey:  data.GetAccesskey(),
-                        Endpoint:  data.GetEndpoint(),
-                        PersistentKeepalive:  data.GetKeepalive(),
-                        Password:  data.GetPassword(),
-                        Interface:  data.GetInterface(),
-                        Network:  data.GetNodenetwork(),
-                        IsPending:  data.GetIspending(),
-                        PublicKey:  data.GetPublickey(),
-                        ListenPort:  data.GetListenport(),
+		MacAddress:          data.GetMacaddress(),
+		LocalAddress:        data.GetLocaladdress(),
+		Name:                data.GetName(),
+		Address:             data.GetAddress(),
+		Address6:            data.GetAddress6(),
+		AccessKey:           data.GetAccesskey(),
+		Endpoint:            data.GetEndpoint(),
+		PersistentKeepalive: data.GetKeepalive(),
+		Password:            data.GetPassword(),
+		Interface:           data.GetInterface(),
+		Network:             data.GetNodenetwork(),
+		IsPending:           data.GetIspending(),
+		PublicKey:           data.GetPublickey(),
+		ListenPort:          data.GetListenport(),
 	}
 	}
 
 
-        err := ValidateNodeCreate(node.Network, node)
+	err := ValidateNodeCreate(node.Network, node)
 
 
-        if err != nil {
-                // return internal gRPC error to be handled later
-                return nil, err
-        }
+	if err != nil {
+		// return internal gRPC error to be handled later
+		return nil, err
+	}
 
 
-        //Check to see if key is valid
-        //TODO: Triple inefficient!!! This is the third call to the DB we make for networks
-        validKey := functions.IsKeyValid(node.Network, node.AccessKey)
-        network, err := functions.GetParentNetwork(node.Network)
-        if err != nil {
-                return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not find network: %v", err))
-        } else {
+	//Check to see if key is valid
+	//TODO: Triple inefficient!!! This is the third call to the DB we make for networks
+	validKey := functions.IsKeyValid(node.Network, node.AccessKey)
+	network, err := functions.GetParentNetwork(node.Network)
+	if err != nil {
+		return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not find network: %v", err))
+	} else {
 		fmt.Println("Creating node in network " + network.NetID)
 		fmt.Println("Creating node in network " + network.NetID)
 	}
 	}
 
 
-
-
-        if !validKey {
-                //Check to see if network will allow manual sign up
-                //may want to switch this up with the valid key check and avoid a DB call that way.
-                if *network.AllowManualSignUp {
-                        node.IsPending = true
-                } else  {
-	                return nil, status.Errorf(
-		                codes.Internal,
+	if !validKey {
+		//Check to see if network will allow manual sign up
+		//may want to switch this up with the valid key check and avoid a DB call that way.
+		if *network.AllowManualSignUp {
+			node.IsPending = true
+		} else {
+			return nil, status.Errorf(
+				codes.Internal,
 				fmt.Sprintf("Invalid key, and network does not allow no-key signups"),
 				fmt.Sprintf("Invalid key, and network does not allow no-key signups"),
 			)
 			)
-                }
-        }
+		}
+	}
 
 
 	node, err = CreateNode(node, node.Network)
 	node, err = CreateNode(node, node.Network)
 
 
@@ -138,135 +134,131 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
 			fmt.Sprintf("Internal error: %v", err),
 			fmt.Sprintf("Internal error: %v", err),
 		)
 		)
 	}
 	}
-        dualvar := false
-        if network.IsDualStack != nil {
-                dualvar = *network.IsDualStack
-        }
-        localvar := false
-        if network.IsLocal != nil {
-                localvar = *network.IsLocal
-        }
+	dualvar := false
+	if network.IsDualStack != nil {
+		dualvar = *network.IsDualStack
+	}
+	localvar := false
+	if network.IsLocal != nil {
+		localvar = *network.IsLocal
+	}
 
 
 	// return the node in a CreateNodeRes type
 	// return the node in a CreateNodeRes type
 	response := &nodepb.CreateNodeRes{
 	response := &nodepb.CreateNodeRes{
 		Node: &nodepb.Node{
 		Node: &nodepb.Node{
-                        Macaddress: node.MacAddress,
-                        Localaddress: node.LocalAddress,
-                        Name:    node.Name,
-                        Address:  node.Address,
-                        Address6:  node.Address6,
-                        Endpoint:  node.Endpoint,
-                        Password:  node.Password,
-                        Interface:  node.Interface,
-                        Nodenetwork:  node.Network,
-			Dnsoff:  !servercfg.IsDNSMode(),
-                        Ispending:  node.IsPending,
-                        Publickey:  node.PublicKey,
-                        Listenport:  node.ListenPort,
-                        Keepalive:  node.PersistentKeepalive,
-                        Islocal:  localvar,
-                        Isdualstack:  dualvar,
-                        Localrange:  network.LocalRange,
+			Macaddress:   node.MacAddress,
+			Localaddress: node.LocalAddress,
+			Name:         node.Name,
+			Address:      node.Address,
+			Address6:     node.Address6,
+			Endpoint:     node.Endpoint,
+			Password:     node.Password,
+			Interface:    node.Interface,
+			Nodenetwork:  node.Network,
+			Dnsoff:       !servercfg.IsDNSMode(),
+			Ispending:    node.IsPending,
+			Publickey:    node.PublicKey,
+			Listenport:   node.ListenPort,
+			Keepalive:    node.PersistentKeepalive,
+			Islocal:      localvar,
+			Isdualstack:  dualvar,
+			Localrange:   network.LocalRange,
 		},
 		},
 	}
 	}
-        err = SetNetworkNodesLastModified(node.Network)
-        if err != nil {
-                return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not update network last modified date: %v", err))
-        }
+	err = SetNetworkNodesLastModified(node.Network)
+	if err != nil {
+		return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not update network last modified date: %v", err))
+	}
 
 
 	return response, nil
 	return response, nil
 }
 }
 
 
 func (s *NodeServiceServer) CheckIn(ctx context.Context, req *nodepb.CheckInReq) (*nodepb.CheckInRes, error) {
 func (s *NodeServiceServer) CheckIn(ctx context.Context, req *nodepb.CheckInReq) (*nodepb.CheckInRes, error) {
 	// Get the protobuf node type from the protobuf request type
 	// Get the protobuf node type from the protobuf request type
-        // Essentially doing req.Node to access the struct with a nil check
+	// Essentially doing req.Node to access the struct with a nil check
 	data := req.GetNode()
 	data := req.GetNode()
 	//postchanges := req.GetPostchanges()
 	//postchanges := req.GetPostchanges()
 	// Now we have to convert this into a NodeItem type to convert into BSON
 	// Now we have to convert this into a NodeItem type to convert into BSON
-        node := models.Node{
-                // ID:       primitive.NilObjectID,
-                        MacAddress: data.GetMacaddress(),
-                        Address:  data.GetAddress(),
-                        Address6:  data.GetAddress6(),
-                        Endpoint:  data.GetEndpoint(),
-                        Network:  data.GetNodenetwork(),
-                        Password:  data.GetPassword(),
-                        LocalAddress:  data.GetLocaladdress(),
-                        ListenPort:  data.GetListenport(),
-                        PersistentKeepalive:  data.GetKeepalive(),
-                        PublicKey:  data.GetPublickey(),
-        }
+	node := models.Node{
+		// ID:       primitive.NilObjectID,
+		MacAddress:          data.GetMacaddress(),
+		Address:             data.GetAddress(),
+		Address6:            data.GetAddress6(),
+		Endpoint:            data.GetEndpoint(),
+		Network:             data.GetNodenetwork(),
+		Password:            data.GetPassword(),
+		LocalAddress:        data.GetLocaladdress(),
+		ListenPort:          data.GetListenport(),
+		PersistentKeepalive: data.GetKeepalive(),
+		PublicKey:           data.GetPublickey(),
+	}
 
 
 	checkinresponse, err := NodeCheckIn(node, node.Network)
 	checkinresponse, err := NodeCheckIn(node, node.Network)
 
 
-        if err != nil {
-                // return internal gRPC error to be handled later
+	if err != nil {
+		// return internal gRPC error to be handled later
 		if checkinresponse == (models.CheckInResponse{}) || !checkinresponse.IsPending {
 		if checkinresponse == (models.CheckInResponse{}) || !checkinresponse.IsPending {
-                return nil, status.Errorf(
-                        codes.Internal,
-                        fmt.Sprintf("Internal error: %v", err),
-                )
+			return nil, status.Errorf(
+				codes.Internal,
+				fmt.Sprintf("Internal error: %v", err),
+			)
 		}
 		}
-        }
-        // return the node in a CreateNodeRes type
-        response := &nodepb.CheckInRes{
-                Checkinresponse: &nodepb.CheckInResponse{
-                        Success:  checkinresponse.Success,
-                        Needpeerupdate:  checkinresponse.NeedPeerUpdate,
-                        Needdelete:  checkinresponse.NeedDelete,
-                        Needconfigupdate:  checkinresponse.NeedConfigUpdate,
-                        Needkeyupdate:  checkinresponse.NeedKeyUpdate,
-                        Nodemessage:  checkinresponse.NodeMessage,
-                        Ispending:  checkinresponse.IsPending,
-                },
-        }
-        return response, nil
+	}
+	// return the node in a CreateNodeRes type
+	response := &nodepb.CheckInRes{
+		Checkinresponse: &nodepb.CheckInResponse{
+			Success:          checkinresponse.Success,
+			Needpeerupdate:   checkinresponse.NeedPeerUpdate,
+			Needdelete:       checkinresponse.NeedDelete,
+			Needconfigupdate: checkinresponse.NeedConfigUpdate,
+			Needkeyupdate:    checkinresponse.NeedKeyUpdate,
+			Nodemessage:      checkinresponse.NodeMessage,
+			Ispending:        checkinresponse.IsPending,
+		},
+	}
+	return response, nil
 }
 }
 
 
-
 func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.UpdateNodeReq) (*nodepb.UpdateNodeRes, error) {
 func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.UpdateNodeReq) (*nodepb.UpdateNodeRes, error) {
 	// Get the node data from the request
 	// Get the node data from the request
-        data := req.GetNode()
-        // Now we have to convert this into a NodeItem type to convert into BSON
-        nodechange := models.Node{
-                // ID:       primitive.NilObjectID,
-                        MacAddress: data.GetMacaddress(),
-                        Name:    data.GetName(),
-                        Address:  data.GetAddress(),
-                        Address6:  data.GetAddress6(),
-                        LocalAddress:  data.GetLocaladdress(),
-                        Endpoint:  data.GetEndpoint(),
-                        Password:  data.GetPassword(),
-                        PersistentKeepalive:  data.GetKeepalive(),
-                        Network:  data.GetNodenetwork(),
-                        Interface:  data.GetInterface(),
-                        PostDown:  data.GetPostdown(),
-                        PostUp:  data.GetPostup(),
-                        IsPending:  data.GetIspending(),
-                        PublicKey:  data.GetPublickey(),
-                        ListenPort:  data.GetListenport(),
-        }
-
+	data := req.GetNode()
+	// Now we have to convert this into a NodeItem type to convert into BSON
+	nodechange := models.NodeUpdate{
+		// ID:       primitive.NilObjectID,
+		MacAddress:          data.GetMacaddress(),
+		Name:                data.GetName(),
+		Address:             data.GetAddress(),
+		Address6:            data.GetAddress6(),
+		LocalAddress:        data.GetLocaladdress(),
+		Endpoint:            data.GetEndpoint(),
+		Password:            data.GetPassword(),
+		PersistentKeepalive: data.GetKeepalive(),
+		Network:             data.GetNodenetwork(),
+		Interface:           data.GetInterface(),
+		PostDown:            data.GetPostdown(),
+		PostUp:              data.GetPostup(),
+		IsPending:           data.GetIspending(),
+		PublicKey:           data.GetPublickey(),
+		ListenPort:          data.GetListenport(),
+	}
 
 
 	// Convert the Id string to a MongoDB ObjectId
 	// Convert the Id string to a MongoDB ObjectId
 	macaddress := nodechange.MacAddress
 	macaddress := nodechange.MacAddress
 	networkName := nodechange.Network
 	networkName := nodechange.Network
-        network, _ := functions.GetParentNetwork(networkName)
-
+	network, _ := functions.GetParentNetwork(networkName)
 
 
 	err := ValidateNodeUpdate(networkName, nodechange)
 	err := ValidateNodeUpdate(networkName, nodechange)
-        if err != nil {
-                return nil, err
-        }
-
-        node, err := functions.GetNodeByMacAddress(networkName, macaddress)
-        if err != nil {
-               return nil, status.Errorf(
-                        codes.NotFound,
-                        fmt.Sprintf("Could not find node with supplied Mac Address: %v", err),
-                )
+	if err != nil {
+		return nil, err
 	}
 	}
 
 
+	node, err := functions.GetNodeByMacAddress(networkName, macaddress)
+	if err != nil {
+		return nil, status.Errorf(
+			codes.NotFound,
+			fmt.Sprintf("Could not find node with supplied Mac Address: %v", err),
+		)
+	}
 
 
 	newnode, err := UpdateNode(nodechange, node)
 	newnode, err := UpdateNode(nodechange, node)
 
 
@@ -276,37 +268,36 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.UpdateNo
 			fmt.Sprintf("Could not find node with supplied Mac Address: %v", err),
 			fmt.Sprintf("Could not find node with supplied Mac Address: %v", err),
 		)
 		)
 	}
 	}
-        dualvar := false
-        if network.IsDualStack != nil {
-                dualvar = *network.IsDualStack
-        }
-        localvar := false
-        if network.IsLocal != nil {
-                localvar = *network.IsLocal
-        }
+	dualvar := false
+	if network.IsDualStack != nil {
+		dualvar = *network.IsDualStack
+	}
+	localvar := false
+	if network.IsLocal != nil {
+		localvar = *network.IsLocal
+	}
 
 
 	return &nodepb.UpdateNodeRes{
 	return &nodepb.UpdateNodeRes{
 		Node: &nodepb.Node{
 		Node: &nodepb.Node{
-                        Macaddress: newnode.MacAddress,
-                        Localaddress: newnode.LocalAddress,
-                        Name:    newnode.Name,
-                        Address:  newnode.Address,
-                        Address6:  newnode.Address6,
-                        Endpoint:  newnode.Endpoint,
-                        Password:  newnode.Password,
-                        Interface:  newnode.Interface,
-                        Postdown:  newnode.PostDown,
-                        Postup:  newnode.PostUp,
-                        Nodenetwork:  newnode.Network,
-                        Ispending:  newnode.IsPending,
-                        Publickey:  newnode.PublicKey,
-			Dnsoff:  !servercfg.IsDNSMode(),
-                        Listenport:  newnode.ListenPort,
-                        Keepalive:  newnode.PersistentKeepalive,
-                        Islocal:  localvar,
-                        Isdualstack: dualvar,
-                        Localrange:  network.LocalRange,
-
+			Macaddress:   newnode.MacAddress,
+			Localaddress: newnode.LocalAddress,
+			Name:         newnode.Name,
+			Address:      newnode.Address,
+			Address6:     newnode.Address6,
+			Endpoint:     newnode.Endpoint,
+			Password:     newnode.Password,
+			Interface:    newnode.Interface,
+			Postdown:     newnode.PostDown,
+			Postup:       newnode.PostUp,
+			Nodenetwork:  newnode.Network,
+			Ispending:    newnode.IsPending,
+			Publickey:    newnode.PublicKey,
+			Dnsoff:       !servercfg.IsDNSMode(),
+			Listenport:   newnode.ListenPort,
+			Keepalive:    newnode.PersistentKeepalive,
+			Islocal:      localvar,
+			Isdualstack:  dualvar,
+			Localrange:   network.LocalRange,
 		},
 		},
 	}, nil
 	}, nil
 }
 }
@@ -326,12 +317,11 @@ func (s *NodeServiceServer) DeleteNode(ctx context.Context, req *nodepb.DeleteNo
 
 
 	fmt.Println("updating network last modified of " + req.GetNetworkName())
 	fmt.Println("updating network last modified of " + req.GetNetworkName())
 	err = SetNetworkNodesLastModified(req.GetNetworkName())
 	err = SetNetworkNodesLastModified(req.GetNetworkName())
-        if err != nil {
+	if err != nil {
 		fmt.Println("Error updating Network")
 		fmt.Println("Error updating Network")
 		fmt.Println(err)
 		fmt.Println(err)
 		return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not update network last modified date: %v", err))
 		return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not update network last modified date: %v", err))
-        }
-
+	}
 
 
 	return &nodepb.DeleteNodeRes{
 	return &nodepb.DeleteNodeRes{
 		Success: true,
 		Success: true,
@@ -349,35 +339,33 @@ func (s *NodeServiceServer) GetPeers(req *nodepb.GetPeersReq, stream nodepb.Node
 		return status.Errorf(codes.Internal, fmt.Sprintf("Unknown internal error: %v", err))
 		return status.Errorf(codes.Internal, fmt.Sprintf("Unknown internal error: %v", err))
 	}
 	}
 	// cursor.Next() returns a boolean, if false there are no more items and loop will break
 	// cursor.Next() returns a boolean, if false there are no more items and loop will break
-        for i := 0; i < len(peers); i++ {
+	for i := 0; i < len(peers); i++ {
 
 
 		// If no error is found send node over stream
 		// If no error is found send node over stream
 		stream.Send(&nodepb.GetPeersRes{
 		stream.Send(&nodepb.GetPeersRes{
 			Peers: &nodepb.PeersResponse{
 			Peers: &nodepb.PeersResponse{
-                            Address:  peers[i].Address,
-                            Address6:  peers[i].Address6,
-                            Endpoint:  peers[i].Endpoint,
-                            Gatewayrange:  peers[i].GatewayRange,
-                            Isgateway:  peers[i].IsGateway,
-                            Publickey:  peers[i].PublicKey,
-                            Keepalive:  peers[i].KeepAlive,
-                            Listenport:  peers[i].ListenPort,
-                            Localaddress:  peers[i].LocalAddress,
+				Address:      peers[i].Address,
+				Address6:     peers[i].Address6,
+				Endpoint:     peers[i].Endpoint,
+				Gatewayrange: peers[i].GatewayRange,
+				Isgateway:    peers[i].IsGateway,
+				Publickey:    peers[i].PublicKey,
+				Keepalive:    peers[i].KeepAlive,
+				Listenport:   peers[i].ListenPort,
+				Localaddress: peers[i].LocalAddress,
 			},
 			},
 		})
 		})
 	}
 	}
 
 
 	node, err := functions.GetNodeByMacAddress(req.GetNetwork(), req.GetMacaddress())
 	node, err := functions.GetNodeByMacAddress(req.GetNetwork(), req.GetMacaddress())
-       if err != nil {
-                return status.Errorf(codes.Internal, fmt.Sprintf("Could not get node: %v", err))
-        }
-
+	if err != nil {
+		return status.Errorf(codes.Internal, fmt.Sprintf("Could not get node: %v", err))
+	}
 
 
 	err = TimestampNode(node, false, true, false)
 	err = TimestampNode(node, false, true, false)
-        if err != nil {
-                return status.Errorf(codes.Internal, fmt.Sprintf("Internal error occurred: %v", err))
-        }
-
+	if err != nil {
+		return status.Errorf(codes.Internal, fmt.Sprintf("Internal error occurred: %v", err))
+	}
 
 
 	return nil
 	return nil
 }
 }

+ 101 - 167
controllers/nodeHttpController.go

@@ -243,97 +243,61 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
 
 
 	var nodes []models.ReturnNode
 	var nodes []models.ReturnNode
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
+	nodes, err := GetNetworkNodes(params["network"])
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
 
-	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	//Returns all the nodes in JSON format
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(nodes)
+}
 
 
+func GetNetworkNodes(network string) ([]models.ReturnNode, error) {
+	var nodes []models.ReturnNode
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-	filter := bson.M{"network": params["network"]}
-
+	filter := bson.M{"network": network}
 	//Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
 	//Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
 	cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
 	cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return []models.ReturnNode{}, err
 	}
 	}
-
 	defer cancel()
 	defer cancel()
-
 	for cur.Next(context.TODO()) {
 	for cur.Next(context.TODO()) {
-
 		//Using a different model for the ReturnNode (other than regular node).
 		//Using a different model for the ReturnNode (other than regular node).
 		//Either we should do this for ALL structs (so Networks and Keys)
 		//Either we should do this for ALL structs (so Networks and Keys)
 		//OR we should just use the original struct
 		//OR we should just use the original struct
 		//My preference is to make some new return structs
 		//My preference is to make some new return structs
 		//TODO: Think about this. Not an immediate concern. Just need to get some consistency eventually
 		//TODO: Think about this. Not an immediate concern. Just need to get some consistency eventually
 		var node models.ReturnNode
 		var node models.ReturnNode
-
 		err := cur.Decode(&node)
 		err := cur.Decode(&node)
 		if err != nil {
 		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
+			return []models.ReturnNode{}, err
 		}
 		}
-
 		// add item our array of nodes
 		// add item our array of nodes
 		nodes = append(nodes, node)
 		nodes = append(nodes, node)
 	}
 	}
-
 	//TODO: Another fatal error we should take care of.
 	//TODO: Another fatal error we should take care of.
 	if err := cur.Err(); err != nil {
 	if err := cur.Err(); err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return []models.ReturnNode{}, err
 	}
 	}
-
-	//Returns all the nodes in JSON format
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(nodes)
-
+	return nodes, nil
 }
 }
 
 
 //A separate function to get all nodes, not just nodes for a particular network.
 //A separate function to get all nodes, not just nodes for a particular network.
 //Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
 //Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
 func getAllNodes(w http.ResponseWriter, r *http.Request) {
 func getAllNodes(w http.ResponseWriter, r *http.Request) {
-
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
-	var nodes []models.ReturnNode
-
-	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-	// Filter out them ID's again
-	cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0}))
+	nodes, err := functions.GetAllNodes()
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
-
-	defer cancel()
-
-	for cur.Next(context.TODO()) {
-
-		var node models.ReturnNode
-		err := cur.Decode(&node)
-		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
-		}
-		// add node to our array
-		nodes = append(nodes, node)
-	}
-
-	//TODO: Fatal error
-	if err := cur.Err(); err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
-	}
-
 	//Return all the nodes in JSON format
 	//Return all the nodes in JSON format
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(nodes)
 	json.NewEncoder(w).Encode(nodes)
-
 }
 }
 
 
 //This function get's called when a node "checks in" at check in interval
 //This function get's called when a node "checks in" at check in interval
@@ -353,49 +317,43 @@ func checkIn(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
+	node, err := CheckIn(params["network"], params["macaddress"])
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(node)
+}
+func CheckIn(network, macaddress string) (models.Node, error) {
 	var node models.Node
 	var node models.Node
 
 
 	//Retrieves node with DB Call which is inefficient. Let's just get the time and set it.
 	//Retrieves node with DB Call which is inefficient. Let's just get the time and set it.
 	//node = functions.GetNodeByMacAddress(params["network"], params["macaddress"])
 	//node = functions.GetNodeByMacAddress(params["network"], params["macaddress"])
-
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-	filter := bson.M{"macaddress": params["macaddress"], "network": params["network"]}
-
+	filter := bson.M{"macaddress": macaddress, "network": network}
 	//old code was inefficient, this is all we need.
 	//old code was inefficient, this is all we need.
 	time := time.Now().Unix()
 	time := time.Now().Unix()
-
 	//node.SetLastCheckIn()
 	//node.SetLastCheckIn()
-
 	// prepare update model with new time
 	// prepare update model with new time
 	update := bson.D{
 	update := bson.D{
 		{"$set", bson.D{
 		{"$set", bson.D{
 			{"lastcheckin", time},
 			{"lastcheckin", time},
 		}},
 		}},
 	}
 	}
-
 	err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
 	err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-
 	//TODO: check node last modified vs network last modified
 	//TODO: check node last modified vs network last modified
 	//Get Updated node to return
 	//Get Updated node to return
-	node, err = GetNode(params["macaddress"], params["network"])
+	node, err = GetNode(macaddress, network)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
-
+	return node, nil
 }
 }
 
 
 //Get an individual node. Nothin fancy here folks.
 //Get an individual node. Nothin fancy here folks.
@@ -422,26 +380,28 @@ func getLastModified(w http.ResponseWriter, r *http.Request) {
 	// set header.
 	// set header.
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
-	var network models.Network
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
-	collection := mongoconn.Client.Database("netmaker").Collection("networks")
-
-	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
-	filter := bson.M{"netid": params["network"]}
-	err := collection.FindOne(ctx, filter).Decode(&network)
-
-	defer cancel()
-
+	network, err := GetLastModified(params["network"])
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
-
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	w.Write([]byte(string(network.NodesLastModified)))
+	json.NewEncoder(w).Encode(network.NodesLastModified)
+}
 
 
+func GetLastModified(network string) (models.Network, error) {
+	var net models.Network
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	filter := bson.M{"netid": network}
+	err := collection.FindOne(ctx, filter).Decode(&net)
+	defer cancel()
+	if err != nil {
+		fmt.Println(err)
+		return models.Network{}, err
+	}
+	return net, nil
 }
 }
 
 
 //This one's a doozy
 //This one's a doozy
@@ -527,57 +487,47 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 //Takes node out of pending state
 //Takes node out of pending state
 //TODO: May want to use cordon/uncordon terminology instead of "ispending".
 //TODO: May want to use cordon/uncordon terminology instead of "ispending".
 func uncordonNode(w http.ResponseWriter, r *http.Request) {
 func uncordonNode(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
-	var node models.Node
-
-	node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
+	w.Header().Set("Content-Type", "application/json")
+	node, err := UncordonNode(params["network"], params["macaddress"])
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
+	fmt.Println("Node " + node.Name + " uncordoned.")
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode("SUCCESS")
+}
 
 
+func UncordonNode(network, macaddress string) (models.Node, error) {
+	node, err := functions.GetNodeByMacAddress(network, macaddress)
+	if err != nil {
+		return models.Node{}, err
+	}
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
 	// Create filter
 	// Create filter
-	filter := bson.M{"macaddress": params["macaddress"], "network": params["network"]}
-
+	filter := bson.M{"macaddress": macaddress, "network": network}
 	node.SetLastModified()
 	node.SetLastModified()
-
 	fmt.Println("Uncordoning node " + node.Name)
 	fmt.Println("Uncordoning node " + node.Name)
-
 	// prepare update model.
 	// prepare update model.
 	update := bson.D{
 	update := bson.D{
 		{"$set", bson.D{
 		{"$set", bson.D{
 			{"ispending", false},
 			{"ispending", false},
 		}},
 		}},
 	}
 	}
-
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-
-	fmt.Println("Node " + node.Name + " uncordoned.")
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode("SUCCESS")
+	return node, nil
 }
 }
 
 
 func createGateway(w http.ResponseWriter, r *http.Request) {
 func createGateway(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-
 	var gateway models.GatewayRequest
 	var gateway models.GatewayRequest
-
+	var params = mux.Vars(r)
+	w.Header().Set("Content-Type", "application/json")
 	err := json.NewDecoder(r.Body).Decode(&gateway)
 	err := json.NewDecoder(r.Body).Decode(&gateway)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
@@ -585,21 +535,25 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	gateway.NetID = params["network"]
 	gateway.NetID = params["network"]
 	gateway.NodeID = params["macaddress"]
 	gateway.NodeID = params["macaddress"]
-
-	node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
+	node, err := CreateGateway(gateway)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(node)
+}
 
 
-	err = validateGateway(gateway)
+func CreateGateway(gateway models.GatewayRequest) (models.Node, error) {
+	node, err := functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
+	}
+	err = ValidateGateway(gateway)
+	if err != nil {
+		return models.Node{}, err
 	}
 	}
-
 	var nodechange models.Node
 	var nodechange models.Node
-
 	nodechange.IsGateway = true
 	nodechange.IsGateway = true
 	nodechange.GatewayRange = gateway.RangeString
 	nodechange.GatewayRange = gateway.RangeString
 	if gateway.PostUp == "" {
 	if gateway.PostUp == "" {
@@ -614,14 +568,10 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 
 
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
 	// Create filter
 	// Create filter
-	filter := bson.M{"macaddress": params["macaddress"], "network": params["network"]}
-
+	filter := bson.M{"macaddress": gateway.NodeID, "network": gateway.NetID}
 	nodechange.SetLastModified()
 	nodechange.SetLastModified()
-
 	// prepare update model.
 	// prepare update model.
 	update := bson.D{
 	update := bson.D{
 		{"$set", bson.D{
 		{"$set", bson.D{
@@ -633,33 +583,24 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
 		}},
 		}},
 	}
 	}
 	var nodeupdate models.Node
 	var nodeupdate models.Node
-
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-
-	err = SetNetworkNodesLastModified(params["network"])
+	err = SetNetworkNodesLastModified(gateway.NetID)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-
 	//Get updated values to return
 	//Get updated values to return
-	node, err = functions.GetNodeByMacAddress(params["network"], params["macaddress"])
+	node, err = functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	return node, nil
 }
 }
 
 
-func validateGateway(gateway models.GatewayRequest) error {
+func ValidateGateway(gateway models.GatewayRequest) error {
 	var err error
 	var err error
 	isIp := functions.IsIpCIDR(gateway.RangeString)
 	isIp := functions.IsIpCIDR(gateway.RangeString)
 	empty := gateway.RangeString == ""
 	empty := gateway.RangeString == ""
@@ -675,16 +616,24 @@ func validateGateway(gateway models.GatewayRequest) error {
 
 
 func deleteGateway(w http.ResponseWriter, r *http.Request) {
 func deleteGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
-	node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
+	node, err := DeleteGateway(params["network"], params["macaddress"])
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	}
 	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(node)
+}
+
+func DeleteGateway(network, macaddress string) (models.Node, error) {
 
 
+	var nodeupdate models.Node
 	var nodechange models.Node
 	var nodechange models.Node
+	node, err := functions.GetNodeByMacAddress(network, macaddress)
+	if err != nil {
+		return models.Node{}, err
+	}
 
 
 	nodechange.IsGateway = false
 	nodechange.IsGateway = false
 	nodechange.GatewayRange = ""
 	nodechange.GatewayRange = ""
@@ -692,14 +641,10 @@ func deleteGateway(w http.ResponseWriter, r *http.Request) {
 	nodechange.PostDown = ""
 	nodechange.PostDown = ""
 
 
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
 	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-
 	// Create filter
 	// Create filter
-	filter := bson.M{"macaddress": params["macaddress"], "network": params["network"]}
-
+	filter := bson.M{"macaddress": macaddress, "network": network}
 	nodechange.SetLastModified()
 	nodechange.SetLastModified()
-
 	// prepare update model.
 	// prepare update model.
 	update := bson.D{
 	update := bson.D{
 		{"$set", bson.D{
 		{"$set", bson.D{
@@ -710,32 +655,21 @@ func deleteGateway(w http.ResponseWriter, r *http.Request) {
 			{"lastmodified", nodechange.LastModified},
 			{"lastmodified", nodechange.LastModified},
 		}},
 		}},
 	}
 	}
-	var nodeupdate models.Node
-
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
-
 	defer cancel()
 	defer cancel()
-
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-
-	err = SetNetworkNodesLastModified(params["network"])
+	err = SetNetworkNodesLastModified(network)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-
 	//Get updated values to return
 	//Get updated values to return
-	node, err = functions.GetNodeByMacAddress(params["network"], params["macaddress"])
+	node, err = functions.GetNodeByMacAddress(network, macaddress)
 	if err != nil {
 	if err != nil {
-		returnErrorResponse(w, r, formatError(err, "internal"))
-		return
+		return models.Node{}, err
 	}
 	}
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(node)
+	return node, nil
 }
 }
 
 
 func updateNode(w http.ResponseWriter, r *http.Request) {
 func updateNode(w http.ResponseWriter, r *http.Request) {
@@ -755,7 +689,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	var nodechange models.Node
+	var nodechange models.NodeUpdate
 
 
 	// we decode our body request params
 	// we decode our body request params
 	_ = json.NewDecoder(r.Body).Decode(&nodechange)
 	_ = json.NewDecoder(r.Body).Decode(&nodechange)

+ 194 - 0
controllers/nodeHttpController_test.go

@@ -0,0 +1,194 @@
+package controller
+
+import (
+	"testing"
+	"time"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCheckIn(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	node := createTestNode(t)
+	time.Sleep(time.Second * 1)
+	t.Run("BadNet", func(t *testing.T) {
+		resp, err := CheckIn("badnet", node.MacAddress)
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, resp)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("BadMac", func(t *testing.T) {
+		resp, err := CheckIn("skynet", "01:02:03")
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, resp)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("Success", func(t *testing.T) {
+		resp, err := CheckIn("skynet", node.MacAddress)
+		assert.Nil(t, err)
+		assert.Greater(t, resp.LastCheckIn, node.LastCheckIn)
+	})
+}
+func TestCreateGateway(t *testing.T) {
+	var gateway models.GatewayRequest
+	gateway.Interface = "eth0"
+	gateway.RangeString = "10.100.100.0/24"
+	deleteNet(t)
+	createNet()
+	t.Run("NoNodes", func(t *testing.T) {
+		node, err := CreateGateway(gateway)
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, node)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("Success", func(t *testing.T) {
+		testnode := createTestNode(t)
+		gateway.NetID = "skynet"
+		gateway.NodeID = testnode.MacAddress
+
+		node, err := CreateGateway(gateway)
+		assert.Nil(t, err)
+		assert.Equal(t, true, node.IsGateway)
+		assert.Equal(t, "10.100.100.0/24", node.GatewayRange)
+	})
+
+}
+func TestDeleteGateway(t *testing.T) {
+	var gateway models.GatewayRequest
+	deleteNet(t)
+	createNet()
+	createTestNode(t)
+	testnode := createTestNode(t)
+	gateway.Interface = "eth0"
+	gateway.RangeString = "10.100.100.0/24"
+	gateway.NetID = "skynet"
+	gateway.NodeID = testnode.MacAddress
+	t.Run("Success", func(t *testing.T) {
+		node, err := CreateGateway(gateway)
+		assert.Nil(t, err)
+		assert.Equal(t, true, node.IsGateway)
+		assert.Equal(t, "10.100.100.0/24", node.GatewayRange)
+		node, err = DeleteGateway(gateway.NetID, gateway.NodeID)
+		assert.Nil(t, err)
+		assert.Equal(t, false, node.IsGateway)
+		assert.Equal(t, "", node.GatewayRange)
+		assert.Equal(t, "", node.PostUp)
+		assert.Equal(t, "", node.PostDown)
+	})
+	t.Run("NotGateway", func(t *testing.T) {
+		node, err := DeleteGateway(gateway.NetID, gateway.NodeID)
+		assert.Nil(t, err)
+		assert.Equal(t, false, node.IsGateway)
+		assert.Equal(t, "", node.GatewayRange)
+		assert.Equal(t, "", node.PostUp)
+		assert.Equal(t, "", node.PostDown)
+	})
+	t.Run("BadNode", func(t *testing.T) {
+		node, err := DeleteGateway(gateway.NetID, "01:02:03")
+		assert.NotNil(t, err)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+		assert.Equal(t, models.Node{}, node)
+	})
+	t.Run("BadNet", func(t *testing.T) {
+		node, err := DeleteGateway("badnet", gateway.NodeID)
+		assert.NotNil(t, err)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+		assert.Equal(t, models.Node{}, node)
+	})
+
+}
+func TestGetLastModified(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	createTestNode(t)
+	t.Run("BadNet", func(t *testing.T) {
+		network, err := GetLastModified("badnet")
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Network{}, network)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("Success", func(t *testing.T) {
+		network, err := GetLastModified("skynet")
+		assert.Nil(t, err)
+		assert.NotEqual(t, models.Network{}, network)
+	})
+}
+func TestGetNetworkNodes(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	t.Run("BadNet", func(t *testing.T) {
+		node, err := GetNetworkNodes("badnet")
+		assert.Nil(t, err)
+		assert.Equal(t, []models.ReturnNode(nil), node)
+		//assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("NoNodes", func(t *testing.T) {
+		node, err := GetNetworkNodes("skynet")
+		assert.Nil(t, err)
+		assert.Equal(t, []models.ReturnNode(nil), node)
+	})
+	t.Run("Success", func(t *testing.T) {
+		createTestNode(t)
+		node, err := GetNetworkNodes("skynet")
+		assert.Nil(t, err)
+		assert.NotEqual(t, []models.ReturnNode(nil), node)
+	})
+
+}
+func TestUncordonNode(t *testing.T) {
+	deleteNet(t)
+	createNet()
+	node := createTestNode(t)
+	t.Run("BadNet", func(t *testing.T) {
+		resp, err := UncordonNode("badnet", node.MacAddress)
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, resp)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("BadMac", func(t *testing.T) {
+		resp, err := UncordonNode("skynet", "01:02:03")
+		assert.NotNil(t, err)
+		assert.Equal(t, models.Node{}, resp)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+	t.Run("Success", func(t *testing.T) {
+		resp, err := CheckIn("skynet", node.MacAddress)
+		assert.Nil(t, err)
+		assert.Equal(t, false, resp.IsPending)
+	})
+
+}
+func TestValidateGateway(t *testing.T) {
+	var gateway models.GatewayRequest
+	t.Run("InvalidRange", func(t *testing.T) {
+		gateway.Interface = "eth0"
+		gateway.RangeString = "helloworld"
+		err := ValidateGateway(gateway)
+		assert.NotNil(t, err)
+		assert.Equal(t, "IP Range Not Valid", err.Error())
+	})
+	t.Run("EmptyRange", func(t *testing.T) {
+		gateway.Interface = "eth0"
+		gateway.RangeString = ""
+		err := ValidateGateway(gateway)
+		assert.NotNil(t, err)
+		assert.Equal(t, "IP Range Not Valid", err.Error())
+	})
+	t.Run("EmptyInterface", func(t *testing.T) {
+		gateway.Interface = ""
+		err := ValidateGateway(gateway)
+		assert.NotNil(t, err)
+		assert.Equal(t, "Interface cannot be empty", err.Error())
+	})
+	t.Run("Success", func(t *testing.T) {
+		gateway.Interface = "eth0"
+		gateway.RangeString = "10.100.100.0/24"
+		err := ValidateGateway(gateway)
+		assert.Nil(t, err)
+	})
+}
+
+//func TestUpdateNode(t *testing.T) {
+//}

+ 57 - 0
controllers/responseHttp_test.go

@@ -0,0 +1,57 @@
+package controller
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFormatError(t *testing.T) {
+	response := formatError(errors.New("this is a sample error"), "badrequest")
+	assert.Equal(t, http.StatusBadRequest, response.Code)
+	assert.Equal(t, "this is a sample error", response.Message)
+}
+
+func TestReturnSuccessResponse(t *testing.T) {
+	var response models.SuccessResponse
+	handler := func(rw http.ResponseWriter, r *http.Request) {
+		returnSuccessResponse(rw, r, "This is a test message")
+	}
+	req := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
+	w := httptest.NewRecorder()
+	handler(w, req)
+	resp := w.Result()
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
+	//body, err := ioutil.ReadAll(resp.Body)
+	//assert.Nil(t, err)
+	//t.Log(body, string(body))
+	err := json.NewDecoder(resp.Body).Decode(&response)
+	assert.Nil(t, err)
+	assert.Equal(t, http.StatusOK, response.Code)
+	assert.Equal(t, "This is a test message", response.Message)
+}
+
+func testReturnErrorResponse(t *testing.T) {
+	var response, errMessage models.ErrorResponse
+	errMessage.Code = http.StatusUnauthorized
+	errMessage.Message = "You are not authorized to access this endpoint"
+	handler := func(rw http.ResponseWriter, r *http.Request) {
+		returnErrorResponse(rw, r, errMessage)
+	}
+	req := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
+	w := httptest.NewRecorder()
+	handler(w, req)
+	resp := w.Result()
+	assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
+	assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
+	err := json.NewDecoder(resp.Body).Decode(&response)
+	assert.Nil(t, err)
+	assert.Equal(t, http.StatusUnauthorized, response.Code)
+	assert.Equal(t, "You are not authorized to access this endpoint", response.Message)
+}

+ 125 - 178
controllers/userHttpController.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/go-playground/validator/v10"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
@@ -17,7 +18,6 @@ import (
 	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo/options"
 	"go.mongodb.org/mongo-driver/mongo/options"
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/bcrypt"
-	"gopkg.in/go-playground/validator.v9"
 )
 )
 
 
 func userHandlers(r *mux.Router) {
 func userHandlers(r *mux.Router) {
@@ -36,7 +36,6 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 	//Auth request consists of Mac Address and Password (from node that is authorizing
 	//Auth request consists of Mac Address and Password (from node that is authorizing
 	//in case of Master, auth is ignored and mac is set to "mastermac"
 	//in case of Master, auth is ignored and mac is set to "mastermac"
 	var authRequest models.UserAuthParams
 	var authRequest models.UserAuthParams
-	var result models.User
 	var errorResponse = models.ErrorResponse{
 	var errorResponse = models.ErrorResponse{
 		Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
 		Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
 	}
 	}
@@ -44,74 +43,74 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 	decoder := json.NewDecoder(request.Body)
 	decoder := json.NewDecoder(request.Body)
 	decoderErr := decoder.Decode(&authRequest)
 	decoderErr := decoder.Decode(&authRequest)
 	defer request.Body.Close()
 	defer request.Body.Close()
-
 	if decoderErr != nil {
 	if decoderErr != nil {
 		returnErrorResponse(response, request, errorResponse)
 		returnErrorResponse(response, request, errorResponse)
 		return
 		return
-	} else {
-		errorResponse.Code = http.StatusBadRequest
-		if authRequest.UserName == "" {
-			errorResponse.Message = "W1R3: Username can't be empty"
-			returnErrorResponse(response, request, errorResponse)
-			return
-		} else if authRequest.Password == "" {
-			errorResponse.Message = "W1R3: Password can't be empty"
-			returnErrorResponse(response, request, errorResponse)
-			return
-		} 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("users")
-			ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-			var err = collection.FindOne(ctx, bson.M{"username": authRequest.UserName}).Decode(&result)
-
-			defer cancel()
-
-			if err != nil {
-				errorResponse.Message = "W1R3: User " + authRequest.UserName + " not found."
-				returnErrorResponse(response, request, errorResponse)
-				return
-			}
-
-			//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(authRequest.Password))
-			if err != nil {
-				errorResponse = models.ErrorResponse{
-					Code: http.StatusUnauthorized, Message: "W1R3: Wrong Password.",
-				}
-				returnErrorResponse(response, request, errorResponse)
-				return
-			} else {
-				//Create a new JWT for the node
-				tokenString, _ := functions.CreateUserJWT(authRequest.UserName, result.IsAdmin)
-
-				if tokenString == "" {
-					returnErrorResponse(response, request, errorResponse)
-					return
-				}
-
-				var successResponse = models.SuccessResponse{
-					Code:    http.StatusOK,
-					Message: "W1R3: Device " + authRequest.UserName + " Authorized",
-					Response: models.SuccessfulUserLoginResponse{
-						AuthToken: tokenString,
-						UserName:  authRequest.UserName,
-					},
-				}
-				//Send back the JWT
-				successJSONResponse, jsonError := json.Marshal(successResponse)
-
-				if jsonError != nil {
-					returnErrorResponse(response, request, errorResponse)
-					return
-				}
-				response.Header().Set("Content-Type", "application/json")
-				response.Write(successJSONResponse)
-			}
-		}
 	}
 	}
+
+	jwt, err := VerifyAuthRequest(authRequest)
+	if err != nil {
+		returnErrorResponse(response, request, formatError(err, "badrequest"))
+		return
+	}
+
+	if jwt == "" {
+		//very unlikely that err is !nil and no jwt returned, but handle it anyways.
+		returnErrorResponse(response, request, formatError(errors.New("No token returned"), "internal"))
+		return
+	}
+
+	var successResponse = models.SuccessResponse{
+		Code:    http.StatusOK,
+		Message: "W1R3: Device " + authRequest.UserName + " Authorized",
+		Response: models.SuccessfulUserLoginResponse{
+			AuthToken: jwt,
+			UserName:  authRequest.UserName,
+		},
+	}
+	//Send back the JWT
+	successJSONResponse, jsonError := json.Marshal(successResponse)
+
+	if jsonError != nil {
+		returnErrorResponse(response, request, errorResponse)
+		return
+	}
+	response.Header().Set("Content-Type", "application/json")
+	response.Write(successJSONResponse)
+}
+
+func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
+	var result models.User
+	if authRequest.UserName == "" {
+		return "", errors.New("Username can't be empty")
+	} else if authRequest.Password == "" {
+		return "", errors.New("Password can't be empty")
+	}
+	//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("users")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	var err = collection.FindOne(ctx, bson.M{"username": authRequest.UserName}).Decode(&result)
+
+	defer cancel()
+	if err != nil {
+		return "", errors.New("User " + authRequest.UserName + " not found")
+	}
+	// This is a a useless test as cannot create user that is not an an admin
+	if !result.IsAdmin {
+		return "", errors.New("User is not an admin")
+	}
+
+	//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(authRequest.Password))
+	if err != nil {
+		return "", errors.New("Wrong Password")
+	}
+
+	//Create a new JWT for the node
+	tokenString, _ := functions.CreateUserJWT(authRequest.UserName, true)
+	return tokenString, nil
 }
 }
 
 
 //The middleware for most requests to the API
 //The middleware for most requests to the API
@@ -123,56 +122,47 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 //TODO: Consider better RBAC implementations
 //TODO: Consider better RBAC implementations
 func authorizeUser(next http.Handler) http.HandlerFunc {
 func authorizeUser(next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
-
-		var errorResponse = models.ErrorResponse{
-			Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
-		}
-
 		w.Header().Set("Content-Type", "application/json")
 		w.Header().Set("Content-Type", "application/json")
 
 
 		//get the auth token
 		//get the auth token
 		bearerToken := r.Header.Get("Authorization")
 		bearerToken := r.Header.Get("Authorization")
-
-		var tokenSplit = strings.Split(bearerToken, " ")
-
-		//I put this in in case the user doesn't put in a token at all (in which case it's empty)
-		//There's probably a smarter way of handling this.
-		var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
-
-		if len(tokenSplit) > 1 {
-			authToken = tokenSplit[1]
-		} else {
-			errorResponse = models.ErrorResponse{
-				Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
-			}
-			returnErrorResponse(w, r, errorResponse)
+		err := ValidateToken(bearerToken)
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "unauthorized"))
 			return
 			return
 		}
 		}
+		next.ServeHTTP(w, r)
+	}
+}
 
 
-		//This checks if
-		//A: the token is the master password
-		//B: the token corresponds to a mac address, and if so, which one
-		//TODO: There's probably a better way of dealing with the "master token"/master password. Plz Halp.
-		username, _, err := functions.VerifyUserToken(authToken)
+func ValidateToken(token string) error {
+	var tokenSplit = strings.Split(token, " ")
 
 
-		if err != nil {
-			returnErrorResponse(w, r, formatError(err, "internal"))
-			return
-		}
+	//I put this in in case the user doesn't put in a token at all (in which case it's empty)
+	//There's probably a smarter way of handling this.
+	var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
 
 
-		isAuthorized := username != ""
+	if len(tokenSplit) > 1 {
+		authToken = tokenSplit[1]
+	} else {
+		return errors.New("Missing Auth Token.")
+	}
 
 
-		if !isAuthorized {
-			errorResponse = models.ErrorResponse{
-				Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
-			}
-			returnErrorResponse(w, r, errorResponse)
-			return
-		} else {
-			//If authorized, this function passes along it's request and output to the appropriate route function.
-			next.ServeHTTP(w, r)
-		}
+	//This checks if
+	//A: the token is the master password
+	//B: the token corresponds to a mac address, and if so, which one
+	//TODO: There's probably a better way of dealing with the "master token"/master password. Plz Halp.
+	username, _, err := functions.VerifyUserToken(authToken)
+	if err != nil {
+		return errors.New("Error Verifying Auth Token")
+	}
+
+	isAuthorized := username != ""
+	if !isAuthorized {
+		return errors.New("You are unauthorized to access this endpoint.")
 	}
 	}
+
+	return nil
 }
 }
 
 
 func HasAdmin() (bool, error) {
 func HasAdmin() (bool, error) {
@@ -192,8 +182,9 @@ func HasAdmin() (bool, error) {
 
 
 	if err != nil {
 	if err != nil {
 		if err == mongo.ErrNoDocuments {
 		if err == mongo.ErrNoDocuments {
-			return false, err
+			return false, nil
 		}
 		}
+		return false, err
 		fmt.Println(err)
 		fmt.Println(err)
 	}
 	}
 	return true, err
 	return true, err
@@ -237,18 +228,27 @@ func getUser(w http.ResponseWriter, r *http.Request) {
 	user, err := GetUser(params["username"])
 	user, err := GetUser(params["username"])
 
 
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
 	}
 	}
 
 
 	json.NewEncoder(w).Encode(user)
 	json.NewEncoder(w).Encode(user)
 }
 }
 
 
 func CreateUser(user models.User) (models.User, error) {
 func CreateUser(user models.User) (models.User, error) {
+	hasadmin, err := HasAdmin()
+	if hasadmin {
+		return models.User{}, errors.New("Admin already Exists")
+	}
+
+	user.IsAdmin = true
+	err = ValidateUser("create", user)
+	if err != nil {
+		return models.User{}, err
+	}
 
 
 	//encrypt that password so we never see it again
 	//encrypt that password so we never see it again
 	hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
 	hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
-
 	if err != nil {
 	if err != nil {
 		return user, err
 		return user, err
 	}
 	}
@@ -267,9 +267,7 @@ func CreateUser(user models.User) (models.User, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
 	// insert our node to the node db.
 	// insert our node to the node db.
-	result, err := collection.InsertOne(ctx, user)
-	_ = result
-
+	_, err = collection.InsertOne(ctx, user)
 	defer cancel()
 	defer cancel()
 
 
 	return user, err
 	return user, err
@@ -278,37 +276,15 @@ func CreateUser(user models.User) (models.User, error) {
 func createAdmin(w http.ResponseWriter, r *http.Request) {
 func createAdmin(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
-	var errorResponse = models.ErrorResponse{
-		Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
-	}
-
-	hasadmin, err := HasAdmin()
-
-	if hasadmin {
-		errorResponse = models.ErrorResponse{
-			Code: http.StatusUnauthorized, Message: "W1R3: Admin already exists! ",
-		}
-		returnErrorResponse(w, r, errorResponse)
-		return
-	}
-
 	var admin models.User
 	var admin models.User
-
 	//get node from body of request
 	//get node from body of request
 	_ = json.NewDecoder(r.Body).Decode(&admin)
 	_ = json.NewDecoder(r.Body).Decode(&admin)
 
 
-	admin.IsAdmin = true
-
-	err = ValidateUser("create", admin)
-	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
-	}
+	admin, err := CreateUser(admin)
 
 
-	admin, err = CreateUser(admin)
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
 	}
 	}
 
 
 	json.NewEncoder(w).Encode(admin)
 	json.NewEncoder(w).Encode(admin)
@@ -316,6 +292,11 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 
 
 func UpdateUser(userchange models.User, user models.User) (models.User, error) {
 func UpdateUser(userchange models.User, user models.User) (models.User, error) {
 
 
+	err := ValidateUser("update", userchange)
+	if err != nil {
+		return models.User{}, err
+	}
+
 	queryUser := user.UserName
 	queryUser := user.UserName
 
 
 	if userchange.UserName != "" {
 	if userchange.UserName != "" {
@@ -363,48 +344,31 @@ func UpdateUser(userchange models.User, user models.User) (models.User, error) {
 
 
 	defer cancel()
 	defer cancel()
 
 
-	return userupdate, errN
+	return user, errN
 }
 }
 
 
 func updateUser(w http.ResponseWriter, r *http.Request) {
 func updateUser(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
 	var user models.User
 	var user models.User
-
 	//start here
 	//start here
 	user, err := GetUser(params["username"])
 	user, err := GetUser(params["username"])
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
 	}
 	}
-
 	var userchange models.User
 	var userchange models.User
-
 	// we decode our body request params
 	// we decode our body request params
 	err = json.NewDecoder(r.Body).Decode(&userchange)
 	err = json.NewDecoder(r.Body).Decode(&userchange)
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
-	}
-
-	userchange.IsAdmin = true
-
-	err = ValidateUser("update", userchange)
-
-	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
 	}
 	}
-
 	user, err = UpdateUser(userchange, user)
 	user, err = UpdateUser(userchange, user)
-
 	if err != nil {
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-                return
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
 	}
 	}
-
 	json.NewEncoder(w).Encode(user)
 	json.NewEncoder(w).Encode(user)
 }
 }
 
 
@@ -454,23 +418,6 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 func ValidateUser(operation string, user models.User) error {
 func ValidateUser(operation string, user models.User) error {
 
 
 	v := validator.New()
 	v := validator.New()
-
-	_ = v.RegisterValidation("username_unique", func(fl validator.FieldLevel) bool {
-		_, err := GetUser(user.UserName)
-		return err == nil || operation == "update"
-	})
-
-	_ = v.RegisterValidation("username_valid", func(fl validator.FieldLevel) bool {
-		isvalid := functions.NameInNodeCharSet(user.UserName)
-		return isvalid
-	})
-
-	_ = v.RegisterValidation("password_check", func(fl validator.FieldLevel) bool {
-		notEmptyCheck := user.Password != ""
-		goodLength := len(user.Password) > 5
-		return (notEmptyCheck && goodLength) || operation == "update"
-	})
-
 	err := v.Struct(user)
 	err := v.Struct(user)
 
 
 	if err != nil {
 	if err != nil {

+ 257 - 0
controllers/userHttpController_test.go

@@ -0,0 +1,257 @@
+package controller
+
+import (
+	"context"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMain(m *testing.M) {
+	mongoconn.ConnectDatabase()
+	var gconf models.GlobalConfig
+	gconf.ServerGRPC = "localhost:8081"
+	gconf.PortGRPC = "50051"
+	//err := SetGlobalConfig(gconf)
+	collection := mongoconn.Client.Database("netmaker").Collection("config")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	defer cancel()
+	//create, _, err := functions.GetGlobalConfig()
+	_, err := collection.InsertOne(ctx, gconf)
+	if err != nil {
+		panic("could not create config store")
+	}
+	//drop network, nodes, and user collections
+	var collections = []string{"networks", "nodes", "users", "dns"}
+	for _, table := range collections {
+		collection := mongoconn.Client.Database("netmaker").Collection(table)
+		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+		defer cancel()
+		err := collection.Drop(ctx)
+		if err != nil {
+			panic("could not drop collection")
+		}
+	}
+	os.Exit(m.Run())
+}
+
+func TestHasAdmin(t *testing.T) {
+	_, err := DeleteUser("admin")
+	assert.Nil(t, err)
+	user := models.User{"admin", "password", true}
+	_, err = CreateUser(user)
+	assert.Nil(t, err)
+	t.Run("AdminExists", func(t *testing.T) {
+		found, err := HasAdmin()
+		assert.Nil(t, err)
+		assert.True(t, found)
+	})
+	t.Run("NoUser", func(t *testing.T) {
+		_, err := DeleteUser("admin")
+		assert.Nil(t, err)
+		found, err := HasAdmin()
+		assert.Nil(t, err)
+		assert.False(t, found)
+	})
+}
+
+func TestCreateUser(t *testing.T) {
+	user := models.User{"admin", "password", true}
+	t.Run("NoUser", func(t *testing.T) {
+		_, err := DeleteUser("admin")
+		assert.Nil(t, err)
+		admin, err := CreateUser(user)
+		assert.Nil(t, err)
+		assert.Equal(t, user.UserName, admin.UserName)
+	})
+	t.Run("AdminExists", func(t *testing.T) {
+		_, err := CreateUser(user)
+		assert.NotNil(t, err)
+		assert.Equal(t, "Admin already Exists", err.Error())
+	})
+}
+
+func TestDeleteUser(t *testing.T) {
+	hasadmin, err := HasAdmin()
+	assert.Nil(t, err)
+	if !hasadmin {
+		user := models.User{"admin", "pasword", true}
+		_, err := CreateUser(user)
+		assert.Nil(t, err)
+	}
+	t.Run("ExistingUser", func(t *testing.T) {
+		deleted, err := DeleteUser("admin")
+		assert.Nil(t, err)
+		assert.True(t, deleted)
+		t.Log(deleted, err)
+	})
+	t.Run("NonExistantUser", func(t *testing.T) {
+		deleted, err := DeleteUser("admin")
+		assert.Nil(t, err)
+		assert.False(t, deleted)
+	})
+}
+
+func TestValidateUser(t *testing.T) {
+	var user models.User
+	t.Run("ValidCreate", func(t *testing.T) {
+		user.UserName = "admin"
+		user.Password = "validpass"
+		err := ValidateUser("create", user)
+		assert.Nil(t, err)
+	})
+	t.Run("ValidUpdate", func(t *testing.T) {
+		user.UserName = "admin"
+		user.Password = "password"
+		err := ValidateUser("update", user)
+		assert.Nil(t, err)
+	})
+	t.Run("InvalidUserName", func(t *testing.T) {
+		user.UserName = "invalid*"
+		err := ValidateUser("update", user)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'UserName' failed")
+	})
+	t.Run("ShortUserName", func(t *testing.T) {
+		user.UserName = "12"
+		err := ValidateUser("create", user)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'UserName' failed")
+	})
+	t.Run("EmptyPassword", func(t *testing.T) {
+		user.Password = ""
+		err := ValidateUser("create", user)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Password' failed")
+	})
+	t.Run("ShortPassword", func(t *testing.T) {
+		user.Password = "123"
+		err := ValidateUser("create", user)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Password' failed")
+	})
+}
+
+func TestGetUser(t *testing.T) {
+	t.Run("UserExisits", func(t *testing.T) {
+		user := models.User{"admin", "password", true}
+		hasadmin, err := HasAdmin()
+		assert.Nil(t, err)
+		if !hasadmin {
+			_, err := CreateUser(user)
+			assert.Nil(t, err)
+		}
+		admin, err := GetUser("admin")
+		assert.Nil(t, err)
+		assert.Equal(t, user.UserName, admin.UserName)
+	})
+	t.Run("NonExistantUser", func(t *testing.T) {
+		_, err := DeleteUser("admin")
+		assert.Nil(t, err)
+		admin, err := GetUser("admin")
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+		assert.Equal(t, "", admin.UserName)
+	})
+}
+
+func TestUpdateUser(t *testing.T) {
+	user := models.User{"admin", "password", true}
+	newuser := models.User{"hello", "world", true}
+	t.Run("UserExisits", func(t *testing.T) {
+		_, err := DeleteUser("admin")
+		_, err = CreateUser(user)
+		assert.Nil(t, err)
+		admin, err := UpdateUser(newuser, user)
+		assert.Nil(t, err)
+		assert.Equal(t, newuser.UserName, admin.UserName)
+	})
+	t.Run("NonExistantUser", func(t *testing.T) {
+		_, err := DeleteUser("hello")
+		assert.Nil(t, err)
+		_, err = UpdateUser(newuser, user)
+		assert.Equal(t, "mongo: no documents in result", err.Error())
+	})
+}
+
+func TestValidateToken(t *testing.T) {
+	t.Run("EmptyToken", func(t *testing.T) {
+		err := ValidateToken("")
+		assert.NotNil(t, err)
+		assert.Equal(t, "Missing Auth Token.", err.Error())
+	})
+	t.Run("InvalidToken", func(t *testing.T) {
+		err := ValidateToken("Bearer: badtoken")
+		assert.NotNil(t, err)
+		assert.Equal(t, "Error Verifying Auth Token", err.Error())
+	})
+	t.Run("InvalidUser", func(t *testing.T) {
+		t.Skip()
+		//need authorization
+	})
+	t.Run("ValidToken", func(t *testing.T) {
+		err := ValidateToken("Bearer: secretkey")
+		assert.Nil(t, err)
+	})
+}
+
+func TestVerifyAuthRequest(t *testing.T) {
+	var authRequest models.UserAuthParams
+	t.Run("EmptyUserName", func(t *testing.T) {
+		authRequest.UserName = ""
+		authRequest.Password = "Password"
+		jwt, err := VerifyAuthRequest(authRequest)
+		assert.NotNil(t, err)
+		assert.Equal(t, "", jwt)
+		assert.Equal(t, "Username can't be empty", err.Error())
+	})
+	t.Run("EmptyPassword", func(t *testing.T) {
+		authRequest.UserName = "admin"
+		authRequest.Password = ""
+		jwt, err := VerifyAuthRequest(authRequest)
+		assert.NotNil(t, err)
+		assert.Equal(t, "", jwt)
+		assert.Equal(t, "Password can't be empty", err.Error())
+	})
+	t.Run("NonExistantUser", func(t *testing.T) {
+		_, err := DeleteUser("admin")
+		authRequest.UserName = "admin"
+		authRequest.Password = "password"
+		jwt, err := VerifyAuthRequest(authRequest)
+		assert.NotNil(t, err)
+		assert.Equal(t, "", jwt)
+		assert.Equal(t, "User admin not found", err.Error())
+	})
+	t.Run("Non-Admin", func(t *testing.T) {
+		//can't create a user that is not a an admin
+		t.Skip()
+		user := models.User{"admin", "admin", false}
+		_, err := CreateUser(user)
+		assert.Nil(t, err)
+		authRequest := models.UserAuthParams{"admin", "admin"}
+		jwt, err := VerifyAuthRequest(authRequest)
+		assert.NotNil(t, err)
+		assert.Equal(t, "", jwt)
+		assert.Equal(t, "User is not an admin", err.Error())
+	})
+	t.Run("WrongPassword", func(t *testing.T) {
+		_, err := DeleteUser("admin")
+		user := models.User{"admin", "password", true}
+		_, err = CreateUser(user)
+		assert.Nil(t, err)
+		authRequest := models.UserAuthParams{"admin", "badpass"}
+		jwt, err := VerifyAuthRequest(authRequest)
+		assert.NotNil(t, err)
+		assert.Equal(t, "", jwt)
+		assert.Equal(t, "Wrong Password", err.Error())
+	})
+	t.Run("Success", func(t *testing.T) {
+		authRequest := models.UserAuthParams{"admin", "password"}
+		jwt, err := VerifyAuthRequest(authRequest)
+		assert.Nil(t, err)
+		assert.NotNil(t, jwt)
+	})
+}

+ 75 - 50
functions/helpers.go

@@ -435,16 +435,15 @@ func NameInNetworkCharSet(name string) bool {
 
 
 func NameInDNSCharSet(name string) bool {
 func NameInDNSCharSet(name string) bool {
 
 
-        charset := "abcdefghijklmnopqrstuvwxyz1234567890-."
-
-        for _, char := range name {
-                if !strings.Contains(charset, strings.ToLower(string(char))) {
-                        return false
-                }
-        }
-        return true
-}
+	charset := "abcdefghijklmnopqrstuvwxyz1234567890-."
 
 
+	for _, char := range name {
+		if !strings.Contains(charset, strings.ToLower(string(char))) {
+			return false
+		}
+	}
+	return true
+}
 
 
 func NameInNodeCharSet(name string) bool {
 func NameInNodeCharSet(name string) bool {
 
 
@@ -518,34 +517,34 @@ func UniqueAddress(networkName string) (string, error) {
 
 
 func UniqueAddress6(networkName string) (string, error) {
 func UniqueAddress6(networkName string) (string, error) {
 
 
-        var network models.Network
-        network, err := GetParentNetwork(networkName)
-        if err != nil {
-                fmt.Println("Network Not Found")
-                return "", err
-        }
+	var network models.Network
+	network, err := GetParentNetwork(networkName)
+	if err != nil {
+		fmt.Println("Network Not Found")
+		return "", err
+	}
 	if network.IsDualStack == nil || *network.IsDualStack == false {
 	if network.IsDualStack == nil || *network.IsDualStack == false {
 		return "", nil
 		return "", nil
 	}
 	}
 
 
-        offset := true
-        ip, ipnet, err := net.ParseCIDR(network.AddressRange6)
-        if err != nil {
-                fmt.Println("UniqueAddress6 encountered  an error")
-                return "666", err
-        }
-        for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); Inc(ip) {
-                if offset {
-                        offset = false
-                        continue
-                }
-                if IsIP6Unique(networkName, ip.String()) {
-                        return ip.String(), err
-                }
-        }
-        //TODO
-        err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
-        return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
+	offset := true
+	ip, ipnet, err := net.ParseCIDR(network.AddressRange6)
+	if err != nil {
+		fmt.Println("UniqueAddress6 encountered  an error")
+		return "666", err
+	}
+	for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); Inc(ip) {
+		if offset {
+			offset = false
+			continue
+		}
+		if IsIP6Unique(networkName, ip.String()) {
+			return ip.String(), err
+		}
+	}
+	//TODO
+	err1 := errors.New("ERROR: No unique addresses available. Check network subnet.")
+	return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
 }
 }
 
 
 //generate an access key value
 //generate an access key value
@@ -580,7 +579,7 @@ func GenKeyName() string {
 	for i := range b {
 	for i := range b {
 		b[i] = charset[seededRand.Intn(len(charset))]
 		b[i] = charset[seededRand.Intn(len(charset))]
 	}
 	}
-	return "key-" + string(b)
+	return "key" + string(b)
 }
 }
 
 
 //checks if IP is unique in the address range
 //checks if IP is unique in the address range
@@ -615,31 +614,30 @@ func IsIPUnique(network string, ip string) bool {
 //used by UniqueAddress
 //used by UniqueAddress
 func IsIP6Unique(network string, ip string) bool {
 func IsIP6Unique(network string, ip string) bool {
 
 
-        var node models.Node
+	var node models.Node
 
 
-        isunique := true
+	isunique := true
 
 
-        collection := mongoconn.Client.Database("netmaker").Collection("nodes")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
-        filter := bson.M{"address6": ip, "network": network}
+	filter := bson.M{"address6": ip, "network": network}
 
 
-        err := collection.FindOne(ctx, filter).Decode(&node)
+	err := collection.FindOne(ctx, filter).Decode(&node)
 
 
-        defer cancel()
+	defer cancel()
 
 
-        if err != nil {
-                fmt.Println(err)
-                return isunique
-        }
+	if err != nil {
+		fmt.Println(err)
+		return isunique
+	}
 
 
-        if node.Address6 == ip {
-                isunique = false
-        }
-        return isunique
+	if node.Address6 == ip {
+		isunique = false
+	}
+	return isunique
 }
 }
 
 
-
 //called once key has been used by createNode
 //called once key has been used by createNode
 //reduces value by one and deletes if necessary
 //reduces value by one and deletes if necessary
 func DecrimentKey(networkName string, keyvalue string) {
 func DecrimentKey(networkName string, keyvalue string) {
@@ -723,3 +721,30 @@ func Inc(ip net.IP) {
 		}
 		}
 	}
 	}
 }
 }
+
+func GetAllNodes() ([]models.ReturnNode, error) {
+	var node models.ReturnNode
+	var nodes []models.ReturnNode
+	collection := mongoconn.Client.Database("netmaker").Collection("nodes")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	// Filter out them ID's again
+	cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0}))
+	if err != nil {
+		return []models.ReturnNode{}, err
+	}
+	defer cancel()
+	for cur.Next(context.TODO()) {
+		err := cur.Decode(&node)
+		if err != nil {
+			return []models.ReturnNode{}, err
+		}
+		// add node to our array
+		nodes = append(nodes, node)
+	}
+
+	//TODO: Fatal error
+	if err := cur.Err(); err != nil {
+		return []models.ReturnNode{}, err
+	}
+	return nodes, nil
+}

+ 29 - 26
functions/local.go

@@ -1,36 +1,39 @@
 package functions
 package functions
 
 
 import (
 import (
-        "fmt"
-	"path/filepath"
-        "log"
-        "os"
+	"fmt"
 	"io/ioutil"
 	"io/ioutil"
+	"log"
+	"os"
 )
 )
 
 
-
 func FileExists(f string) bool {
 func FileExists(f string) bool {
-    info, err := os.Stat(f)
-    if os.IsNotExist(err) {
-        return false
-    }
-    return !info.IsDir()
+	info, err := os.Stat(f)
+	if os.IsNotExist(err) {
+		return false
+	}
+	return !info.IsDir()
 }
 }
 
 
 func SetCorefile(domains string) error {
 func SetCorefile(domains string) error {
-	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
+	//does not work when executing tests
+	//dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
+	//if err != nil {
+	//	return err
+	//}
+	dir, err := os.Getwd()
 	if err != nil {
 	if err != nil {
-            return err
+		return err
 	}
 	}
 	_, err = os.Stat(dir + "/config/dnsconfig")
 	_, err = os.Stat(dir + "/config/dnsconfig")
-        if os.IsNotExist(err) {
-                os.Mkdir(dir +"/config/dnsconfig", 744)
-        } else if err != nil {
-                fmt.Println("couldnt find or create /config/dnsconfig")
-                return err
-        }
+	if os.IsNotExist(err) {
+		os.Mkdir(dir+"/config/dnsconfig", 744)
+	} else if err != nil {
+		fmt.Println("couldnt find or create /config/dnsconfig")
+		return err
+	}
 
 
-		corefile := domains + ` {
+	corefile := domains + ` {
     reload 15s
     reload 15s
     hosts /root/dnsconfig/netmaker.hosts {
     hosts /root/dnsconfig/netmaker.hosts {
 	fallthrough	
 	fallthrough	
@@ -39,13 +42,13 @@ func SetCorefile(domains string) error {
     log
     log
 }
 }
 `
 `
-		corebytes := []byte(corefile)
+	corebytes := []byte(corefile)
 
 
-		err = ioutil.WriteFile(dir + "/config/dnsconfig/Corefile", corebytes, 0644)
-		if err != nil {
-			log.Println(err)
-			log.Println("")
-			return err
-		}
+	err = ioutil.WriteFile(dir+"/config/dnsconfig/Corefile", corebytes, 0644)
+	if err != nil {
+		log.Println(err)
+		log.Println("")
+		return err
+	}
 	return err
 	return err
 }
 }

+ 4 - 8
go.mod

@@ -3,15 +3,13 @@ module github.com/gravitl/netmaker
 go 1.15
 go 1.15
 
 
 require (
 require (
-	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
-	github.com/go-playground/universal-translator v0.17.0 // indirect
-	github.com/golang/protobuf v1.4.3
+	github.com/go-playground/validator/v10 v10.5.0
+	github.com/golang/protobuf v1.4.3 // indirect
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/gorilla/mux v1.8.0
-	github.com/leodido/go-urn v1.2.0 // indirect
 	github.com/stretchr/testify v1.6.1
 	github.com/stretchr/testify v1.6.1
-	github.com/txn2/txeh v1.3.0 // indirect
+	github.com/txn2/txeh v1.3.0
 	go.mongodb.org/mongo-driver v1.4.3
 	go.mongodb.org/mongo-driver v1.4.3
 	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
 	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
 	golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
 	golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
@@ -22,8 +20,6 @@ require (
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
 	google.golang.org/genproto v0.0.0-20210201151548-94839c025ad4 // indirect
 	google.golang.org/genproto v0.0.0-20210201151548-94839c025ad4 // indirect
 	google.golang.org/grpc v1.35.0
 	google.golang.org/grpc v1.35.0
-	google.golang.org/protobuf v1.25.0 // indirect
-	gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
-	gopkg.in/go-playground/validator.v9 v9.31.0
+	google.golang.org/protobuf v1.25.0
 	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
 	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
 )
 )

+ 4 - 4
go.sum

@@ -22,10 +22,14 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
 github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
 github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
 github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
 github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
 github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.5.0 h1:X9rflw/KmpACwT8zdrm1upefpvdy6ur8d1kWyq6sg3E=
+github.com/go-playground/validator/v10 v10.5.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -261,10 +265,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
-gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
-gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
-gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 0 - 620
group_test.go

@@ -1,620 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"io/ioutil"
-	"net/http"
-	"testing"
-
-	"github.com/gravitl/netmaker/models"
-	"github.com/stretchr/testify/assert"
-	"go.mongodb.org/mongo-driver/mongo"
-)
-
-var Networks []models.Network
-
-func TestCreateNetwork(t *testing.T) {
-	network := models.Network{}
-	network.NetID = "skynet"
-	network.AddressRange = "10.71.0.0/16"
-	t.Run("CreateNetwork", func(t *testing.T) {
-		response, err := api(t, network, http.MethodPost, "http://localhost:8081/api/networks", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-	})
-	t.Run("InvalidToken", func(t *testing.T) {
-		response, err := api(t, network, http.MethodPost, "http://localhost:8081/api/networks", "badkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-	t.Run("BadName", func(t *testing.T) {
-		//issue #42
-		t.Skip()
-	})
-	t.Run("BadAddress", func(t *testing.T) {
-		//issue #42
-		t.Skip()
-	})
-	t.Run("DuplicateNetwork", func(t *testing.T) {
-		//issue #42
-		t.Skip()
-	})
-}
-
-func TestGetNetworks(t *testing.T) {
-	t.Run("ValidToken", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		err = json.NewDecoder(response.Body).Decode(&Networks)
-		assert.Nil(t, err, err)
-	})
-	t.Run("InvalidToken", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks", "badkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-}
-
-func TestGetNetwork(t *testing.T) {
-	t.Run("ValidToken", func(t *testing.T) {
-		var network models.Network
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		err = json.NewDecoder(response.Body).Decode(&network)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "skynet", network.DisplayName)
-	})
-	t.Run("InvalidToken", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/skynet", "badkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-	t.Run("InvalidNetwork", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/badnetwork", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-}
-
-func TestGetNetworkNodeNumber(t *testing.T) {
-	t.Run("ValidKey", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/skynet/numnodes", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message int
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		//assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-	})
-	t.Run("InvalidKey", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/skynet/numnodes", "badkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-	t.Run("BadNetwork", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/badnetwork/numnodes", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-}
-
-func TestDeleteNetwork(t *testing.T) {
-	t.Run("InvalidKey", func(t *testing.T) {
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/networks/skynet", "badkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-	t.Run("ValidKey", func(t *testing.T) {
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message mongo.DeleteResult
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		assert.Equal(t, int64(1), message.DeletedCount)
-
-	})
-	t.Run("BadNetwork", func(t *testing.T) {
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/networks/badnetwork", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-	t.Run("NodesExist", func(t *testing.T) {
-		t.Skip()
-	})
-	//Create Network for follow-on tests
-	createNetwork(t)
-}
-
-func TestCreateAccessKey(t *testing.T) {
-	key := models.AccessKey{}
-	key.Name = "skynet"
-	key.Uses = 10
-	t.Run("MultiUse", func(t *testing.T) {
-		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/networks/skynet/keys", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		message, err := ioutil.ReadAll(response.Body)
-		assert.Nil(t, err, err)
-		assert.NotNil(t, message, message)
-		returnedkey := getKey(t, key.Name)
-		assert.Equal(t, key.Name, returnedkey.Name)
-		assert.Equal(t, key.Uses, returnedkey.Uses)
-	})
-	deleteKey(t, "skynet", "skynet")
-	t.Run("ZeroUse", func(t *testing.T) {
-		//t.Skip()
-		key.Uses = 0
-		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/networks/skynet/keys", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		message, err := ioutil.ReadAll(response.Body)
-		assert.Nil(t, err, err)
-		assert.NotNil(t, message, message)
-		returnedkey := getKey(t, key.Name)
-		assert.Equal(t, key.Name, returnedkey.Name)
-		assert.Equal(t, 1, returnedkey.Uses)
-	})
-	t.Run("DuplicateAccessKey", func(t *testing.T) {
-		//t.Skip()
-		//this will fail
-		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/networks/skynet/keys", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
-		deleteKey(t, key.Name, "skynet")
-	})
-
-	t.Run("InvalidToken", func(t *testing.T) {
-		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/networks/skynet/keys", "badkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-	t.Run("BadNetwork", func(t *testing.T) {
-		response, err := api(t, key, http.MethodPost, "http://localhost:8081/api/networks/badnetwork/keys", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-}
-
-func TestDeleteKey(t *testing.T) {
-	t.Run("KeyValid", func(t *testing.T) {
-		//fails -- deletecount not returned
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/networks/skynet/keys/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message mongo.DeleteResult
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		assert.Equal(t, int64(1), message.DeletedCount)
-	})
-	t.Run("InValidKey", func(t *testing.T) {
-		//fails -- status message  not returned
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/networks/skynet/keys/badkey", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This key does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-	t.Run("KeyInValidNetwork", func(t *testing.T) {
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/networks/badnetwork/keys/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-	t.Run("InvalidCredentials", func(t *testing.T) {
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/networks/skynet/keys/skynet", "badkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-}
-
-func TestGetKeys(t *testing.T) {
-	createKey(t)
-	t.Run("Valid", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/skynet/keys", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		var keys []models.AccessKey
-		err = json.NewDecoder(response.Body).Decode(&keys)
-		assert.Nil(t, err, err)
-	})
-	//deletekeys
-	t.Run("InvalidNetwork", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/badnetwork/keys", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-	t.Run("InvalidCredentials", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/networks/skynet/keys", "badkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-	})
-}
-
-func TestUpdateNetwork(t *testing.T) {
-	var returnedNetwork models.Network
-	t.Run("UpdateNetID", func(t *testing.T) {
-		type Network struct {
-			NetID string
-		}
-		var network Network
-		network.NetID = "wirecat"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.NetID, returnedNetwork.NetID)
-	})
-	t.Run("NetIDInvalidCredentials", func(t *testing.T) {
-		type Network struct {
-			NetID string
-		}
-		var network Network
-		network.NetID = "wirecat"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "badkey")
-		assert.Nil(t, err, err)
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-	})
-	t.Run("InvalidNetwork", func(t *testing.T) {
-		type Network struct {
-			NetID string
-		}
-		var network Network
-		network.NetID = "wirecat"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/badnetwork", "secretkey")
-		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusNotFound, message.Code)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
-	})
-	t.Run("UpdateNetIDTooLong", func(t *testing.T) {
-		type Network struct {
-			NetID string
-		}
-		var network Network
-		network.NetID = "wirecat-skynet"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
-	})
-	t.Run("UpdateAddress", func(t *testing.T) {
-		type Network struct {
-			AddressRange string
-		}
-		var network Network
-		network.AddressRange = "10.0.0.1/24"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.AddressRange, returnedNetwork.AddressRange)
-	})
-	t.Run("UpdateAddressInvalid", func(t *testing.T) {
-		type Network struct {
-			AddressRange string
-		}
-		var network Network
-		network.AddressRange = "10.0.0.1/36"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
-	})
-	t.Run("UpdateDisplayName", func(t *testing.T) {
-		type Network struct {
-			DisplayName string
-		}
-		var network Network
-		network.DisplayName = "wirecat"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DisplayName, returnedNetwork.DisplayName)
-
-	})
-	t.Run("UpdateDisplayNameInvalidName", func(t *testing.T) {
-		type Network struct {
-			DisplayName string
-		}
-		var network Network
-		//create name that is longer than 100 chars
-		name := ""
-		for i := 0; i < 101; i++ {
-			name = name + "a"
-		}
-		network.DisplayName = name
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
-		assert.Equal(t, "W1R3: Field validation for 'DisplayName' failed.", message.Message)
-		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
-	})
-	t.Run("UpdateInterface", func(t *testing.T) {
-		type Network struct {
-			DefaultInterface string
-		}
-		var network Network
-		network.DefaultInterface = "netmaker"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DefaultInterface, returnedNetwork.DefaultInterface)
-
-	})
-	t.Run("UpdateListenPort", func(t *testing.T) {
-		type Network struct {
-			DefaultListenPort int32
-		}
-		var network Network
-		network.DefaultListenPort = 6000
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DefaultListenPort, returnedNetwork.DefaultListenPort)
-	})
-	t.Run("UpdateListenPortInvalidPort", func(t *testing.T) {
-		type Network struct {
-			DefaultListenPort int32
-		}
-		var network Network
-		network.DefaultListenPort = 1023
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
-		assert.Equal(t, "W1R3: Field validation for 'DefaultListenPort' failed.", message.Message)
-		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
-	})
-	t.Run("UpdatePostUP", func(t *testing.T) {
-		type Network struct {
-			DefaultPostUp string
-		}
-		var network Network
-		network.DefaultPostUp = "sudo wg add-conf wc-netmaker /etc/wireguard/peers/conf"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DefaultPostUp, returnedNetwork.DefaultPostUp)
-	})
-	t.Run("UpdatePostDown", func(t *testing.T) {
-		type Network struct {
-			DefaultPostDown string
-		}
-		var network Network
-		network.DefaultPostDown = "test string"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DefaultPostDown, returnedNetwork.DefaultPostDown)
-	})
-	t.Run("UpdateKeepAlive", func(t *testing.T) {
-		type Network struct {
-			DefaultKeepalive int32
-		}
-		var network Network
-		network.DefaultKeepalive = 60
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DefaultKeepalive, returnedNetwork.DefaultKeepalive)
-	})
-	t.Run("UpdateKeepAliveTooBig", func(t *testing.T) {
-		type Network struct {
-			DefaultKeepAlive int32
-		}
-		var network Network
-		network.DefaultKeepAlive = 1001
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
-		assert.Equal(t, "W1R3: Field validation for 'DefaultKeepAlive' failed.", message.Message)
-		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
-	})
-	t.Run("UpdateSaveConfig", func(t *testing.T) {
-		//causes panic
-		t.Skip()
-		type Network struct {
-			DefaultSaveConfig *bool
-		}
-		var network Network
-		value := false
-		network.DefaultSaveConfig = &value
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, *network.DefaultSaveConfig, *returnedNetwork.DefaultSaveConfig)
-	})
-	t.Run("UpdateManualSignUP", func(t *testing.T) {
-		t.Skip()
-		type Network struct {
-			AllowManualSignUp *bool
-		}
-		var network Network
-		value := true
-		network.AllowManualSignUp = &value
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, *network.AllowManualSignUp, *returnedNetwork.AllowManualSignUp)
-	})
-	t.Run("DefaultCheckInterval", func(t *testing.T) {
-		type Network struct {
-			DefaultCheckInInterval int32
-		}
-		var network Network
-		network.DefaultCheckInInterval = 6000
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DefaultCheckInInterval, returnedNetwork.DefaultCheckInInterval)
-	})
-	t.Run("DefaultCheckIntervalTooBig", func(t *testing.T) {
-		type Network struct {
-			DefaultCheckInInterval int32
-		}
-		var network Network
-		network.DefaultCheckInInterval = 100001
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		var message models.ErrorResponse
-		err = json.NewDecoder(response.Body).Decode(&message)
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnprocessableEntity, message.Code)
-		assert.Equal(t, "W1R3: Field validation for 'DefaultCheckInInterval' failed.", message.Message)
-		assert.Equal(t, http.StatusUnprocessableEntity, response.StatusCode)
-	})
-	t.Run("MultipleFields", func(t *testing.T) {
-		type Network struct {
-			DisplayName       string
-			DefaultListenPort int32
-		}
-		var network Network
-		network.DefaultListenPort = 7777
-		network.DisplayName = "multi"
-		response, err := api(t, network, http.MethodPut, "http://localhost:8081/api/networks/skynet", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		assert.Equal(t, network.DisplayName, returnedNetwork.DisplayName)
-		assert.Equal(t, network.DefaultListenPort, returnedNetwork.DefaultListenPort)
-	})
-}

+ 3 - 3
models/dnsEntry.go

@@ -2,7 +2,7 @@
 package models
 package models
 
 
 type DNSEntry struct {
 type DNSEntry struct {
-	Address	string `json:"address" bson:"address" validate:"address_valid"`
-	Name	string `json:"name" bson:"name" validate:"name_valid,name_unique,max=120"`
-	Network	string `json:"network" bson:"network" validate:"network_exists"`
+	Address string `json:"address" bson:"address" validate:"required,ip"`
+	Name    string `json:"name" bson:"name" validate:"required,alphanum,name_unique,max=120"`
+	Network string `json:"network" bson:"network" validate:"network_exists"`
 }
 }

+ 88 - 56
models/network.go

@@ -1,74 +1,106 @@
 package models
 package models
 
 
 import (
 import (
-//  "../mongoconn"
-  "go.mongodb.org/mongo-driver/bson/primitive"
-  "time"
+	//  "../mongoconn"
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 )
 
 
 //Network Struct
 //Network Struct
 //At  some point, need to replace all instances of Name with something else like  Identifier
 //At  some point, need to replace all instances of Name with something else like  Identifier
 type Network struct {
 type Network struct {
-	ID	primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
-	AddressRange	string `json:"addressrange" bson:"addressrange" validate:"required,addressrange_valid"`
-	AddressRange6	string `json:"addressrange6" bson:"addressrange6" validate:"addressrange6_valid"`
-	DisplayName string `json:"displayname,omitempty" bson:"displayname,omitempty" validate:"omitempty,displayname_unique,min=1,max=100"`
-	NetID string `json:"netid" bson:"netid" validate:"required,netid_valid,min=1,max=12"`
-	NodesLastModified	int64 `json:"nodeslastmodified" bson:"nodeslastmodified"`
-	NetworkLastModified int64 `json:"networklastmodified" bson:"networklastmodified"`
-	DefaultInterface	string `json:"defaultinterface" bson:"defaultinterface"`
-        DefaultListenPort      int32 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,numeric,min=1024,max=65535"`
-        DefaultPostUp  string `json:"defaultpostup" bson:"defaultpostup"`
-        DefaultPostDown   string `json:"defaultpostdown" bson:"defaultpostdown"`
-        KeyUpdateTimeStamp      int64 `json:"keyupdatetimestamp" bson:"keyupdatetimestamp"`
-        DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate: "omitempty,numeric,max=1000"`
-        DefaultSaveConfig      *bool `json:"defaultsaveconfig" bson:"defaultsaveconfig"`
-	AccessKeys	[]AccessKey `json:"accesskeys" bson:"accesskeys"`
-	AllowManualSignUp *bool `json:"allowmanualsignup" bson:"allowmanualsignup"`
-	IsLocal *bool `json:"islocal" bson:"islocal"`
-	IsDualStack *bool `json:"isdualstack" bson:"isdualstack"`
-	LocalRange string `json:"localrange" bson:"localrange" validate:"localrange_valid"`
-	DefaultCheckInInterval int32 `json:"checkininterval,omitempty" bson:"checkininterval,omitempty" validate:"omitempty,numeric,min=1,max=100000"`
+	ID           primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
+	AddressRange string             `json:"addressrange" bson:"addressrange" validate:"required,cidr"`
+	// bug in validator --- required_with does not work with bools  issue#683
+	//	AddressRange6          string             `json:"addressrange6" bson:"addressrange6" validate:"required_with=isdualstack true,cidrv6"`
+	AddressRange6 string `json:"addressrange6" bson:"addressrange6" validate:"addressrange6_valid"`
+	//can't have min=1 with omitempty
+	DisplayName         string      `json:"displayname,omitempty" bson:"displayname,omitempty" validate:"omitempty,alphanum,min=2,max=20,displayname_unique"`
+	NetID               string      `json:"netid" bson:"netid" validate:"required,alphanum,min=1,max=12,netid_valid"`
+	NodesLastModified   int64       `json:"nodeslastmodified" bson:"nodeslastmodified"`
+	NetworkLastModified int64       `json:"networklastmodified" bson:"networklastmodified"`
+	DefaultInterface    string      `json:"defaultinterface" bson:"defaultinterface"`
+	DefaultListenPort   int32       `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
+	DefaultPostUp       string      `json:"defaultpostup" bson:"defaultpostup"`
+	DefaultPostDown     string      `json:"defaultpostdown" bson:"defaultpostdown"`
+	KeyUpdateTimeStamp  int64       `json:"keyupdatetimestamp" bson:"keyupdatetimestamp"`
+	DefaultKeepalive    int32       `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
+	DefaultSaveConfig   *bool       `json:"defaultsaveconfig" bson:"defaultsaveconfig"`
+	AccessKeys          []AccessKey `json:"accesskeys" bson:"accesskeys"`
+	AllowManualSignUp   *bool       `json:"allowmanualsignup" bson:"allowmanualsignup"`
+	IsLocal             *bool       `json:"islocal" bson:"islocal"`
+	IsDualStack         *bool       `json:"isdualstack" bson:"isdualstack"`
+	LocalRange          string      `json:"localrange" bson:"localrange" validate:"omitempty,cidr"`
+	//can't have min=1 with omitempty
+	DefaultCheckInInterval int32 `json:"checkininterval,omitempty" bson:"checkininterval,omitempty" validate:"omitempty,numeric,min=2,max=100000"`
+}
+type NetworkUpdate struct {
+	ID           primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
+	AddressRange string             `json:"addressrange" bson:"addressrange" validate:"omitempty,cidr"`
+
+	// bug in validator --- required_with does not work with bools  issue#683
+	//	AddressRange6          string             `json:"addressrange6" bson:"addressrange6" validate:"required_with=isdualstack true,cidrv6"`
+	AddressRange6 string `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidr"`
+	//can't have min=1 with omitempty
+	DisplayName         string      `json:"displayname,omitempty" bson:"displayname,omitempty" validate:"omitempty,alphanum,min=2,max=20"`
+	NetID               string      `json:"netid" bson:"netid" validate:"omitempty,alphanum,min=1,max=12"`
+	NodesLastModified   int64       `json:"nodeslastmodified" bson:"nodeslastmodified"`
+	NetworkLastModified int64       `json:"networklastmodified" bson:"networklastmodified"`
+	DefaultInterface    string      `json:"defaultinterface" bson:"defaultinterface"`
+	DefaultListenPort   int32       `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
+	DefaultPostUp       string      `json:"defaultpostup" bson:"defaultpostup"`
+	DefaultPostDown     string      `json:"defaultpostdown" bson:"defaultpostdown"`
+	KeyUpdateTimeStamp  int64       `json:"keyupdatetimestamp" bson:"keyupdatetimestamp"`
+	DefaultKeepalive    int32       `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
+	DefaultSaveConfig   *bool       `json:"defaultsaveconfig" bson:"defaultsaveconfig"`
+	AccessKeys          []AccessKey `json:"accesskeys" bson:"accesskeys"`
+	AllowManualSignUp   *bool       `json:"allowmanualsignup" bson:"allowmanualsignup"`
+	IsLocal             *bool       `json:"islocal" bson:"islocal"`
+	IsDualStack         *bool       `json:"isdualstack" bson:"isdualstack"`
+	LocalRange          string      `json:"localrange" bson:"localrange" validate:"omitempty,cidr"`
+	//can't have min=1 with omitempty
+	DefaultCheckInInterval int32 `json:"checkininterval,omitempty" bson:"checkininterval,omitempty" validate:"omitempty,numeric,min=2,max=100000"`
 }
 }
 
 
 //TODO:
 //TODO:
 //Not  sure if we  need the below two functions. Got rid  of one of the calls. May want  to revisit
 //Not  sure if we  need the below two functions. Got rid  of one of the calls. May want  to revisit
-func(network *Network) SetNodesLastModified(){
-        network.NodesLastModified = time.Now().Unix()
+func (network *Network) SetNodesLastModified() {
+	network.NodesLastModified = time.Now().Unix()
 }
 }
 
 
-func(network *Network) SetNetworkLastModified(){
-        network.NetworkLastModified = time.Now().Unix()
+func (network *Network) SetNetworkLastModified() {
+	network.NetworkLastModified = time.Now().Unix()
 }
 }
 
 
-func(network *Network) SetDefaults(){
-    if network.DisplayName == "" {
-        network.DisplayName = network.NetID
-    }
-    if network.DefaultInterface == "" {
-	network.DefaultInterface = "nm-" + network.NetID
-    }
-    if network.DefaultListenPort == 0 {
-        network.DefaultListenPort = 51821
-    }
-    if network.DefaultPostDown == "" {
+func (network *Network) SetDefaults() {
+	if network.DisplayName == "" {
+		network.DisplayName = network.NetID
+	}
+	if network.DefaultInterface == "" {
+		network.DefaultInterface = "nm-" + network.NetID
+	}
+	if network.DefaultListenPort == 0 {
+		network.DefaultListenPort = 51821
+	}
+	if network.DefaultPostDown == "" {
 
 
-    }
-    if network.DefaultSaveConfig == nil {
-	defaultsave := true
-        network.DefaultSaveConfig = &defaultsave
-    }
-    if network.DefaultKeepalive == 0 {
-        network.DefaultKeepalive = 20
-    }
-    if network.DefaultPostUp == "" {
-    }
-    //Check-In Interval for Nodes, In Seconds
-    if network.DefaultCheckInInterval == 0 {
-        network.DefaultCheckInInterval = 30
-    }
-    if network.AllowManualSignUp == nil {
-	signup := false
-        network.AllowManualSignUp = &signup
-    }
+	}
+	if network.DefaultSaveConfig == nil {
+		defaultsave := true
+		network.DefaultSaveConfig = &defaultsave
+	}
+	if network.DefaultKeepalive == 0 {
+		network.DefaultKeepalive = 20
+	}
+	if network.DefaultPostUp == "" {
+	}
+	//Check-In Interval for Nodes, In Seconds
+	if network.DefaultCheckInInterval == 0 {
+		network.DefaultCheckInInterval = 30
+	}
+	if network.AllowManualSignUp == nil {
+		signup := false
+		network.AllowManualSignUp = &signup
+	}
 }
 }

+ 70 - 13
models/node.go

@@ -19,17 +19,17 @@ var seededRand *rand.Rand = rand.New(
 //node struct
 //node struct
 type Node struct {
 type Node struct {
 	ID                  primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
 	ID                  primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
-	Address             string             `json:"address" bson:"address" validate:"address_check"`
-	Address6             string             `json:"address6" bson:"address6" validate:"address6_check"`
-	LocalAddress        string             `json:"localaddress" bson:"localaddress" validate:"localaddress_check"`
-	Name                string             `json:"name" bson:"name" validate:"omitempty,name_valid,max=12"`
+	Address             string             `json:"address" bson:"address" validate:"omitempty,ipv4"`
+	Address6            string             `json:"address6" bson:"address6" validate:"omitempty,ipv6"`
+	LocalAddress        string             `json:"localaddress" bson:"localaddress" validate:"omitempty,ip"`
+	Name                string             `json:"name" bson:"name" validate:"omitempty,alphanum,max=12"`
 	ListenPort          int32              `json:"listenport" bson:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
 	ListenPort          int32              `json:"listenport" bson:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
-	PublicKey           string             `json:"publickey" bson:"publickey" validate:"pubkey_check"`
-	Endpoint            string             `json:"endpoint" bson:"endpoint" validate:"endpoint_check"`
+	PublicKey           string             `json:"publickey" bson:"publickey" validate:"required,base64"`
+	Endpoint            string             `json:"endpoint" bson:"endpoint" validate:"required,ip"`
 	PostUp              string             `json:"postup" bson:"postup"`
 	PostUp              string             `json:"postup" bson:"postup"`
 	PostDown            string             `json:"postdown" bson:"postdown"`
 	PostDown            string             `json:"postdown" bson:"postdown"`
 	AllowedIPs          string             `json:"allowedips" bson:"allowedips"`
 	AllowedIPs          string             `json:"allowedips" bson:"allowedips"`
-	PersistentKeepalive int32              `json:"persistentkeepalive" bson:"persistentkeepalive" validate: "omitempty,numeric,max=1000"`
+	PersistentKeepalive int32              `json:"persistentkeepalive" bson:"persistentkeepalive" validate:"omitempty,numeric,max=1000"`
 	SaveConfig          *bool              `json:"saveconfig" bson:"saveconfig"`
 	SaveConfig          *bool              `json:"saveconfig" bson:"saveconfig"`
 	AccessKey           string             `json:"accesskey" bson:"accesskey"`
 	AccessKey           string             `json:"accesskey" bson:"accesskey"`
 	Interface           string             `json:"interface" bson:"interface"`
 	Interface           string             `json:"interface" bson:"interface"`
@@ -38,9 +38,9 @@ type Node struct {
 	ExpirationDateTime  int64              `json:"expdatetime" bson:"expdatetime"`
 	ExpirationDateTime  int64              `json:"expdatetime" bson:"expdatetime"`
 	LastPeerUpdate      int64              `json:"lastpeerupdate" bson:"lastpeerupdate"`
 	LastPeerUpdate      int64              `json:"lastpeerupdate" bson:"lastpeerupdate"`
 	LastCheckIn         int64              `json:"lastcheckin" bson:"lastcheckin"`
 	LastCheckIn         int64              `json:"lastcheckin" bson:"lastcheckin"`
-	MacAddress          string             `json:"macaddress" bson:"macaddress" validate:"required,macaddress_valid,macaddress_unique"`
+	MacAddress          string             `json:"macaddress" bson:"macaddress" validate:"required,mac,macaddress_unique"`
 	CheckInInterval     int32              `json:"checkininterval" bson:"checkininterval"`
 	CheckInInterval     int32              `json:"checkininterval" bson:"checkininterval"`
-	Password            string             `json:"password" bson:"password" validate:"password_check"`
+	Password            string             `json:"password" bson:"password" validate:"required,min=6"`
 	Network             string             `json:"network" bson:"network" validate:"network_exists"`
 	Network             string             `json:"network" bson:"network" validate:"network_exists"`
 	IsPending           bool               `json:"ispending" bson:"ispending"`
 	IsPending           bool               `json:"ispending" bson:"ispending"`
 	IsGateway           bool               `json:"isgateway" bson:"isgateway"`
 	IsGateway           bool               `json:"isgateway" bson:"isgateway"`
@@ -48,14 +48,69 @@ type Node struct {
 	PostChanges         string             `json:"postchanges" bson:"postchanges"`
 	PostChanges         string             `json:"postchanges" bson:"postchanges"`
 }
 }
 
 
+//node update struct --- only validations are different
+type NodeUpdate struct {
+	ID                  primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
+	Address             string             `json:"address" bson:"address" validate:"omitempty,ip"`
+	Address6            string             `json:"address6" bson:"address6" validate:"omitempty,ipv6"`
+	LocalAddress        string             `json:"localaddress" bson:"localaddress" validate:"omitempty,ip"`
+	Name                string             `json:"name" bson:"name" validate:"omitempty,alphanum,max=12"`
+	ListenPort          int32              `json:"listenport" bson:"listenport" validate:"omitempty,numeric,min=1024,max=65535"`
+	PublicKey           string             `json:"publickey" bson:"publickey" validate:"omitempty,base64"`
+	Endpoint            string             `json:"endpoint" bson:"endpoint" validate:"omitempty,ip"`
+	PostUp              string             `json:"postup" bson:"postup"`
+	PostDown            string             `json:"postdown" bson:"postdown"`
+	AllowedIPs          string             `json:"allowedips" bson:"allowedips"`
+	PersistentKeepalive int32              `json:"persistentkeepalive" bson:"persistentkeepalive" validate:"omitempty,numeric,max=1000"`
+	SaveConfig          *bool              `json:"saveconfig" bson:"saveconfig"`
+	AccessKey           string             `json:"accesskey" bson:"accesskey"`
+	Interface           string             `json:"interface" bson:"interface"`
+	LastModified        int64              `json:"lastmodified" bson:"lastmodified"`
+	KeyUpdateTimeStamp  int64              `json:"keyupdatetimestamp" bson:"keyupdatetimestamp"`
+	ExpirationDateTime  int64              `json:"expdatetime" bson:"expdatetime"`
+	LastPeerUpdate      int64              `json:"lastpeerupdate" bson:"lastpeerupdate"`
+	LastCheckIn         int64              `json:"lastcheckin" bson:"lastcheckin"`
+	MacAddress          string             `json:"macaddress" bson:"macaddress" validate:"required,mac"`
+	CheckInInterval     int32              `json:"checkininterval" bson:"checkininterval"`
+	Password            string             `json:"password" bson:"password" validate:"omitempty,min=5"`
+	Network             string             `json:"network" bson:"network" validate:"network_exists"`
+	IsPending           bool               `json:"ispending" bson:"ispending"`
+	IsGateway           bool               `json:"isgateway" bson:"isgateway"`
+	GatewayRange        string             `json:"gatewayrange" bson:"gatewayrange"`
+	PostChanges         string             `json:"postchanges" bson:"postchanges"`
+}
+
+//Duplicated function for NodeUpdates
+func (node *NodeUpdate) GetNetwork() (Network, error) {
+
+	var network Network
+
+	collection := mongoconn.NetworkDB
+	//collection := mongoconn.Client.Database("netmaker").Collection("networks")
+
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+
+	filter := bson.M{"netid": node.Network}
+	err := collection.FindOne(ctx, filter).Decode(&network)
+
+	defer cancel()
+
+	if err != nil {
+		//log.Fatal(err)
+		return network, err
+	}
+
+	return network, err
+}
+
 //TODO: Contains a fatal error return. Need to change
 //TODO: Contains a fatal error return. Need to change
 //Used in contexts where it's not the Parent network.
 //Used in contexts where it's not the Parent network.
 func (node *Node) GetNetwork() (Network, error) {
 func (node *Node) GetNetwork() (Network, error) {
 
 
 	var network Network
 	var network Network
 
 
-	collection := mongoconn.NetworkDB
-	//collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	//collection := mongoconn.NetworkDB
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
 
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
 
@@ -117,8 +172,10 @@ func (node *Node) SetDefaults() {
 	//TODO: This is dumb and doesn't work
 	//TODO: This is dumb and doesn't work
 	//Need to change
 	//Need to change
 	if node.SaveConfig == nil {
 	if node.SaveConfig == nil {
-		defaultsave := *parentNetwork.DefaultSaveConfig
-		node.SaveConfig = &defaultsave
+		if parentNetwork.DefaultSaveConfig != nil {
+			defaultsave := *parentNetwork.DefaultSaveConfig
+			node.SaveConfig = &defaultsave
+		}
 	}
 	}
 	if node.Interface == "" {
 	if node.Interface == "" {
 		node.Interface = parentNetwork.DefaultInterface
 		node.Interface = parentNetwork.DefaultInterface

+ 58 - 61
models/structs.go

@@ -3,113 +3,110 @@ package models
 import jwt "github.com/dgrijalva/jwt-go"
 import jwt "github.com/dgrijalva/jwt-go"
 
 
 type AuthParams struct {
 type AuthParams struct {
-    MacAddress    string `json:"macaddress"`
-    Password string `json:"password"`
+	MacAddress string `json:"macaddress"`
+	Password   string `json:"password"`
 }
 }
 
 
 type User struct {
 type User struct {
-    UserName string `json:"username" bson:"username" validate:username_valid,username_unique,min=3`
-    Password string `json:"password" bson:"password" validate:password_check`
-    IsAdmin bool `json:"isadmin" bson:"isadmin"`
+	UserName string `json:"username" bson:"username" validate:"alphanum,min=3"`
+	Password string `json:"password" bson:"password" validate:"required,min=5"`
+	IsAdmin  bool   `json:"isadmin" bson:"isadmin"`
 }
 }
 
 
 type UserAuthParams struct {
 type UserAuthParams struct {
-    UserName    string `json:"username"`
-    Password string `json:"password"`
+	UserName string `json:"username"`
+	Password string `json:"password"`
 }
 }
 
 
 type UserClaims struct {
 type UserClaims struct {
-    IsAdmin bool
-    UserName string
-    jwt.StandardClaims
+	IsAdmin  bool
+	UserName string
+	jwt.StandardClaims
 }
 }
 
 
 type SuccessfulUserLoginResponse struct {
 type SuccessfulUserLoginResponse struct {
-    UserName     string
-    AuthToken string
+	UserName  string
+	AuthToken string
 }
 }
 
 
 // Claims is  a struct that will be encoded to a JWT.
 // Claims is  a struct that will be encoded to a JWT.
 // jwt.StandardClaims is an embedded type to provide expiry time
 // jwt.StandardClaims is an embedded type to provide expiry time
 type Claims struct {
 type Claims struct {
-    Network string
-    MacAddress string
-    jwt.StandardClaims
+	Network    string
+	MacAddress string
+	jwt.StandardClaims
 }
 }
 
 
 // SuccessfulLoginResponse is struct to send the request response
 // SuccessfulLoginResponse is struct to send the request response
 type SuccessfulLoginResponse struct {
 type SuccessfulLoginResponse struct {
-    MacAddress     string
-    AuthToken string
+	MacAddress string
+	AuthToken  string
 }
 }
 
 
 type ErrorResponse struct {
 type ErrorResponse struct {
-    Code    int
-    Message string
+	Code    int
+	Message string
 }
 }
 
 
 type NodeAuth struct {
 type NodeAuth struct {
-    Network    string
-    Password string
-    MacAddress string
+	Network    string
+	Password   string
+	MacAddress string
 }
 }
 
 
 // SuccessResponse is struct for sending error message with code.
 // SuccessResponse is struct for sending error message with code.
 type SuccessResponse struct {
 type SuccessResponse struct {
-    Code     int
-    Message  string
-    Response interface{}
+	Code     int
+	Message  string
+	Response interface{}
 }
 }
 
 
 type AccessKey struct {
 type AccessKey struct {
-    Name string `json:"name" bson:"name"`
-    Value string `json:"value" bson:"value"`
-    AccessString string `json:"accessstring" bson:"accessstring"`
-    Uses int `json:"uses" bson:"uses"`
+	Name         string `json:"name" bson:"name" validate:"omitempty,alphanum,max=20"`
+	Value        string `json:"value" bson:"value" validate:"omitempty,alphanum,max=16"`
+	AccessString string `json:"accessstring" bson:"accessstring"`
+	Uses         int    `json:"uses" bson:"uses"`
 }
 }
 
 
 type DisplayKey struct {
 type DisplayKey struct {
-    Name string `json:"name" bson:"name"`
-    Uses int `json:"uses" bson:"uses"`
+	Name string `json:"name" bson:"name"`
+	Uses int    `json:"uses" bson:"uses"`
 }
 }
 
 
 type GlobalConfig struct {
 type GlobalConfig struct {
-    Name string `json:"name" bson:"name"`
-    PortGRPC string `json:"portgrpc" bson:"portgrpc"`
-    ServerGRPC string `json:"servergrpc" bson:"servergrpc"`
+	Name       string `json:"name" bson:"name"`
+	PortGRPC   string `json:"portgrpc" bson:"portgrpc"`
+	ServerGRPC string `json:"servergrpc" bson:"servergrpc"`
 }
 }
 
 
-
-type CheckInResponse struct{
-    Success bool `json:"success" bson:"success"`
-    NeedPeerUpdate bool `json:"needpeerupdate" bson:"needpeerupdate"`
-    NeedConfigUpdate bool `json:"needconfigupdate" bson:"needconfigupdate"`
-    NeedKeyUpdate bool `json:"needkeyupdate" bson:"needkeyupdate"`
-    NeedDelete bool `json:"needdelete" bson:"needdelete"`
-    NodeMessage string `json:"nodemessage" bson:"nodemessage"`
-    IsPending bool `json:"ispending" bson:"ispending"`
+type CheckInResponse struct {
+	Success          bool   `json:"success" bson:"success"`
+	NeedPeerUpdate   bool   `json:"needpeerupdate" bson:"needpeerupdate"`
+	NeedConfigUpdate bool   `json:"needconfigupdate" bson:"needconfigupdate"`
+	NeedKeyUpdate    bool   `json:"needkeyupdate" bson:"needkeyupdate"`
+	NeedDelete       bool   `json:"needdelete" bson:"needdelete"`
+	NodeMessage      string `json:"nodemessage" bson:"nodemessage"`
+	IsPending        bool   `json:"ispending" bson:"ispending"`
 }
 }
 
 
 type PeersResponse struct {
 type PeersResponse struct {
-    PublicKey string `json:"publickey" bson:"publickey"`
-    Endpoint string `json:"endpoint" bson:"endpoint"`
-    Address string `json:"address" bson:"address"`
-    Address6 string `json:"address6" bson:"address6"`
-    LocalAddress string `json:"localaddress" bson:"localaddress"`
-    IsGateway bool `json:"isgateway" bson:"isgateway"`
-    GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
-    ListenPort int32 `json:"listenport" bson:"listenport"`
-    KeepAlive int32 `json:"persistentkeepalive" bson:"persistentkeepalive"`
+	PublicKey    string `json:"publickey" bson:"publickey"`
+	Endpoint     string `json:"endpoint" bson:"endpoint"`
+	Address      string `json:"address" bson:"address"`
+	Address6     string `json:"address6" bson:"address6"`
+	LocalAddress string `json:"localaddress" bson:"localaddress"`
+	IsGateway    bool   `json:"isgateway" bson:"isgateway"`
+	GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
+	ListenPort   int32  `json:"listenport" bson:"listenport"`
+	KeepAlive    int32  `json:"persistentkeepalive" bson:"persistentkeepalive"`
 }
 }
 
 
 type GatewayRequest struct {
 type GatewayRequest struct {
-    NodeID string `json:"nodeid" bson:"nodeid"`
-    NetID string `json:"netid" bson:"netid"`
-    RangeString string `json:"rangestring" bson:"rangestring"`
-    Ranges []string `json:"ranges" bson:"ranges"`
-    Interface string `json:"interface" bson:"interface"`
-    PostUp string `json:"postup" bson:"postup"`
-    PostDown string `json:"postdown" bson:"postdown"`
+	NodeID      string   `json:"nodeid" bson:"nodeid"`
+	NetID       string   `json:"netid" bson:"netid"`
+	RangeString string   `json:"rangestring" bson:"rangestring"`
+	Ranges      []string `json:"ranges" bson:"ranges"`
+	Interface   string   `json:"interface" bson:"interface"`
+	PostUp      string   `json:"postup" bson:"postup"`
+	PostDown    string   `json:"postdown" bson:"postdown"`
 }
 }
-
-

+ 3 - 1
test/api_test.go

@@ -120,6 +120,7 @@ func createNetwork(t *testing.T) {
 	network.AddressRange = "10.71.0.0/16"
 	network.AddressRange = "10.71.0.0/16"
 	response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
 	response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
 	assert.Nil(t, err, err)
 	assert.Nil(t, err, err)
+	t.Log(err)
 	assert.Equal(t, http.StatusOK, response.StatusCode)
 	assert.Equal(t, http.StatusOK, response.StatusCode)
 }
 }
 
 
@@ -137,6 +138,8 @@ func createKey(t *testing.T) {
 }
 }
 
 
 func createAccessKey(t *testing.T) (key models.AccessKey) {
 func createAccessKey(t *testing.T) (key models.AccessKey) {
+	//delete existing key if
+	_, _ = api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet/keys/skynet", "secretkey")
 	createkey := models.AccessKey{}
 	createkey := models.AccessKey{}
 	createkey.Name = "skynet"
 	createkey.Name = "skynet"
 	createkey.Uses = 10
 	createkey.Uses = 10
@@ -221,7 +224,6 @@ func createNode(t *testing.T) {
 	node.Name = "myNode"
 	node.Name = "myNode"
 	node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
 	node.PublicKey = "DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
 	node.Password = "tobedetermined"
 	node.Password = "tobedetermined"
-	node.LocalAddress = "192.168.0.1"
 	node.Endpoint = "10.100.100.4"
 	node.Endpoint = "10.100.100.4"
 	response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
 	response, err := api(t, node, http.MethodPost, "http://localhost:8081:/api/nodes/skynet", "secretkey")
 	assert.Nil(t, err, err)
 	assert.Nil(t, err, err)

+ 8 - 0
test/config/dnsconfig/Corefile

@@ -0,0 +1,8 @@
+skynet  {
+    reload 15s
+    hosts /root/dnsconfig/netmaker.hosts {
+	fallthrough	
+    }
+    forward . 8.8.8.8 8.8.4.4
+    log
+}

+ 1 - 0
test/config/dnsconfig/netmaker.hosts

@@ -0,0 +1 @@
+10.71.0.1        mynode.skynet

+ 31 - 36
test/network_test.go

@@ -26,7 +26,7 @@ func TestCreateNetwork(t *testing.T) {
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, "You are unauthorized to access this endpoint", message.Message)
 	})
 	})
 	t.Run("CreateNetwork", func(t *testing.T) {
 	t.Run("CreateNetwork", func(t *testing.T) {
 		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
 		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
@@ -73,7 +73,7 @@ func TestGetNetworks(t *testing.T) {
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, "You are unauthorized to access this endpoint", message.Message)
 	})
 	})
 }
 }
 
 
@@ -99,7 +99,7 @@ func TestGetNetwork(t *testing.T) {
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, "You are unauthorized to access this endpoint", message.Message)
 	})
 	})
 	t.Run("InvalidNetwork", func(t *testing.T) {
 	t.Run("InvalidNetwork", func(t *testing.T) {
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/networks/badnetwork", "secretkey")
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/networks/badnetwork", "secretkey")
@@ -108,8 +108,8 @@ func TestGetNetwork(t *testing.T) {
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+		assert.Equal(t, "This network does not exist", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 }
 }
 
 
@@ -125,7 +125,7 @@ func TestDeleteNetwork(t *testing.T) {
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, "You are unauthorized to access this endpoint", message.Message)
 	})
 	})
 	t.Run("Badnetwork", func(t *testing.T) {
 	t.Run("Badnetwork", func(t *testing.T) {
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/badnetwork", "secretkey")
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/badnetwork", "secretkey")
@@ -134,20 +134,20 @@ func TestDeleteNetwork(t *testing.T) {
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+		assert.Equal(t, "This network does not exist", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 	t.Run("NodesExist", func(t *testing.T) {
 	t.Run("NodesExist", func(t *testing.T) {
 		setup(t)
 		setup(t)
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet", "secretkey")
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet", "secretkey")
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusForbidden, response.StatusCode)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 		defer response.Body.Close()
 		defer response.Body.Close()
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Contains(t, message.Message, "Node check failed")
 		assert.Contains(t, message.Message, "Node check failed")
-		assert.Equal(t, http.StatusForbidden, message.Code)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
 	})
 	})
 	t.Run("ValidKey", func(t *testing.T) {
 	t.Run("ValidKey", func(t *testing.T) {
 		type Message struct {
 		type Message struct {
@@ -202,11 +202,15 @@ func TestCreateKey(t *testing.T) {
 		assert.Equal(t, 1, returnedkey.Uses)
 		assert.Equal(t, 1, returnedkey.Uses)
 	})
 	})
 	t.Run("DuplicateAccessKey", func(t *testing.T) {
 	t.Run("DuplicateAccessKey", func(t *testing.T) {
-		//this is allowed I think it should fail fail
 		response, err := api(t, key, http.MethodPost, baseURL+"/api/networks/skynet/keys", "secretkey")
 		response, err := api(t, key, http.MethodPost, baseURL+"/api/networks/skynet/keys", "secretkey")
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-		deleteKey(t, key.Name, "skynet")
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		defer response.Body.Close()
+		var message models.ErrorResponse
+		err = json.NewDecoder(response.Body).Decode(&message)
+		assert.Nil(t, err)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "Duplicate AccessKey Name", message.Message)
 	})
 	})
 
 
 	t.Run("InvalidToken", func(t *testing.T) {
 	t.Run("InvalidToken", func(t *testing.T) {
@@ -218,7 +222,7 @@ func TestCreateKey(t *testing.T) {
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, "You are unauthorized to access this endpoint", message.Message)
 	})
 	})
 	t.Run("Badnetwork", func(t *testing.T) {
 	t.Run("Badnetwork", func(t *testing.T) {
 		response, err := api(t, key, http.MethodPost, baseURL+"/api/networks/badnetwork/keys", "secretkey")
 		response, err := api(t, key, http.MethodPost, baseURL+"/api/networks/badnetwork/keys", "secretkey")
@@ -227,8 +231,8 @@ func TestCreateKey(t *testing.T) {
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+		assert.Equal(t, "This network does not exist", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 }
 }
 
 
@@ -239,18 +243,9 @@ func TestDeleteKey(t *testing.T) {
 	//ensure key exists
 	//ensure key exists
 	createKey(t)
 	createKey(t)
 	t.Run("KeyValid", func(t *testing.T) {
 	t.Run("KeyValid", func(t *testing.T) {
-		//fails -- deletecount not returned
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet/keys/skynet", "secretkey")
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet/keys/skynet", "secretkey")
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		defer response.Body.Close()
-		//var message mongo.DeleteResult
-		var messages []models.AccessKey
-		err = json.NewDecoder(response.Body).Decode(&messages)
-		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
-		for _, message := range messages {
-			assert.Equal(t, "skynet", message.Name)
-		}
 	})
 	})
 	t.Run("InValidKey", func(t *testing.T) {
 	t.Run("InValidKey", func(t *testing.T) {
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet/keys/badkey", "secretkey")
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet/keys/badkey", "secretkey")
@@ -270,8 +265,8 @@ func TestDeleteKey(t *testing.T) {
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+		assert.Equal(t, "This network does not exist", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 	t.Run("InvalidCredentials", func(t *testing.T) {
 	t.Run("InvalidCredentials", func(t *testing.T) {
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet/keys/skynet", "badkey")
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet/keys/skynet", "badkey")
@@ -282,7 +277,7 @@ func TestDeleteKey(t *testing.T) {
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, "You are unauthorized to access this endpoint", message.Message)
 	})
 	})
 }
 }
 
 
@@ -307,8 +302,8 @@ func TestGetKeys(t *testing.T) {
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+		assert.Equal(t, "This network does not exist", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 	t.Run("InvalidCredentials", func(t *testing.T) {
 	t.Run("InvalidCredentials", func(t *testing.T) {
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/networks/skynet/keys", "badkey")
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/networks/skynet/keys", "badkey")
@@ -319,7 +314,7 @@ func TestGetKeys(t *testing.T) {
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
+		assert.Equal(t, "You are unauthorized to access this endpoint", message.Message)
 	})
 	})
 }
 }
 
 
@@ -356,9 +351,9 @@ func TestUpdateNetwork(t *testing.T) {
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusNotFound, message.Code)
-		assert.Equal(t, "W1R3: This network does not exist.", message.Message)
-		assert.Equal(t, http.StatusNotFound, response.StatusCode)
+		assert.Equal(t, http.StatusUnauthorized, message.Code)
+		assert.Equal(t, "This network does not exist", message.Message)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 	t.Run("UpdateAddress", func(t *testing.T) {
 	t.Run("UpdateAddress", func(t *testing.T) {
 		type Network struct {
 		type Network struct {
@@ -515,7 +510,7 @@ func TestUpdateNetwork(t *testing.T) {
 	t.Run("UpdateKeepAliveTooBig", func(t *testing.T) {
 	t.Run("UpdateKeepAliveTooBig", func(t *testing.T) {
 		//does not fails ----- value gets updated.
 		//does not fails ----- value gets updated.
 		// ----- needs fixing -----
 		// ----- needs fixing -----
-		t.Skip()
+		//t.Skip()
 		type Network struct {
 		type Network struct {
 			DefaultKeepAlive int32
 			DefaultKeepAlive int32
 		}
 		}
@@ -527,7 +522,7 @@ func TestUpdateNetwork(t *testing.T) {
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusBadRequest, message.Code)
 		assert.Equal(t, http.StatusBadRequest, message.Code)
-		assert.Contains(t, message.Message, "Field validation for 'DefaultKeepAlive' failed")
+		assert.Contains(t, message.Message, "Field validation for 'DefaultKeepalive' failed on the 'max' tag")
 		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 	})
 	})
 	t.Run("UpdateSaveConfig", func(t *testing.T) {
 	t.Run("UpdateSaveConfig", func(t *testing.T) {

+ 7 - 4
test/node_test.go

@@ -284,6 +284,7 @@ func TestCreateGateway(t *testing.T) {
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.True(t, message.IsGateway)
 		assert.True(t, message.IsGateway)
+		t.Log(err)
 	})
 	})
 	t.Run("BadRange", func(t *testing.T) {
 	t.Run("BadRange", func(t *testing.T) {
 		gateway.RangeString = "0.0.0.0/36"
 		gateway.RangeString = "0.0.0.0/36"
@@ -399,7 +400,7 @@ func TestCreateNode(t *testing.T) {
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusBadRequest, message.Code)
 		assert.Equal(t, http.StatusBadRequest, message.Code)
-		assert.Contains(t, message.Message, "Field validation for 'MacAddress' failed on the 'macaddress_valid' tag")
+		assert.Contains(t, message.Message, "Field validation for 'MacAddress' failed on the 'mac' tag")
 	})
 	})
 	t.Run("BadPublicKey", func(t *testing.T) {
 	t.Run("BadPublicKey", func(t *testing.T) {
 		var node models.Node
 		var node models.Node
@@ -503,8 +504,8 @@ func TestCreateNode(t *testing.T) {
 		assert.Contains(t, "W1R3: Network does not exist! ", message.Message)
 		assert.Contains(t, "W1R3: Network does not exist! ", message.Message)
 	})
 	})
 	t.Run("Valid", func(t *testing.T) {
 	t.Run("Valid", func(t *testing.T) {
-		setup(t)
-		deleteNode(t)
+		deleteNetworks(t)
+		createNetwork(t)
 		key := createAccessKey(t)
 		key := createAccessKey(t)
 
 
 		var node models.Node
 		var node models.Node
@@ -531,13 +532,15 @@ func TestCreateNode(t *testing.T) {
 func TestGetLastModified(t *testing.T) {
 func TestGetLastModified(t *testing.T) {
 	deleteNetworks(t)
 	deleteNetworks(t)
 	createNetwork(t)
 	createNetwork(t)
+	//t.FailNow()
 	t.Run("Valid", func(t *testing.T) {
 	t.Run("Valid", func(t *testing.T) {
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/lastmodified", "secretkey")
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/lastmodified", "secretkey")
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
 	})
 	})
-	deleteNetworks(t)
 	t.Run("NoNetwork", func(t *testing.T) {
 	t.Run("NoNetwork", func(t *testing.T) {
+		t.Skip()
+		deleteNetworks(t)
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/lastmodified", "secretkey")
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/nodes/adm/skynet/lastmodified", "secretkey")
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusNotFound, response.StatusCode)
 		assert.Equal(t, http.StatusNotFound, response.StatusCode)

+ 24 - 12
test/user_test.go

@@ -2,6 +2,7 @@ package main
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"io/ioutil"
 	"net/http"
 	"net/http"
 	"testing"
 	"testing"
 
 
@@ -38,11 +39,9 @@ func TestAdminCreation(t *testing.T) {
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
-		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: Admin already exists! ", message.Message)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		assert.Equal(t, "Admin already Exists", message.Message)
 	})
 	})
-
 }
 }
 
 
 func TestGetUser(t *testing.T) {
 func TestGetUser(t *testing.T) {
@@ -70,12 +69,13 @@ func TestGetUser(t *testing.T) {
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
-		assert.Equal(t, "W1R3: Error Verifying Auth Token.", message.Message)
+		assert.Equal(t, "Error Verifying Auth Token", message.Message)
 
 
 	})
 	})
 }
 }
 
 
 func TestUpdateUser(t *testing.T) {
 func TestUpdateUser(t *testing.T) {
+	deleteAdmin(t)
 	if !adminExists(t) {
 	if !adminExists(t) {
 		addAdmin(t)
 		addAdmin(t)
 	}
 	}
@@ -92,7 +92,7 @@ func TestUpdateUser(t *testing.T) {
 		defer response.Body.Close()
 		defer response.Body.Close()
 		err = json.NewDecoder(response.Body).Decode(&message)
 		err = json.NewDecoder(response.Body).Decode(&message)
 		assert.Nil(t, err, err)
 		assert.Nil(t, err, err)
-		assert.Equal(t, "W1R3: Error Verifying Auth Token.", message.Message)
+		assert.Equal(t, "Error Verifying Auth Token", message.Message)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 	t.Run("UpdateSuccess", func(t *testing.T) {
 	t.Run("UpdateSuccess", func(t *testing.T) {
@@ -107,6 +107,18 @@ func TestUpdateUser(t *testing.T) {
 		assert.Equal(t, true, user.IsAdmin)
 		assert.Equal(t, true, user.IsAdmin)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
 	})
 	})
+	t.Run("ShortPassword", func(t *testing.T) {
+		admin.UserName = "user"
+		admin.Password = "123"
+		response, err := api(t, admin, http.MethodPut, "http://localhost:8081/api/users/admin", token)
+		assert.Nil(t, err, err)
+		defer response.Body.Close()
+		message, err := ioutil.ReadAll(response.Body)
+		assert.Nil(t, err, err)
+		assert.Contains(t, string(message), "Field validation for 'Password' failed")
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+	})
+
 }
 }
 
 
 func TestDeleteUser(t *testing.T) {
 func TestDeleteUser(t *testing.T) {
@@ -123,7 +135,7 @@ func TestDeleteUser(t *testing.T) {
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		var message models.ErrorResponse
 		var message models.ErrorResponse
 		json.NewDecoder(response.Body).Decode(&message)
 		json.NewDecoder(response.Body).Decode(&message)
-		assert.Equal(t, "W1R3: Error Verifying Auth Token.", message.Message)
+		assert.Equal(t, "Error Verifying Auth Token", message.Message)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 	})
 	})
 	t.Run("DeleteUser-ValidCredentials", func(t *testing.T) {
 	t.Run("DeleteUser-ValidCredentials", func(t *testing.T) {
@@ -155,7 +167,7 @@ func TestAuthenticateUser(t *testing.T) {
 			password:      "password",
 			password:      "password",
 			code:          http.StatusBadRequest,
 			code:          http.StatusBadRequest,
 			tokenExpected: false,
 			tokenExpected: false,
-			errMessage:    "W1R3: User invaliduser not found.",
+			errMessage:    "User invaliduser not found",
 		},
 		},
 		AuthorizeTestCase{
 		AuthorizeTestCase{
 			testname:      "empty user",
 			testname:      "empty user",
@@ -163,7 +175,7 @@ func TestAuthenticateUser(t *testing.T) {
 			password:      "password",
 			password:      "password",
 			code:          http.StatusBadRequest,
 			code:          http.StatusBadRequest,
 			tokenExpected: false,
 			tokenExpected: false,
-			errMessage:    "W1R3: Username can't be empty",
+			errMessage:    "Username can't be empty",
 		},
 		},
 		AuthorizeTestCase{
 		AuthorizeTestCase{
 			testname:      "empty password",
 			testname:      "empty password",
@@ -171,15 +183,15 @@ func TestAuthenticateUser(t *testing.T) {
 			password:      "",
 			password:      "",
 			code:          http.StatusBadRequest,
 			code:          http.StatusBadRequest,
 			tokenExpected: false,
 			tokenExpected: false,
-			errMessage:    "W1R3: Password can't be empty",
+			errMessage:    "Password can't be empty",
 		},
 		},
 		AuthorizeTestCase{
 		AuthorizeTestCase{
 			testname:      "Invalid Password",
 			testname:      "Invalid Password",
 			name:          "admin",
 			name:          "admin",
 			password:      "xxxxxxx",
 			password:      "xxxxxxx",
-			code:          http.StatusUnauthorized,
+			code:          http.StatusBadRequest,
 			tokenExpected: false,
 			tokenExpected: false,
-			errMessage:    "W1R3: Wrong Password.",
+			errMessage:    "Wrong Password",
 		},
 		},
 		AuthorizeTestCase{
 		AuthorizeTestCase{
 			testname:      "Valid User",
 			testname:      "Valid User",