Initial Codebase (untested)
This commit is contained in:
166
internal/session/manager.go
Normal file
166
internal/session/manager.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/andreadipersio/securecookie"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Key ...
|
||||
type Key int
|
||||
|
||||
const (
|
||||
SessionKey Key = iota
|
||||
)
|
||||
|
||||
// Options ...
|
||||
type Options struct {
|
||||
name string
|
||||
secret string
|
||||
secure bool
|
||||
expiry time.Duration
|
||||
}
|
||||
|
||||
// NewOptions ...
|
||||
func NewOptions(name, secret string, secure bool, expiry time.Duration) *Options {
|
||||
return &Options{name, secret, secure, expiry}
|
||||
}
|
||||
|
||||
// Manager ...
|
||||
type Manager struct {
|
||||
options *Options
|
||||
store Store
|
||||
}
|
||||
|
||||
// NewManager ...
|
||||
func NewManager(options *Options, store Store) *Manager {
|
||||
return &Manager{options, store}
|
||||
}
|
||||
|
||||
// Create ...
|
||||
func (m *Manager) Create(w http.ResponseWriter) (*Session, error) {
|
||||
sid, err := NewSessionID(m.options.secret)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error creating new session")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: m.options.name,
|
||||
Value: sid.String(),
|
||||
Path: "/",
|
||||
Secure: m.options.secure,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
MaxAge: int(m.options.expiry.Seconds()),
|
||||
Expires: time.Now().Add(m.options.expiry),
|
||||
}
|
||||
|
||||
securecookie.SetSecureCookie(w, m.options.secret, cookie)
|
||||
|
||||
return &Session{
|
||||
store: m.store,
|
||||
|
||||
ID: sid.String(),
|
||||
Data: make(Map),
|
||||
CreatedAt: time.Now(),
|
||||
ExpiresAt: time.Now().Add(m.options.expiry),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Validate ....
|
||||
func (m *Manager) Validate(value string) (ID, error) {
|
||||
sessionID, err := ValidateSessionID(value, m.options.secret)
|
||||
return sessionID, err
|
||||
}
|
||||
|
||||
// GetOrCreate ...
|
||||
func (m *Manager) GetOrCreate(w http.ResponseWriter, r *http.Request) (*Session, error) {
|
||||
cookie, err := securecookie.GetSecureCookie(
|
||||
r,
|
||||
m.options.secret,
|
||||
m.options.name,
|
||||
)
|
||||
if err != nil {
|
||||
sess, err := m.Create(w)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error creating new session")
|
||||
return nil, err
|
||||
}
|
||||
if err = m.store.SetSession(sess.ID, sess); err != nil {
|
||||
log.WithError(err).Errorf("error creating new session for %s", sess.ID)
|
||||
return nil, err
|
||||
}
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
sid, err := m.Validate(cookie.Value)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error validating seesion")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sess, err := m.store.GetSession(sid.String())
|
||||
if err != nil {
|
||||
if err == ErrSessionNotFound {
|
||||
log.WithError(err).Warnf("no session found for %s (creating new one)", sid)
|
||||
m.Delete(w, r)
|
||||
|
||||
sess, err := m.Create(w)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("error creating new session")
|
||||
return nil, err
|
||||
}
|
||||
if err = m.store.SetSession(sess.ID, sess); err != nil {
|
||||
log.WithError(err).Errorf("error creating new session for %s", sess.ID)
|
||||
return nil, err
|
||||
}
|
||||
return sess, nil
|
||||
}
|
||||
log.WithError(err).Errorf("error loading session for %s", sid)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (m *Manager) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
if sess := r.Context().Value(SessionKey); sess != nil {
|
||||
sess := sess.(*Session)
|
||||
if err := m.store.DelSession(sess.ID); err != nil {
|
||||
log.WithError(err).Warnf("error deleting session %s", sess.ID)
|
||||
}
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: m.options.name,
|
||||
Value: "",
|
||||
Secure: m.options.secure,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
MaxAge: -1,
|
||||
Expires: time.Now(),
|
||||
}
|
||||
|
||||
securecookie.SetSecureCookie(w, m.options.secret, cookie)
|
||||
}
|
||||
|
||||
// Handler ...
|
||||
func (m *Manager) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
sess, err := m.GetOrCreate(w, r)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("session error")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), SessionKey, sess)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user