upload-service/internal/handler/auth.go

128 lines
2.9 KiB
Go

package handler
import (
"database/sql"
"embed"
"fmt"
"html/template"
"log"
"net/http"
"time"
"golang.org/x/crypto/bcrypt"
)
var passwordTmpl *template.Template
func InitTemplates(webFS embed.FS) {
funcs := template.FuncMap{"formatSize": formatSizeHTML}
passwordTmpl = template.Must(template.ParseFS(webFS, "web/password.html"))
downloadTmpl = template.Must(template.New("download.html").Funcs(funcs).ParseFS(webFS, "web/download.html"))
batchTmpl = template.Must(template.New("batch.html").Funcs(funcs).ParseFS(webFS, "web/batch.html"))
batchPasswordTmpl = template.Must(template.ParseFS(webFS, "web/batch-password.html"))
}
func formatSizeHTML(bytes int64) string {
const (
KB = 1024
MB = 1024 * KB
GB = 1024 * MB
)
switch {
case bytes >= GB:
return fmt.Sprintf("%.2f GB", float64(bytes)/float64(GB))
case bytes >= MB:
return fmt.Sprintf("%.1f MB", float64(bytes)/float64(MB))
case bytes >= KB:
return fmt.Sprintf("%.1f KB", float64(bytes)/float64(KB))
default:
return fmt.Sprintf("%d B", bytes)
}
}
func (h *Handler) AuthPage(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if id == "" {
http.NotFound(w, r)
return
}
rec, err := h.store.Get(id)
if err == sql.ErrNoRows {
http.NotFound(w, r)
return
}
if err != nil {
log.Printf("db get error: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
if rec.PasswordHash == nil {
http.Redirect(w, r, "/f/"+id, http.StatusSeeOther)
return
}
data := map[string]any{
"ID": id,
"Filename": rec.Filename,
"Error": r.URL.Query().Get("error"),
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
passwordTmpl.Execute(w, data)
}
func (h *Handler) AuthSubmit(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
if id == "" {
http.NotFound(w, r)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
password := r.FormValue("password")
if password == "" {
http.Redirect(w, r, "/f/"+id+"/auth?error=password+required", http.StatusSeeOther)
return
}
rec, err := h.store.Get(id)
if err == sql.ErrNoRows {
http.NotFound(w, r)
return
}
if err != nil {
log.Printf("db get error: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
if rec.PasswordHash == nil {
http.Redirect(w, r, "/f/"+id, http.StatusSeeOther)
return
}
if err := bcrypt.CompareHashAndPassword([]byte(*rec.PasswordHash), []byte(password)); err != nil {
http.Redirect(w, r, "/f/"+id+"/auth?error=wrong+password", http.StatusSeeOther)
return
}
// Set auth cookie (10 min lifetime)
http.SetCookie(w, &http.Cookie{
Name: "auth_" + id,
Value: "granted",
Path: "/f/" + id,
MaxAge: 600,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Secure: true,
Expires: time.Now().Add(10 * time.Minute),
})
http.Redirect(w, r, "/f/"+id, http.StatusSeeOther)
}