package handlers import ( "database/sql" "html/template" "log" "net/http" "os" "time" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" "paterissa.net/mblog/internal/models" ) type loginContext struct { err *log.Logger db *sql.DB } func (ctx *loginContext) index(w http.ResponseWriter, r *http.Request) { files := []string{ "ui/html/base.tmpl.html", "ui/html/login.tmpl.html", "ui/html/music_player.tmpl.html", } compiled, err := template.ParseFiles(files...) if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Server Error")) ctx.err.Printf("Failed to parse templates: %v\n", err) return } err = compiled.ExecuteTemplate(w, "base", ctx) if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Server Error")) ctx.err.Printf("Failed to parse templates: %v\n", err) return } return } func (ctx *loginContext) login(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(405) w.Write([]byte("Method Not Allowed")) return } err := r.ParseForm() if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Error")) ctx.err.Printf("Failed to retrieve form data: %v\n", err) return } user := r.PostForm.Get("user") passOne := r.PostForm.Get("pass_one") passTwo := r.PostForm.Get("pass_two") stmt, err := ctx.db.Prepare("SELECT * FROM logins WHERE name = $1;") if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Error")) ctx.err.Printf("Failed to retrieve form data: %v\n", err) return } defer stmt.Close() var u models.User row := stmt.QueryRow(user) err = row.Scan(&u.Id, &u.Name, &u.Time, &u.PassOne, &u.PassTwo) if err != nil { w.WriteHeader(401) w.Write([]byte("Unauthorized")) ctx.err.Printf("Failed to retrieve user info from DB: %v\n", err) return } passOneErr := bcrypt.CompareHashAndPassword([]byte(u.PassOne), []byte(passOne)) passTwoErr := bcrypt.CompareHashAndPassword([]byte(u.PassTwo), []byte(passTwo)) if passOneErr != nil || passTwoErr != nil { w.WriteHeader(401) w.Write([]byte("Failed to login - not authorized")) return } cookie := http.Cookie{ Name: "paterissa_session_token", Value: uuid.New().String(), Expires: time.Now().AddDate(0, 0, 1), Path: "/", Domain: os.Getenv("serv"), HttpOnly: true, Secure: true, } commit, err := ctx.db.Prepare("INSERT INTO cookies (content, user_id, expiration) VALUES ($1, $2, $3);") if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Error")) ctx.err.Printf("Failed to prepare DB statement: %v\n", err) return } _, err = commit.Exec(cookie.Value, u.Id, cookie.Expires) if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Error")) ctx.err.Printf("Failed to prepare DB statement: %v\n", err) } http.SetCookie(w, &cookie) http.Redirect(w, r, "/", http.StatusFound) return } func (ctx *loginContext) logout(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("paterissa_session_token") if err != nil { w.WriteHeader(405) w.Write([]byte("Unauthorized")) return } stmt, err := ctx.db.Prepare("UPDATE cookies SET expiration = $1 WHERE content = $2;") if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not prepare DB statement: %v\n", err) return } defer stmt.Close() _, err = stmt.Exec(time.Now(), cookie.Value) if err != nil { w.WriteHeader(500) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not execute DB statement: %v\n", err) return } cookie = &http.Cookie{ Name: "paterissa_session_token", Value: "", Path: "/", MaxAge: -1, HttpOnly: true, } http.SetCookie(w, cookie) http.Redirect(w, r, "/", http.StatusFound) return } func (ctx *loginContext) handle(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { ctx.index(w, r) return } else { ctx.login(w, r) return } }