From cfb006c2be8717ebd10d39b9dd64211ff7ce1eb0 Mon Sep 17 00:00:00 2001 From: maride Date: Tue, 14 Aug 2018 13:57:13 +0200 Subject: Add Login/Logout/Sessions using given access codes --- README.md | 11 +++++ src/credentials.go | 18 +++++++ src/http.go | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.go | 17 +++++++ src/session.go | 60 ++++++++++++++++++++++ 5 files changed, 249 insertions(+) create mode 100644 src/credentials.go create mode 100644 src/http.go create mode 100644 src/main.go create mode 100644 src/session.go diff --git a/README.md b/README.md index 9e218b7..433f658 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,14 @@ The companion ~cube~ container spawned specifically for one user. - Validator for flags - Temporary secret vault for the user - control endpoint of challenge containers for the user + +## Usage + +This executable needs some parameters to work properly: + +| Key | Required? | Description | +|---|---|---| +| `-port` | No | The port for HTTP. *Default: 8080* | +| `-username` | No | Name of our user, as used e.g. in salutation. No length or charset limitation. *Default: Player 1* | +| `-accessCode` | Yes | Access code for the user. *Default: AllYourCodesAreBelongToUs* | +| `-sessionSalt` | Yes | Variable to salt the session token generator with. | diff --git a/src/credentials.go b/src/credentials.go new file mode 100644 index 0000000..7bbcf19 --- /dev/null +++ b/src/credentials.go @@ -0,0 +1,18 @@ +package main + +import "flag" + +var ( + username* string + accessCode* string +) + +func registerCredentialsFlags() { + username = flag.String("username", "Player 1", "Username") + accessCode = flag.String("accessCode", "AllYourCodesAreBelongToUs", "Access code for the user") +} + + +func verifyCredentials(un string, ac string) (bool) { + return *username == un && *accessCode == ac +} \ No newline at end of file diff --git a/src/http.go b/src/http.go new file mode 100644 index 0000000..9f97352 --- /dev/null +++ b/src/http.go @@ -0,0 +1,143 @@ +package main + +import ( + "flag" + "github.com/gorilla/mux" + "net/http" + "fmt" + "strings" + "io/ioutil" + "time" +) + +var ( + port *int +) + +func registerHTTPFlags() { + port = flag.Int("port", 8080, "The port for HTTP") +} + +func runHTTPServer() (error) { + r := mux.NewRouter() + + r.HandleFunc("/", indexHandler) + r.HandleFunc("/files/{file}", fileHandler) + r.HandleFunc("/login", loginGetHandler).Methods("GET") + r.HandleFunc("/login", loginPostHandler).Methods("POST") + r.HandleFunc("/logout", logoutHandler).Methods("GET") + + address := fmt.Sprintf(":%d", *port) + return http.ListenAndServe(address, r) +} + +// Host the index file +func indexHandler(w http.ResponseWriter, r *http.Request) { + session, cookieNotFoundError := r.Cookie("session") + + if cookieNotFoundError != nil || !isValidSession(session.Value) { + // either no session cookie found, or it contains an invalid session token. Redirect. + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) + } else { + // valid session token found, redirect to frontpage + readFileToResponse(w, "/index.html") + } +} + +// Static host files +func fileHandler(w http.ResponseWriter, r *http.Request) { + readFileToResponse(w, mux.Vars(r)["file"]) +} + +// Helper function to host files off of "hosted/" directory +func readFileToResponse(w http.ResponseWriter, path string) { + requestedFile := strings.Replace(path, "..", "", -1) + + contents, readError := ioutil.ReadFile(fmt.Sprintf("hosted/%s", requestedFile)) + + if readError != nil { + w.Write([]byte(fmt.Sprintf("unable to read %s", requestedFile))) + } else { + w.Write([]byte(contents)) + } +} + +// Read login page +func loginGetHandler(w http.ResponseWriter, r *http.Request) { + session, cookieNotFoundError := r.Cookie("session") + + // we need to verify that the client doesn't already have a valid session + if cookieNotFoundError == nil && isValidSession(session.Value) { + // session already valid, redirect the user + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + } else { + readFileToResponse(w, "/login.html") + } +} + +// Process login data +func loginPostHandler(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + username := r.Form.Get("username") + accessCode := r.Form.Get("accesscode") + session, cookieNotFoundError := r.Cookie("session") + + validRedirect := false + + // we need to verify that the client doesn't already have a valid session + if cookieNotFoundError == nil && isValidSession(session.Value) { + // session already valid, redirect the user + validRedirect = true + } else { + // no valid session present, check the given credentials + + if verifyCredentials(username, accessCode) { + // credentials valid, create session + newSessionToken := createSession() + http.SetCookie(w, &http.Cookie{ + Name: "session", + Value: newSessionToken, + Path: "/", + Expires: time.Now().Add(time.Hour * 24), + }) + validRedirect = true + } + } + + if validRedirect { + // Redirect to frontpage + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + return + } else { + // credentials invalid or database error occured + w.Write([]byte("Wrong credentials")) + return + } + + // Something else weird happened. + w.WriteHeader(500) + w.Write([]byte("Server Error")) +} + +// Process logout and deletion of cookie +func logoutHandler(w http.ResponseWriter, r *http.Request) { + session, cookieNotFoundError := r.Cookie("session") + + if cookieNotFoundError == nil { + // cookie was found + if isValidSession(session.Value) { + // and it contains a valid session. Delete the session... + destroySession(session.Value) + } + + // and delete the cookie + http.SetCookie(w, &http.Cookie{ + Name: "session", + Value: "", + Path: "/", + Expires: time.Unix(0, 0), + }) + } + + http.Redirect(w, r, "/", http.StatusTemporaryRedirect) +} diff --git a/src/main.go b/src/main.go new file mode 100644 index 0000000..519943a --- /dev/null +++ b/src/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "flag" + "log" +) + +func main() { + // Set up flags + registerHTTPFlags() + registerSessionFlags() + registerCredentialsFlags() + flag.Parse() + + // Run HTTP server + log.Fatalln(runHTTPServer()) +} \ No newline at end of file diff --git a/src/session.go b/src/session.go new file mode 100644 index 0000000..bfaf685 --- /dev/null +++ b/src/session.go @@ -0,0 +1,60 @@ +package main + +import ( + "crypto/sha512" + "math/rand" + "encoding/base64" + "flag" +) + +var ( + tokenSalt *string + sessions = []string{} +) + +func registerSessionFlags() { + tokenSalt = flag.String("sessionSalt", "", "Salt for the session token") +} + +// Generates a random token +func randToken() string { + // Generating the random part + randBuff := make([]byte, 128) + rand.Read(randBuff) + + // Hashing the random part + hasher := sha512.New() + hasher.Write(randBuff) + hasher.Write([]byte(*tokenSalt)) + hashBuff := hasher.Sum(nil) + + // Returning that + return base64.URLEncoding.EncodeToString(hashBuff) +} + +// Verifies a given session token against the internal session array +func isValidSession(sessionToken string) (bool){ + for _, token := range sessions { + if token == sessionToken { + return true + } + } + return false +} + +// Generates a new session token and adds it to the internal session array +func createSession() (string) { + newToken := randToken() + sessions = append(sessions, newToken) + return newToken +} + +// Removes a session from the internal session array +func destroySession(sessionToken string) { + for index, token := range sessions { + if token == sessionToken { + // delete session from slice + sessions = append(sessions[:index], sessions[:index + 1] ...) + } + } +} -- cgit 1.4.1