From 38710484f9d5b5beb558050ddf997af351f866c6 Mon Sep 17 00:00:00 2001 From: Bennett Wetters Date: Sun, 14 Jan 2024 01:04:01 +0100 Subject: [PATCH] Generate HTTP endpoints for environment sensors - Move update request sanity checks to new method in handlers/util.go - Change EnvironmentSensor.Value type because ParseFloat returns float64 --- handlers/sensors.go | 39 +++++++++++++++++++++++++++++++++++++++ handlers/state.go | 27 +-------------------------- handlers/util.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ main.go | 15 +++++++++++++++ types/v14.go | 3 ++- 5 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 handlers/sensors.go create mode 100644 handlers/util.go diff --git a/handlers/sensors.go b/handlers/sensors.go new file mode 100644 index 0000000..7441699 --- /dev/null +++ b/handlers/sensors.go @@ -0,0 +1,39 @@ +package handlers + +import ( + "io" + "log" + "math" + "net/http" + "strconv" + "time" + + "gitlab.hamburg.ccc.de/ccchh/spaceapid/config" + "gitlab.hamburg.ccc.de/ccchh/spaceapid/types" +) + +func EnvironmentSensor( + authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID, + resp *types.EnvironmentSensor, +) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + body := updateEndpointValidator(authDB, validCredentials, w, r) + + // Parse request body + newState, err := strconv.ParseFloat(string(body), 64) + if err != nil || math.IsInf(newState, 0) { + log.Println("Failed to parse request body from", r.RemoteAddr) + w.WriteHeader(http.StatusBadRequest) + _, _ = io.WriteString(w, "HTTP request body has to be a valid float64 value != +/-Inf") + return + } + + // Set SpaceAPI response values + resp.Value = newState + resp.LastChange = time.Now().Unix() + + // Respond with OK + w.WriteHeader(http.StatusOK) + _, _ = io.WriteString(w, "Update Successful") + } +} diff --git a/handlers/state.go b/handlers/state.go index dcd191a..13f37c0 100644 --- a/handlers/state.go +++ b/handlers/state.go @@ -9,7 +9,6 @@ import ( "gitlab.hamburg.ccc.de/ccchh/spaceapid/config" "gitlab.hamburg.ccc.de/ccchh/spaceapid/types" - "gitlab.hamburg.ccc.de/ccchh/spaceapid/util" ) func StateOpen( @@ -17,31 +16,7 @@ func StateOpen( resp *types.SpaceAPIResponseV14, ) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - // Check BasicAuth credentials - username, password, ok := r.BasicAuth() - if !ok || !util.CheckCredentials(authDB, validCredentials, username, password) { - log.Println("Unauthorized request from", r.RemoteAddr) - w.Header().Set("WWW-Authenticate", "Basic realm=\"space-api\"") - w.WriteHeader(http.StatusUnauthorized) - return - } - - // Check if PUT method - if r.Method != http.MethodPut { - log.Println("Wrong METHOD from", r.RemoteAddr) - w.Header().Set("Allow", http.MethodPut) - w.WriteHeader(http.StatusMethodNotAllowed) - return - } - - // Read request body - body, err := io.ReadAll(r.Body) - if err != nil { - log.Println("Failed to read request body from", r.RemoteAddr) - w.WriteHeader(http.StatusInternalServerError) - _, _ = io.WriteString(w, "Failed reading HTTP request body") - return - } + body := updateEndpointValidator(authDB, validCredentials, w, r) // Parse request body newState, err := strconv.ParseBool(string(body)) diff --git a/handlers/util.go b/handlers/util.go new file mode 100644 index 0000000..625a9c0 --- /dev/null +++ b/handlers/util.go @@ -0,0 +1,45 @@ +package handlers + +import ( + "io" + "log" + "net/http" + + "gitlab.hamburg.ccc.de/ccchh/spaceapid/config" + "gitlab.hamburg.ccc.de/ccchh/spaceapid/util" +) + +// updateEndpointValidator checks BasicAuth credentials, +// checks for correct HTTP method and then returns the request body +func updateEndpointValidator( + authDB config.HTTPBACredentials, validCredentials []config.HTTPBACredentialID, + w http.ResponseWriter, r *http.Request, +) (body []byte) { + // Check BasicAuth credentials + username, password, ok := r.BasicAuth() + if !ok || !util.CheckCredentials(authDB, validCredentials, username, password) { + log.Println("Unauthorized request from", r.RemoteAddr) + w.Header().Set("WWW-Authenticate", "Basic realm=\"space-api\"") + w.WriteHeader(http.StatusUnauthorized) + return + } + + // Check if PUT method + if r.Method != http.MethodPut { + log.Println("Wrong Method: ", r.Method, "from", r.RemoteAddr, "at", r.RequestURI) + w.Header().Set("Allow", http.MethodPut) + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + // Read request body + body, err := io.ReadAll(r.Body) + if err != nil { + log.Println("Failed to read request body from", r.RemoteAddr) + w.WriteHeader(http.StatusInternalServerError) + _, _ = io.WriteString(w, "Failed reading HTTP request body") + return + } + + return +} diff --git a/main.go b/main.go index 98b8b05..c9060fb 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,12 @@ package main import ( + "fmt" "log" "net/http" "os" "os/signal" + "strings" "syscall" "gitlab.hamburg.ccc.de/ccchh/spaceapid/config" @@ -37,6 +39,19 @@ func main() { http.HandleFunc("/state/open", handlers.StateOpen(conf.Credentials, conf.Dynamic.State.Open.AllowedCredentials, &conf.Response), ) + // Register handlers for Environmental Sensors + for key, envSensorConfigs := range conf.Dynamic.Sensors { + for i, envSensorConfig := range envSensorConfigs { + http.HandleFunc( + strings.ToLower(fmt.Sprintf( + "/sensors/%s/%s/%s", key, envSensorConfig.SensorData.Location, envSensorConfig.SensorData.Name, + )), + handlers.EnvironmentSensor( + conf.Credentials, envSensorConfig.AllowedCredentials, &conf.Response.Sensors[key][i], + ), + ) + } + } // Start webserver log.Println("Starting HTTP server...") diff --git a/types/v14.go b/types/v14.go index 81b92a6..2c354bf 100644 --- a/types/v14.go +++ b/types/v14.go @@ -41,11 +41,12 @@ type SpaceAPIResponseV14 struct { } type EnvironmentSensor struct { - Value float32 `json:"value"` + Value float64 `json:"value"` Unit string `json:"unit"` Location string `json:"location"` Name string `json:"name"` Description string `json:"description"` + LastChange int64 `json:"lastchange"` } type PersistentStateV14 struct {