diff --git a/db.json b/db.json new file mode 100644 index 0000000..f7fe8a3 --- /dev/null +++ b/db.json @@ -0,0 +1 @@ +{"xterminate18181@gmail.com":{"email":"xterminate18181@gmail.com","password":"123","account-created":"2024-09-27T05:39:11.331520582-05:00","last-login":"2024-09-27T05:39:11.331520621-05:00"}} diff --git a/go.mod b/go.mod index 3494795..d3e4ddd 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,7 @@ go 1.23 require github.com/gorilla/sessions v1.4.0 -require github.com/gorilla/securecookie v1.1.2 // indirect +require ( + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect +) diff --git a/go.sum b/go.sum index fe47730..7e011b0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= diff --git a/server.go b/server.go index b75530b..734938b 100644 --- a/server.go +++ b/server.go @@ -1,10 +1,10 @@ package main import ( - "crypto/rand" "fmt" "log" "net/http" + "strings" "github.com/gorilla/sessions" ) @@ -18,17 +18,8 @@ type Server struct { } func main() { - // Set up encryption for session tokens - fmt.Print("Generating encryption key... ") - secret := make([]byte, 32) - _, err := rand.Read(secret) - if err != nil { - fmt.Println("Error generating key:") - log.Fatal(err) - return - } - fmt.Println("Done!") // Create server object + secret := []byte("super-secret-key") server := Server{ Users: make(map[string]UserData), Sessions: sessions.NewCookieStore(secret), @@ -36,10 +27,16 @@ func main() { // Host static files static_files := http.FileServer(http.Dir("static/")) http.Handle("/", static_files) - // Response generated by code - http.HandleFunc("/handle-register", server.handle_register) - http.HandleFunc("/handle-login", server.handle_login) - + // Redirect .html to clean URL + http.Handle("/register.html", http.RedirectHandler("/register", 301)) + http.Handle("/login.html", http.RedirectHandler("/login", 301)) + // Handle user authentication + http.HandleFunc("/register", server.handle_register) + http.HandleFunc("/login", server.handle_login) + http.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) { + server.handle_logout(w, r) + http.Redirect(w, r, "/", http.StatusFound) + }) // Start web server at 127.0.0.1:8080 fmt.Printf("Listening to %s on port %s...\n", ADDRESS, PORT) e := http.ListenAndServe(ADDRESS+":"+PORT, nil) diff --git a/static/index.html b/static/index.html index 823624b..af83bfb 100644 --- a/static/index.html +++ b/static/index.html @@ -14,11 +14,14 @@ diff --git a/static/login.html b/static/login.html index ebdcb4a..1446f54 100644 --- a/static/login.html +++ b/static/login.html @@ -10,15 +10,15 @@
-
+
- +
We'll never share your email with anyone else.
- +
diff --git a/static/register.html b/static/register.html index eba8c7e..29b40eb 100644 --- a/static/register.html +++ b/static/register.html @@ -10,18 +10,18 @@
- +
- - + +
We'll never share your email with anyone else.
- - + +
- +
diff --git a/users.go b/users.go index 95b9f5a..f7dd3a6 100644 --- a/users.go +++ b/users.go @@ -1,7 +1,10 @@ package main import ( + "encoding/json" + "fmt" "net/http" + "os" "time" ) @@ -15,77 +18,108 @@ type UserData struct { LastLogin time.Time `json:"last-login"` } +// Handles requests to /login.html func (s *Server) handle_login(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { + switch r.Method { + case http.MethodGet: + http.ServeFile(w, r, "./static/register.html") + case http.MethodPost: + // Get data from form + email := r.FormValue("email") + password := r.FormValue("password") + // Get user from database + user, ok := s.Users[email] + // If user does not exist + if !ok { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + // If password does not match + if password != user.Password { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + // Generate session + session, err := s.Sessions.Get(r, SESSION_COOKIE_NAME) + if err != nil { + s.handle_logout(w, r) + http.Error(w, "Invalid session", http.StatusUnauthorized) + return + } + session.Values[SESSION_AUTH] = true + session.Save(r, w) + // Update last-login on DB + user.LastLogin = time.Now() + s.Users[email] = user + // Redirect to index.html + http.Redirect(w, r, "/", http.StatusFound) + default: http.Error(w, "Forbidden", http.StatusForbidden) - return } - // Get data from form - email := r.FormValue("email") - password := r.FormValue("password") - // Get user from database - user, ok := s.Users[email] - // If user does not exist - if !ok { - http.Error(w, "Forbidden", http.StatusForbidden) - return - } - // If password does not match - if password != user.Password { - http.Error(w, "Forbidden", http.StatusForbidden) - return - } - // Generate session - session, err := s.Sessions.Get(r, SESSION_COOKIE_NAME) - if err != nil { - http.Error(w, "Internal Error", http.StatusInternalServerError) - return - } - session.Values[SESSION_AUTH] = true - if err := session.Save(r, w); err != nil { - http.Error(w, "Internal Error", http.StatusInternalServerError) - return - } - // Update last-login on DB - user.LastLogin = time.Now() - s.Users[email] = user - // Redirect to index.html - http.Redirect(w, r, "/", http.StatusFound) } +// Handles requests to /register.html func (s *Server) handle_register(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { + switch r.Method { + case http.MethodGet: + http.ServeFile(w, r, "./static/register.html") + case http.MethodPost: + // Get data from form + email := r.FormValue("email") + password := r.FormValue("password") + fmt.Println(r.Form) + // Check that this email is not already registered + if _, ok := s.Users[email]; ok { + fmt.Println("Already registered") + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + // Generate session + session, err := s.Sessions.Get(r, SESSION_COOKIE_NAME) + // If session cookie invalid + if err != nil { + s.handle_logout(w, r) + http.Error(w, "Invalid session", http.StatusUnauthorized) + return + } + // Save user information to DB + s.Users[email] = UserData{ + Email: email, + Password: password, + AccountCreated: time.Now(), + LastLogin: time.Now(), + } + // Make session valid + session.Values[SESSION_AUTH] = true + // Send session token to browser + session.Save(r, w) + // Redirect to index.html + s.save_state() + http.Redirect(w, r, "/", http.StatusFound) + default: http.Error(w, "Forbidden", http.StatusForbidden) - return } - // Get data from form - email := r.FormValue("email") - password := r.FormValue("password") - // Check that this email is not already registered - if _, ok := s.Users[email]; ok { - http.Error(w, "Forbidden", http.StatusForbidden) - return - } - // Generate session - session, err := s.Sessions.Get(r, SESSION_COOKIE_NAME) - if err != nil { - http.Error(w, "Internal Error", http.StatusInternalServerError) - return - } - // Save user information to DB - s.Users[email] = UserData{ - Email: email, - Password: password, - AccountCreated: time.Now(), - LastLogin: time.Now(), - } - // Make session valid - session.Values[SESSION_AUTH] = true - // Send session token to browser - if err := session.Save(r, w); err != nil { - http.Error(w, "Internal Error", http.StatusInternalServerError) - return - } - // Redirect to index.html - http.Redirect(w, r, "/", http.StatusFound) +} + +func (s *Server) handle_logout(w http.ResponseWriter, r *http.Request) { + // If session exists + if session, err := s.Sessions.Get(r, SESSION_COOKIE_NAME); err == nil { + // Remove authorization + session.Values[SESSION_AUTH] = false + session.Save(r, w) + } + // Remove session cookie + http.SetCookie(w, &http.Cookie{ + Name: SESSION_COOKIE_NAME, + MaxAge: -1, + }) +} + +func (s *Server) save_state() { + file, err := os.Create("db.json") + if err != nil { + panic(err) + } + defer file.Close() + json.NewEncoder(file).Encode(s.Users) } diff --git a/users.json b/users.json deleted file mode 100644 index 418dde4..0000000 --- a/users.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "users" = {} -}