diff --git a/controllers/controller.go b/controllers/controller.go index 571671bd..e4d0ef50 100644 --- a/controllers/controller.go +++ b/controllers/controller.go @@ -29,6 +29,7 @@ var HttpHandlers = []interface{}{ ipHandlers, loggerHandlers, hostHandlers, + enrollmentKeyHandlers, } // HandleRESTRequests - handles the rest requests diff --git a/controllers/enrollmentkeys.go b/controllers/enrollmentkeys.go new file mode 100644 index 00000000..44773aeb --- /dev/null +++ b/controllers/enrollmentkeys.go @@ -0,0 +1,96 @@ +package controller + +import ( + "encoding/json" + "net/http" + + "github.com/gorilla/mux" + "github.com/gravitl/netmaker/logger" + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/servercfg" +) + +func enrollmentKeyHandlers(r *mux.Router) { + r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(getEnrollmentKeys))).Methods(http.MethodGet) + r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(deleteEnrollmentKey))).Methods(http.MethodDelete) + r.HandleFunc("/api/v1/host/register", logic.SecurityCheck(true, http.HandlerFunc(handleHostRegister))).Methods(http.MethodPost) +} + +// swagger:route GET /api/v1/enrollment-keys enrollmentKeys getEnrollmentKeys +// +// Lists all hosts. +// +// Schemes: https +// +// Security: +// oauth +// +// Responses: +// 200: getEnrollmentKeysSlice +func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) { + currentKeys, err := logic.GetAllEnrollmentKeys() + if err != nil { + logger.Log(0, r.Header.Get("user"), "failed to fetch enrollment keys: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + for i := range currentKeys { + if err = logic.Tokenize(currentKeys[i], servercfg.GetServer()); err != nil { + logger.Log(0, r.Header.Get("user"), "failed to get token values for keys:", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + } + // return JSON/API formatted hosts + logger.Log(2, r.Header.Get("user"), "fetched enrollment keys") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(currentKeys) +} + +// swagger:route DELETE /api/v1/enrollment-keys/{keyID} enrollmentKeys deleteEnrollmentKey +// +// Deletes a Netclient host from Netmaker server. +// +// Schemes: https +// +// Security: +// oauth +// +// Responses: +// 200: deleteEnrollmentKeyResponse +func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) { + var params = mux.Vars(r) + keyID := params["keyID"] + err := logic.DeleteEnrollmentKey(keyID) + if err != nil { + logger.Log(0, r.Header.Get("user"), "failed to remove enrollment key: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + logger.Log(2, r.Header.Get("user"), "deleted enrollment key", keyID) + w.WriteHeader(http.StatusOK) +} + +// swagger:route DELETE /api/v1/enrollment-keys/{keyID} enrollmentKeys deleteEnrollmentKey +// +// Deletes a Netclient host from Netmaker server. +// +// Schemes: https +// +// Security: +// oauth +// +// Responses: +// 200: hostRegisterResponse +func handleHostRegister(w http.ResponseWriter, r *http.Request) { + var params = mux.Vars(r) + keyID := params["keyID"] + err := logic.DeleteEnrollmentKey(keyID) + if err != nil { + logger.Log(0, r.Header.Get("user"), "failed to remove enrollment key: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + logger.Log(2, r.Header.Get("user"), "deleted enrollment key", keyID) + w.WriteHeader(http.StatusOK) +} diff --git a/logic/enrollmentkey.go b/logic/enrollmentkey.go index e0fe3b58..778f28de 100644 --- a/logic/enrollmentkey.go +++ b/logic/enrollmentkey.go @@ -1,6 +1,7 @@ package logic import ( + b64 "encoding/base64" "encoding/json" "errors" "fmt" @@ -13,15 +14,19 @@ import ( // EnrollmentKeyErrors - struct for holding EnrollmentKey error messages var EnrollmentKeyErrors = struct { - InvalidCreate error - NoKeyFound error - InvalidKey error - NoUsesRemaining error + InvalidCreate error + NoKeyFound error + InvalidKey error + NoUsesRemaining error + FailedToTokenize error + FailedToDeTokenize error }{ - InvalidCreate: fmt.Errorf("invalid enrollment key created"), - NoKeyFound: fmt.Errorf("no enrollmentkey found"), - InvalidKey: fmt.Errorf("invalid key provided"), - NoUsesRemaining: fmt.Errorf("no uses remaining"), + InvalidCreate: fmt.Errorf("invalid enrollment key created"), + NoKeyFound: fmt.Errorf("no enrollmentkey found"), + InvalidKey: fmt.Errorf("invalid key provided"), + NoUsesRemaining: fmt.Errorf("no uses remaining"), + FailedToTokenize: fmt.Errorf("failed to tokenize"), + FailedToDeTokenize: fmt.Errorf("failed to detokenize"), } // CreateEnrollmentKey - creates a new enrollment key in db @@ -109,6 +114,47 @@ func TryToUseEnrollmentKey(k *models.EnrollmentKey) bool { return false } +// Tokenize - tokenizes an enrollment key to be used via registration +// and attaches it to the Token field on the struct +func Tokenize(k *models.EnrollmentKey, serverAddr string) error { + if len(serverAddr) == 0 { + return EnrollmentKeyErrors.FailedToTokenize + } + newToken := models.EnrollmentToken{ + Server: serverAddr, + Value: k.Value, + } + data, err := json.Marshal(&newToken) + if err != nil { + return err + } + k.Token = b64.StdEncoding.EncodeToString([]byte(data)) + return nil +} + +// DeTokenize - detokenizes a base64 encoded string +// and finds the associated enrollment key +func DeTokenize(b64Token string) (*models.EnrollmentKey, error) { + if len(b64Token) == 0 { + return nil, EnrollmentKeyErrors.FailedToDeTokenize + } + tokenData, err := b64.StdEncoding.DecodeString(b64Token) + if err != nil { + return nil, err + } + + var newToken models.EnrollmentToken + err = json.Unmarshal(tokenData, &newToken) + if err != nil { + return nil, err + } + k, err := GetEnrollmentKey(newToken.Value) + if err != nil { + return nil, err + } + return k, nil +} + // == private == // decrementEnrollmentKey - decrements the uses on a key if above 0 remaining diff --git a/models/enrollment_key.go b/models/enrollment_key.go index 394a25c0..ae89ee48 100644 --- a/models/enrollment_key.go +++ b/models/enrollment_key.go @@ -4,6 +4,13 @@ import ( "time" ) +// EnrollmentToken - the tokenized version of an enrollmentkey; +// to be used for host registration +type EnrollmentToken struct { + Server string `json:"value"` + Value string `json:"value"` +} + // EnrollmentKeyLength - the length of an enrollment key const EnrollmentKeyLength = 32 @@ -15,6 +22,7 @@ type EnrollmentKey struct { Networks []string `json:"networks"` Unlimited bool `json:"unlimited"` Tags []string `json:"tags"` + Token string `json:"token,omitempty"` // B64 value of EnrollmentToken } // EnrollmentKey.IsValid - checks if the key is still valid to use