Migrate CSRF, web templates, session cookies, and snapshot to mcdsl
CSRF: Replace local csrfProtect with mcdsl/csrf.Protect. Delete internal/webserver/csrf.go. Web: Replace renderTemplate with web.RenderTemplate + csrf.TemplateFunc. Replace extractCookie with web.GetSessionToken. Replace manual session cookie SetCookie with web.SetSessionCookie. Snapshot: Replace local sqliteBackup with mcdsl/db.Snapshot. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,9 +4,9 @@ package webserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
"github.com/go-chi/chi/v5"
|
||||
|
||||
mcdslauth "git.wntrmute.dev/kyle/mcdsl/auth"
|
||||
"git.wntrmute.dev/kyle/mcdsl/csrf"
|
||||
"git.wntrmute.dev/kyle/mcdsl/web"
|
||||
"git.wntrmute.dev/kyle/metacrypt/internal/config"
|
||||
webui "git.wntrmute.dev/kyle/metacrypt/web"
|
||||
)
|
||||
@@ -116,7 +118,7 @@ type WebServer struct {
|
||||
logger *slog.Logger
|
||||
httpSrv *http.Server
|
||||
staticFS fs.FS
|
||||
csrf *csrfProtect
|
||||
csrf *csrf.Protect
|
||||
tgzCache sync.Map // key: UUID string → *tgzEntry
|
||||
userCache sync.Map // key: UUID string → *cachedUsername
|
||||
}
|
||||
@@ -154,12 +156,17 @@ func New(cfg *config.Config, logger *slog.Logger) (*WebServer, error) {
|
||||
return nil, fmt.Errorf("webserver: static FS: %w", err)
|
||||
}
|
||||
|
||||
secret := make([]byte, 32)
|
||||
if _, err := rand.Read(secret); err != nil {
|
||||
return nil, fmt.Errorf("webserver: generate CSRF secret: %w", err)
|
||||
}
|
||||
|
||||
ws := &WebServer{
|
||||
cfg: cfg,
|
||||
vault: vault,
|
||||
logger: logger,
|
||||
staticFS: staticFS,
|
||||
csrf: newCSRFProtect(),
|
||||
csrf: csrf.New(secret, "metacrypt_csrf", "csrf_token"),
|
||||
}
|
||||
|
||||
if tok := cfg.MCIAS.ServiceToken; tok != "" {
|
||||
@@ -220,7 +227,7 @@ func (lw *loggingResponseWriter) Unwrap() http.ResponseWriter {
|
||||
func (ws *WebServer) Start() error {
|
||||
r := chi.NewRouter()
|
||||
r.Use(ws.loggingMiddleware)
|
||||
r.Use(ws.csrf.middleware)
|
||||
r.Use(ws.csrf.Middleware)
|
||||
ws.registerRoutes(r)
|
||||
|
||||
ws.httpSrv = &http.Server{
|
||||
@@ -259,36 +266,9 @@ func (ws *WebServer) Shutdown(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (ws *WebServer) renderTemplate(w http.ResponseWriter, name string, data interface{}) {
|
||||
csrfToken := ws.csrf.setToken(w)
|
||||
|
||||
funcMap := template.FuncMap{
|
||||
"csrfField": func() template.HTML {
|
||||
return template.HTML(fmt.Sprintf(
|
||||
`<input type="hidden" name="%s" value="%s">`,
|
||||
csrfFieldName, template.HTMLEscapeString(csrfToken),
|
||||
))
|
||||
},
|
||||
}
|
||||
|
||||
tmpl, err := template.New("").Funcs(funcMap).ParseFS(webui.FS,
|
||||
"templates/layout.html",
|
||||
"templates/"+name,
|
||||
)
|
||||
if err != nil {
|
||||
ws.logger.Error("parse template", "name", name, "error", err)
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
|
||||
ws.logger.Error("execute template", "name", name, "error", err)
|
||||
}
|
||||
web.RenderTemplate(w, webui.FS, name, data, ws.csrf.TemplateFunc(w))
|
||||
}
|
||||
|
||||
func extractCookie(r *http.Request) string {
|
||||
c, err := r.Cookie("metacrypt_token")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return c.Value
|
||||
return web.GetSessionToken(r, "metacrypt_token")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user