diff --git a/controllers/externalHttpController.go.backup b/controllers/externalHttpController.go.backup new file mode 100644 index 00000000..f21eba26 --- /dev/null +++ b/controllers/externalHttpController.go.backup @@ -0,0 +1,233 @@ +package controller + +import ( + "context" + "encoding/json" + "errors" + // "fmt" + "net/http" + "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/options" + // "github.com/skip2/go-qrcode" +) + +func externalHandlers(r *mux.Router) { + + r.HandleFunc("/api/externals", securityCheck(http.HandlerFunc(getAllExternals))).Methods("GET") + r.HandleFunc("/api/externals/{network}", securityCheck(http.HandlerFunc(getNetworkExternals))).Methods("GET") + r.HandleFunc("/api/externals/{network}/{clientid}", securityCheck(http.HandlerFunc(getExternal))).Methods("GET") + r.HandleFunc("/api/externals/{network}/{clientid}/qr", securityCheck(http.HandlerFunc(getExternal))).Methods("GET") + r.HandleFunc("/api/externals/{network}/{ingressgateway}", securityCheck(http.HandlerFunc(getExternal))).Methods("GET") + r.HandleFunc("/api/externals/{network}/{clientid}", securityCheck(http.HandlerFunc(updateExternal))).Methods("PUT") + r.HandleFunc("/api/externals/{network}/{clientid}", securityCheck(http.HandlerFunc(deleteExternal))).Methods("DELETE") + r.HandleFunc("/api/externals/{network}", securityCheck(http.HandlerFunc(createExternal))).Methods("POST") +} + +//Gets all nodes associated with network, including pending nodes +func getNetworkExternals(w http.ResponseWriter, r *http.Request) { + + w.Header().Set("Content-Type", "application/json") + + var nodes []models.External + var params = mux.Vars(r) + nodes, err := GetNetworkExternals(params["network"]) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + + //Returns all the nodes in JSON format + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(nodes) +} + +func GetNetworkExternals(network string) ([]models.External, error) { + var nodes []models.External + collection := mongoconn.Client.Database("netmaker").Collection("nodes") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + filter := bson.M{"network": network} + //Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future + cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0})) + if err != nil { + return []models.External{}, err + } + defer cancel() + for cur.Next(context.TODO()) { + //Using a different model for the ReturnExternal (other than regular node). + //Either we should do this for ALL structs (so Networks and Keys) + //OR we should just use the original struct + //My preference is to make some new return structs + //TODO: Think about this. Not an immediate concern. Just need to get some consistency eventually + var node models.External + err := cur.Decode(&node) + if err != nil { + return []models.External{}, err + } + // add item our array of nodes + nodes = append(nodes, node) + } + //TODO: Another fatal error we should take care of. + if err := cur.Err(); err != nil { + return []models.External{}, err + } + return nodes, nil +} + +//A separate function to get all nodes, not just nodes for a particular network. +//Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not +func getAllExternals(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + nodes, err := functions.GetAllExternals() + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + //Return all the nodes in JSON format + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(nodes) +} + +//Get an individual node. Nothin fancy here folks. +func getExternal(w http.ResponseWriter, r *http.Request) { + // set header. + w.Header().Set("Content-Type", "application/json") + + var params = mux.Vars(r) + + var node models.Node + + collection := mongoconn.Client.Database("netmaker").Collection("nodes") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + + filter := bson.M{"macaddress": params["macaddress"], "network": params["network"]} + err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&node) + + defer cancel() + + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(node) +} + +//This one's a doozy +//To create a node +//Must have valid key and be unique +func createExternal(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + var params = mux.Vars(r) + + var errorResponse = models.ErrorResponse{ + Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.", + } + + networkName := params["network"] + + //Check if network exists first + //TODO: This is inefficient. Let's find a better way. + //Just a few rows down we grab the network anyway + networkexists, err := functions.NetworkExists(networkName) + + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } else if !networkexists { + errorResponse = models.ErrorResponse{ + Code: http.StatusNotFound, Message: "W1R3: Network does not exist! ", + } + returnErrorResponse(w, r, errorResponse) + return + } + + var external models.External + + //get node from body of request + err = json.NewDecoder(r.Body).Decode(&external) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + err = ValidateExternalCreate(external) + if err != nil { + returnErrorResponse(w, r, formatError(err, "badrequest")) + return + } + + node, err = CreateExternal(node, networkName) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(node) +} + +func updateExternal(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + var params = mux.Vars(r) + + //Get id from parameters + //id, _ := primitive.ObjectIDFromHex(params["id"]) + + var node models.External + + //start here + node, err := functions.GetExternalByMacAddress(params["network"], params["macaddress"]) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + + var nodechange models.ExternalUpdate + + // we decode our body request params + _ = json.NewDecoder(r.Body).Decode(&nodechange) + if nodechange.Network == "" { + nodechange.Network = node.Network + } + if nodechange.MacAddress == "" { + nodechange.MacAddress = node.MacAddress + } + err = ValidateExternalUpdate(params["network"], nodechange) + if err != nil { + returnErrorResponse(w, r, formatError(err, "badrequest")) + return + } + + node, err = UpdateExternal(nodechange, node) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(node) +} + +//Delete a node +//Pretty straightforward +func deleteExternal(w http.ResponseWriter, r *http.Request) { + // Set header + w.Header().Set("Content-Type", "application/json") + + // get params + var params = mux.Vars(r) + + success, err := DeleteExternal(params["macaddress"], params["network"]) + + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } else if !success { + err = errors.New("Could not delete node " + params["macaddress"]) + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + returnSuccessResponse(w, r, params["macaddress"]+" deleted.") +} diff --git a/controllers/nodeHttpController.go b/controllers/nodeHttpController.go index 594fd298..6ffe8c82 100644 --- a/controllers/nodeHttpController.go +++ b/controllers/nodeHttpController.go @@ -28,6 +28,8 @@ func nodeHandlers(r *mux.Router) { 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(createGateway))).Methods("POST") r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "master", http.HandlerFunc(deleteGateway))).Methods("DELETE") + r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(http.HandlerFunc(createIngress))).Methods("POST") + r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(http.HandlerFunc(deleteIngress))).Methods("DELETE") r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "master", http.HandlerFunc(uncordonNode))).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") @@ -671,6 +673,97 @@ func DeleteGateway(network, macaddress string) (models.Node, error) { } return node, nil } +// == INGRESS == +func createIngress(w http.ResponseWriter, r *http.Request) { + var params = mux.Vars(r) + w.Header().Set("Content-Type", "application/json") + node, err := CreateIngress(params["network"], params["macaddress"]) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(node) +} + +func CreateIngress(network string, macaddress string) (models.Node, error) { + node, err := functions.GetNodeByMacAddress(network, macaddress) + if err != nil { + return models.Node{}, err + } + + collection := mongoconn.Client.Database("netmaker").Collection("nodes") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + // Create filter + filter := bson.M{"macaddress": macaddress, "network": network} + // prepare update model. + update := bson.D{ + {"$set", bson.D{ + {"isingress", true}, + {"lastmodified", time.Now().Unix()}, + }}, + } + var nodeupdate models.Node + err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate) + defer cancel() + if err != nil { + return models.Node{}, err + } + //Get updated values to return + node, err = functions.GetNodeByMacAddress(network, macaddress) + if err != nil { + return models.Node{}, err + } + return node, nil +} + +func deleteIngress(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + node, err := DeleteIngress(params["network"], params["macaddress"]) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(node) +} + +func DeleteIngress(network, macaddress string) (models.Node, error) { + + var nodeupdate models.Node + node, err := functions.GetNodeByMacAddress(network, macaddress) + if err != nil { + return models.Node{}, err + } + + collection := mongoconn.Client.Database("netmaker").Collection("nodes") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + // Create filter + filter := bson.M{"macaddress": macaddress, "network": network} + // prepare update model. + update := bson.D{ + {"$set", bson.D{ + {"lastmodified", time.Now().Unix()}, + {"isingress", false}, + }}, + } + err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate) + defer cancel() + if err != nil { + return models.Node{}, err + } + err = SetNetworkNodesLastModified(network) + if err != nil { + return models.Node{}, err + } + //Get updated values to return + node, err = functions.GetNodeByMacAddress(network, macaddress) + if err != nil { + return models.Node{}, err + } + return node, nil +} func updateNode(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 00000000..b536106a --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,2 @@ +FROM nginx:1.19 +COPY _build/html /usr/share/nginx/html diff --git a/docs/_build/html/_images/mesh-diagram.png b/docs/_build/html/_images/mesh-diagram.png new file mode 100644 index 00000000..dd63f578 Binary files /dev/null and b/docs/_build/html/_images/mesh-diagram.png differ diff --git a/functions/helpers.go b/functions/helpers.go index 9509edda..a1f30fd9 100644 --- a/functions/helpers.go +++ b/functions/helpers.go @@ -748,3 +748,31 @@ func GetAllNodes() ([]models.ReturnNode, error) { } return nodes, nil } + +func GetAllExternals() ([]models.ReturnNode, error) { + var node models.ReturnNode + var nodes []models.ReturnNode + collection := mongoconn.Client.Database("netmaker").Collection("nodes") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + // Filter out them ID's again + cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) + if err != nil { + return []models.ReturnNode{}, err + } + defer cancel() + for cur.Next(context.TODO()) { + err := cur.Decode(&node) + if err != nil { + return []models.ReturnNode{}, err + } + // add node to our array + nodes = append(nodes, node) + } + + //TODO: Fatal error + if err := cur.Err(); err != nil { + return []models.ReturnNode{}, err + } + return nodes, nil +} + diff --git a/go.mod b/go.mod index 0667e9c3..ed37ef3a 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang/protobuf v1.4.3 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/stretchr/testify v1.6.1 github.com/txn2/txeh v1.3.0 go.mongodb.org/mongo-driver v1.4.3 diff --git a/go.sum b/go.sum index ab83eb1b..fee764ed 100644 --- a/go.sum +++ b/go.sum @@ -137,6 +137,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= diff --git a/models/external.go b/models/external.go new file mode 100644 index 00000000..2d32c782 --- /dev/null +++ b/models/external.go @@ -0,0 +1,18 @@ +package models + +import ( + "go.mongodb.org/mongo-driver/bson/primitive" +) + +//External Struct +//At some point, need to replace all instances of Name with something else like Identifier +type External struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + ClientID string `json:"clientid" bson:"clientid"` + PrivateKey string `json:"privatekey" bson:"privatekey"` + PublicKey string `json:"publickey" bson:"publickey"` + Network string `json:"network" bson:"network"` + Address string `json:"address" bson:"address"` + LastModified string `json:"lastmodified" bson:"lastmodified"` + IngressGateway string `json:"ingressgateway" bson:"ingressgateway"` +} diff --git a/test/groupcreate.sh b/test/groupcreate.sh index 59b1a8d7..0eb52937 100755 --- a/test/groupcreate.sh +++ b/test/groupcreate.sh @@ -15,4 +15,4 @@ EOF 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/networks +curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer secretkey" localhost:8082/api/networks diff --git a/test/keycreate.sh b/test/keycreate.sh index 7a77094f..4d57e117 100755 --- a/test/keycreate.sh +++ b/test/keycreate.sh @@ -13,4 +13,4 @@ EOF 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/networks/skynet/keys +curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer secretkey" localhost:8082/api/networks/skynet/keys diff --git a/test/nodescreate.sh b/test/nodescreate.sh index 804c4877..05fa536f 100755 --- a/test/nodescreate.sh +++ b/test/nodescreate.sh @@ -3,7 +3,7 @@ PUBKEY="DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=" IPADDR="69.169.21.167" MACADDRESS="56:2a:9c:d4:e2:16" -ACCESSKEY="aA3bVG0rnItIRXDx" +ACCESSKEY="HzIeRewZEyMWtUSF" PASSWORD="password" LOCALADDRESS="192.168.1.21" @@ -23,7 +23,7 @@ EOF POST_JSON=$(generate_post_json) -curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer mastertoken" localhost:8081/api/skynet/nodes +curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer mastertoken" localhost:8082/api/nodes/skynet PUBKEY="ap200E90in4uyIyR0b3wuX24ZAp0WFL8q37UtL3CWFI=" IPADDR="70.169.21.168" @@ -34,7 +34,7 @@ LOCALADDRESS="192.73.1.2" POST_JSON=$(generate_post_json) -curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer mastertoken" localhost:8081/api/skynet/nodes +curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer mastertoken" localhost:8082/api/nodes/skynet PUBKEY="CAHIgkHXOsTEmY9XKhEI3CO5iYAo0X4U/yTX+L/yJ2E=" IPADDR="71.169.21.169" @@ -44,4 +44,4 @@ LOCALADDRESS="192.73.1.3" POST_JSON=$(generate_post_json) -curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer mastertoken" localhost:8081/api/skynet/nodes +curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer mastertoken" localhost:8082/api/nodes/skynet diff --git a/test/restartmongo.sh b/test/restartmongo.sh old mode 100644 new mode 100755