diff options
| -rw-r--r-- | app/controllers/sessions/controller.go | 7 | ||||
| -rw-r--r-- | app/controllers/sessions/controller_test.go | 10 | ||||
| -rw-r--r-- | go.mod | 8 | ||||
| -rw-r--r-- | go.sum | 10 | ||||
| -rw-r--r-- | pkg/web/cookie/expire.go | 14 | ||||
| -rw-r--r-- | pkg/web/cookie/new.go | 18 | ||||
| -rw-r--r-- | pkg/web/cookie/option.go | 57 | ||||
| -rw-r--r-- | pkg/web/cookie/option_test.go | 53 | ||||
| -rw-r--r-- | pkg/web/cookie/reset.go | 16 |
9 files changed, 56 insertions, 137 deletions
diff --git a/app/controllers/sessions/controller.go b/app/controllers/sessions/controller.go index b9240c6..50aac63 100644 --- a/app/controllers/sessions/controller.go +++ b/app/controllers/sessions/controller.go @@ -4,6 +4,7 @@ import ( "net/http" "time" + xcookie "github.com/xlgmokha/x/pkg/cookie" "github.com/xlgmokha/x/pkg/log" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/middleware" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc" @@ -37,8 +38,8 @@ func (c *Controller) New(w http.ResponseWriter, r *http.Request) { http.SetCookie(w, cookie.New( "oauth_state", nonce, - cookie.WithSameSite(http.SameSiteLaxMode), - cookie.WithExpiration(time.Now().Add(10*time.Minute)), + xcookie.WithSameSite(http.SameSiteLaxMode), + xcookie.WithExpiration(time.Now().Add(10*time.Minute)), )) http.Redirect(w, r, url, http.StatusFound) } @@ -138,7 +139,7 @@ func (c *Controller) Create(w http.ResponseWriter, r *http.Request) { return } - http.SetCookie(w, cookie.New("session", encoded, cookie.WithExpiration(tokens.Expiry))) + http.SetCookie(w, cookie.New("session", encoded, xcookie.WithExpiration(tokens.Expiry))) http.Redirect(w, r, "/dashboard", http.StatusFound) } diff --git a/app/controllers/sessions/controller_test.go b/app/controllers/sessions/controller_test.go index c40dbe7..8efc813 100644 --- a/app/controllers/sessions/controller_test.go +++ b/app/controllers/sessions/controller_test.go @@ -6,10 +6,12 @@ import ( "net/http" "net/url" "testing" + "time" "github.com/oauth2-proxy/mockoidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/xlgmokha/x/pkg/x" xcfg "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/cfg" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc" @@ -150,6 +152,14 @@ func TestSessions(t *testing.T) { sub, err := token.Claims.GetSubject() require.NoError(t, err) assert.Equal(t, user.Subject, sub) + + assert.Equal(t, "/", cookie.Path) + assert.Equal(t, "localhost", cookie.Domain) + assert.Equal(t, "session", cookie.Name) + assert.Zero(t, cookie.SameSite) + assert.Equal(t, x.Must(time.Parse(time.RFC3339, tokens["expiry"].(string))).Unix(), cookie.Expires.Unix()) + assert.True(t, cookie.HttpOnly) + assert.True(t, cookie.Secure) }) t.Run("stores the refresh token in a session cookie", func(t *testing.T) { @@ -9,8 +9,8 @@ require ( github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.36.0 - github.com/xlgmokha/x v0.0.0-20250430185455-b691eda27477 - golang.org/x/oauth2 v0.29.0 + github.com/xlgmokha/x v0.0.0-20250507143633-104ccfbf81d1 + golang.org/x/oauth2 v0.30.0 ) require ( @@ -69,8 +69,8 @@ require ( go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/crypto v0.37.0 // indirect - golang.org/x/sys v0.32.0 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/sys v0.33.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -138,6 +138,8 @@ github.com/xlgmokha/x v0.0.0-20250428220023-07723124c0a2 h1:bMuGvVZ8MEV3De4yn/+5 github.com/xlgmokha/x v0.0.0-20250428220023-07723124c0a2/go.mod h1:axGPKzoJCNTmPJxYqN5l+Z9gGbPe0yolkT61a5p3QiI= github.com/xlgmokha/x v0.0.0-20250430185455-b691eda27477 h1:oeKrn9BSDSic5MTXKhQesxhIhB7byxl86IHtCD+yw4k= github.com/xlgmokha/x v0.0.0-20250430185455-b691eda27477/go.mod h1:axGPKzoJCNTmPJxYqN5l+Z9gGbPe0yolkT61a5p3QiI= +github.com/xlgmokha/x v0.0.0-20250507143633-104ccfbf81d1 h1:iW9a6GiZ8kYduHkUXl6v81CpKyBzSqBMh/fBMqBStKk= +github.com/xlgmokha/x v0.0.0-20250507143633-104ccfbf81d1/go.mod h1:FhvU8e/Zcpo0Lw8n5WkPtqQwzGrLD7FhFEi0MbEBLGk= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -168,6 +170,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -184,6 +188,8 @@ golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -209,6 +215,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -216,6 +224,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -224,6 +233,7 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/web/cookie/expire.go b/pkg/web/cookie/expire.go index b44fc38..7d90274 100644 --- a/pkg/web/cookie/expire.go +++ b/pkg/web/cookie/expire.go @@ -1,7 +1,17 @@ package cookie -import "net/http" +import ( + "net/http" + + "github.com/xlgmokha/x/pkg/cookie" + "github.com/xlgmokha/x/pkg/env" +) func Expire(w http.ResponseWriter, name string) { - http.SetCookie(w, Reset(name)) + cookie.Expire(w, name, + cookie.WithPath("/"), + cookie.WithDomain(env.Fetch("HOST", "localhost")), + cookie.WithHttpOnly(true), + cookie.WithSecure(true), + ) } diff --git a/pkg/web/cookie/new.go b/pkg/web/cookie/new.go index 8c04dd6..be0241d 100644 --- a/pkg/web/cookie/new.go +++ b/pkg/web/cookie/new.go @@ -3,6 +3,7 @@ package cookie import ( "net/http" + "github.com/xlgmokha/x/pkg/cookie" "github.com/xlgmokha/x/pkg/env" "github.com/xlgmokha/x/pkg/x" ) @@ -10,15 +11,14 @@ import ( func New(name, value string, options ...x.Option[*http.Cookie]) *http.Cookie { options = x.Prepend[x.Option[*http.Cookie]]( options, - With(func(c *http.Cookie) { - c.Name = name - c.Value = value // TODO:: digitally sign the value - }), - WithPath("/"), - WithHttpOnly(true), - WithSecure(true), - WithSameSite(http.SameSiteDefaultMode), - WithDomain(env.Fetch("HOST", "localhost")), + cookie.WithName(name), + cookie.WithValue(value), // TODO:: digitally sign the value + cookie.WithPath("/"), + cookie.WithHttpOnly(true), + cookie.WithSecure(true), + cookie.WithSameSite(http.SameSiteDefaultMode), + cookie.WithDomain(env.Fetch("HOST", "localhost")), ) + return x.New[*http.Cookie](options...) } diff --git a/pkg/web/cookie/option.go b/pkg/web/cookie/option.go deleted file mode 100644 index 58a2e93..0000000 --- a/pkg/web/cookie/option.go +++ /dev/null @@ -1,57 +0,0 @@ -package cookie - -import ( - "net/http" - "time" - - "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 - } -} - -func WithPath(value string) x.Option[*http.Cookie] { - return With(func(c *http.Cookie) { - c.Path = value - }) -} - -func WithHttpOnly(value bool) x.Option[*http.Cookie] { - return With(func(c *http.Cookie) { - c.HttpOnly = value - }) -} - -func WithSecure(value bool) x.Option[*http.Cookie] { - return With(func(c *http.Cookie) { - c.Secure = value - }) -} - -func WithDomain(value string) x.Option[*http.Cookie] { - return With(func(c *http.Cookie) { - c.Domain = value - }) -} - -func WithSameSite(value http.SameSite) x.Option[*http.Cookie] { - return With(func(c *http.Cookie) { - c.SameSite = value - }) -} - -func WithExpiration(expires time.Time) x.Option[*http.Cookie] { - return With(func(c *http.Cookie) { - c.Expires = expires - if expires.Before(time.Now()) { - c.MaxAge = -1 - } else { - duration := time.Until(expires).Round(time.Second) - c.MaxAge = int(duration.Seconds()) - } - }) -} diff --git a/pkg/web/cookie/option_test.go b/pkg/web/cookie/option_test.go deleted file mode 100644 index 97913ba..0000000 --- a/pkg/web/cookie/option_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package cookie - -import ( - "net/http" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestOption(t *testing.T) { - t.Run("WithPath", func(t *testing.T) { - assert.Equal(t, "/blah", New("name", "value", WithPath("/blah")).Path) - }) - - t.Run("WithHttpOnly", func(t *testing.T) { - assert.False(t, New("x", "v", WithHttpOnly(false)).HttpOnly) - assert.True(t, New("x", "v", WithHttpOnly(true)).HttpOnly) - }) - - t.Run("WithSecure", func(t *testing.T) { - assert.False(t, New("x", "v", WithSecure(false)).Secure) - assert.True(t, New("x", "v", WithSecure(true)).Secure) - }) - - t.Run("WithDomain", func(t *testing.T) { - assert.Equal(t, "example.com", New("x", "v", WithDomain("example.com")).Domain) - }) - - t.Run("WithSameSite", func(t *testing.T) { - assert.Equal(t, http.SameSiteLaxMode, New("x", "v", WithSameSite(http.SameSiteLaxMode)).SameSite) - assert.Equal(t, http.SameSiteStrictMode, New("x", "v", WithSameSite(http.SameSiteStrictMode)).SameSite) - assert.Equal(t, http.SameSiteNoneMode, New("x", "v", WithSameSite(http.SameSiteNoneMode)).SameSite) - }) - - t.Run("WithExpiration", func(t *testing.T) { - now := time.Now() - - t.Run("with future time", func(t *testing.T) { - expires := now.Add(1 * time.Second) - cookie := New("x", "v", WithExpiration(expires)) - assert.Equal(t, expires, cookie.Expires) - assert.Equal(t, 1, cookie.MaxAge) - }) - - t.Run("with past time", func(t *testing.T) { - expires := now.Add(-1 * time.Second) - cookie := New("x", "v", WithExpiration(expires)) - assert.Equal(t, expires, cookie.Expires) - assert.Equal(t, -1, cookie.MaxAge) - }) - }) -} diff --git a/pkg/web/cookie/reset.go b/pkg/web/cookie/reset.go index 167cdb6..39625e6 100644 --- a/pkg/web/cookie/reset.go +++ b/pkg/web/cookie/reset.go @@ -2,20 +2,18 @@ package cookie import ( "net/http" - "time" + "github.com/xlgmokha/x/pkg/cookie" "github.com/xlgmokha/x/pkg/env" ) func Reset(name string) *http.Cookie { - return New( + return cookie.Reset( name, - "", - WithExpiration(time.Unix(0, 0)), - WithPath("/"), - WithHttpOnly(true), - WithSecure(true), - WithSameSite(http.SameSiteDefaultMode), - WithDomain(env.Fetch("HOST", "localhost")), + cookie.WithPath("/"), + cookie.WithHttpOnly(true), + cookie.WithSecure(true), + cookie.WithSameSite(http.SameSiteDefaultMode), + cookie.WithDomain(env.Fetch("HOST", "localhost")), ) } |
