diff options
| -rw-r--r-- | roseh.moe.go | 108 |
1 files changed, 72 insertions, 36 deletions
diff --git a/roseh.moe.go b/roseh.moe.go index 5e9dd92..da88003 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -30,7 +30,8 @@ var ( var ( notepadPassword []byte - secretKey ed25519.PrivateKey + privateKey ed25519.PrivateKey + publicKey ed25519.PublicKey ) func loadSecrets() error { @@ -38,7 +39,7 @@ func loadSecrets() error { if err != nil { return err } - for _, line := range bytes.Split(bytes.TrimSpace(secrets), []byte("\n")) { + for _, line := range bytes.Split(bytes.TrimSuffix(secrets, []byte("\n")), []byte("\n")) { if pw, ok := bytes.CutPrefix(line, []byte("notepad-password=")); ok { notepadPassword = make([]byte, hex.DecodedLen(len(pw))) if _, err := hex.Decode(notepadPassword, pw); err != nil { @@ -52,7 +53,8 @@ func loadSecrets() error { if len(seed) != ed25519.SeedSize { return fmt.Errorf("invalid ed25519 key") } - secretKey = ed25519.NewKeyFromSeed(seed) + privateKey = ed25519.NewKeyFromSeed(seed) + publicKey = privateKey.Public().(ed25519.PublicKey) } } return nil @@ -81,11 +83,11 @@ var ( //go:embed templates/index.html.template indexString string - indexTemplate = template.Must(template.Must(outlineTemplate.Clone()).New("body").Parse(indexString)) + indexTemplate = template.Must(template.Must(outlineTemplate.Clone()).New("body").Parse(indexString)).Lookup("outline") ) func index(w http.ResponseWriter, _ *http.Request) { - if err := indexTemplate.ExecuteTemplate(w, "outline", nil); err != nil { + if err := indexTemplate.Execute(w, nil); err != nil { log.Printf("Warning: index: %s", err) } } @@ -130,7 +132,7 @@ func attachCookie(w http.ResponseWriter) error { if err != nil { return err } - sig := ed25519.Sign(secretKey, nowBytes) + sig := ed25519.Sign(privateKey, nowBytes) http.SetCookie(w, &http.Cookie{ Name: "auth", Value: base64.RawStdEncoding.EncodeToString(append(nowBytes, sig...)), @@ -144,8 +146,12 @@ func attachCookie(w http.ResponseWriter) error { return nil } -func cookieOK(cookie string) bool { - bytes, err := base64.RawStdEncoding.DecodeString(cookie) +func checkCookie(w http.ResponseWriter, r *http.Request) bool { + cookie, err := r.Cookie("auth") + if err != nil { + return false + } + bytes, err := base64.RawStdEncoding.DecodeString(cookie.Value) if err != nil { return false } @@ -153,38 +159,46 @@ func cookieOK(cookie string) bool { return false } msg, sig := bytes[:len(bytes)-ed25519.SignatureSize], bytes[len(bytes)-ed25519.SignatureSize:] - if !ed25519.Verify(secretKey.Public().(ed25519.PublicKey), msg, sig) { + if !ed25519.Verify(publicKey, msg, sig) { return false } var t time.Time if err := t.UnmarshalBinary(msg); err != nil { return false } - return time.Since(t) < cookieExpiration + if time.Since(t) > cookieExpiration { + return false + } + attachCookie(w) + return true } -func authenticate(next http.Handler) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if cookie, err := r.Cookie("auth"); err == nil && cookieOK(cookie.Value) { - attachCookie(w) - next.ServeHTTP(w, r) - return - } - _, password, ok := r.BasicAuth() - if !ok { - w.Header().Set("WWW-Authenticate", `Basic realm="notepad"`) - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - pwHash := sha512.Sum512([]byte(password)) - if subtle.ConstantTimeCompare(pwHash[:], notepadPassword) == 0 { - w.Header().Set("WWW-Authenticate", `Basic realm="notepad"`) - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - attachCookie(w) - next.ServeHTTP(w, r) +func cookieAuth(w http.ResponseWriter, r *http.Request) bool { + if !checkCookie(w, r) { + http.Error(w, "Forbidden", http.StatusForbidden) + return false + } + return true +} + +func basicAuth(w http.ResponseWriter, r *http.Request) bool { + if checkCookie(w, r) { + return true + } + _, password, ok := r.BasicAuth() + if !ok { + w.Header().Set("WWW-Authenticate", `Basic realm="notepad"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return false + } + pwHash := sha512.Sum512([]byte(password)) + if subtle.ConstantTimeCompare(pwHash[:], notepadPassword) == 0 { + w.Header().Set("WWW-Authenticate", `Basic realm="notepad"`) + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return false } + attachCookie(w) + return true } var ( @@ -193,17 +207,21 @@ var ( notepadTemplate = template.Must(template.Must(outlineTemplate.Clone()).New("body").Parse(notepadString)).Lookup("outline") ) +type notepadTemplateArgs struct { + Content string +} + func notepad(w http.ResponseWriter, r *http.Request) { + if !basicAuth(w, r) { + return + } var currentContent string if currentContentBytes, err := os.ReadFile(*notepadFile); err != nil { currentContent = fmt.Sprintf("Error reading notepad file: %s", err) } else { currentContent = string(currentContentBytes) } - type args struct { - Content string - } - if err := notepadTemplate.Execute(w, args{Content: currentContent}); err != nil { + if err := notepadTemplate.Execute(w, notepadTemplateArgs{Content: currentContent}); err != nil { log.Printf("Warning: notepad: %s", err) } } @@ -224,6 +242,9 @@ func saveNote(content []byte) error { } func autosave(w http.ResponseWriter, r *http.Request) { + if !cookieAuth(w, r) { + return + } msg := "Saved ✓" if err := saveNote([]byte(r.FormValue("content"))); err != nil { msg = fmt.Sprintf("Error: %s", err) @@ -233,6 +254,20 @@ func autosave(w http.ResponseWriter, r *http.Request) { } } +func manualSave(w http.ResponseWriter, r *http.Request) { + if !cookieAuth(w, r) { + return + } + content := r.FormValue("content") + if err := saveNote([]byte(content)); err != nil { + http.Error(w, fmt.Sprintf("Error: %s", err), http.StatusInternalServerError) + return + } + if err := notepadTemplate.Execute(w, notepadTemplateArgs{Content: content}); err != nil { + log.Printf("Warning: manualSave: %s", err) + } +} + func main() { flag.Parse() @@ -242,7 +277,8 @@ func main() { } http.HandleFunc("GET /pong", pong) - http.Handle("GET /notepad", authenticate(http.HandlerFunc(notepad))) + http.HandleFunc("GET /notepad", notepad) + http.HandleFunc("POST /notepad", manualSave) http.HandleFunc("POST /notepad/autosave", autosave) http.HandleFunc("GET /static/", static) http.HandleFunc("GET /favicon.ico", favicon) |
