refactor: extract signature package from imgcache
All checks were successful
check / check (push) Successful in 1m46s

Move HMAC-SHA256 request signing out of internal/imgcache/ into its own
internal/signature/ package, per the plan in issue #39. This is one of the
remaining "easily separable" extractions; only the signer is moved here so
the diff stays reviewable.

What moved (from internal/imgcache/signature.go):

- Signer type, its New constructor, Sign, Verify, GenerateSignedURL
- ParseParams (query-string signature/expiration parsing)
- Signature error sentinels

To keep the import edge one-way (imgcache depends on signature, never the
reverse), the package defines a standalone Request type carrying just the
fields the signature covers, instead of importing imgcache.ImageRequest.
imgcache projects its ImageRequest onto signature.Request via a small
unexported helper.

Renames (no stuttering):

- NewSigner -> signature.New
- ParseSignatureParams -> signature.ParseParams
- ErrSignatureRequired/Invalid/Expired -> signature.ErrRequired/Invalid/Expired

The "non-whitelisted host" error message is updated to "non-allowlisted"
for inclusive terminology, consistent with the allowlist rename.

Pure refactor. The bytes fed to the HMAC are unchanged, so signatures remain
compatible. All existing tests move with the package; docker build passes
(fmt-check, lint, test, build).

refs #39
This commit is contained in:
2026-07-25 17:51:30 +07:00
parent b6e9ac2a93
commit 6526b717dd
5 changed files with 223 additions and 168 deletions

View File

@@ -7,6 +7,7 @@ import (
"time"
"sneak.berlin/go/pixa/internal/magic"
"sneak.berlin/go/pixa/internal/signature"
)
func TestService_Get_WhitelistedHost(t *testing.T) {
@@ -77,9 +78,9 @@ func TestService_Get_NonWhitelistedHost_ValidSignature(t *testing.T) {
}
// Generate a valid signature
signer := NewSigner(signingKey)
signer := signature.New(signingKey)
req.Expires = time.Now().Add(time.Hour)
req.Signature = signer.Sign(req)
req.Signature = signer.Sign(signatureRequest(req))
// Should pass validation
err := svc.ValidateRequest(req)
@@ -118,9 +119,9 @@ func TestService_Get_NonWhitelistedHost_ExpiredSignature(t *testing.T) {
}
// Generate an expired signature
signer := NewSigner(signingKey)
signer := signature.New(signingKey)
req.Expires = time.Now().Add(-time.Hour) // Already expired
req.Signature = signer.Sign(req)
req.Signature = signer.Sign(signatureRequest(req))
// Should fail validation
err := svc.ValidateRequest(req)
@@ -164,7 +165,7 @@ func TestService_ValidateRequest_SignatureExactHostMatch(t *testing.T) {
WithNoWhitelist(),
)
signer := NewSigner(signingKey)
signer := signature.New(signingKey)
// Sign a request for "cdn.example.com"
signedReq := &ImageRequest{
@@ -176,7 +177,7 @@ func TestService_ValidateRequest_SignatureExactHostMatch(t *testing.T) {
FitMode: FitCover,
Expires: time.Now().Add(time.Hour),
}
signedReq.Signature = signer.Sign(signedReq)
signedReq.Signature = signer.Sign(signatureRequest(signedReq))
// The original request should pass validation
t.Run("exact host passes", func(t *testing.T) {