Commit 1937445
Changed files (2)
serverHandlers.go
@@ -1,8 +1,10 @@
package main
import (
+ "encoding/base32"
"encoding/json"
"fmt"
+ "github.com/pquerna/otp/totp"
"io/ioutil"
"log"
"math/rand"
@@ -12,6 +14,7 @@ import (
"strconv"
"strings"
"sync"
+ "time"
)
// userTests is a map that contains arrays of client tests
@@ -291,6 +294,57 @@ func handleScoreRequests(writer http.ResponseWriter, request *http.Request) {
}
}
+// parseReceivedQuestions takes the questions from the client submission
+// and adds them to the database. The server expects a JSON-formatted array
+// of Records. There should be a TOKEN in the url. This TOKEN is a six-digit,
+// TOTP token based on SUBMISSION_SECRET.
+func parseReceivedQuestions(writer http.ResponseWriter, request *http.Request) {
+ token := request.FormValue("token")
+ correctToken, err := totp.GenerateCode(base32.StdEncoding.EncodeToString([]byte(SUBMISSION_SECRET)),
+ time.Now())
+ if err != nil {
+ log.Printf("Error generating token: %+v", err)
+ }
+ if token != correctToken {
+ http.Error(writer, "Invalid token.", http.StatusForbidden)
+ log.Printf("Invalid token from: %+v\tvia %+v\tis: %s, should be: %s", request.RemoteAddr,
+ request.UserAgent(), token, correctToken)
+ return
+ }
+ var newQuestions []Record
+ data, err := ioutil.ReadAll(request.Body)
+ if err != nil {
+ http.Error(writer, "Could not read the question submission.", http.StatusBadRequest)
+ log.Printf("Error for connection %s while reading questions: %+v", request.RemoteAddr, err)
+ return
+ }
+ // check to make sure properly formatted client response
+ err = json.Unmarshal(data, &newQuestions)
+ if err != nil {
+ http.Error(writer, "Could not parse the question submission.", http.StatusBadRequest)
+ log.Printf("Error while parsing submitted questions: %+v", err)
+ return
+ }
+ for _, record := range newQuestions {
+ err = addRecordToDB(&record)
+ if err != nil {
+ log.Printf("Could not add\n\t%+v\nto DB due to: %+v", err)
+ }
+ }
+}
+
+// handleQuestionQueries handles the submissions for questions
+func handleQuestionQueries(writer http.ResponseWriter, request *http.Request) {
+ defer request.Body.Close()
+ switch request.Method {
+ case "POST":
+ log.Printf("Client attempted to POST to %s/questions: %+v\tvia %+v", API_ROOT, request.RemoteAddr, request.UserAgent())
+ parseReceivedQuestions(writer, request)
+ case "GET":
+ log.Printf("Client attempted to GET from %s/questions: %+v\tvia %+v", API_ROOT, request.RemoteAddr, request.UserAgent())
+ }
+}
+
type ServerHandler struct {
Request string
HandleFunction func(writer http.ResponseWriter, request *http.Request)
@@ -309,6 +363,9 @@ func init() {
API_ROOT + "/questions/categories": ServerHandler{
Request: API_ROOT + "/questions/categories",
HandleFunction: handleCategoryQueries},
+ API_ROOT + "/questions": ServerHandler{
+ Request: API_ROOT + "/questions",
+ HandleFunction: handleQuestionQueries},
}
userTests = map[string][]ClientTest{}
}
structures.go
@@ -7,6 +7,9 @@ import (
// API_ROOT defines the root path for the web api interface
const API_ROOT = "/api"
+// SUBMISSION_SECRET holds the secret for submitting questions
+const SUBMISSION_SECRET = "shadows"
+
// mapLock locks the maps to prevent concurrency issues
var mapLock *sync.Mutex