129 lines
2.8 KiB
Go
129 lines
2.8 KiB
Go
package session
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"go.neonxp.ru/mux"
|
|
"go.neonxp.ru/objectid"
|
|
)
|
|
|
|
type Config struct {
|
|
SessionCookie string
|
|
Path string
|
|
Domain string
|
|
Secure bool
|
|
HttpOnly bool
|
|
MaxAge time.Duration
|
|
}
|
|
|
|
var DefaultConfig Config = Config{
|
|
SessionCookie: "_session",
|
|
Path: "/",
|
|
Domain: "",
|
|
Secure: false,
|
|
HttpOnly: true,
|
|
MaxAge: 365 * 24 * time.Hour,
|
|
}
|
|
|
|
var (
|
|
ErrSessionNotFound = errors.New("session not found")
|
|
ErrNoSessionInContext = errors.New("no session in context")
|
|
)
|
|
|
|
type SessionManager struct {
|
|
config *Config
|
|
storer Store
|
|
}
|
|
|
|
func New(storer Store) *SessionManager {
|
|
return NewWithConfig(&DefaultConfig, storer)
|
|
}
|
|
|
|
func NewWithConfig(config *Config, storer Store) *SessionManager {
|
|
return &SessionManager{
|
|
config: config,
|
|
storer: storer,
|
|
}
|
|
}
|
|
|
|
func (s *SessionManager) Middleware() mux.Middleware {
|
|
return func(h http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var (
|
|
sessionID string
|
|
values Values
|
|
)
|
|
cookie, err := r.Cookie(s.config.SessionCookie)
|
|
switch {
|
|
case err == nil:
|
|
sessionID = cookie.Value
|
|
values = s.storer.Load(sessionID)
|
|
case errors.Is(err, http.ErrNoCookie):
|
|
sessionID = objectid.New().String()
|
|
}
|
|
|
|
ctx := context.WithValue(r.Context(), sessionManagerKey, s)
|
|
ctx = context.WithValue(ctx, sessionIDKey, sessionID)
|
|
ctx = context.WithValue(ctx, sessionValueKey, values)
|
|
|
|
h.ServeHTTP(w, r.WithContext(ctx))
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s *SessionManager) Values(ctx context.Context) Values {
|
|
aValue := ctx.Value(sessionValueKey)
|
|
values, ok := aValue.(Values)
|
|
if !ok || values == nil {
|
|
values = Values{}
|
|
}
|
|
|
|
return values
|
|
}
|
|
|
|
func (s *SessionManager) Save(w http.ResponseWriter, r *http.Request, values Values) error {
|
|
aSessionID := r.Context().Value(sessionIDKey)
|
|
sessionID, ok := aSessionID.(string)
|
|
if !ok {
|
|
return ErrNoSessionInContext
|
|
}
|
|
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: s.config.SessionCookie,
|
|
Value: sessionID,
|
|
Path: s.config.Path,
|
|
Domain: s.config.Domain,
|
|
Secure: s.config.Secure,
|
|
HttpOnly: s.config.HttpOnly,
|
|
MaxAge: int(s.config.MaxAge.Seconds()),
|
|
})
|
|
|
|
return s.storer.Save(sessionID, values)
|
|
}
|
|
func (s *SessionManager) Clear(w http.ResponseWriter, r *http.Request) error {
|
|
aSessionID := r.Context().Value(sessionIDKey)
|
|
sessionID, ok := aSessionID.(string)
|
|
if !ok {
|
|
return ErrNoSessionInContext
|
|
}
|
|
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: s.config.SessionCookie,
|
|
Value: sessionID,
|
|
Path: s.config.Path,
|
|
Domain: s.config.Domain,
|
|
Secure: s.config.Secure,
|
|
HttpOnly: s.config.HttpOnly,
|
|
MaxAge: -1,
|
|
})
|
|
|
|
return s.storer.Remove(sessionID)
|
|
}
|
|
|
|
func FromRequest(r *http.Request) *SessionManager {
|
|
return r.Context().Value(sessionManagerKey).(*SessionManager)
|
|
}
|