Commit a3d5689

mo khan <mo@mokhan.ca>
2025-05-06 20:12:27
feat: extract signer interface
1 parent 1eca32e
pkg/cookie/option.go
@@ -5,15 +5,12 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/xlgmokha/x/pkg/pls"
+	"github.com/xlgmokha/x/pkg/crypt"
 	"github.com/xlgmokha/x/pkg/x"
 )
 
 func With(with func(*http.Cookie)) x.Option[*http.Cookie] {
-	return func(c *http.Cookie) *http.Cookie {
-		with(c)
-		return c
-	}
+	return x.With[*http.Cookie](with)
 }
 
 func WithValue(value string) x.Option[*http.Cookie] {
@@ -22,6 +19,12 @@ func WithValue(value string) x.Option[*http.Cookie] {
 	})
 }
 
+func WithSignedValue(value string, signer crypt.Signer) x.Option[*http.Cookie] {
+	signature, _ := signer.Sign([]byte(value))
+	expected := fmt.Sprintf("%v--%v", value, string(signature))
+	return WithValue(expected)
+}
+
 func WithPath(value string) x.Option[*http.Cookie] {
 	return With(func(c *http.Cookie) {
 		c.Path = value
@@ -63,9 +66,3 @@ func WithExpiration(expires time.Time) x.Option[*http.Cookie] {
 		}
 	})
 }
-
-func WithSignedValue(value string, secret string) x.Option[*http.Cookie] {
-	signature := pls.GenerateHMAC256([]byte(secret), []byte(value))
-	expected := fmt.Sprintf("%v--%v", value, string(signature))
-	return WithValue(expected)
-}
pkg/cookie/option_test.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	"github.com/xlgmokha/x/pkg/crypt"
 	"github.com/xlgmokha/x/pkg/pls"
 )
 
@@ -58,7 +59,8 @@ func TestOption(t *testing.T) {
 	t.Run("WithSignedValue", func(t *testing.T) {
 		value := "1"
 		secret := "secret"
-		cookie := New("session", WithSignedValue(value, secret))
+		signer := crypt.NewHMACSigner([]byte(secret))
+		cookie := New("session", WithSignedValue(value, signer))
 
 		require.NotNil(t, cookie)
 		assert.NotEqual(t, "1", cookie.Value)
pkg/crypt/hmac.go
@@ -0,0 +1,42 @@
+package crypt
+
+import (
+	"crypto/hmac"
+	"crypto/sha256"
+	"hash"
+
+	"github.com/xlgmokha/x/pkg/x"
+)
+
+type HMACSigner struct {
+	key     []byte
+	factory func() hash.Hash
+}
+
+func NewHMACSigner(key []byte) *HMACSigner {
+	return x.New[*HMACSigner](
+		WithAlgorithm(sha256.New),
+		WithKey(key),
+	)
+}
+
+func (s *HMACSigner) Sign(data []byte) ([]byte, error) {
+	mac := hmac.New(s.factory, s.key)
+	_, err := mac.Write(data)
+	if err != nil {
+		return nil, err
+	}
+	return mac.Sum(nil), nil
+}
+
+func WithAlgorithm(factory x.Factory[hash.Hash]) x.Option[*HMACSigner] {
+	return x.With[*HMACSigner](x.Configure[*HMACSigner](func(item *HMACSigner) {
+		item.factory = factory
+	}))
+}
+
+func WithKey(key []byte) x.Option[*HMACSigner] {
+	return x.With[*HMACSigner](x.Configure[*HMACSigner](func(item *HMACSigner) {
+		item.key = key
+	}))
+}
pkg/crypt/signer.go
@@ -0,0 +1,5 @@
+package crypt
+
+type Signer interface {
+	Sign([]byte) ([]byte, error)
+}
pkg/x/option.go
@@ -0,0 +1,20 @@
+package x
+
+type Configure[T any] func(T)
+type Option[T any] func(T) T
+type Factory[T any] func() T
+
+func New[T any](options ...Option[T]) T {
+	item := Default[T]()
+	for _, option := range options {
+		item = option(item)
+	}
+	return item
+}
+
+func With[T any](with Configure[T]) Option[T] {
+	return func(item T) T {
+		with(item)
+		return item
+	}
+}
pkg/x/types.go
@@ -2,8 +2,6 @@ package x
 
 import "reflect"
 
-type Option[T any] func(T) T
-
 func Default[T any]() T {
 	item := Zero[T]()
 
@@ -14,14 +12,6 @@ func Default[T any]() T {
 	return item
 }
 
-func New[T any](options ...Option[T]) T {
-	item := Default[T]()
-	for _, option := range options {
-		item = option(item)
-	}
-	return item
-}
-
 func Zero[T any]() T {
 	var item T
 	return item