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/music_player.tmpl.html",
"ui/html/pages/login.tmpl.html",
}
compiled, err := template.ParseFiles(files...)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
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(http.StatusInternalServerError)
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(http.StatusMethodNotAllowed)
w.Write([]byte("Method Not Allowed"))
return
}
err := r.ParseForm()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
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(http.StatusInternalServerError)
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(http.StatusUnauthorized)
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(http.StatusUnauthorized)
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(http.StatusInternalServerError)
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(http.StatusInternalServerError)
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(http.StatusUnauthorized)
w.Write([]byte("Unauthorized"))
return
}
stmt, err := ctx.db.Prepare("UPDATE cookies SET expiration = $1 WHERE content = $2;")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
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(http.StatusInternalServerError)
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: "/",
Domain: os.Getenv("serv"),
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
}
}