Skip to content

Commit

Permalink
Merge pull request #29 from vividvilla/v2
Browse files Browse the repository at this point in the history
Remove `simplession.Session` dependency from `Store` interface{} to /v2.
  • Loading branch information
vividvilla authored May 9, 2024
2 parents 992d285 + 8aeddf1 commit 3cc22f9
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 3cc22f9

Please sign in to comment.