Skip to content

Commit

Permalink
v2: minor changes but breaking
Browse files Browse the repository at this point in the history
switch from tinylib/msgp to github.com/vmihailenco/msgpack/v5 as it's
more flexible and doesn't require codegen.  Might be a bit slower since
it uses reflection?  Haven't tested performance.

Use store arbitrary metadata as something which can be serialized and
deserialized to and from the Sessions struct.  Might unexport the Meta
struct
  • Loading branch information
abraithwaite committed Mar 18, 2023
1 parent 679b44d commit 53ff082
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 557 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ifndef Q
GOTESTFLAGS += -v
endif

export GOEXPERIMENT=nocoverageredesign

.PHONY: deps
deps:
$Qgo mod download
Expand Down
14 changes: 9 additions & 5 deletions cmd/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"os"
"time"

"github.com/abraithwaite/jeff"
redis_store "github.com/abraithwaite/jeff/redis"
"github.com/abraithwaite/jeff/v2"
redis_store "github.com/abraithwaite/jeff/v2/redis"
"github.com/gomodule/redigo/redis"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
Expand Down Expand Up @@ -58,13 +58,17 @@ func (s *server) registerFormHandler(w http.ResponseWriter, r *http.Request) {
}

func (s *server) internalPageHandler(w http.ResponseWriter, r *http.Request) {
sess := jeff.ActiveSession(r.Context())
sess, _ := jeff.ActiveSession(r.Context())
fmt.Fprintf(w, internalPage, sess.Key)
}

func (s *server) publicPageHandler(w http.ResponseWriter, r *http.Request) {
sess := jeff.ActiveSession(r.Context())
fmt.Fprintf(w, publicPage, sess.Key)
sess, ok := jeff.ActiveSession(r.Context())
if ok {
fmt.Fprintf(w, publicPage, sess.Key)
} else {
fmt.Fprintf(w, publicPage, "anonymous")
}
}

func (s *server) loginHandler(w http.ResponseWriter, r *http.Request) {
Expand Down
15 changes: 11 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
module github.com/abraithwaite/jeff
module github.com/abraithwaite/jeff/v2

go 1.14
go 1.18

require (
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737
github.com/gomodule/redigo v1.8.5
github.com/gorilla/handlers v1.4.0
github.com/gorilla/mux v1.7.0
github.com/stretchr/testify v1.5.1
github.com/tinylib/msgp v1.1.6
github.com/stretchr/testify v1.6.1
github.com/vmihailenco/msgpack/v5 v5.3.6-0.20220405065333-233c977ae92b
)

require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
40 changes: 10 additions & 30 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,20 @@ github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZs
github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9 h1:sEvmEcJVKBNUvgCUClbUQeHOAa9U0I2Ce1BooMvVCY4=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/msgpack/v5 v5.3.6-0.20220405065333-233c977ae92b h1:xt4Y5ZlhaRjsbmgokF33LR0zTf2DiX+x9r4MbcB1GhA=
github.com/vmihailenco/msgpack/v5 v5.3.6-0.20220405065333-233c977ae92b/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
44 changes: 28 additions & 16 deletions sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import (
"context"
"crypto/rand"
"encoding/base64"
"errors"
"log"
"net/http"
"strings"
"time"

"github.com/vmihailenco/msgpack/v5"
)

// Cookie Format
Expand Down Expand Up @@ -59,10 +62,10 @@ func CookieName(n string) func(*Jeff) {
// default, this redirects to '/'. It's recommended that you replace this with
// your own.
//
// sessions := jeff.New(store, jeff.Redirect(
// http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// http.Redirect(w, r, "/login", http.StatusFound)
// })))
// sessions := jeff.New(store, jeff.Redirect(
// http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// http.Redirect(w, r, "/login", http.StatusFound)
// })))
//
// Setting this is particularly useful if you want to stop a redirect on an
// authenticated route to render a page despite the user not being
Expand Down Expand Up @@ -182,7 +185,7 @@ func (j *Jeff) wrap(redir, wrap http.Handler) http.Handler {
// Set the session cookie on the response. Call after successful
// authentication / login. meta optional parameter sets metadata in the
// session storage.
func (j *Jeff) Set(ctx context.Context, w http.ResponseWriter, key []byte, meta ...[]byte) error {
func (j *Jeff) Set(ctx context.Context, w http.ResponseWriter, key []byte, meta ...any) error {
if len(meta) > 1 {
panic("meta must not be longer than 1")
}
Expand All @@ -205,21 +208,24 @@ func (j *Jeff) Set(ctx context.Context, w http.ResponseWriter, key []byte, meta
exp = now().Add(30 * 24 * time.Hour)
}
http.SetCookie(w, c)
var m []byte
if len(meta) == 1 {
m = meta[0]
metSer, err := msgpack.Marshal(meta[0])
if err != nil {
return err
}
return j.store(ctx, Session{
Key: key,
Token: []byte(secure),
Exp: exp,
Meta: m,
Meta: metSer,
})
}

// Clear the session in the context for the given key.
func (j *Jeff) Clear(ctx context.Context, w http.ResponseWriter) error {
s := ActiveSession(ctx)
s, ok := ActiveSession(ctx)
if !ok {
return errors.New("no session found on context")
}
c := &http.Cookie{
Secure: !j.insecure,
HttpOnly: true,
Expand All @@ -242,13 +248,19 @@ func (j *Jeff) Delete(ctx context.Context, key []byte, tokens ...[]byte) error {
return j.clear(ctx, key, tokens...)
}

// SessionFromRequest returns the currently active session on the request
// context. If there is no active session on the context, it returns an empty
// session object and false.
func SessionFromRequest(r *http.Request) (Session, bool) {
return ActiveSession(r.Context())
}

// ActiveSession returns the currently active session on the context. If there
// is no active session on the context, it returns an empty session object.
func ActiveSession(ctx context.Context) Session {
if v, ok := ctx.Value(sessionKey).(Session); ok {
return v
}
return Session{}
// is no active session on the context, it returns an empty session object and
// false.
func ActiveSession(ctx context.Context) (Session, bool) {
v, ok := ctx.Value(sessionKey).(Session)
return v, ok
}

// SessionsForKey returns the list of active sessions that exist in the
Expand Down
33 changes: 23 additions & 10 deletions sessions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (
"testing"
"time"

"github.com/abraithwaite/jeff"
memcache_store "github.com/abraithwaite/jeff/memcache"
"github.com/abraithwaite/jeff/memory"
redis_store "github.com/abraithwaite/jeff/redis"
"github.com/abraithwaite/jeff/v2"
memcache_store "github.com/abraithwaite/jeff/v2/memcache"
"github.com/abraithwaite/jeff/v2/memory"
redis_store "github.com/abraithwaite/jeff/v2/redis"
"github.com/bradfitz/gomemcache/memcache"
"github.com/gomodule/redigo/redis"
"github.com/stretchr/testify/assert"
Expand All @@ -28,8 +28,14 @@ type server struct {
// email is intentionally invalid
var email = []byte("super@exa::mple.com")

const testUserAgent = "gopherz-rule"

type testMeta struct {
UserAgent string
}

func (s *server) login(w http.ResponseWriter, r *http.Request) {
err := s.j.Set(r.Context(), w, email, []byte(r.UserAgent()))
err := s.j.Set(r.Context(), w, email, testMeta{UserAgent: r.UserAgent()})
assert.NoError(s.t, err)
}

Expand All @@ -43,12 +49,18 @@ var redir = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})

func (s *server) authed(w http.ResponseWriter, r *http.Request) {
v := jeff.ActiveSession(r.Context())
v, ok := jeff.ActiveSession(r.Context())
assert.Equal(s.t, email, v.Key, "authed session should set the user on context")
if ok {
var value testMeta
err := v.Value(&value)
assert.NoError(s.t, err, "loading session value should not error")
assert.Equal(s.t, testMeta{UserAgent: testUserAgent}, value, "session should have the meta set correctly")
}
}

func (s *server) public(w http.ResponseWriter, r *http.Request) {
v := jeff.ActiveSession(r.Context())
v, _ := jeff.SessionFromRequest(r)
if string(v.Key) != "" {
s.authedPub = true
assert.Equal(s.t, email, v.Key, "authed session should set the user on context")
Expand Down Expand Up @@ -139,7 +151,7 @@ func Suite(t *testing.T, store jeff.Storage) {

t.Run("login", func(t *testing.T) {
req = httptest.NewRequest("GET", "http://example.com/login", nil)
req.Header.Set("User-Agent", "golang-user-agent")
req.Header.Set("User-Agent", testUserAgent)
w = httptest.NewRecorder()
r.ServeHTTP(w, req)
resp := w.Result()
Expand All @@ -165,6 +177,7 @@ func Suite(t *testing.T, store jeff.Storage) {

t.Run("new login", func(t *testing.T) {
req = httptest.NewRequest("GET", "http://example.com/login", nil)
req.Header.Set("User-Agent", testUserAgent)
w = httptest.NewRecorder()
r.ServeHTTP(w, req)
resp := w.Result()
Expand Down Expand Up @@ -256,7 +269,7 @@ func Suite(t *testing.T, store jeff.Storage) {

t.Run("login", func(t *testing.T) {
req = httptest.NewRequest("GET", "http://example.com/login", nil)
req.Header.Set("User-Agent", "golang-user-agent")
req.Header.Set("User-Agent", testUserAgent)
w = httptest.NewRecorder()
r.ServeHTTP(w, req)
resp := w.Result()
Expand All @@ -278,7 +291,7 @@ func Suite(t *testing.T, store jeff.Storage) {

t.Run("logout", func(t *testing.T) {
req = httptest.NewRequest("GET", "http://example.com/logout", nil)
req.Header.Set("User-Agent", "golang-user-agent")
req.Header.Set("User-Agent", testUserAgent)
req.AddCookie(cookie)
w = httptest.NewRecorder()
r.ServeHTTP(w, req)
Expand Down
8 changes: 5 additions & 3 deletions storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"crypto/subtle"
"errors"
"time"

"github.com/vmihailenco/msgpack/v5"
)

// Storage provides the base level abstraction for implementing session
Expand Down Expand Up @@ -43,7 +45,7 @@ func (j *Jeff) load(ctx context.Context, key []byte) (SessionList, error) {
return nil, err
}
var sl SessionList
_, err = sl.UnmarshalMsg(stored)
err = msgpack.Unmarshal(stored, &sl)
return sl, err
}

Expand Down Expand Up @@ -81,7 +83,7 @@ func (j *Jeff) store(ctx context.Context, s Session) error {
sl = append(sl, s)
}
sl = prune(sl)
bts, err := sl.MarshalMsg(nil)
bts, err := msgpack.Marshal(sl)
if err != nil {
return err
}
Expand Down Expand Up @@ -112,7 +114,7 @@ func (j *Jeff) clear(ctx context.Context, key []byte, tokens ...[]byte) error {

// prune expired sessions
sl = prune(sl)
bts, err := sl.MarshalMsg(nil)
bts, err := msgpack.Marshal(sl)
if err != nil {
return err
}
Expand Down
24 changes: 18 additions & 6 deletions types.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
//go:generate msgp
package jeff

import "time"
import (
"fmt"
"time"

"github.com/vmihailenco/msgpack/v5"
)

// Session represents the Session as it's stored in serialized form. It's the
// object that gets returned to the caller when checking a session.
type Session struct {
Key []byte `msg:"key"`
Token []byte `msg:"token"`
Meta []byte `msg:"meta"`
Exp time.Time `msg:"exp"`
Key []byte `msgpack:"key"`
Token []byte `msgpack:"token"`
Exp time.Time `msgpack:"exp"`
Meta []byte `msgpack:"meta"`
}

func (s Session) String() string {
return fmt.Sprintf("key: %s, token: %s, exp: %v, meta: %s", string(s.Key), string(s.Token), s.Exp, s.Meta)
}

func (s *Session) Value(v interface{}) error {
return msgpack.Unmarshal(s.Meta, v)
}

// SessionList is a list of active sessions for a given key
Expand Down
Loading

0 comments on commit 53ff082

Please sign in to comment.