Browse Source

refactoring validation and models

afeiszli 4 years ago
parent
commit
a982b62445

+ 1 - 1
config/dnsconfig/Corefile

@@ -1,4 +1,4 @@
-default k8scom tester textx comms  {
+comms my-net one-more other-net  {
     reload 15s
     reload 15s
     hosts /root/dnsconfig/netmaker.hosts {
     hosts /root/dnsconfig/netmaker.hosts {
 	fallthrough	
 	fallthrough	

+ 1 - 0
config/dnsconfig/netmaker.hosts

@@ -0,0 +1 @@
+10.10.0.12       node-nokdw.my-net

+ 4 - 3
controllers/common.go

@@ -54,6 +54,10 @@ func GetExtPeersList(networkName string, macaddress string) ([]models.ExtPeersRe
 		var peer models.ExtPeersResponse
 		var peer models.ExtPeersResponse
 		var extClient models.ExtClient
 		var extClient models.ExtClient
 		err = json.Unmarshal([]byte(value), &peer)
 		err = json.Unmarshal([]byte(value), &peer)
+		if err != nil {
+			functions.PrintUserLog("netmaker", "failed to unmarshal peer", 2)
+			continue
+		}
 		err = json.Unmarshal([]byte(value), &extClient)
 		err = json.Unmarshal([]byte(value), &extClient)
 		if err != nil {
 		if err != nil {
 			functions.PrintUserLog("netmaker", "failed to unmarshal ext client", 2)
 			functions.PrintUserLog("netmaker", "failed to unmarshal ext client", 2)
@@ -193,16 +197,13 @@ func UpdateNode(nodechange models.NodeUpdate, node models.Node) (models.Node, er
 	if err != nil {
 	if err != nil {
 		return models.Node{}, err
 		return models.Node{}, err
 	}
 	}
-
 	err = database.Insert(newkey, string(value), database.NODES_TABLE_NAME)
 	err = database.Insert(newkey, string(value), database.NODES_TABLE_NAME)
-
 	if notifynetwork {
 	if notifynetwork {
 		err = SetNetworkNodesLastModified(node.Network)
 		err = SetNetworkNodesLastModified(node.Network)
 	}
 	}
 	if servercfg.IsDNSMode() {
 	if servercfg.IsDNSMode() {
 		err = SetDNS()
 		err = SetDNS()
 	}
 	}
-
 	return node, err
 	return node, err
 }
 }
 
 

+ 2 - 2
controllers/dnsHttpController.go

@@ -59,7 +59,7 @@ func getAllDNS(w http.ResponseWriter, r *http.Request) {
 
 
 func GetAllDNS() ([]models.DNSEntry, error) {
 func GetAllDNS() ([]models.DNSEntry, error) {
 	var dns []models.DNSEntry
 	var dns []models.DNSEntry
-	networks, err := functions.ListNetworks()
+	networks, err := models.GetNetworks()
 	if err != nil {
 	if err != nil {
 		return []models.DNSEntry{}, err
 		return []models.DNSEntry{}, err
 	}
 	}
@@ -141,7 +141,7 @@ func GetCustomDNS(network string) ([]models.DNSEntry, error) {
 func SetDNS() error {
 func SetDNS() error {
 	hostfile := txeh.Hosts{}
 	hostfile := txeh.Hosts{}
 	var corefilestring string
 	var corefilestring string
-	networks, err := functions.ListNetworks()
+	networks, err := models.GetNetworks()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 30 - 196
controllers/networkHttpController.go

@@ -132,7 +132,7 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 	allnetworks := []models.Network{}
 	allnetworks := []models.Network{}
 	err := errors.New("Networks Error")
 	err := errors.New("Networks Error")
 	if networksSlice[0] == ALL_NETWORK_ACCESS {
 	if networksSlice[0] == ALL_NETWORK_ACCESS {
-		allnetworks, err = functions.ListNetworks()
+		allnetworks, err = models.GetNetworks()
 		if err != nil {
 		if err != nil {
 			returnErrorResponse(w, r, formatError(err, "internal"))
 			returnErrorResponse(w, r, formatError(err, "internal"))
 			return
 			return
@@ -166,7 +166,7 @@ func RemoveComms(networks []models.Network) []models.Network {
 	return append(returnable, networks[index+1:]...)
 	return append(returnable, networks[index+1:]...)
 }
 }
 
 
-func ValidateNetworkUpdate(network models.NetworkUpdate) error {
+func ValidateNetworkUpdate(network models.Network) error {
 	v := validator.New()
 	v := validator.New()
 
 
 	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
 	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
@@ -177,71 +177,6 @@ func ValidateNetworkUpdate(network models.NetworkUpdate) error {
 		return inCharSet
 		return inCharSet
 	})
 	})
 
 
-	//	_ = 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("netid_valid", func(fl validator.FieldLevel) bool {
-	//		return true
-	//	})
-
-	//	_ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
-	//		return true
-	//	})
-
-	err := v.Struct(network)
-
-	if err != nil {
-		for _, e := range err.(validator.ValidationErrors) {
-			fmt.Println(e)
-		}
-	}
-	return err
-}
-
-func ValidateNetworkCreate(network models.Network) error {
-
-	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 {
-		isvalid := true
-		if *network.IsDualStack {
-			isvalid = 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 {
-		isFieldUnique, _ := functions.IsNetworkNameUnique(fl.Field().String())
-		inCharSet := functions.NameInNetworkCharSet(fl.Field().String())
-		return isFieldUnique && inCharSet
-	})
-	//
-	_ = v.RegisterValidation("displayname_valid", func(fl validator.FieldLevel) bool {
-		isFieldUnique, _ := functions.IsNetworkDisplayNameUnique(fl.Field().String())
-		inCharSet := functions.NameInNetworkCharSet(fl.Field().String())
-		return isFieldUnique && inCharSet
-	})
-
 	err := v.Struct(network)
 	err := v.Struct(network)
 
 
 	if err != nil {
 	if err != nil {
@@ -341,33 +276,36 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	var networkChange models.NetworkUpdate
-
-	_ = json.NewDecoder(r.Body).Decode(&networkChange)
-	if networkChange.AddressRange == "" {
-		networkChange.AddressRange = network.AddressRange
-	}
-	if networkChange.AddressRange6 == "" {
-		networkChange.AddressRange6 = network.AddressRange6
-	}
-	if networkChange.NetID == "" {
-		networkChange.NetID = network.NetID
-	}
-
-	err = ValidateNetworkUpdate(networkChange)
+	var newNetwork models.Network
+	err = json.NewDecoder(r.Body).Decode(&newNetwork)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	returnednetwork, err := UpdateNetwork(networkChange, network)
+
+	rangeupdate, localrangeupdate, err := network.Update(&newNetwork)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
 
 
+	if rangeupdate {
+		err = functions.UpdateNetworkNodeAddresses(network.NetID)
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+	}
+	if localrangeupdate {
+		err = functions.UpdateNetworkLocalAddresses(network.NetID)
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+	}
 	functions.PrintUserLog(r.Header.Get("user"), "updated network "+netname, 1)
 	functions.PrintUserLog(r.Header.Get("user"), "updated network "+netname, 1)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(returnednetwork)
+	json.NewEncoder(w).Encode(newNetwork)
 }
 }
 
 
 func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
 func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
@@ -381,7 +319,7 @@ func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	var networkChange models.NetworkUpdate
+	var networkChange models.Network
 
 
 	_ = json.NewDecoder(r.Body).Decode(&networkChange)
 	_ = json.NewDecoder(r.Body).Decode(&networkChange)
 
 
@@ -399,98 +337,6 @@ func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(network)
 	json.NewEncoder(w).Encode(network)
 }
 }
 
 
-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.
-	//DisplayName can be changed instead, which is what shows on the front end
-	if networkChange.NetID != network.NetID {
-		return models.Network{}, errors.New("NetID is not editable")
-	}
-
-	haschange := false
-	hasrangeupdate := false
-	haslocalrangeupdate := false
-
-	if networkChange.AddressRange != "" {
-		haschange = true
-		hasrangeupdate = true
-		network.AddressRange = networkChange.AddressRange
-	}
-	if networkChange.LocalRange != "" {
-		haschange = true
-		haslocalrangeupdate = true
-		network.LocalRange = networkChange.LocalRange
-	}
-	if networkChange.IsLocal != nil {
-		network.IsLocal = networkChange.IsLocal
-	}
-	if networkChange.IsDualStack != nil {
-		network.IsDualStack = networkChange.IsDualStack
-	}
-	if networkChange.DefaultListenPort != 0 {
-		network.DefaultListenPort = networkChange.DefaultListenPort
-		haschange = true
-	}
-	if networkChange.DefaultPostDown != "" {
-		network.DefaultPostDown = networkChange.DefaultPostDown
-		haschange = true
-	}
-	if networkChange.DefaultInterface != "" {
-		network.DefaultInterface = networkChange.DefaultInterface
-		haschange = true
-	}
-	if networkChange.DefaultPostUp != "" {
-		network.DefaultPostUp = networkChange.DefaultPostUp
-		haschange = true
-	}
-	if networkChange.DefaultKeepalive != 0 {
-		network.DefaultKeepalive = networkChange.DefaultKeepalive
-		haschange = true
-	}
-	if networkChange.DisplayName != "" {
-		network.DisplayName = networkChange.DisplayName
-		haschange = true
-	}
-	if networkChange.DefaultCheckInInterval != 0 {
-		network.DefaultCheckInInterval = networkChange.DefaultCheckInInterval
-		haschange = true
-	}
-	if networkChange.AllowManualSignUp != nil {
-		network.AllowManualSignUp = networkChange.AllowManualSignUp
-		haschange = true
-	}
-
-	if haschange {
-		network.SetNetworkLastModified()
-	}
-
-	data, err := json.Marshal(&network)
-	if err != nil {
-		return models.Network{}, err
-	}
-
-	database.Insert(network.NetID, string(data), database.NETWORKS_TABLE_NAME)
-
-	//Cycles through nodes and gives them new IP's based on the new range
-	//Pretty cool, but also pretty inefficient currently
-	if hasrangeupdate {
-		err = functions.UpdateNetworkNodeAddresses(network.NetID)
-		if err != nil {
-			return models.Network{}, err
-		}
-	}
-	if haslocalrangeupdate {
-		err = functions.UpdateNetworkLocalAddresses(network.NetID)
-		if err != nil {
-			return models.Network{}, err
-		}
-	}
-	returnnetwork, err := functions.GetParentNetwork(network.NetID)
-	if err != nil {
-		return models.Network{}, err
-	}
-	return returnnetwork, nil
-}
-
 //Delete a network
 //Delete a network
 //Will stop you if  there's any nodes associated
 //Will stop you if  there's any nodes associated
 func deleteNetwork(w http.ResponseWriter, r *http.Request) {
 func deleteNetwork(w http.ResponseWriter, r *http.Request) {
@@ -553,27 +399,17 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 }
 }
 
 
 func CreateNetwork(network models.Network) error {
 func CreateNetwork(network models.Network) error {
-	//TODO: Not really doing good validation here. Same as createNode, updateNode, and updateNetwork
-	//Need to implement some better validation across the board
 
 
-	if network.IsLocal == nil {
-		falsevar := false
-		network.IsLocal = &falsevar
-	}
-	if network.IsDualStack == nil {
-		falsevar := false
-		network.IsDualStack = &falsevar
-	}
+	network.SetDefaults()
+	network.SetNodesLastModified()
+	network.SetNetworkLastModified()
+	network.KeyUpdateTimeStamp = time.Now().Unix()
 
 
-	err := ValidateNetworkCreate(network)
+	err := network.Validate()
 	if err != nil {
 	if err != nil {
 		//returnErrorResponse(w, r, formatError(err, "badrequest"))
 		//returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return err
 		return err
 	}
 	}
-	network.SetDefaults()
-	network.SetNodesLastModified()
-	network.SetNetworkLastModified()
-	network.KeyUpdateTimeStamp = time.Now().Unix()
 
 
 	data, err := json.Marshal(&network)
 	data, err := json.Marshal(&network)
 	if err != nil {
 	if err != nil {
@@ -611,7 +447,7 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		returnErrorResponse(w, r, formatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	functions.PrintUserLog(r.Header.Get("user"), "created access key "+netname, 1)
+	functions.PrintUserLog(r.Header.Get("user"), "created access key "+accesskey.Name+" on "+netname, 1)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(key)
 	json.NewEncoder(w).Encode(key)
 	//w.Write([]byte(accesskey.AccessString))
 	//w.Write([]byte(accesskey.AccessString))
@@ -641,10 +477,8 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
 		}
 		}
 	}
 	}
 	privAddr := ""
 	privAddr := ""
-	if network.IsLocal != nil {
-		if *network.IsLocal {
-			privAddr = network.LocalRange
-		}
+	if network.IsLocal != "" {
+		privAddr = network.LocalRange
 	}
 	}
 
 
 	netID := network.NetID
 	netID := network.NetID

+ 5 - 5
controllers/networkHttpController_test.go

@@ -30,7 +30,7 @@ func deleteNet(t *testing.T) {
 		err := DeleteDNS(entry.Name, entry.Network)
 		err := DeleteDNS(entry.Name, entry.Network)
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 	}
 	}
-	networks, _ := functions.ListNetworks()
+	networks, _ := models.GetNetworks()
 	for _, network := range networks {
 	for _, network := range networks {
 		t.Log("deleting network", network.NetID)
 		t.Log("deleting network", network.NetID)
 		success, err := DeleteNetwork(network.NetID)
 		success, err := DeleteNetwork(network.NetID)
@@ -54,7 +54,7 @@ func getNet() models.Network {
 }
 }
 
 
 func TestGetNetworks(t *testing.T) {
 func TestGetNetworks(t *testing.T) {
-	//calls functions.ListNetworks --- nothing to be done
+	//calls models.GetNetworks --- nothing to be done
 }
 }
 func TestCreateNetwork(t *testing.T) {
 func TestCreateNetwork(t *testing.T) {
 	deleteNet(t)
 	deleteNet(t)
@@ -108,14 +108,14 @@ func TestUpdateNetwork(t *testing.T) {
 	createNet()
 	createNet()
 	network := getNet()
 	network := getNet()
 	t.Run("NetID", func(t *testing.T) {
 	t.Run("NetID", func(t *testing.T) {
-		var networkupdate models.NetworkUpdate
+		var networkupdate models.Network
 		networkupdate.NetID = "wirecat"
 		networkupdate.NetID = "wirecat"
 		_, err := UpdateNetwork(networkupdate, network)
 		_, err := UpdateNetwork(networkupdate, network)
 		assert.NotNil(t, err)
 		assert.NotNil(t, err)
 		assert.Equal(t, "NetID is not editable", err.Error())
 		assert.Equal(t, "NetID is not editable", err.Error())
 	})
 	})
 	t.Run("LocalRange", func(t *testing.T) {
 	t.Run("LocalRange", func(t *testing.T) {
-		var networkupdate models.NetworkUpdate
+		var networkupdate models.Network
 		//NetID needs to be set as it will be in updateNetwork
 		//NetID needs to be set as it will be in updateNetwork
 		networkupdate.NetID = "skynet"
 		networkupdate.NetID = "skynet"
 		networkupdate.LocalRange = "192.168.0.1/24"
 		networkupdate.LocalRange = "192.168.0.1/24"
@@ -372,7 +372,7 @@ func TestValidateNetworkUpdate(t *testing.T) {
 	}
 	}
 	for _, tc := range cases {
 	for _, tc := range cases {
 		t.Run(tc.testname, func(t *testing.T) {
 		t.Run(tc.testname, func(t *testing.T) {
-			network := models.NetworkUpdate(tc.network)
+			network := models.Network(tc.network)
 			err := ValidateNetworkUpdate(network)
 			err := ValidateNetworkUpdate(network)
 			assert.NotNil(t, err)
 			assert.NotNil(t, err)
 			assert.Contains(t, err.Error(), tc.errMessage)
 			assert.Contains(t, err.Error(), tc.errMessage)

+ 7 - 32
controllers/nodeGrpcController.go

@@ -34,14 +34,6 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeRe
 		}
 		}
 	*/
 	*/
 	// Cast to ReadNodeRes type
 	// Cast to ReadNodeRes type
-	dualvar := false
-	if network.IsDualStack != nil {
-		dualvar = *network.IsDualStack
-	}
-	localvar := false
-	if network.IsLocal != nil {
-		localvar = *network.IsLocal
-	}
 
 
 	response := &nodepb.ReadNodeRes{
 	response := &nodepb.ReadNodeRes{
 		Node: &nodepb.Node{
 		Node: &nodepb.Node{
@@ -64,8 +56,8 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeRe
 			Publickey:           node.PublicKey,
 			Publickey:           node.PublicKey,
 			Listenport:          node.ListenPort,
 			Listenport:          node.ListenPort,
 			Keepalive:           node.PersistentKeepalive,
 			Keepalive:           node.PersistentKeepalive,
-			Islocal:             localvar,
-			Isdualstack:         dualvar,
+			Islocal:             network.IsLocal == "yes",
+			Isdualstack:         network.IsDualStack == "yes",
 			Localrange:          network.LocalRange,
 			Localrange:          network.LocalRange,
 		},
 		},
 	}
 	}
@@ -165,7 +157,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
 	if !validKey {
 	if !validKey {
 		//Check to see if network will allow manual sign up
 		//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.
 		//may want to switch this up with the valid key check and avoid a DB call that way.
-		if *network.AllowManualSignUp {
+		if network.AllowManualSignUp == "yes" {
 			node.IsPending = true
 			node.IsPending = true
 		} else {
 		} else {
 			return nil, status.Errorf(
 			return nil, status.Errorf(
@@ -184,14 +176,6 @@ 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
-	}
 
 
 	// return the node in a CreateNodeRes type
 	// return the node in a CreateNodeRes type
 	response := &nodepb.CreateNodeRes{
 	response := &nodepb.CreateNodeRes{
@@ -210,8 +194,8 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
 			Publickey:    node.PublicKey,
 			Publickey:    node.PublicKey,
 			Listenport:   node.ListenPort,
 			Listenport:   node.ListenPort,
 			Keepalive:    node.PersistentKeepalive,
 			Keepalive:    node.PersistentKeepalive,
-			Islocal:      localvar,
-			Isdualstack:  dualvar,
+			Islocal:      network.IsLocal == "yes",
+			Isdualstack:  network.IsDualStack == "yes",
 			Localrange:   network.LocalRange,
 			Localrange:   network.LocalRange,
 		},
 		},
 	}
 	}
@@ -318,15 +302,6 @@ 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
-	}
-
 	return &nodepb.UpdateNodeRes{
 	return &nodepb.UpdateNodeRes{
 		Node: &nodepb.Node{
 		Node: &nodepb.Node{
 			Macaddress:   newnode.MacAddress,
 			Macaddress:   newnode.MacAddress,
@@ -345,8 +320,8 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.UpdateNo
 			Dnsoff:       !servercfg.IsDNSMode(),
 			Dnsoff:       !servercfg.IsDNSMode(),
 			Listenport:   newnode.ListenPort,
 			Listenport:   newnode.ListenPort,
 			Keepalive:    newnode.PersistentKeepalive,
 			Keepalive:    newnode.PersistentKeepalive,
-			Islocal:      localvar,
-			Isdualstack:  dualvar,
+			Islocal:      network.IsLocal == "yes",
+			Isdualstack:  network.IsDualStack == "yes",
 			Localrange:   network.LocalRange,
 			Localrange:   network.LocalRange,
 		},
 		},
 	}, nil
 	}, nil

+ 27 - 15
controllers/nodeHttpController.go

@@ -17,17 +17,17 @@ import (
 
 
 func nodeHandlers(r *mux.Router) {
 func nodeHandlers(r *mux.Router) {
 
 
-	r.HandleFunc("/api/nodes", authorize(false, "master", http.HandlerFunc(getAllNodes))).Methods("GET")
+	r.HandleFunc("/api/nodes", authorize(false, "user", http.HandlerFunc(getAllNodes))).Methods("GET")
 	r.HandleFunc("/api/nodes/{network}", authorize(true, "network", http.HandlerFunc(getNetworkNodes))).Methods("GET")
 	r.HandleFunc("/api/nodes/{network}", authorize(true, "network", http.HandlerFunc(getNetworkNodes))).Methods("GET")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(getNode))).Methods("GET")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(getNode))).Methods("GET")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/checkin", authorize(true, "node", http.HandlerFunc(checkIn))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/checkin", authorize(true, "node", http.HandlerFunc(checkIn))).Methods("POST")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/creategateway", authorize(true, "master", http.HandlerFunc(createEgressGateway))).Methods("POST")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "master", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
+	r.HandleFunc("/api/nodes/{network}/{macaddress}/creategateway", authorize(true, "user", http.HandlerFunc(createEgressGateway))).Methods("POST")
+	r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "user", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
 	r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
-	r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "master", http.HandlerFunc(uncordonNode))).Methods("POST")
+	r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "user", http.HandlerFunc(uncordonNode))).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
 	r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
@@ -147,7 +147,6 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
 //TODO: Consider better RBAC implementations
 //TODO: Consider better RBAC implementations
 func authorize(networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
 func authorize(networkCheck bool, authNetwork string, 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.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
 		}
 		}
@@ -155,7 +154,6 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 		var params = mux.Vars(r)
 		var params = mux.Vars(r)
 
 
 		networkexists, _ := functions.NetworkExists(params["network"])
 		networkexists, _ := functions.NetworkExists(params["network"])
-
 		//check that the request is for a valid network
 		//check that the request is for a valid network
 		//if (networkCheck && !networkexists) || err != nil {
 		//if (networkCheck && !networkexists) || err != nil {
 		if networkCheck && !networkexists {
 		if networkCheck && !networkexists {
@@ -164,9 +162,7 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 			}
 			}
 			returnErrorResponse(w, r, errorResponse)
 			returnErrorResponse(w, r, errorResponse)
 			return
 			return
-
 		} else {
 		} else {
-
 			w.Header().Set("Content-Type", "application/json")
 			w.Header().Set("Content-Type", "application/json")
 
 
 			//get the auth token
 			//get the auth token
@@ -192,7 +188,6 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 			//A: the token is the master password
 			//A: the token is the master password
 			//B: the token corresponds to a mac address, and if so, which one
 			//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.
 			//TODO: There's probably a better way of dealing with the "master token"/master password. Plz Halp.
-
 			var isAuthorized = false
 			var isAuthorized = false
 			var macaddress = ""
 			var macaddress = ""
 			username, networks, isadmin, errN := functions.VerifyUserToken(authToken)
 			username, networks, isadmin, errN := functions.VerifyUserToken(authToken)
@@ -219,7 +214,6 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 			//The mastermac (login with masterkey from config) can do everything!! May be dangerous.
 			//The mastermac (login with masterkey from config) can do everything!! May be dangerous.
 			if macaddress == "mastermac" {
 			if macaddress == "mastermac" {
 				isAuthorized = true
 				isAuthorized = true
-
 				//for everyone else, there's poor man's RBAC. The "cases" are defined in the routes in the handlers
 				//for everyone else, there's poor man's RBAC. The "cases" are defined in the routes in the handlers
 				//So each route defines which access network should be allowed to access it
 				//So each route defines which access network should be allowed to access it
 			} else {
 			} else {
@@ -248,8 +242,8 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
 					} else {
 					} else {
 						isAuthorized = (macaddress == params["macaddress"])
 						isAuthorized = (macaddress == params["macaddress"])
 					}
 					}
-				case "master":
-					isAuthorized = (macaddress == "mastermac")
+				case "user":
+					isAuthorized = true
 				default:
 				default:
 					isAuthorized = false
 					isAuthorized = false
 				}
 				}
@@ -316,7 +310,7 @@ func GetNetworkNodes(network string) ([]models.Node, error) {
 //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")
-	nodes, err := functions.GetAllNodes()
+	nodes, err := getUsersNodes(r.Header.Get("user"))
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
@@ -327,6 +321,22 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(nodes)
 	json.NewEncoder(w).Encode(nodes)
 }
 }
 
 
+func getUsersNodes(username string) ([]models.Node, error) {
+	var nodes []models.Node
+	user, err := functions.GetUser(username)
+	if err != nil {
+		return nodes, err
+	}
+	for _, networkName := range user.Networks {
+		tmpNodes, err := GetNetworkNodes(networkName)
+		if err != nil {
+			continue
+		}
+		nodes = append(nodes, tmpNodes...)
+	}
+	return nodes, err
+}
+
 //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
 //Honestly I'm not sure what all it should be doing
 //Honestly I'm not sure what all it should be doing
 //TODO: Implement the necessary stuff, including the below
 //TODO: Implement the necessary stuff, including the below
@@ -460,7 +470,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
 	if !validKey {
 	if !validKey {
 		//Check to see if network will allow manual sign up
 		//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.
 		//may want to switch this up with the valid key check and avoid a DB call that way.
-		if *network.AllowManualSignUp {
+		if network.AllowManualSignUp == "yes" {
 			node.IsPending = true
 			node.IsPending = true
 		} else {
 		} else {
 			errorResponse = models.ErrorResponse{
 			errorResponse = models.ErrorResponse{
@@ -678,6 +688,7 @@ func CreateIngressGateway(netid string, macaddress string) (models.Node, error)
 		log.Println("Could not find network.")
 		log.Println("Could not find network.")
 		return models.Node{}, err
 		return models.Node{}, err
 	}
 	}
+	node.IsIngressGateway = true
 	node.IngressGatewayRange = network.AddressRange
 	node.IngressGatewayRange = network.AddressRange
 	postUpCmd := "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + node.Interface + " -j MASQUERADE"
 	postUpCmd := "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + node.Interface + " -j MASQUERADE"
 	postDownCmd := "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE"
 	postDownCmd := "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE"
@@ -748,6 +759,7 @@ func DeleteIngressGateway(network, macaddress string) (models.Node, error) {
 }
 }
 
 
 func updateNode(w http.ResponseWriter, r *http.Request) {
 func updateNode(w http.ResponseWriter, r *http.Request) {
+	log.Println("update reached.")
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
@@ -756,7 +768,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	//id, _ := primitive.ObjectIDFromHex(params["id"])
 	//id, _ := primitive.ObjectIDFromHex(params["id"])
 
 
 	var node models.Node
 	var node models.Node
-
+	log.Println("Called", params["network"], params["macaddress"])
 	//start here
 	//start here
 	node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
 	node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
 	if err != nil {
 	if err != nil {

+ 24 - 8
controllers/userHttpController.go

@@ -152,7 +152,6 @@ func authorizeUserAdm(next http.Handler) http.HandlerFunc {
 
 
 func ValidateUserToken(token string, user string, adminonly bool) error {
 func ValidateUserToken(token string, user string, adminonly bool) error {
 	var tokenSplit = strings.Split(token, " ")
 	var tokenSplit = strings.Split(token, " ")
-
 	//I put this in in case the user doesn't put in a token at all (in which case it's empty)
 	//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.
 	//There's probably a smarter way of handling this.
 	var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
 	var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
@@ -213,7 +212,20 @@ func hasAdmin(w http.ResponseWriter, r *http.Request) {
 
 
 }
 }
 
 
-func GetUser(username string) (models.User, error) {
+func GetUser(username string) (models.ReturnUser, error) {
+
+	var user models.ReturnUser
+	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
+	if err != nil {
+		return user, err
+	}
+	if err = json.Unmarshal([]byte(record), &user); err != nil {
+		return models.ReturnUser{}, err
+	}
+	return user, err
+}
+
+func GetUserInternal(username string) (models.User, error) {
 
 
 	var user models.User
 	var user models.User
 	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
 	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
@@ -226,9 +238,9 @@ func GetUser(username string) (models.User, error) {
 	return user, err
 	return user, err
 }
 }
 
 
-func GetUsers() ([]models.User, error) {
+func GetUsers() ([]models.ReturnUser, error) {
 
 
-	var users []models.User
+	var users []models.ReturnUser
 
 
 	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
 	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
 
 
@@ -238,7 +250,7 @@ func GetUsers() ([]models.User, error) {
 
 
 	for _, value := range collection {
 	for _, value := range collection {
 
 
-		var user models.User
+		var user models.ReturnUser
 		err = json.Unmarshal([]byte(value), &user)
 		err = json.Unmarshal([]byte(value), &user)
 		if err != nil {
 		if err != nil {
 			continue // get users
 			continue // get users
@@ -394,7 +406,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 	var user models.User
 	var user models.User
 	//start here
 	//start here
 	username := params["username"]
 	username := params["username"]
-	user, err := GetUser(username)
+	user, err := GetUserInternal(username)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
@@ -422,7 +434,7 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) {
 	var user models.User
 	var user models.User
 	//start here
 	//start here
 	username := params["username"]
 	username := params["username"]
-	user, err := GetUser(username)
+	user, err := GetUserInternal(username)
 	if err != nil {
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
@@ -445,6 +457,10 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) {
 
 
 func DeleteUser(user string) (bool, error) {
 func DeleteUser(user string) (bool, error) {
 
 
+	if userRecord, err := database.FetchRecord(database.USERS_TABLE_NAME, user); err != nil || len(userRecord) == 0 {
+		return false, errors.New("user does not exist")
+	}
+
 	err := database.DeleteRecord(database.USERS_TABLE_NAME, user)
 	err := database.DeleteRecord(database.USERS_TABLE_NAME, user)
 	if err != nil {
 	if err != nil {
 		return false, err
 		return false, err
@@ -466,7 +482,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 		return
 	} else if !success {
 	} else if !success {
-		returnErrorResponse(w, r, formatError(errors.New("Delete unsuccessful."), "badrequest"))
+		returnErrorResponse(w, r, formatError(errors.New("delete unsuccessful."), "badrequest"))
 		return
 		return
 	}
 	}
 
 

+ 1 - 0
database/database.go

@@ -93,5 +93,6 @@ func FetchRecords(tableName string) (map[string]string, error) {
 		row.Scan(&key, &value)
 		row.Scan(&key, &value)
 		records[key] = value
 		records[key] = value
 	}
 	}
+	// log.Println(records)
 	return records, nil
 	return records, nil
 }
 }

+ 25 - 56
functions/helpers.go

@@ -54,6 +54,19 @@ func ParseIntClient(value string) (models.IntClient, error) {
 //Takes in an arbitrary field and value for field and checks to see if any other
 //Takes in an arbitrary field and value for field and checks to see if any other
 //node has that value for the same field within the network
 //node has that value for the same field within the network
 
 
+func GetUser(username string) (models.User, error) {
+
+	var user models.User
+	record, err := database.FetchRecord(database.USERS_TABLE_NAME, username)
+	if err != nil {
+		return user, err
+	}
+	if err = json.Unmarshal([]byte(record), &user); err != nil {
+		return models.User{}, err
+	}
+	return user, err
+}
+
 func SliceContains(slice []string, item string) bool {
 func SliceContains(slice []string, item string) bool {
 	set := make(map[string]struct{}, len(slice))
 	set := make(map[string]struct{}, len(slice))
 	for _, s := range slice {
 	for _, s := range slice {
@@ -252,32 +265,11 @@ func UpdateNetworkLocalAddresses(networkName string) error {
 	return nil
 	return nil
 }
 }
 
 
-//Checks to see if any other networks have the same name (id)
-func IsNetworkNameUnique(name string) (bool, error) {
-
-	isunique := true
-
-	dbs, err := ListNetworks()
-
-	if err != nil {
-		return false, err
-	}
-
-	for i := 0; i < len(dbs); i++ {
-
-		if name == dbs[i].NetID {
-			isunique = false
-		}
-	}
-
-	return isunique, nil
-}
-
 func IsNetworkDisplayNameUnique(name string) (bool, error) {
 func IsNetworkDisplayNameUnique(name string) (bool, error) {
 
 
 	isunique := true
 	isunique := true
 
 
-	dbs, err := ListNetworks()
+	dbs, err := models.GetNetworks()
 	if err != nil {
 	if err != nil {
 		return false, err
 		return false, err
 	}
 	}
@@ -333,30 +325,6 @@ func GetNetworkNodeNumber(networkName string) (int, error) {
 	return count, nil
 	return count, nil
 }
 }
 
 
-// Anyway, returns all the networks
-func ListNetworks() ([]models.Network, error) {
-
-	var networks []models.Network
-
-	collection, err := database.FetchRecords(database.NETWORKS_TABLE_NAME)
-
-	if err != nil {
-		return networks, err
-	}
-
-	for _, value := range collection {
-
-		var network models.Network
-		if err := json.Unmarshal([]byte(value), &network); err != nil {
-			return networks, err
-		}
-		// add network our array
-		networks = append(networks, network)
-	}
-
-	return networks, err
-}
-
 //Checks to see if access key is valid
 //Checks to see if access key is valid
 //Does so by checking against all keys and seeing if any have the same value
 //Does so by checking against all keys and seeing if any have the same value
 //may want to hash values before comparing...consider this
 //may want to hash values before comparing...consider this
@@ -385,7 +353,7 @@ func IsKeyValid(networkname string, keyvalue string) bool {
 
 
 func IsKeyValidGlobal(keyvalue string) bool {
 func IsKeyValidGlobal(keyvalue string) bool {
 
 
-	networks, _ := ListNetworks()
+	networks, _ := models.GetNetworks()
 	var key models.AccessKey
 	var key models.AccessKey
 	foundkey := false
 	foundkey := false
 	isvalid := false
 	isvalid := false
@@ -499,20 +467,21 @@ func GetNodeByMacAddress(network string, macaddress string) (models.Node, error)
 
 
 	var node models.Node
 	var node models.Node
 
 
-	records, err := database.FetchRecords(database.NODES_TABLE_NAME)
-
+	key, err := GetRecordKey(macaddress, network)
 	if err != nil {
 	if err != nil {
 		return node, err
 		return node, err
 	}
 	}
 
 
-	for _, value := range records {
-		json.Unmarshal([]byte(value), &node)
-		if node.MacAddress == macaddress && node.Network == network {
-			return node, nil
-		}
+	record, err := database.FetchRecord(database.NODES_TABLE_NAME, key)
+	if err != nil {
+		return models.Node{}, err
+	}
+
+	if err = json.Unmarshal([]byte(record), &node); err != nil {
+		return models.Node{}, err
 	}
 	}
 
 
-	return models.Node{}, nil
+	return node, nil
 }
 }
 
 
 func DeleteAllIntClients() error {
 func DeleteAllIntClients() error {
@@ -614,7 +583,7 @@ func UniqueAddress6(networkName string) (string, error) {
 		fmt.Println("Network Not Found")
 		fmt.Println("Network Not Found")
 		return "", err
 		return "", err
 	}
 	}
-	if network.IsDualStack == nil || *network.IsDualStack == false {
+	if network.IsDualStack == "no" {
 		if networkName != "comms" {
 		if networkName != "comms" {
 			return "", nil
 			return "", nil
 		}
 		}

+ 206 - 72
models/network.go

@@ -1,73 +1,183 @@
 package models
 package models
 
 
 import (
 import (
+	"encoding/json"
+	"errors"
+	"strings"
 	"time"
 	"time"
 
 
-	"go.mongodb.org/mongo-driver/bson/primitive"
+	"github.com/go-playground/validator/v10"
+	"github.com/gravitl/netmaker/database"
 )
 )
 
 
 //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,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,min=1,max=20,displayname_valid"`
-	NetID               string      `json:"netid" bson:"netid" validate:"required,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"`
-	NodeLimit           int32       `json:"nodelimit" bson:"nodelimit"`
-	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"`
-	IsIPv4              string      `json:"isipv4" bson:"isipv4"`
-	IsIPv6              string      `json:"isipv6" bson:"isipv6"`
-	IsGRPCHub           string      `json:"isgrpchub" bson:"isgrpchub"`
-	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"`
+	AddressRange           string      `json:"addressrange" bson:"addressrange" validate:"required,cidr"`
+	AddressRange6          string      `json:"addressrange6" bson:"addressrange6" validate:"regexp=^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$"`
+	DisplayName            string      `json:"displayname,omitempty" bson:"displayname,omitempty" validate:"omitempty,min=1,max=20,displayname_valid"`
+	NetID                  string      `json:"netid" bson:"netid" validate:"required,min=1,max=12,netid_valid"`
+	NodesLastModified      int64       `json:"nodeslastmodified" bson:"nodeslastmodified"`
+	NetworkLastModified    int64       `json:"networklastmodified" bson:"networklastmodified"`
+	DefaultInterface       string      `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=15"`
+	DefaultListenPort      int32       `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
+	NodeLimit              int32       `json:"nodelimit" bson:"nodelimit"`
+	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      string      `json:"defaultsaveconfig" bson:"defaultsaveconfig" validate:"regexp=^(yes|no)$"`
+	AccessKeys             []AccessKey `json:"accesskeys" bson:"accesskeys"`
+	AllowManualSignUp      string      `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"regexp=^(yes|no)$"`
+	IsLocal                string      `json:"islocal" bson:"islocal" validate:"regexp=^(yes|no)$"`
+	IsDualStack            string      `json:"isdualstack" bson:"isdualstack" validate:"regexp=^(yes|no)$"`
+	IsIPv4                 string      `json:"isipv4" bson:"isipv4" validate:"regexp=^(yes|no)$"`
+	IsIPv6                 string      `json:"isipv6" bson:"isipv6" validate:"regexp=^(yes|no)$"`
+	IsGRPCHub              string      `json:"isgrpchub" bson:"isgrpchub" validate:"regexp=^(yes|no)$"`
+	LocalRange             string      `json:"localrange" bson:"localrange" validate:"omitempty,cidr"`
+	DefaultCheckInInterval int32       `json:"checkininterval,omitempty" bson:"checkininterval,omitempty" validate:"omitempty,numeric,min=2,max=100000"`
+	DefaultUDPHolePunch    string      `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"regexp=^(yes|no)$"`
 }
 }
-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,netid_valid,min=1,max=20"`
-	NetID               string      `json:"netid" bson:"netid" validate:"omitempty,netid_valid,min=1,max=15"`
-	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"`
-	NodeLimit           int32       `json:"nodelimit" bson:"nodelimit"`
-	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"`
-	IsIPv4              string      `json:"isipv4" bson:"isipv4"`
-	IsIPv6              string      `json:"isipv6" bson:"isipv6"`
-	IsGRPCHub           string      `json:"isgrpchub" bson:"isgrpchub"`
-	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 SaveData struct { // put sensitive fields here
+	NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=12,netid_valid"`
+}
+
+var FIELDS = map[string][]string{
+	// "id":                  {"ID", "string"},
+	"addressrange":        {"AddressRange", "string"},
+	"addressrange6":       {"AddressRange6", "string"},
+	"displayname":         {"DisplayName", "string"},
+	"netid":               {"NetID", "string"},
+	"nodeslastmodified":   {"NodesLastModified", "int64"},
+	"networklastmodified": {"NetworkLastModified", "int64"},
+	"defaultinterface":    {"DefaultInterface", "string"},
+	"defaultlistenport":   {"DefaultListenPort", "int32"},
+	"nodelimit":           {"NodeLimit", "int32"},
+	"defaultpostup":       {"DefaultPostUp", "string"},
+	"defaultpostdown":     {"DefaultPostDown", "string"},
+	"keyupdatetimestamp":  {"KeyUpdateTimeStamp", "int64"},
+	"defaultkeepalive":    {"DefaultKeepalive", "int32"},
+	"defaultsaveconfig":   {"DefaultSaveConfig", "string"},
+	"accesskeys":          {"AccessKeys", "[]AccessKey"},
+	"allowmanualsignup":   {"AllowManualSignUp", "string"},
+	"islocal":             {"IsLocal", "string"},
+	"isdualstack":         {"IsDualStack", "string"},
+	"isipv4":              {"IsIPv4", "string"},
+	"isipv6":              {"IsIPv6", "string"},
+	"isgrpchub":           {"IsGRPCHub", "string"},
+	"localrange":          {"LocalRange", "string"},
+	"checkininterval":     {"DefaultCheckInInterval", "int32"},
+	"defaultudpholepunch": {"DefaultUDPHolePunch", "string"},
+}
+
+func (network *Network) FieldExists(field string) bool {
+	return len(FIELDS[field]) > 0
+}
+
+func (network *Network) NetIDInNetworkCharSet() bool {
+
+	charset := "abcdefghijklmnopqrstuvwxyz1234567890-_."
+
+	for _, char := range network.NetID {
+		if !strings.Contains(charset, strings.ToLower(string(char))) {
+			return false
+		}
+	}
+	return true
+}
+
+func (network *Network) DisplayNameInNetworkCharSet() bool {
+
+	charset := "abcdefghijklmnopqrstuvwxyz1234567890-_./;% ^#()!@$*"
+
+	for _, char := range network.DisplayName {
+		if !strings.Contains(charset, strings.ToLower(string(char))) {
+			return false
+		}
+	}
+	return true
+}
+
+// Anyway, returns all the networks
+func GetNetworks() ([]Network, error) {
+	var networks []Network
+
+	collection, err := database.FetchRecords(database.NETWORKS_TABLE_NAME)
+
+	if err != nil {
+		return networks, err
+	}
+
+	for _, value := range collection {
+		var network Network
+		if err := json.Unmarshal([]byte(value), &network); err != nil {
+			return networks, err
+		}
+		// add network our array
+		networks = append(networks, network)
+	}
+
+	return networks, err
+}
+
+func (network *Network) IsNetworkDisplayNameUnique() (bool, error) {
+
+	isunique := true
+
+	records, err := GetNetworks()
+
+	if err != nil {
+		return false, err
+	}
+
+	for i := 0; i < len(records); i++ {
+
+		if network.NetID == records[i].DisplayName {
+			isunique = false
+		}
+	}
+
+	return isunique, nil
+}
+
+//Checks to see if any other networks have the same name (id)
+func (network *Network) IsNetworkNameUnique() (bool, error) {
+
+	isunique := true
+
+	dbs, err := GetNetworks()
+
+	if err != nil {
+		return false, err
+	}
+
+	for i := 0; i < len(dbs); i++ {
+
+		if network.NetID == dbs[i].NetID {
+			isunique = false
+		}
+	}
+
+	return isunique, nil
+}
+
+func (network *Network) Validate() error {
+	v := validator.New()
+	_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
+		isFieldUnique, _ := network.IsNetworkNameUnique()
+		inCharSet := network.NetIDInNetworkCharSet()
+		return isFieldUnique && inCharSet
+	})
+	//
+	_ = v.RegisterValidation("displayname_valid", func(fl validator.FieldLevel) bool {
+		isFieldUnique, _ := network.IsNetworkDisplayNameUnique()
+		inCharSet := network.DisplayNameInNetworkCharSet()
+		return isFieldUnique && inCharSet
+	})
+
+	err := v.Struct(network)
+	return err
 }
 }
 
 
 //TODO:
 //TODO:
@@ -81,6 +191,15 @@ func (network *Network) SetNetworkLastModified() {
 }
 }
 
 
 func (network *Network) SetDefaults() {
 func (network *Network) SetDefaults() {
+	if network.DefaultUDPHolePunch == "" {
+		network.DefaultUDPHolePunch = "yes"
+	}
+	if network.IsLocal == "" {
+		network.IsLocal = "no"
+	}
+	if network.IsGRPCHub == "" {
+		network.IsGRPCHub = "no"
+	}
 	if network.DisplayName == "" {
 	if network.DisplayName == "" {
 		network.DisplayName = network.NetID
 		network.DisplayName = network.NetID
 	}
 	}
@@ -97,31 +216,46 @@ func (network *Network) SetDefaults() {
 	if network.NodeLimit == 0 {
 	if network.NodeLimit == 0 {
 		network.NodeLimit = 999999999
 		network.NodeLimit = 999999999
 	}
 	}
-	if network.DefaultPostDown == "" {
-
-	}
-	if network.DefaultSaveConfig == nil {
-		defaultsave := true
-		network.DefaultSaveConfig = &defaultsave
+	if network.DefaultSaveConfig == "" {
+		network.DefaultSaveConfig = "no"
 	}
 	}
 	if network.DefaultKeepalive == 0 {
 	if network.DefaultKeepalive == 0 {
 		network.DefaultKeepalive = 20
 		network.DefaultKeepalive = 20
 	}
 	}
-	if network.DefaultPostUp == "" {
-	}
 	//Check-In Interval for Nodes, In Seconds
 	//Check-In Interval for Nodes, In Seconds
 	if network.DefaultCheckInInterval == 0 {
 	if network.DefaultCheckInInterval == 0 {
 		network.DefaultCheckInInterval = 30
 		network.DefaultCheckInInterval = 30
 	}
 	}
-	if network.AllowManualSignUp == nil {
-		signup := false
-		network.AllowManualSignUp = &signup
+	if network.AllowManualSignUp == "" {
+		network.AllowManualSignUp = "no"
+	}
+	if network.IsDualStack == "" {
+		network.IsDualStack = "no"
 	}
 	}
-	if (network.IsDualStack != nil) && *network.IsDualStack {
+	if network.IsDualStack == "yes" {
 		network.IsIPv6 = "yes"
 		network.IsIPv6 = "yes"
 		network.IsIPv4 = "yes"
 		network.IsIPv4 = "yes"
-	} else if network.IsGRPCHub != "yes" {
+	} else {
 		network.IsIPv6 = "no"
 		network.IsIPv6 = "no"
 		network.IsIPv4 = "yes"
 		network.IsIPv4 = "yes"
 	}
 	}
 }
 }
+
+func (currentNetwork *Network) Update(newNetwork *Network) (bool, bool, error) {
+	if err := newNetwork.Validate(); err != nil {
+		return false, false, err
+	}
+	if newNetwork.NetID == currentNetwork.NetID {
+		hasrangeupdate := newNetwork.AddressRange != currentNetwork.AddressRange
+		localrangeupdate := newNetwork.LocalRange != currentNetwork.LocalRange
+		if data, err := json.Marshal(newNetwork); err != nil {
+			return false, false, err
+		} else {
+			newNetwork.SetNetworkLastModified()
+			err = database.Insert(newNetwork.NetID, string(data), database.NETWORKS_TABLE_NAME)
+			return hasrangeupdate, localrangeupdate, err
+		}
+	}
+	// copy values
+	return false, false, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
+}

+ 2 - 2
models/node.go

@@ -145,8 +145,8 @@ 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 {
-		if parentNetwork.DefaultSaveConfig != nil {
-			defaultsave := *parentNetwork.DefaultSaveConfig
+		if parentNetwork.DefaultSaveConfig != "" {
+			defaultsave := parentNetwork.DefaultSaveConfig == "yes"
 			node.SaveConfig = &defaultsave
 			node.SaveConfig = &defaultsave
 		}
 		}
 	}
 	}

+ 21 - 15
models/structs.go

@@ -8,10 +8,16 @@ type AuthParams struct {
 }
 }
 
 
 type User struct {
 type User struct {
-	UserName string `json:"username" bson:"username" validate:"min=3,max=40,regexp=^(([a-zA-Z,\-,\.]*)|([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})){3,40}$"`
-	Password string `json:"password" bson:"password" validate:"required,min=5"`
+	UserName string   `json:"username" bson:"username" validate:"min=3,max=40,regexp=^(([a-zA-Z,\-,\.]*)|([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})){3,40}$"`
+	Password string   `json:"password" bson:"password" validate:"required,min=5"`
 	Networks []string `json:"networks" bson:"networks"`
 	Networks []string `json:"networks" bson:"networks"`
-	IsAdmin  bool   `json:"isadmin" bson:"isadmin"`
+	IsAdmin  bool     `json:"isadmin" bson:"isadmin"`
+}
+
+type ReturnUser struct {
+	UserName string   `json:"username" bson:"username" validate:"min=3,max=40,regexp=^(([a-zA-Z,\-,\.]*)|([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})){3,40}$"`
+	Networks []string `json:"networks" bson:"networks"`
+	IsAdmin  bool     `json:"isadmin" bson:"isadmin"`
 }
 }
 
 
 type UserAuthParams struct {
 type UserAuthParams struct {
@@ -92,27 +98,27 @@ type CheckInResponse struct {
 }
 }
 
 
 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"`
+	IsEgressGateway    bool   `json:"isegressgateway" bson:"isegressgateway"`
+	EgressGatewayRange string `json:"egressgatewayrange" bson:"egressgatewayrange"`
+	ListenPort         int32  `json:"listenport" bson:"listenport"`
+	KeepAlive          int32  `json:"persistentkeepalive" bson:"persistentkeepalive"`
+}
+
+type ExtPeersResponse struct {
 	PublicKey    string `json:"publickey" bson:"publickey"`
 	PublicKey    string `json:"publickey" bson:"publickey"`
 	Endpoint     string `json:"endpoint" bson:"endpoint"`
 	Endpoint     string `json:"endpoint" bson:"endpoint"`
 	Address      string `json:"address" bson:"address"`
 	Address      string `json:"address" bson:"address"`
 	Address6     string `json:"address6" bson:"address6"`
 	Address6     string `json:"address6" bson:"address6"`
 	LocalAddress string `json:"localaddress" bson:"localaddress"`
 	LocalAddress string `json:"localaddress" bson:"localaddress"`
-	IsEgressGateway    bool   `json:"isegressgateway" bson:"isegressgateway"`
-	EgressGatewayRange string `json:"egressgatewayrange" bson:"egressgatewayrange"`
 	ListenPort   int32  `json:"listenport" bson:"listenport"`
 	ListenPort   int32  `json:"listenport" bson:"listenport"`
 	KeepAlive    int32  `json:"persistentkeepalive" bson:"persistentkeepalive"`
 	KeepAlive    int32  `json:"persistentkeepalive" bson:"persistentkeepalive"`
 }
 }
 
 
-type ExtPeersResponse 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"`
-        ListenPort   int32  `json:"listenport" bson:"listenport"`
-        KeepAlive    int32  `json:"persistentkeepalive" bson:"persistentkeepalive"`
-}
-
 type EgressGatewayRequest struct {
 type EgressGatewayRequest struct {
 	NodeID      string   `json:"nodeid" bson:"nodeid"`
 	NodeID      string   `json:"nodeid" bson:"nodeid"`
 	NetID       string   `json:"netid" bson:"netid"`
 	NetID       string   `json:"netid" bson:"netid"`

+ 1 - 2
serverctl/serverctl.go

@@ -51,8 +51,7 @@ func CreateCommsNetwork() (bool, error) {
 		network.SetNodesLastModified()
 		network.SetNodesLastModified()
 		network.SetNetworkLastModified()
 		network.SetNetworkLastModified()
 		network.KeyUpdateTimeStamp = time.Now().Unix()
 		network.KeyUpdateTimeStamp = time.Now().Unix()
-		priv := false
-		network.IsLocal = &priv
+		network.IsLocal = "no"
 		network.KeyUpdateTimeStamp = time.Now().Unix()
 		network.KeyUpdateTimeStamp = time.Now().Unix()
 
 
 		log.Println("Creating comms network...")
 		log.Println("Creating comms network...")

+ 2 - 2
test/nodecreate.sh

@@ -3,7 +3,7 @@
 PUBKEY="DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
 PUBKEY="DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
 IPADDR="69.173.21.202"
 IPADDR="69.173.21.202"
 MACADDRESS="59:2a:9c:d4:e2:49"
 MACADDRESS="59:2a:9c:d4:e2:49"
-ACCESSKEY="I6Pc8UFlszO1MWh3"
+ACCESSKEY="q5VHqUmamqsfgnpy"
 PASSWORD="ppppppp"
 PASSWORD="ppppppp"
 
 
 generate_post_json ()
 generate_post_json ()
@@ -22,5 +22,5 @@ EOF
 
 
 POST_JSON=$(generate_post_json)
 POST_JSON=$(generate_post_json)
 
 
-curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer secretkey" localhost:8081/api/nodes/hello
+curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer secretkey" localhost:8081/api/nodes/my-net