Files
volumen/internal/preview/preview.go
T
petrbalvin f8ed33df83
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s
Initial commit
Assisted-by: GLM 5.3
2026-09-29 10:03:32 +02:00

56 lines
1.7 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
// Package preview signs the shareable links that show an unpublished
// post on the public API. The signature is an HMAC over the slug and an
// expiry stamp, keyed with the admin session key, so a link can be
// handed to a reviewer without granting them anything else.
package preview
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strconv"
"strings"
"time"
)
// TTL is how long a preview link stays valid.
const TTL = 7 * 24 * time.Hour
// Token returns the preview token for slug, or "" when no session key
// is configured: without a key the signature could not be verified, so
// no link is offered at all.
func Token(slug, sessionKey string, now time.Time) string {
if slug == "" || sessionKey == "" {
return ""
}
expiry := now.Add(TTL).Unix()
return sign(slug, expiry, sessionKey) + "-" + strconv.FormatInt(expiry, 10)
}
// Valid reports whether token authorises a preview of slug.
func Valid(token, slug, sessionKey string, now time.Time) bool {
if token == "" || slug == "" || sessionKey == "" {
return false
}
mac, stamp, found := strings.CutLast(token, "-")
if !found {
return false
}
expiry, err := strconv.ParseInt(stamp, 10, 64)
if err != nil || now.Unix() > expiry {
return false
}
return hmac.Equal([]byte(mac), []byte(sign(slug, expiry, sessionKey)))
}
func sign(slug string, expiry int64, sessionKey string) string {
mac := hmac.New(sha256.New, []byte(sessionKey))
mac.Write([]byte(slug))
mac.Write([]byte("-"))
mac.Write([]byte(strconv.FormatInt(expiry, 10)))
return hex.EncodeToString(mac.Sum(nil))[:32]
}