notatio/user.go

200 lines
5.3 KiB
Go

package main
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"time"
"github.com/google/uuid"
"golang.org/x/crypto/bcrypt"
)
func Login(w http.ResponseWriter, r *http.Request) {
var creds Credentials
err := r.ParseMultipartForm(0)
if err != nil {
log.Printf("Error parsing form: %v", err)
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
log.Println(r.FormValue("username"))
creds.Username = strings.ToLower(r.FormValue("username"))
creds.Password = r.FormValue("password")
// Retrieve the hashed password from the database
hashedPassword, userExists := getUserFromDatabase(creds.Username)
if !userExists {
errorMessage := creds.Username + " does not exist"
log.Println(errorMessage)
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": "Incorrect username or password",
})
return
}
// Compare the stored hashed password with the provided password
err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(creds.Password))
if err != nil {
errorMessage := "Incorrect username or password"
log.Println(errorMessage)
w.WriteHeader(http.StatusUnauthorized)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": errorMessage,
})
return
}
// Create a new session token
sessionToken := uuid.NewString()
expiresAt := time.Now().Add(sessionLength)
// Store the token in the session map
sessions[sessionToken] = session{
username: creds.Username,
expiry: expiresAt,
}
// Set the session token as a cookie
http.SetCookie(w, &http.Cookie{
Name: "session_token",
Value: sessionToken,
Expires: expiresAt,
})
log.Printf("User logged in: %s", creds.Username)
// Send JSON response indicating successful login
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"message": "Login successful",
})
}
func getUserFromDatabase(username string) (string, bool) {
var hashedPassword string
err := db.QueryRow("SELECT password FROM users WHERE username = $1", username).Scan(&hashedPassword)
if err == sql.ErrNoRows {
// User not found
return "", false
} else if err != nil {
log.Printf("Error retrieving user from the database: %v", err)
return "", false
}
return hashedPassword, true
}
func getUserUUID(username string) (string, error) {
var uuid string
err := db.QueryRow("SELECT uuid FROM users WHERE username = $1", username).Scan(&uuid)
if err != nil {
if err == sql.ErrNoRows {
// User not found
return "", fmt.Errorf("user not found: %s", username)
}
return "", err
}
return uuid, nil
}
func Logout(w http.ResponseWriter, r *http.Request) {
// Get the session token from the request cookies
sessionCookie, err := r.Cookie("session_token")
if err != nil {
log.Printf("Error getting session cookie: %v", err)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
sessionToken := sessionCookie.Value
// Remove the user's session from the session map
delete(sessions, sessionToken)
log.Printf("User logged out")
// Set the user's `session_token` cookie to an empty value and an immediate expiry time
http.SetCookie(w, &http.Cookie{
Name: "session_token",
Value: "",
Expires: time.Now(),
})
// Redirect to the index page
http.Redirect(w, r, "/", http.StatusSeeOther)
}
func Signup(w http.ResponseWriter, r *http.Request) {
// Check if the request method is POST
if r.Method == http.MethodPost {
var creds Credentials
err := r.ParseForm()
if err != nil {
log.Printf("Error parsing form: %v", err)
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
creds.Username = strings.ToLower(r.FormValue("username"))
creds.Password = r.FormValue("password")
name := r.FormValue("name")
email := r.FormValue("email")
// Check if the username is already taken
if _, userExists := getUserFromDatabase(creds.Username); userExists {
log.Printf("Username already exists: %s", creds.Username)
http.Error(w, "Username Already Taken", http.StatusConflict)
return
}
// Hash the user's password
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(creds.Password), hashCost)
if err != nil {
log.Printf("Error hashing password: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Generate a UUID for the user
userUUID := uuid.New()
// Store the user in the database with the generated UUID
if err := insertUserIntoDatabase(creds.Username, string(hashedPassword), string("normal"), userUUID.String(), name, email); err != nil {
log.Printf("Error inserting user into database: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Create a new session token
sessionToken := uuid.NewString()
expiresAt := time.Now().Add(sessionLength)
// Store the token in the session map
sessions[sessionToken] = session{
username: creds.Username,
expiry: expiresAt,
sessionUUID: userUUID.String(),
}
// Set the session token as a cookie
http.SetCookie(w, &http.Cookie{
Name: "session_token",
Value: sessionToken,
Expires: expiresAt,
})
log.Printf("User signed up and added to database: %s", creds.Username)
createUserFolder(creds.Username)
// Redirect to the new user page
http.Redirect(w, r, "/welcome", http.StatusSeeOther)
return
}
}