Browse Source

Merge branch 'develop' into bugfix_v0.2_misc

Alex 4 years ago
parent
commit
5d8d279d4d

+ 96 - 0
.github/workflows/buildandrelease.yml

@@ -0,0 +1,96 @@
+name: Build and Release
+
+on:
+  workflow_dispatch:
+    inputs:
+      version:
+        description: 'Netmaker version'
+        required: false
+  release:
+    types: [created]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+
+      - name: Get Version Number
+        run: |
+          if [[ -n "${{ github.event.inputs.version }}" ]]; then
+            NETMAKER_VERSION=${{ github.event.inputs.version }}
+          else
+            NETMAKER_VERSION=$(curl -fsSL https://api.github.com/repos/gravitl/netmaker/tags | grep 'name' | head -1 | cut -d'"' -f4)
+          fi
+          echo "NETMAKER_VERSION=${NETMAKER_VERSION}" >> $GITHUB_ENV
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Setup go
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.16
+
+      - name: Build
+        run: |
+          cd netclient
+          env GOOS=linux GOARCH=amd64 go build -o build/netclient main.go
+          env GOOS=linux GOARCH=arm GOARM=5 go build -o build/netclient-arm5/ main.go
+          env GOOS=linux GOARCH=arm GOARM=6 go build -o build/netclient-arm6/ main.go
+          env GOOS=linux GOARCH=arm GOARM=7 go build -o build/netclient-arm7/ main.go
+          env GOOS=linux GOARCH=arm64 go build -o build/netclient-arm64 main.go
+
+      - name: Upload arm5 to Release
+        if: github.event.inputs.version == ''
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-arm5
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-arm5
+
+      - name: Upload arm6 to Release
+        if: github.event.inputs.version == ''
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-arm6
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-arm6
+
+      - name: Upload arm7 to Release
+        if: github.event.inputs.version == ''
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: netclient/build/netclient-arm7
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-arm7
+
+      - name: Upload arm64 to Release
+        if: github.event.inputs.version == ''
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: build/netclient-arm64
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient-arm64
+
+      - name: Upload x86 to Release
+        if: github.event.inputs.version == ''
+        uses: svenstaro/upload-release-action@v2
+        with:
+          repo_token: ${{ secrets.GITHUB_TOKEN }}
+          file: build/netclient
+          tag: ${{ env.NETMAKER_VERSION }}
+          overwrite: true
+          prerelease: true
+          asset_name: netclient

+ 390 - 383
controllers/networkHttpController.go

@@ -1,34 +1,35 @@
 package controller
 
 import (
-    "gopkg.in/go-playground/validator.v9"
-    "github.com/gravitl/netmaker/models"
-    "errors"
-    "encoding/base64"
-    "github.com/gravitl/netmaker/functions"
-    "github.com/gravitl/netmaker/mongoconn"
-    "time"
-    "strings"
-    "fmt"
-    "context"
-    "encoding/json"
-    "net/http"
-    "github.com/gorilla/mux"
-    "go.mongodb.org/mongo-driver/bson"
-    "go.mongodb.org/mongo-driver/mongo/options"
-    "github.com/gravitl/netmaker/config"
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/config"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"gopkg.in/go-playground/validator.v9"
 )
 
 func networkHandlers(r *mux.Router) {
-    r.HandleFunc("/api/networks", securityCheck(http.HandlerFunc(getNetworks))).Methods("GET")
-    r.HandleFunc("/api/networks", securityCheck(http.HandlerFunc(createNetwork))).Methods("POST")
-    r.HandleFunc("/api/networks/{networkname}", securityCheck(http.HandlerFunc(getNetwork))).Methods("GET")
-    r.HandleFunc("/api/networks/{networkname}", securityCheck(http.HandlerFunc(updateNetwork))).Methods("PUT")
-    r.HandleFunc("/api/networks/{networkname}", securityCheck(http.HandlerFunc(deleteNetwork))).Methods("DELETE")
-    r.HandleFunc("/api/networks/{networkname}/keyupdate", securityCheck(http.HandlerFunc(keyUpdate))).Methods("POST")
-    r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(createAccessKey))).Methods("POST")
-    r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(getAccessKeys))).Methods("GET")
-    r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(http.HandlerFunc(deleteAccessKey))).Methods("DELETE")
+	r.HandleFunc("/api/networks", securityCheck(http.HandlerFunc(getNetworks))).Methods("GET")
+	r.HandleFunc("/api/networks", securityCheck(http.HandlerFunc(createNetwork))).Methods("POST")
+	r.HandleFunc("/api/networks/{networkname}", securityCheck(http.HandlerFunc(getNetwork))).Methods("GET")
+	r.HandleFunc("/api/networks/{networkname}", securityCheck(http.HandlerFunc(updateNetwork))).Methods("PUT")
+	r.HandleFunc("/api/networks/{networkname}", securityCheck(http.HandlerFunc(deleteNetwork))).Methods("DELETE")
+	r.HandleFunc("/api/networks/{networkname}/keyupdate", securityCheck(http.HandlerFunc(keyUpdate))).Methods("POST")
+	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(createAccessKey))).Methods("POST")
+	r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(http.HandlerFunc(getAccessKeys))).Methods("GET")
+	r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(http.HandlerFunc(deleteAccessKey))).Methods("DELETE")
 }
 
 //Security check is middleware for every function and just checks to make sure that its the master calling
@@ -43,48 +44,49 @@ func securityCheck(next http.Handler) http.HandlerFunc {
 		var params = mux.Vars(r)
 		hasnetwork := params["networkname"] != ""
 		networkexists, err := functions.NetworkExists(params["networkname"])
-                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.",
-                        }
-                        returnErrorResponse(w, r, errorResponse)
-			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.",
+				Code: http.StatusNotFound, Message: "W1R3: This network does not exist.",
 			}
 			returnErrorResponse(w, r, errorResponse)
 			return
 		} else {
-			next.ServeHTTP(w, r)
-		}
+
+			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)
+			}
 		}
 	}
 }
+
 //Consider a more secure way of setting master key
 func authenticateMaster(tokenString string) bool {
-    if tokenString == config.Config.Server.MasterKey {
-        return true
-    }
-    return false
+	if tokenString == config.Config.Server.MasterKey {
+		return true
+	}
+	return false
 }
 
 //simple get all networks function
@@ -104,118 +106,117 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
 
 func validateNetworkUpdate(network models.Network) error {
 
-        v := validator.New()
+	v := validator.New()
 
-        _ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
-                isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(fl.Field().String())
-                return isvalid
-        })
+	_ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
+		isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(fl.Field().String())
+		return isvalid
+	})
 
-        _ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
-                isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(fl.Field().String())
-                return isvalid
-        })
+	_ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
+		isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(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)
 
-        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
 }
 
 func validateNetworkCreate(network models.Network) error {
 
-        v := validator.New()
+	v := validator.New()
 
-        _ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
+	_ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
 		isvalid := functions.IsIpv4CIDR(fl.Field().String())
-                return isvalid
-        })
+		return isvalid
+	})
 
-        _ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
-                isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(fl.Field().String())
-                return isvalid
-        })
+	_ = v.RegisterValidation("localrange_valid", func(fl validator.FieldLevel) bool {
+		isvalid := fl.Field().String() == "" || functions.IsIpv4CIDR(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())
-		inCharSet        := functions.NameInNetworkCharSet(fl.Field().String())
+		inCharSet := functions.NameInNetworkCharSet(fl.Field().String())
 		return isFieldUnique && inCharSet
-        })
+	})
 
-        _ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
-                isFieldUnique, _ := functions.IsNetworkDisplayNameUnique(fl.Field().String())
-                return isFieldUnique
-        })
+	_ = v.RegisterValidation("displayname_unique", func(fl validator.FieldLevel) bool {
+		isFieldUnique, _ := functions.IsNetworkDisplayNameUnique(fl.Field().String())
+		return isFieldUnique
+	})
 
-        err := v.Struct(network)
+	err := v.Struct(network)
 
-        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
 }
 
 //Simple get network function
 func getNetwork(w http.ResponseWriter, r *http.Request) {
 
-        // set header.
-        w.Header().Set("Content-Type", "application/json")
+	// set header.
+	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
 
-        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"]}
-        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
+	filter := bson.M{"netid": params["networkname"]}
+	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
-		returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 	w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(network)
+	json.NewEncoder(w).Encode(network)
 }
 
 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
+	var network models.Network
 
-        network, err := functions.GetParentNetwork(params["networkname"])
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
+	network, err := functions.GetParentNetwork(params["networkname"])
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
 	}
 
-
 	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.
@@ -226,74 +227,73 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
                         {"defaultlistenport", network.DefaultListenPort},
                         {"defaultpostup", network.DefaultPostUp},
                         {"defaultpostdown", network.DefaultPostDown},
-			{"defaultkeepalive", network.DefaultKeepalive},
-                        {"keyupdatetimestamp", network.KeyUpdateTimeStamp},
-                        {"defaultsaveconfig", network.DefaultSaveConfig},
-                        {"defaultinterface", network.DefaultInterface},
-                        {"nodeslastmodified", network.NodesLastModified},
-                        {"networklastmodified", network.NetworkLastModified},
-                        {"allowmanualsignup", network.AllowManualSignUp},
-			{"defaultcheckininterval", network.DefaultCheckInInterval},
-                }},
-        }
+                  			{"defaultkeepalive", network.DefaultKeepalive},
+			                  {"keyupdatetimestamp", network.KeyUpdateTimeStamp},
+			                  {"defaultsaveconfig", network.DefaultSaveConfig},
+                  			{"defaultinterface", network.DefaultInterface},
+			                  {"nodeslastmodified", network.NodesLastModified},
+                  			{"networklastmodified", network.NetworkLastModified},
+			                  {"allowmanualsignup", network.AllowManualSignUp},
+			                  {"defaultcheckininterval", 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 {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(network)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(network)
 }
 
 //Update a network
-func AlertNetwork(netid string) error{
-
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-        filter := bson.M{"netid": netid}
+func AlertNetwork(netid string) error {
 
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	filter := bson.M{"netid": netid}
 
-        var network models.Network
+	var network models.Network
 
-        network, err := functions.GetParentNetwork(netid)
-        if err != nil {
-                return err
-        }
+	network, err := functions.GetParentNetwork(netid)
+	if err != nil {
+		return err
+	}
 	updatetime := time.Now().Unix()
-        update := bson.D{
-                {"$set", bson.D{
-                        {"nodeslastmodified", updatetime},
-                        {"networklastmodified", updatetime},
-                }},
-        }
+	update := bson.D{
+		{"$set", bson.D{
+			{"nodeslastmodified", updatetime},
+			{"networklastmodified", updatetime},
+		}},
+	}
 
-        err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
-        defer cancel()
+	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
+	defer cancel()
 
-        return err
+	return err
 }
 
 //Update a network
 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"])
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
-        var networkChange models.Network
+	var networkChange models.Network
 
 	haschange := false
 	hasrangeupdate := false
@@ -305,44 +305,47 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 		networkChange.AddressRange = network.AddressRange
 	}
 	if networkChange.NetID == "" {
-		networkChange.NetID =  network.NetID
+		networkChange.NetID = network.NetID
 	}
 
+	err = validateNetworkUpdate(networkChange)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
+	}
 
-        err = validateNetworkUpdate(networkChange)
-        if err != nil {
-		returnErrorResponse(w,r,formatError(err, "badrequest"))
-                return
-        }
-
-	//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
+	if networkChange.NetID != network.NetID {
+		returnErrorResponse(w, r, formatError(errors.New("NetID is not editable"), "badrequest"))
+		return
+	}
+	//MRK:  I think this code block is redundant.  valdiateNetworkUpdate(networkChange) covers this
+	if networkChange.AddressRange != "" {
 
-        if networkChange.AddressRange != "" {
-
-            network.AddressRange = networkChange.AddressRange
+		network.AddressRange = networkChange.AddressRange
 
-	    var isAddressOK bool = functions.IsIpv4CIDR(networkChange.AddressRange)
-            if !isAddressOK {
-		    err := errors.New("Invalid Range of " +  networkChange.AddressRange + " for addresses.")
-		    returnErrorResponse(w,r,formatError(err, "internal"))
-                    return
-            }
-             haschange = true
-	     hasrangeupdate = true
+		var isAddressOK bool = functions.IsIpv4CIDR(networkChange.AddressRange)
+		if !isAddressOK {
+			err := errors.New("Invalid Range of " + networkChange.AddressRange + " for addresses.")
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+		haschange = true
+		hasrangeupdate = true
 
-        }
+	}
 	if networkChange.LocalRange != "" {
-            network.LocalRange = networkChange.LocalRange
+		network.LocalRange = networkChange.LocalRange
 
-            var isAddressOK bool = functions.IsIpv4CIDR(networkChange.LocalRange)
-            if !isAddressOK {
-		    err := errors.New("Invalid Range of " +  networkChange.LocalRange + " for internal addresses.")
-                    returnErrorResponse(w,r,formatError(err, "internal"))
-                    return
-            }
-             haschange = true
-             haslocalrangeupdate = true
+		var isAddressOK bool = functions.IsIpv4CIDR(networkChange.LocalRange)
+		if !isAddressOK {
+			err := errors.New("Invalid Range of " + networkChange.LocalRange + " for internal addresses.")
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
+		haschange = true
+		haslocalrangeupdate = true
 	}
 	if networkChange.IsLocal != nil {
 		network.IsLocal = networkChange.IsLocal
@@ -350,44 +353,43 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 	if networkChange.DefaultListenPort != 0 {
 		network.DefaultListenPort = networkChange.DefaultListenPort
 		haschange = true
-        }
-        if networkChange.DefaultPostDown != "" {
+	}
+	if networkChange.DefaultPostDown != "" {
 		network.DefaultPostDown = networkChange.DefaultPostDown
 		haschange = true
-        }
-        if networkChange.DefaultInterface != "" {
+	}
+	if networkChange.DefaultInterface != "" {
 		network.DefaultInterface = networkChange.DefaultInterface
 		haschange = true
-        }
-        if networkChange.DefaultPostUp != "" {
+	}
+	if networkChange.DefaultPostUp != "" {
 		network.DefaultPostUp = networkChange.DefaultPostUp
 		haschange = true
-        }
-        if networkChange.DefaultKeepalive != 0 {
+	}
+	if networkChange.DefaultKeepalive != 0 {
 		network.DefaultKeepalive = networkChange.DefaultKeepalive
 		haschange = true
-        }
-        if networkChange.DisplayName != "" {
+	}
+	if networkChange.DisplayName != "" {
 		network.DisplayName = networkChange.DisplayName
 		haschange = true
-        }
-        if networkChange.DefaultCheckInInterval != 0 {
+	}
+	if networkChange.DefaultCheckInInterval != 0 {
 		network.DefaultCheckInInterval = networkChange.DefaultCheckInInterval
 		haschange = true
-        }
-        if networkChange.AllowManualSignUp != nil {
+	}
+	if networkChange.AllowManualSignUp != nil {
 		network.AllowManualSignUp = networkChange.AllowManualSignUp
 		haschange = true
-        }
+	}
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
-        filter := bson.M{"netid": params["networkname"]}
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	filter := bson.M{"netid": params["networkname"]}
 
 	if haschange {
 		network.SetNetworkLastModified()
 	}
-
         // prepare update model.
         update := bson.D{
                 {"$set", bson.D{
@@ -405,179 +407,178 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
                         {"localrange", network.LocalRange},
                         {"islocal", network.IsLocal},
                         {"defaultcheckininterval", network.DefaultCheckInInterval},
-		}},
-        }
+		              }},
+	}
 
 	err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&network)
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
 	//Cycles through nodes and gives them new IP's based on the new range
 	//Pretty cool, but also pretty inefficient currently
-        if hasrangeupdate {
+	if hasrangeupdate {
 		err = functions.UpdateNetworkNodeAddresses(params["networkname"])
 		if err != nil {
-			returnErrorResponse(w,r,formatError(err, "internal"))
+			returnErrorResponse(w, r, formatError(err, "internal"))
 			return
 		}
 	}
 	if haslocalrangeupdate {
-                err = functions.UpdateNetworkPrivateAddresses(params["networkname"])
-                if err != nil {
-                        returnErrorResponse(w,r,formatError(err, "internal"))
-                        return
-                }
+		err = functions.UpdateNetworkPrivateAddresses(params["networkname"])
+		if err != nil {
+			returnErrorResponse(w, r, formatError(err, "internal"))
+			return
+		}
 	}
 	returnnetwork, err := functions.GetParentNetwork(network.NetID)
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(returnnetwork)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(returnnetwork)
 }
 
 //Delete a network
 //Will stop you if  there's any nodes associated
 func deleteNetwork(w http.ResponseWriter, r *http.Request) {
-        // Set header
-        w.Header().Set("Content-Type", "application/json")
+	// Set header
+	w.Header().Set("Content-Type", "application/json")
 
-        var params = mux.Vars(r)
+	var params = mux.Vars(r)
 
 	nodecount, err := functions.GetNetworkNodeNumber(params["networkname"])
 	if err != nil {
 		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
-	} else if nodecount > 0  {
+	} 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
-        }
+			Code: http.StatusForbidden, Message: "W1R3: Node check failed. All nodes must be deleted before deleting network.",
+		}
+		returnErrorResponse(w, r, errorResponse)
+		return
+	}
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
 
-        filter := bson.M{"netid": params["networkname"]}
+	filter := bson.M{"netid": params["networkname"]}
 
-        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 {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(deleteResult)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(deleteResult)
 }
 
 //Create a network
 //Pretty simple
 func createNetwork(w http.ResponseWriter, r *http.Request) {
 
-        w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Type", "application/json")
 
-        var network models.Network
+	var network models.Network
 
-        // we decode our body request params
+	// we decode our body request params
 	err := json.NewDecoder(r.Body).Decode(&network)
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
 	//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
-        }
-
-        err = validateNetworkCreate(network)
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "badrequest"))
-                return
-        }
-	network.SetDefaults()
-        network.SetNodesLastModified()
-        network.SetNetworkLastModified()
-        network.KeyUpdateTimeStamp = time.Now().Unix()
 
-        collection := mongoconn.Client.Database("netmaker").Collection("networks")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	if network.IsLocal == nil {
+		falsevar := false
+		network.IsLocal = &falsevar
+	}
+
+	err = validateNetworkCreate(network)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
+	}
+	network.SetDefaults()
+	network.SetNodesLastModified()
+	network.SetNetworkLastModified()
+	network.KeyUpdateTimeStamp = time.Now().Unix()
 
+	collection := mongoconn.Client.Database("netmaker").Collection("networks")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        // insert our network into the network table
-        result, err := collection.InsertOne(ctx, network)
+	// insert our network into the network table
+	result, err := collection.InsertOne(ctx, network)
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(result)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(result)
 }
 
 // BEGIN KEY MANAGEMENT SECTION
 
-
 //TODO: Very little error handling
 //accesskey is created as a json string inside the Network collection item in mongo
 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 network models.Network
+	var accesskey models.AccessKey
 
-        //start here
+	//start here
 	network, err := functions.GetParentNetwork(params["networkname"])
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
-        err = json.NewDecoder(r.Body).Decode(&accesskey)
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	err = json.NewDecoder(r.Body).Decode(&accesskey)
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
 	if accesskey.Name == "" {
-                accesskey.Name = functions.GenKeyName()
-        }
+		accesskey.Name = functions.GenKeyName()
+	}
 	if accesskey.Value == "" {
 		accesskey.Value = functions.GenKey()
 	}
-        if accesskey.Uses == 0 {
-                accesskey.Uses = 1
-        }
+
+	if accesskey.Uses == 0 {
+		accesskey.Uses = 1
+	}
 	_, gconf, err := functions.GetGlobalConfig()
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
 	privAddr := ""
 	if *network.IsLocal {
 		privAddr = network.LocalRange
 	}
 
-
 	netID := params["networkname"]
 	address := gconf.ServerGRPC + gconf.PortGRPC
 
@@ -586,33 +587,33 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
 
 	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
-        filter := bson.M{"netid": params["networkname"]}
+	// Create filter
+	filter := bson.M{"netid": params["networkname"]}
 
-        // Read update model from body request
-        fmt.Println("Adding key to " + network.NetID)
+	// Read update model from body request
+	fmt.Println("Adding key to " + network.NetID)
 
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"accesskeys", network.AccessKeys},
-                }},
-        }
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"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 {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(accesskey)
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(accesskey)
 	//w.Write([]byte(accesskey.AccessString))
 }
 
@@ -620,98 +621,104 @@ func createAccessKey(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)
 
-        var network models.Network
-        //var keys []models.DisplayKey
+	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"]}
-        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
+	filter := bson.M{"netid": params["networkname"]}
+	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&network)
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 	keydata, err := json.Marshal(network.AccessKeys)
 
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
 	json.Unmarshal(keydata, &keys)
 
 	w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(keys)
+	json.NewEncoder(w).Encode(keys)
 }
 
-
 //delete key. Has to do a little funky logic since it's not a collection item
 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
+	var network models.Network
 	keyname := params["name"]
 
-        //start here
+	//start here
 	network, err := functions.GetParentNetwork(params["networkname"])
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 	//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.
+	found := false
 	for i := len(network.AccessKeys) - 1; i >= 0; i-- {
 
-		currentkey:= network.AccessKeys[i]
+		currentkey := network.AccessKeys[i]
 		if currentkey.Name == keyname {
 			network.AccessKeys = append(network.AccessKeys[:i],
 				network.AccessKeys[i+1:]...)
+			found = true
 		}
 	}
+	if !found {
+		err = errors.New("key " + keyname + " does not exist")
+		returnErrorResponse(w, r, formatError(err, "badrequest"))
+		return
+	}
 
-        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
-        filter := bson.M{"netid": params["networkname"]}
+	// Create filter
+	filter := bson.M{"netid": params["networkname"]}
 
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"accesskeys", network.AccessKeys},
-                }},
-        }
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"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 {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
-        var keys []models.AccessKey
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
+	var keys []models.AccessKey
 	keydata, err := json.Marshal(network.AccessKeys)
-        if err != nil {
-                returnErrorResponse(w,r,formatError(err, "internal"))
-                return
-        }
+	if err != nil {
+		returnErrorResponse(w, r, formatError(err, "internal"))
+		return
+	}
 
-        json.Unmarshal(keydata, &keys)
+	json.Unmarshal(keydata, &keys)
 
-        w.WriteHeader(http.StatusOK)
-        json.NewEncoder(w).Encode(keys)
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(keys)
 }

+ 260 - 265
controllers/userHttpController.go

@@ -1,117 +1,117 @@
 package controller
 
 import (
-    "gopkg.in/go-playground/validator.v9"
-    "github.com/gravitl/netmaker/models"
-    "github.com/gravitl/netmaker/functions"
-    "github.com/gravitl/netmaker/mongoconn"
-    "golang.org/x/crypto/bcrypt"
-    "time"
-    "errors"
-    "strings"
-    "fmt"
-    "context"
-    "encoding/json"
-    "net/http"
-    "github.com/gorilla/mux"
-    "go.mongodb.org/mongo-driver/bson"
-    "go.mongodb.org/mongo-driver/mongo/options"
-    "go.mongodb.org/mongo-driver/mongo"
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/functions"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mongoconn"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"golang.org/x/crypto/bcrypt"
+	"gopkg.in/go-playground/validator.v9"
 )
 
-
 func userHandlers(r *mux.Router) {
 
-    r.HandleFunc("/api/users/adm/hasadmin", hasAdmin).Methods("GET")
-    r.HandleFunc("/api/users/adm/createadmin", createAdmin).Methods("POST")
-    r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods("POST")
-    r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(updateUser))).Methods("PUT")
-    r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(deleteUser))).Methods("DELETE")
-    r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(getUser))).Methods("GET")
+	r.HandleFunc("/api/users/adm/hasadmin", hasAdmin).Methods("GET")
+	r.HandleFunc("/api/users/adm/createadmin", createAdmin).Methods("POST")
+	r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods("POST")
+	r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(updateUser))).Methods("PUT")
+	r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(deleteUser))).Methods("DELETE")
+	r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(getUser))).Methods("GET")
 }
 
 //Node authenticates using its password and retrieves a JWT for authorization.
 func authenticateUser(response http.ResponseWriter, request *http.Request) {
 
-    //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"
-    var authRequest models.UserAuthParams
-    var result models.User
-    var errorResponse = models.ErrorResponse{
-	    Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
-    }
-
-    decoder := json.NewDecoder(request.Body)
-    decoderErr := decoder.Decode(&authRequest)
-    defer request.Body.Close()
-
-    if decoderErr != nil {
-        returnErrorResponse(response, request, errorResponse)
-	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)
+	//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"
+	var authRequest models.UserAuthParams
+	var result models.User
+	var errorResponse = models.ErrorResponse{
+		Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
+	}
+
+	decoder := json.NewDecoder(request.Body)
+	decoderErr := decoder.Decode(&authRequest)
+	defer request.Body.Close()
+
+	if decoderErr != nil {
+		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)
-            }
+	} 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)
+			}
+		}
 	}
-    }
 }
 
 //The middleware for most requests to the API
@@ -122,30 +122,30 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 //This is kind of a poor man's RBAC. There's probably a better/smarter way.
 //TODO: Consider better RBAC implementations
 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.",
-                }
+		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
 		bearerToken := r.Header.Get("Authorization")
 
-                var tokenSplit = strings.Split(bearerToken, " ")
+		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)
+		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)
 			return
 		}
 
@@ -156,10 +156,10 @@ func authorizeUser(next http.Handler) http.HandlerFunc {
 		username, _, err := functions.VerifyUserToken(authToken)
 
 		if err != nil {
-                        errorResponse = models.ErrorResponse{
-                                Code: http.StatusUnauthorized, Message: "W1R3: Error Verifying Auth Token.",
-                        }
-                        returnErrorResponse(w, r, errorResponse)
+			errorResponse = models.ErrorResponse{
+				Code: http.StatusUnauthorized, Message: "W1R3: Error Verifying Auth Token.",
+			}
+			returnErrorResponse(w, r, errorResponse)
 			return
 		}
 
@@ -178,22 +178,22 @@ func authorizeUser(next http.Handler) http.HandlerFunc {
 	}
 }
 
-func HasAdmin() (bool, error){
+func HasAdmin() (bool, error) {
 
-        collection := mongoconn.Client.Database("netmaker").Collection("users")
+	collection := mongoconn.Client.Database("netmaker").Collection("users")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"isadmin": true}
+	filter := bson.M{"isadmin": true}
 
-        //Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
-        var result bson.M
+	//Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
+	var result bson.M
 
 	err := collection.FindOne(ctx, filter).Decode(&result)
 
-        defer cancel()
+	defer cancel()
 
-        if err != nil {
+	if err != nil {
 		if err == mongo.ErrNoDocuments {
 			return false, err
 		}
@@ -216,18 +216,18 @@ func hasAdmin(w http.ResponseWriter, r *http.Request) {
 
 func GetUser(username string) (models.User, error) {
 
-        var user models.User
+	var user models.User
 
-        collection := mongoconn.Client.Database("netmaker").Collection("users")
+	collection := mongoconn.Client.Database("netmaker").Collection("users")
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        filter := bson.M{"username": username}
-        err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&user)
+	filter := bson.M{"username": username}
+	err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&user)
 
-        defer cancel()
+	defer cancel()
 
-        return user, err
+	return user, err
 }
 
 //Get an individual node. Nothin fancy here folks.
@@ -235,7 +235,7 @@ func getUser(w http.ResponseWriter, r *http.Request) {
 	// set header.
 	w.Header().Set("Content-Type", "application/json")
 
-        var params = mux.Vars(r)
+	var params = mux.Vars(r)
 
 	user, err := GetUser(params["username"])
 
@@ -249,51 +249,51 @@ func getUser(w http.ResponseWriter, r *http.Request) {
 
 func CreateUser(user models.User) (models.User, error) {
 
-        //encrypt that password so we never see it again
-        hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
+	//encrypt that password so we never see it again
+	hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
 
-        if err != nil {
-                return user, err
-        }
-        //set password to encrypted password
-        user.Password = string(hash)
+	if err != nil {
+		return user, err
+	}
+	//set password to encrypted password
+	user.Password = string(hash)
 
-        tokenString, _ := functions.CreateUserJWT(user.UserName, user.IsAdmin)
+	tokenString, _ := functions.CreateUserJWT(user.UserName, user.IsAdmin)
 
-        if tokenString == "" {
-                //returnErrorResponse(w, r, errorResponse)
-                return user, err
-        }
+	if tokenString == "" {
+		//returnErrorResponse(w, r, errorResponse)
+		return user, err
+	}
 
-        // connect db
-        collection := mongoconn.Client.Database("netmaker").Collection("users")
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	// connect db
+	collection := mongoconn.Client.Database("netmaker").Collection("users")
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        // insert our node to the node db.
-        result, err := collection.InsertOne(ctx, user)
-        _ = result
+	// insert our node to the node db.
+	result, err := collection.InsertOne(ctx, user)
+	_ = result
 
-        defer cancel()
+	defer cancel()
 
-        return user, err
+	return user, err
 }
 
 func createAdmin(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 
 	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.",
 	}
 
 	hasadmin, err := HasAdmin()
 
-        if hasadmin {
-                errorResponse = models.ErrorResponse{
-                        Code: http.StatusUnauthorized, Message: "W1R3: Admin already exists! ",
-                }
-                returnErrorResponse(w, r, errorResponse)
-                return
-        }
+	if hasadmin {
+		errorResponse = models.ErrorResponse{
+			Code: http.StatusUnauthorized, Message: "W1R3: Admin already exists! ",
+		}
+		returnErrorResponse(w, r, errorResponse)
+		return
+	}
 
 	var admin models.User
 
@@ -302,72 +302,71 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 
 	admin.IsAdmin = true
 
-	err =  ValidateUser("create", admin)
-        if err != nil {
+	err = ValidateUser("create", admin)
+	if err != nil {
 		json.NewEncoder(w).Encode(err)
 		return
-        }
+	}
 
-        admin, err = CreateUser(admin)
-        if err != nil {
+	admin, err = CreateUser(admin)
+	if err != nil {
 		json.NewEncoder(w).Encode(err)
-                return
-        }
+		return
+	}
 
 	json.NewEncoder(w).Encode(admin)
 }
 
 func UpdateUser(userchange models.User, user models.User) (models.User, error) {
 
-    queryUser := user.UserName
-
-    if userchange.UserName != "" {
-        user.UserName = userchange.UserName
-    }
-    if userchange.Password != "" {
-        //encrypt that password so we never see it again
-        hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5)
+	queryUser := user.UserName
 
-        if err != nil {
-                return userchange, err
-        }
-        //set password to encrypted password
-        userchange.Password = string(hash)
+	if userchange.UserName != "" {
+		user.UserName = userchange.UserName
+	}
+	if userchange.Password != "" {
+		//encrypt that password so we never see it again
+		hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5)
 
-        user.Password = userchange.Password
-    }
-        //collection := mongoconn.ConnectDB()
-        collection := mongoconn.Client.Database("netmaker").Collection("users")
+		if err != nil {
+			return userchange, err
+		}
+		//set password to encrypted password
+		userchange.Password = string(hash)
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+		user.Password = userchange.Password
+	}
+	//collection := mongoconn.ConnectDB()
+	collection := mongoconn.Client.Database("netmaker").Collection("users")
 
-        // Create filter
-        filter := bson.M{"username": queryUser}
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        fmt.Println("Updating User " + user.UserName)
+	// Create filter
+	filter := bson.M{"username": queryUser}
 
+	fmt.Println("Updating User " + user.UserName)
 
-        // prepare update model.
-        update := bson.D{
-                {"$set", bson.D{
-                        {"username", user.UserName},
-                        {"password", user.Password},
-                        {"isadmin", user.IsAdmin},
-                }},
-        }
-        var userupdate models.User
+	// prepare update model.
+	update := bson.D{
+		{"$set", bson.D{
+			{"username", user.UserName},
+			{"password", user.Password},
+			{"isadmin", user.IsAdmin},
+		}},
+	}
+	var userupdate models.User
 
-        errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&userupdate)
+	errN := collection.FindOneAndUpdate(ctx, filter, update).Decode(&userupdate)
 	if errN != nil {
-               fmt.Println("Could not update: ")
-               fmt.Println(errN)
-        }  else {
+		fmt.Println("Could not update: ")
+		fmt.Println(errN)
+	} else {
 		fmt.Println("User updated  successfully.")
 	}
 
-        defer cancel()
+	defer cancel()
 
-        return userupdate, errN
+	return userupdate, errN
 }
 
 func updateUser(w http.ResponseWriter, r *http.Request) {
@@ -379,61 +378,60 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 
 	//start here
 	user, err := GetUser(params["username"])
-        if err != nil {
-                json.NewEncoder(w).Encode(err)
-                return
-        }
-
+	if err != nil {
+		json.NewEncoder(w).Encode(err)
+		return
+	}
 
-        var userchange models.User
+	var userchange models.User
 
-        // we decode our body request params
-        err = json.NewDecoder(r.Body).Decode(&userchange)
+	// we decode our body request params
+	err = json.NewDecoder(r.Body).Decode(&userchange)
 	if err != nil {
-                json.NewEncoder(w).Encode(err)
-                return
-        }
+		json.NewEncoder(w).Encode(err)
+		return
+	}
 
 	userchange.IsAdmin = true
 
-        err = ValidateUser("update", userchange)
+	err = ValidateUser("update", userchange)
 
-        if err != nil {
-                json.NewEncoder(w).Encode(err)
-                return
-        }
+	if err != nil {
+		json.NewEncoder(w).Encode(err)
+		return
+	}
 
 	user, err = UpdateUser(userchange, user)
 
 	if err != nil {
-                json.NewEncoder(w).Encode(err)
+		json.NewEncoder(w).Encode(err)
 		return
 	}
 
 	json.NewEncoder(w).Encode(user)
 }
 
-func DeleteUser(user string) (bool, error)  {
+func DeleteUser(user string) (bool, error) {
 
-        deleted := false
+	deleted := false
 
-        collection := mongoconn.Client.Database("netmaker").Collection("users")
+	collection := mongoconn.Client.Database("netmaker").Collection("users")
 
-        filter := bson.M{"username": user}
+	filter := bson.M{"username": user}
 
-        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
 
-        result, err := collection.DeleteOne(ctx, filter)
+	result, err := collection.DeleteOne(ctx, filter)
 
-        deletecount := result.DeletedCount
+	deletecount := result.DeletedCount
 
-        if deletecount > 0 {
-                deleted = true
-        }
+	if deletecount > 0 {
+		deleted = true
+	}
 
-        defer cancel()
+	defer cancel()
 
-        return deleted, err
+	return deleted, err
 }
 
 func deleteUser(w http.ResponseWriter, r *http.Request) {
@@ -446,45 +444,42 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 	success, err := DeleteUser(params["username"])
 
 	if err != nil {
-                returnErrorResponse(w, r, formatError(err, "internal"))
-		json.NewEncoder(w).Encode("Could not delete user " + params["username"])
+		returnErrorResponse(w, r, formatError(err, "internal"))
 		return
 	} else if !success {
-                returnErrorResponse(w, r, formatError(errors.New("Delete unsuccessful."), "internal"))
-                json.NewEncoder(w).Encode("Could not delete user " + params["username"])
-                return
-        }
-
+		returnErrorResponse(w, r, formatError(errors.New("Delete unsuccessful."), "badrequest"))
+		return
+	}
 
 	json.NewEncoder(w).Encode(params["username"] + " deleted.")
 }
 
 func ValidateUser(operation string, user models.User) error {
 
-        v := validator.New()
+	v := validator.New()
 
-        _ = v.RegisterValidation("username_unique", func(fl validator.FieldLevel) bool {
+	_ = 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)
-
-        if err != nil {
-                for _, e := range err.(validator.ValidationErrors) {
-                        fmt.Println(e)
-                }
-        }
-        return err
+	})
+
+	_ = 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)
+
+	if err != nil {
+		for _, e := range err.(validator.ValidationErrors) {
+			fmt.Println(e)
+		}
+	}
+	return err
 }

+ 1 - 1
netclient-install.sh

@@ -3,7 +3,7 @@ set -e
 
 [ -z "$KEY" ] && KEY=nokey;
 
-wget -O --backups=1 https://github.com/gravitl/netmaker/releases/download/latest/netclient
+wget -O netclient https://github.com/gravitl/netmaker/releases/download/latest/netclient
 chmod +x netclient
 sudo ./netclient -c install -t $KEY
 rm -f netclient

+ 23 - 5
test/api_test.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"io/ioutil"
 	"net/http"
@@ -50,6 +51,19 @@ func TestMain(m *testing.M) {
 	var waitgroup sync.WaitGroup
 	waitgroup.Add(1)
 	go controller.HandleRESTRequests(&waitgroup)
+	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")
+	}
+
 	//wait for http server to start
 	time.Sleep(time.Second * 1)
 	os.Exit(m.Run())
@@ -178,11 +192,15 @@ func networkExists(t *testing.T) bool {
 	assert.Equal(t, http.StatusOK, response.StatusCode)
 	err = json.NewDecoder(response.Body).Decode(&Networks)
 	assert.Nil(t, err, err)
-	if Networks == nil {
-		return false
-	} else {
-		return true
+	for i, network := range Networks {
+		t.Log(i, network)
+		if network.NetID == "" {
+			return false
+		} else {
+			return true
+		}
 	}
+	return false
 }
 
 func deleteNetworks(t *testing.T) {
@@ -194,7 +212,7 @@ func deleteNetworks(t *testing.T) {
 	err = json.NewDecoder(response.Body).Decode(&Networks)
 	assert.Nil(t, err, err)
 	for _, network := range Networks {
-		name := network.DisplayName
+		name := network.NetID
 		_, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/"+name, "secretkey")
 		assert.Nil(t, err, err)
 	}

+ 69 - 120
test/group_test.go

@@ -12,15 +12,13 @@ import (
 )
 
 func TestCreateNetwork(t *testing.T) {
+
 	network := models.Network{}
 	network.NetID = "skynet"
 	network.AddressRange = "10.71.0.0/16"
-	deleteNetworks(t)
-	t.Run("CreateNetwork", func(t *testing.T) {
-		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
-		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
-	})
+	if networkExists(t) {
+		deleteNetworks(t)
+	}
 	t.Run("InvalidToken", func(t *testing.T) {
 		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "badkey")
 		assert.Nil(t, err, err)
@@ -32,21 +30,34 @@ func TestCreateNetwork(t *testing.T) {
 		assert.Equal(t, http.StatusUnauthorized, message.Code)
 		assert.Equal(t, "W1R3: You are unauthorized to access this endpoint.", message.Message)
 	})
+	t.Run("CreateNetwork", func(t *testing.T) {
+		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusOK, response.StatusCode)
+	})
+	t.Run("DuplicateNetwork", func(t *testing.T) {
+		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+	})
 	t.Run("BadName", func(t *testing.T) {
-		//issue #42
-		t.Skip()
+		network.NetID = "thisnameistoolong"
+		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 	})
 	t.Run("BadAddress", func(t *testing.T) {
-		//issue #42
-		t.Skip()
-	})
-	t.Run("DuplicateNetwork", func(t *testing.T) {
-		//issue #42
-		t.Skip()
+		network.NetID = "wirecat"
+		network.AddressRange = "10.300.20.56/36"
+		response, err := api(t, network, http.MethodPost, baseURL+"/api/networks", "secretkey")
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 	})
+
 }
 
 func TestGetNetworks(t *testing.T) {
+
 	t.Run("ValidToken", func(t *testing.T) {
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/networks", "secretkey")
 		assert.Nil(t, err, err)
@@ -69,6 +80,7 @@ func TestGetNetworks(t *testing.T) {
 }
 
 func TestGetNetwork(t *testing.T) {
+
 	t.Run("ValidToken", func(t *testing.T) {
 		var network models.Network
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/networks/skynet", "secretkey")
@@ -103,43 +115,8 @@ func TestGetNetwork(t *testing.T) {
 	})
 }
 
-func TestGetnetworkNodeNumber(t *testing.T) {
-	t.Skip()
-	//not part of api anymore
-	t.Run("ValidKey", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, baseURL+"/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, baseURL+"/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, baseURL+"/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 TestDeleteMetwork(t *testing.T) {
 
-func TestDeletenetwork(t *testing.T) {
 	t.Run("InvalidKey", func(t *testing.T) {
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/skynet", "badkey")
 		assert.Nil(t, err, err)
@@ -173,14 +150,14 @@ func TestDeletenetwork(t *testing.T) {
 		assert.Equal(t, http.StatusNotFound, response.StatusCode)
 	})
 	t.Run("NodesExist", func(t *testing.T) {
-		t.Skip()
+
 	})
 }
 
-func TestCreateAccessKey(t *testing.T) {
-	if !networkExists(t) {
-		createNetwork(t)
-	}
+func TestCreateKey(t *testing.T) {
+	//ensure we are working with known networks
+	deleteNetworks(t)
+	createNetwork(t)
 
 	key := models.AccessKey{}
 	key.Name = "skynet"
@@ -199,7 +176,7 @@ func TestCreateAccessKey(t *testing.T) {
 	})
 	deleteKey(t, "skynet", "skynet")
 	t.Run("ZeroUse", func(t *testing.T) {
-		//t.Skip()
+		//
 		key.Uses = 0
 		response, err := api(t, key, http.MethodPost, baseURL+"/api/networks/skynet/keys", "secretkey")
 		assert.Nil(t, err, err)
@@ -244,6 +221,11 @@ func TestCreateAccessKey(t *testing.T) {
 }
 
 func TestDeleteKey(t *testing.T) {
+	//ensure we are working with known networks
+	deleteNetworks(t)
+	createNetwork(t)
+	//ensure key exists
+	createKey(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")
@@ -259,16 +241,15 @@ func TestDeleteKey(t *testing.T) {
 		}
 	})
 	t.Run("InValidKey", func(t *testing.T) {
-		t.Skip()
-		//responds ok, will nil record returned..  should be an error.
 		response, err := api(t, "", http.MethodDelete, baseURL+"/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)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "key badkey does not exist", message.Message)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 	})
 	t.Run("KeyInValidnetwork", func(t *testing.T) {
 		response, err := api(t, "", http.MethodDelete, baseURL+"/api/networks/badnetwork/keys/skynet", "secretkey")
@@ -294,6 +275,9 @@ func TestDeleteKey(t *testing.T) {
 }
 
 func TestGetKeys(t *testing.T) {
+	//ensure we are working with known networks
+	deleteNetworks(t)
+	createNetwork(t)
 	createKey(t)
 	t.Run("Valid", func(t *testing.T) {
 		response, err := api(t, "", http.MethodGet, baseURL+"/api/networks/skynet/keys", "secretkey")
@@ -304,7 +288,6 @@ func TestGetKeys(t *testing.T) {
 		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, baseURL+"/api/networks/badnetwork/keys", "secretkey")
 		assert.Nil(t, err, err)
@@ -328,7 +311,7 @@ func TestGetKeys(t *testing.T) {
 	})
 }
 
-func TestUpdatenetwork(t *testing.T) {
+func TestUpdateNetwork(t *testing.T) {
 	//ensure we are working with known networks
 	deleteNetworks(t)
 	createNetwork(t)
@@ -341,28 +324,13 @@ func TestUpdatenetwork(t *testing.T) {
 		network.NetID = "wirecat"
 		response, err := api(t, network, http.MethodPut, baseURL+"/api/networks/skynet", "secretkey")
 		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusOK, response.StatusCode)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 		defer response.Body.Close()
-		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
-		assert.Nil(t, err, err)
-		//returns previous value not the updated value
-		// ----- needs fixing -----
-		//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, baseURL+"/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)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "NetID is not editable", message.Message)
 	})
 	t.Run("Invalidnetwork", func(t *testing.T) {
 		type Network struct {
@@ -380,19 +348,6 @@ func TestUpdatenetwork(t *testing.T) {
 		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) {
-		// ---- needs fixing -----
-		// succeeds for some reason
-		t.Skip()
-		type Network struct {
-			NetID string
-		}
-		var network Network
-		network.NetID = "wirecat-skynet"
-		response, err := api(t, network, http.MethodPut, baseURL+"/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
@@ -415,13 +370,13 @@ func TestUpdatenetwork(t *testing.T) {
 		network.AddressRange = "10.0.0.1/36"
 		response, err := api(t, network, http.MethodPut, baseURL+"/api/networks/skynet", "secretkey")
 		assert.Nil(t, err, err)
-		assert.Equal(t, http.StatusInternalServerError, response.StatusCode)
+		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, err)
-		assert.Equal(t, http.StatusInternalServerError, message.Code)
-		assert.Contains(t, message.Message, "Invalid Range")
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "validation for 'AddressRange' failed")
 
 	})
 	t.Run("UpdateDisplayName", func(t *testing.T) {
@@ -440,9 +395,6 @@ func TestUpdatenetwork(t *testing.T) {
 
 	})
 	t.Run("UpdateDisplayNameInvalidName", func(t *testing.T) {
-		// -----needs fixing ----
-		// fails silently
-		t.Skip()
 		type Network struct {
 			DisplayName string
 		}
@@ -455,12 +407,12 @@ func TestUpdatenetwork(t *testing.T) {
 		network.DisplayName = name
 		response, err := api(t, network, http.MethodPut, baseURL+"/api/networks/skynet", "secretkey")
 		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 		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)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'DisplayName' failed")
 	})
 	t.Run("UpdateInterface", func(t *testing.T) {
 		type Network struct {
@@ -492,9 +444,6 @@ func TestUpdatenetwork(t *testing.T) {
 		assert.Equal(t, network.DefaultListenPort, returnedNetwork.DefaultListenPort)
 	})
 	t.Run("UpdateListenPortInvalidPort", func(t *testing.T) {
-		// ---needs fixing -----
-		// value is updated anyways
-		t.Skip()
 		type Network struct {
 			DefaultListenPort int32
 		}
@@ -505,9 +454,9 @@ func TestUpdatenetwork(t *testing.T) {
 		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)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'DefaultListenPort' failed")
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 	})
 	t.Run("UpdatePostUP", func(t *testing.T) {
 		type Network struct {
@@ -527,19 +476,19 @@ func TestUpdatenetwork(t *testing.T) {
 		// -------needs fixing ------
 		// mismatch in models.Network between struc name and json/bson name
 		// does not get updated.
-		t.Skip()
+
 		type Network struct {
-			DefaultPostDown string
+			DefaultPreUp string
 		}
 		var network Network
-		network.DefaultPostDown = "test string"
+		network.DefaultPreUp = "test string"
 		response, err := api(t, network, http.MethodPut, baseURL+"/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)
+		assert.Equal(t, network.DefaultPreUp, returnedNetwork.DefaultPostDown)
 	})
 	t.Run("UpdateKeepAlive", func(t *testing.T) {
 		type Network struct {
@@ -556,7 +505,7 @@ func TestUpdatenetwork(t *testing.T) {
 		assert.Equal(t, network.DefaultKeepalive, returnedNetwork.DefaultKeepalive)
 	})
 	t.Run("UpdateKeepAliveTooBig", func(t *testing.T) {
-		//fails silently
+		//does not fails ----- value gets updated.
 		// ----- needs fixing -----
 		t.Skip()
 		type Network struct {
@@ -569,13 +518,13 @@ func TestUpdatenetwork(t *testing.T) {
 		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)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Contains(t, message.Message, "Field validation for 'DefaultKeepAlive' failed")
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
 	})
 	t.Run("UpdateSaveConfig", func(t *testing.T) {
-		//causes panic
 		t.Skip()
+		//does not appear to be updatable
 		type Network struct {
 			DefaultSaveConfig *bool
 		}
@@ -591,7 +540,6 @@ func TestUpdatenetwork(t *testing.T) {
 		assert.Equal(t, *network.DefaultSaveConfig, *returnedNetwork.DefaultSaveConfig)
 	})
 	t.Run("UpdateManualSignUP", func(t *testing.T) {
-		t.Skip()
 		type Network struct {
 			AllowManualSignUp *bool
 		}
@@ -604,10 +552,10 @@ func TestUpdatenetwork(t *testing.T) {
 		defer response.Body.Close()
 		err = json.NewDecoder(response.Body).Decode(&returnedNetwork)
 		assert.Nil(t, err, err)
-		assert.Equal(t, *network.AllowManualSignUp, *returnedNetwork.AllowManualSignUp)
+		assert.Equal(t, network.AllowManualSignUp, returnedNetwork.AllowManualSignUp)
 	})
 	t.Run("DefaultCheckInterval", func(t *testing.T) {
-		//value is not returned in struct ---
+		//value is not updated
 		t.Skip()
 		type Network struct {
 			DefaultCheckInInterval int32
@@ -625,6 +573,7 @@ func TestUpdatenetwork(t *testing.T) {
 	t.Run("DefaultCheckIntervalTooBig", func(t *testing.T) {
 		//value is not returned in struct ---
 		t.Skip()
+
 		type Network struct {
 			DefaultCheckInInterval int32
 		}

+ 23 - 27
test/user_test.go

@@ -40,13 +40,10 @@ func TestAdminCreation(t *testing.T) {
 		defer response.Body.Close()
 		var message models.ErrorResponse
 		err = json.NewDecoder(response.Body).Decode(&message)
-		t.Log(message)
 		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)
-		data, _ := ioutil.ReadAll(response.Body)
-		t.Log(string(data))
 	})
 
 }
@@ -58,13 +55,10 @@ func TestGetUser(t *testing.T) {
 	} else {
 		t.Log("admin exists")
 	}
-
 	t.Run("GetUserWithValidToken", func(t *testing.T) {
-		t.Skip()
 		token, err := authenticate(t)
 		assert.Nil(t, err, err)
 		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/users/admin", token)
-		t.Log(response)
 		assert.Nil(t, err, err)
 		defer response.Body.Close()
 		var user models.User
@@ -74,10 +68,16 @@ func TestGetUser(t *testing.T) {
 		assert.Equal(t, true, user.IsAdmin)
 	})
 	t.Run("GetUserWithInvalidToken", func(t *testing.T) {
-		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/users/admin", "secretkey")
+		response, err := api(t, "", http.MethodGet, "http://localhost:8081/api/users/admin", "badkey")
 		assert.Nil(t, err, err)
 		defer response.Body.Close()
-		t.Log(response.Body)
+		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: Error Verifying Auth Token.", message.Message)
+
 	})
 }
 
@@ -93,7 +93,7 @@ func TestUpdateUser(t *testing.T) {
 	t.Run("UpdateWrongToken", func(t *testing.T) {
 		admin.UserName = "admin"
 		admin.Password = "admin"
-		response, err := api(t, admin, http.MethodPut, "http://localhost:8081/api/users/admin", "secretkey")
+		response, err := api(t, admin, http.MethodPut, "http://localhost:8081/api/users/admin", "badkey")
 		assert.Nil(t, err, err)
 		defer response.Body.Close()
 		err = json.NewDecoder(response.Body).Decode(&message)
@@ -113,26 +113,20 @@ func TestUpdateUser(t *testing.T) {
 		assert.Equal(t, true, user.IsAdmin)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
 	})
-
 }
 
 func TestDeleteUser(t *testing.T) {
+
 	if !adminExists(t) {
+		t.Log("Creating Admin")
 		addAdmin(t)
 	}
 	token, err := authenticate(t)
 	assert.Nil(t, err, err)
-	t.Run("DeleteUser-WongAdmin", func(t *testing.T) {
-		//skip for now ... shouldn't panic
-		t.Skip()
-		function := func() {
-			_, _ = api(t, "", http.MethodDelete, "http://localhost:8081/api/users/xxxx", token)
-		}
-		assert.Panics(t, function, "")
-	})
 	t.Run("DeleteUser-InvalidCredentials", func(t *testing.T) {
-		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/users/admin", "secretkey")
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/users/admin", "badcredentials")
 		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusUnauthorized, response.StatusCode)
 		var message models.ErrorResponse
 		json.NewDecoder(response.Body).Decode(&message)
 		assert.Equal(t, "W1R3: Error Verifying Auth Token.", message.Message)
@@ -146,17 +140,20 @@ func TestDeleteUser(t *testing.T) {
 		assert.Equal(t, "admin deleted.", body)
 		assert.Equal(t, http.StatusOK, response.StatusCode)
 	})
-	t.Run("DeleteUser-NoAdmin", func(t *testing.T) {
-		//skip for now ... shouldn't panic
-		t.Skip()
-		function := func() {
-			_, _ = api(t, "", http.MethodDelete, "http://localhost:8081/api/users/admin", token)
-		}
-		assert.Panics(t, function, "")
+	t.Run("DeleteUser-NonExistantAdmin", func(t *testing.T) {
+		response, err := api(t, "", http.MethodDelete, "http://localhost:8081/api/users/admin", token)
+		assert.Nil(t, err, err)
+		assert.Equal(t, http.StatusBadRequest, response.StatusCode)
+		var message models.ErrorResponse
+		defer response.Body.Close()
+		json.NewDecoder(response.Body).Decode(&message)
+		assert.Equal(t, http.StatusBadRequest, message.Code)
+		assert.Equal(t, "Delete unsuccessful.", message.Message)
 	})
 }
 
 func TestAuthenticateUser(t *testing.T) {
+
 	cases := []AuthorizeTestCase{
 		AuthorizeTestCase{
 			testname:      "Invalid User",
@@ -199,7 +196,6 @@ func TestAuthenticateUser(t *testing.T) {
 			errMessage:    "W1R3: Device Admin Authorized",
 		},
 	}
-
 	if !adminExists(t) {
 		addAdmin(t)
 	}