Commit a69d4a7

Richard Luby <richluby@gmail.com>
2016-10-28 14:57:20
server saves test results
server now saves client test results. results are saved in a directory with the client username. test IDs are randomly generated, 5-digit id numbers. if the id is already selected, the algorithm randomly generates another id. IDs are 0-padded to always be 5 digits long. usernames are passed as URL parameters to the server. files and directories created to store tests are marked with as few permissions as possible for the server to still operate.
1 parent 9eff758
.gitignore
@@ -1,1 +1,2 @@
 *.conf
+tests/
README.md
@@ -16,6 +16,7 @@ PERMIT_BLANK_PASSWORD = true
 USE_HTTPS = false
 PRIVATE_KEY = "~/.ssh/question.priv"
 QUESTIONS = "/path/to/questions"
+USER_TESTS = "/path/to/tests"
 ```
 
 Compile the client with `go install` (from the same directory) and run it by
server.go
@@ -27,6 +27,8 @@ type SERVER_CONFIG struct {
 	USE_HTTPS bool
 	// QUESTIONS contains the path to the questions directory
 	QUESTIONS string
+	// USER_TESTS stores the test results of users
+	USER_TESTS string
 }
 
 // defaultSERVER_CONFIG is used when no SERVER_CONFIGuration file is given
@@ -37,8 +39,11 @@ PERMIT_BLANK_PASSWORD = true
 USE_HTTPS = false
 PRIVATE_KEY = "~/.ssh/question.priv"
 QUESTIONS = "/path/to/questions"
+USER_TESTS = "/path/to/tests"
 `
 
+var serverConfig SERVER_CONFIG
+
 // Contains the questions from which to pull
 var records []Record
 
@@ -84,7 +89,7 @@ func ExecuteServer() {
 	flag.StringVar(&confFile, "f", "", "define a specific configuration file to read")
 	flag.Parse()
 	log.Printf("Configuration: %+v\n", flag.Args())
-	serverConfig := loadServerConfiguration(confFile)
+	serverConfig = loadServerConfiguration(confFile)
 	log.Printf("Configuration: %+v\n", serverConfig)
 	if err = filepath.Walk(serverConfig.QUESTIONS, loadRecords); err != nil {
 		log.Printf("Failed to load questions due to error: %+v", err)
serverHandlers.go
@@ -3,9 +3,12 @@ package main
 import (
 	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"log"
 	"math/rand"
 	"net/http"
+	"os"
+	"path/filepath"
 	"strconv"
 )
 
@@ -27,9 +30,48 @@ func handleRequestForTest(writer http.ResponseWriter, request *http.Request) err
 	return nil
 }
 
+// writeTestFile writes the the data to the given writer for the specified username
+func writeTestFile(username string, data []byte) error {
+	var err error
+	resultsFilePath := filepath.Join(serverConfig.USER_TESTS, username)
+	if err = os.MkdirAll(resultsFilePath, 0700); err != nil {
+		log.Printf("Could not create test directory for user %s: %+v", username, err)
+		return err
+	}
+	id := fmt.Sprintf("%05d", rand.Intn(99999)) // generate random 5-digit id
+	for fileNeedsWrite := true; fileNeedsWrite; {
+		if err = ioutil.WriteFile(filepath.Join(resultsFilePath, id), data, 0400); err != nil {
+			id = fmt.Sprint(rand.Intn(99999))
+		}
+		fileNeedsWrite = false
+	}
+	return err
+}
+
 // handlePostingTest receives a client's test results, and then stores them
 func handlePostingTest(writer http.ResponseWriter, request *http.Request) error {
-	return nil
+	if request.URL.Path != handlers[API_ROOT+"/test"].Request {
+		return fmt.Errorf("Incorrectly formatted URL path: %+v", request.URL.Path)
+	}
+	username := request.FormValue("username")
+	var clientResponseRecord []ClientRecord
+	data, err := ioutil.ReadAll(request.Body)
+	if err != nil {
+		http.Error(writer, "Could not read the test record.", http.StatusBadRequest)
+		return fmt.Errorf("Error for user %s: %+v", username, err)
+	}
+	// check to make sure properly formatted client response
+	err = json.Unmarshal(data, &clientResponseRecord)
+	if err != nil {
+		http.Error(writer, "Could not parse the test record.", http.StatusBadRequest)
+		return fmt.Errorf("Error for user %s: %+v", username, err)
+	}
+	log.Printf("Received test from user: %s at %s\tvia %s", username, request.RemoteAddr, request.UserAgent())
+	fmt.Fprint(writer, "Test received successfully.")
+	if err = writeTestFile(username, data); err != nil {
+		http.Error(writer, "Could not save the test record.", http.StatusInternalServerError)
+	}
+	return err
 }
 
 // handleTestRequests creates an array of records for the client