Skip to content

Commit

Permalink
Remove simplession.Session dependency from Store interface{} to /v2.
Browse files Browse the repository at this point in the history
This is a breaking change. It removes the superfluous dependency
stores had on the simplesessions package solely for accessing string
generation functions, which are not needed in sessions. ID generation
is the purview of individual store implementations.

It also adds a go.mod to the `conv` package as its referenced by
several stores.
  • Loading branch information
knadh committed May 9, 2024
1 parent 992d285 commit 8aeddf1
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 137 deletions.
5 changes: 5 additions & 0 deletions conv/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/vividvilla/simplesessions/conv

go 1.14

require github.com/stretchr/testify v1.9.0
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/vividvilla/simplesessions
module github.com/vividvilla/simplesessions/v2

require github.com/stretchr/testify v1.9.0

Expand Down
70 changes: 18 additions & 52 deletions session.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package simplesessions

import (
"crypto/rand"
"errors"
"net/http"
"time"
"unicode"
)

// Session is utility for get, set or clear session.
Expand Down Expand Up @@ -34,20 +32,25 @@ type Session struct {
var (
// ErrInvalidSession is raised when session is tried to access before setting it or its not set in store.
// Handle this and create new session.
// Store code = 1
ErrInvalidSession = errors.New("simplesession: invalid session")

// ErrFieldNotFound is raised when given key is not found in store
// Store code = 2
ErrFieldNotFound = errors.New("simplesession: session field not found in store")

// ErrAssertType is raised when type assertion fails
// Store code = 3
ErrAssertType = errors.New("simplesession: invalid type assertion")

// ErrNil is raised when returned value is nil.
// Store code = 4
ErrNil = errors.New("simplesession: nil returned")
// Dictionary for generating random string
dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
)

// NewSession creates a new session. Reads cookie info from `GetCookie`` callback
// NewSession creates a new session. Reads cookie info from `GetCookie callback
// and validate the session with current store. If cookie not set then it creates
// new session and calls `SetCookie`` callback. If `DisableAutoSet` is set then it
// new session and calls `SetCookie callback. If `DisableAutoSet` is set then it
// skips new session creation and should be manually done using `Create` method.
// If a cookie is found but its invalid in store then `ErrInvalidSession` error is returned.
func NewSession(m *Manager, r, w interface{}) (*Session, error) {
Expand All @@ -73,7 +76,7 @@ func NewSession(m *Manager, r, w interface{}) (*Session, error) {

// Create new cookie in store and write to front
// Store also calls `WriteCookie`` to write to http interface
cv, err := m.store.Create(sess)
cv, err := m.store.Create()
if err != nil {
return nil, err
}
Expand All @@ -89,49 +92,12 @@ func NewSession(m *Manager, r, w interface{}) (*Session, error) {
return nil, err
}

if isValid, err := m.store.IsValid(sess, sess.cookie.Value); err != nil {
return nil, err
} else if !isValid {
return nil, ErrInvalidSession
}

// Set isSet flag
sess.isSet = true

return sess, nil
}

// GenerateRandomString is a utility method which can be used by store to
// generate cryptographically random alphanumeric string of length n.
func (s *Session) GenerateRandomString(n int) (string, error) {
var bytes = make([]byte, n)
if _, err := rand.Read(bytes); err != nil {
return "", err
}

for k, v := range bytes {
bytes[k] = dictionary[v%byte(len(dictionary))]
}

return string(bytes), nil
}

// IsValidRandomString validates the random string generated by `GenerateRandomString` method.
func (s *Session) IsValidRandomString(val string) bool {
return s.isAlphaNum(val)
}

// isAlphaNum checks if the provided string is Alphanumeric
func (s *Session) isAlphaNum(val string) bool {
for _, r := range val {
if !unicode.IsDigit(r) && !unicode.IsLetter(r) {
return false
}
}

return true
}

// WriteCookie updates the cookie and calls `SetCookie` callback.
// This method can also be used by store to update cookie whenever the cookie value changes.
func (s *Session) WriteCookie(cv string) error {
Expand Down Expand Up @@ -171,7 +137,7 @@ func (s *Session) clearCookie() error {
// else session has to be manually created before setting or getting values.
func (s *Session) Create() error {
// Create new cookie in store and write to front.
cv, err := s.manager.store.Create(s)
cv, err := s.manager.store.Create()
if err != nil {
return err
}
Expand Down Expand Up @@ -221,7 +187,7 @@ func (s *Session) GetAll() (map[string]interface{}, error) {
return s.values, nil
}

return s.manager.store.GetAll(s, s.cookie.Value)
return s.manager.store.GetAll(s.cookie.Value)
}

// GetMulti gets a map of values for multiple session keys.
Expand All @@ -243,7 +209,7 @@ func (s *Session) GetMulti(keys ...string) (map[string]interface{}, error) {
return vals, nil
}

return s.manager.store.GetMulti(s, s.cookie.Value, keys...)
return s.manager.store.GetMulti(s.cookie.Value, keys...)
}

// Get gets a value for given key in session.
Expand All @@ -263,7 +229,7 @@ func (s *Session) Get(key string) (interface{}, error) {
}

// Get from backend if not found in previous step
return s.manager.store.Get(s, s.cookie.Value, key)
return s.manager.store.Get(s.cookie.Value, key)
}

// Set sets a value for given key in session. Its up to store to commit
Expand All @@ -274,7 +240,7 @@ func (s *Session) Set(key string, val interface{}) error {
return ErrInvalidSession
}

return s.manager.store.Set(s, s.cookie.Value, key, val)
return s.manager.store.Set(s.cookie.Value, key, val)
}

// Commit commits all set to store. Its up to store to commit
Expand All @@ -285,7 +251,7 @@ func (s *Session) Commit() error {
return ErrInvalidSession
}

return s.manager.store.Commit(s, s.cookie.Value)
return s.manager.store.Commit(s.cookie.Value)
}

// Delete deletes a field from session.
Expand All @@ -295,7 +261,7 @@ func (s *Session) Delete(key string) error {
return ErrInvalidSession
}

return s.manager.store.Delete(s, s.cookie.Value, key)
return s.manager.store.Delete(s.cookie.Value, key)
}

// Clear clears session data from store and clears the cookie
Expand All @@ -305,7 +271,7 @@ func (s *Session) Clear() error {
return ErrInvalidSession
}

if err := s.manager.store.Clear(s, s.cookie.Value); err != nil {
if err := s.manager.store.Clear(s.cookie.Value); err != nil {
return err
}

Expand Down
61 changes: 0 additions & 61 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package simplesessions

import (
"errors"
"math/rand"
"net/http"
"testing"
"time"
Expand Down Expand Up @@ -83,43 +82,6 @@ func TestSessionHelpers(t *testing.T) {
assert.Error(err, "test error")
}

func TestSessionGenerateRandomString(t *testing.T) {
assert := assert.New(t)
sess := Session{}

// Create random length string
rand.Seed(time.Now().Unix())
strLen := rand.Intn(1000-1) + 1
randStr, err := sess.GenerateRandomString(strLen)
assert.NoError(err)
assert.Equal(strLen, len(randStr))

// Check if it doesn't generate same id
randStr1, err := sess.GenerateRandomString(100)
assert.NoError(err)
randStr2, err := sess.GenerateRandomString(100)
assert.NoError(err)
assert.NotEqual(randStr1, randStr2)
}

func TestSessionisAlphaNum(t *testing.T) {
assert := assert.New(t)
sess := Session{}

assert.Equal(sess.isAlphaNum("thisisvalidstring"), true)
assert.Equal(sess.isAlphaNum("thisisNot$ a .validstring"), false)
}

func TestSessionIsValidRandomString(t *testing.T) {
assert := assert.New(t)
sess := Session{}

randStr, err := sess.GenerateRandomString(100)
assert.NoError(err)
assert.Equal(sess.IsValidRandomString(randStr), true)
assert.Equal(sess.IsValidRandomString(dictionary), true)
}

func TestSessionNewSession(t *testing.T) {
reader := "some reader"
writer := "some writer"
Expand All @@ -140,16 +102,6 @@ func TestSessionNewSession(t *testing.T) {
assert.True(sess.isSet)
}

func TestSessionNewSessionInvalidSession(t *testing.T) {
assert := assert.New(t)
mockStore := newMockStore()
mockManager := newMockManager(mockStore)

sess, err := NewSession(mockManager, nil, nil)
assert.Error(err, ErrInvalidSession.Error())
assert.Nil(sess)
}

func TestSessionNewSessionErrorStoreCreate(t *testing.T) {
assert := assert.New(t)
mockStore := newMockStore()
Expand Down Expand Up @@ -190,19 +142,6 @@ func TestSessionNewSessionErrorWriteCookie(t *testing.T) {
assert.Nil(sess)
}

func TestSessionNewSessionErrorStoreIsValid(t *testing.T) {
assert := assert.New(t)
mockStore := newMockStore()
mockManager := newMockManager(mockStore)

testError := errors.New("this is test error")
mockStore.err = testError

sess, err := NewSession(mockManager, nil, nil)
assert.Error(err, testError.Error())
assert.Nil(sess)
}

func TestSessionNewSessionInvalidGetCookie(t *testing.T) {
assert := assert.New(t)
mockStore := newMockStore()
Expand Down
19 changes: 8 additions & 11 deletions store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,30 @@ package simplesessions
// Store represents store interface. This interface can be
// implemented to create various backend stores for session.
type Store interface {
// IsExist checks if session is set in store.
IsValid(session *Session, cookieValue string) (isExist bool, err error)

// Create creates new session in store and returns the cookie value.
Create(session *Session) (cookieValue string, err error)
Create() (cookieValue string, err error)

// Get gets a value for given key from session.
Get(session *Session, cookieValue, key string) (value interface{}, err error)
Get(cookieValue, key string) (value interface{}, err error)

// GetMulti gets a maps of multiple values for given keys.
GetMulti(session *Session, cookieValue string, keys ...string) (values map[string]interface{}, err error)
GetMulti(cookieValue string, keys ...string) (values map[string]interface{}, err error)

// GetAll gets all key and value from session,
GetAll(session *Session, cookieValue string) (values map[string]interface{}, err error)
GetAll(cookieValue string) (values map[string]interface{}, err error)

// Set sets an value for a field in session.
// Its up to store to either store it in session right after set or after commit.
Set(session *Session, cookieValue, key string, value interface{}) error
Set(cookieValue, key string, value interface{}) error

// Commit commits all the previously set values to store.
Commit(session *Session, cookieValue string) error
Commit(cookieValue string) error

// Delete a field from session.
Delete(session *Session, cookieValue string, key string) error
Delete(cookieValue string, key string) error

// Clear clears the session key from backend if exists.
Clear(session *Session, cookieValue string) error
Clear(cookieValue string) error

// Helper method for typecasting/asserting.
Int(interface{}, error) (int, error)
Expand Down
20 changes: 8 additions & 12 deletions store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,42 @@ func (s *MockStore) reset() {
s.isCommited = false
}

func (s *MockStore) IsValid(ss *Session, cv string) (isExist bool, err error) {
return s.isValid, s.err
}

func (s *MockStore) Create(ss *Session) (cv string, err error) {
func (s *MockStore) Create() (cv string, err error) {
return s.val.(string), s.err
}

func (s *MockStore) Get(ss *Session, cv, key string) (value interface{}, err error) {
func (s *MockStore) Get(cv, key string) (value interface{}, err error) {
return s.val, s.err
}

func (s *MockStore) GetMulti(ss *Session, cv string, keys ...string) (values map[string]interface{}, err error) {
func (s *MockStore) GetMulti(cv string, keys ...string) (values map[string]interface{}, err error) {
vals := make(map[string]interface{})
vals["val"] = s.val
return vals, s.err
}

func (s *MockStore) GetAll(ss *Session, cv string) (values map[string]interface{}, err error) {
func (s *MockStore) GetAll(cv string) (values map[string]interface{}, err error) {
vals := make(map[string]interface{})
vals["val"] = s.val
return vals, s.err
}

func (s *MockStore) Set(ss *Session, cv, key string, value interface{}) error {
func (s *MockStore) Set(cv, key string, value interface{}) error {
s.val = value
return s.err
}

func (s *MockStore) Commit(ss *Session, cv string) error {
func (s *MockStore) Commit(cv string) error {
s.isCommited = true
return s.err
}

func (s *MockStore) Delete(ss *Session, cv string, key string) error {
func (s *MockStore) Delete(cv string, key string) error {
s.val = nil
return s.err
}

func (s *MockStore) Clear(ss *Session, cv string) error {
func (s *MockStore) Clear(cv string) error {
s.val = nil
return s.err
}
Expand Down

0 comments on commit 8aeddf1

Please sign in to comment.