diff options
| author | Samuel Johnson <[email protected]> | 2025-11-24 13:53:18 -0500 |
|---|---|---|
| committer | Samuel Johnson <[email protected]> | 2025-11-24 13:53:18 -0500 |
| commit | 368a462bc744d8e9084eacfaddeb9afcaf7f7133 (patch) | |
| tree | c6e8f665d6cb9713b9226b10c4a341e60b8e91c2 /cmd/web/handlers/login.go | |
| parent | 4d4419f51557bef6b64dca8635ed61616d262a9b (diff) | |
Add session management
Diffstat (limited to 'cmd/web/handlers/login.go')
| -rw-r--r-- | cmd/web/handlers/login.go | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/cmd/web/handlers/login.go b/cmd/web/handlers/login.go new file mode 100644 index 0000000..e867e07 --- /dev/null +++ b/cmd/web/handlers/login.go @@ -0,0 +1,170 @@ +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 + } +} |
