Add ability to set cookie max age - issue #91

This commit is contained in:
2025-11-12 12:46:41 +00:00
parent ccc6f76b0e
commit e453a15922
22 changed files with 195 additions and 93 deletions
+3
View File
@@ -50,6 +50,7 @@ func TestCookiePrefix(t *testing.T) {
false,
"",
tt.cookiePrefix,
0,
logger,
chunkManager,
)
@@ -87,6 +88,7 @@ func TestMultipleInstancesWithDifferentPrefixes(t *testing.T) {
false,
"example.com",
"_oidc_app1_",
0,
logger,
chunkManager1,
)
@@ -99,6 +101,7 @@ func TestMultipleInstancesWithDifferentPrefixes(t *testing.T) {
false,
"example.com",
"_oidc_app2_",
0,
logger,
chunkManager2,
)
+24 -17
View File
@@ -18,15 +18,16 @@ const (
// SessionManager handles session creation, management and cleanup
type SessionManager struct {
sessionPool sync.Pool
store sessions.Store
logger Logger
chunkManager ChunkManager
cookieDomain string
cookiePrefix string // Prefix for cookie names (default: "_oidc_raczylo_")
cleanupMutex sync.RWMutex
forceHTTPS bool
cleanupDone bool
sessionPool sync.Pool
store sessions.Store
logger Logger
chunkManager ChunkManager
cookieDomain string
cookiePrefix string // Prefix for cookie names (default: "_oidc_raczylo_")
sessionMaxAge time.Duration // Maximum session age (default: 24 hours)
cleanupMutex sync.RWMutex
forceHTTPS bool
cleanupDone bool
}
// Logger interface for dependency injection
@@ -70,7 +71,7 @@ type SessionData interface {
// NewSessionManager creates a new SessionManager instance with secure defaults.
// It initializes the cookie store with encryption, sets up session pooling,
// and configures chunk management for large tokens.
func NewSessionManager(encryptionKey string, forceHTTPS bool, cookieDomain string, cookiePrefix string, logger Logger, chunkManager ChunkManager) (*SessionManager, error) {
func NewSessionManager(encryptionKey string, forceHTTPS bool, cookieDomain string, cookiePrefix string, sessionMaxAge time.Duration, logger Logger, chunkManager ChunkManager) (*SessionManager, error) {
if len(encryptionKey) < minEncryptionKeyLength {
return nil, fmt.Errorf("encryption key must be at least %d bytes long", minEncryptionKeyLength)
}
@@ -80,13 +81,19 @@ func NewSessionManager(encryptionKey string, forceHTTPS bool, cookieDomain strin
cookiePrefix = "_oidc_raczylo_"
}
// Set default session max age if not provided (24 hours for backward compatibility)
if sessionMaxAge == 0 {
sessionMaxAge = absoluteSessionTimeout
}
sm := &SessionManager{
store: sessions.NewCookieStore([]byte(encryptionKey)),
forceHTTPS: forceHTTPS,
cookieDomain: cookieDomain,
cookiePrefix: cookiePrefix,
logger: logger,
chunkManager: chunkManager,
store: sessions.NewCookieStore([]byte(encryptionKey)),
forceHTTPS: forceHTTPS,
cookieDomain: cookieDomain,
cookiePrefix: cookiePrefix,
sessionMaxAge: sessionMaxAge,
logger: logger,
chunkManager: chunkManager,
}
sm.sessionPool.New = func() interface{} {
@@ -322,7 +329,7 @@ func (sm *SessionManager) getSessionOptions(isSecure bool) *sessions.Options {
return &sessions.Options{
Path: "/",
Domain: sm.cookieDomain,
MaxAge: int(absoluteSessionTimeout.Seconds()),
MaxAge: int(sm.sessionMaxAge.Seconds()),
Secure: isSecure,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
+14 -14
View File
@@ -165,7 +165,7 @@ func TestSessionManagerCreation(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager(tt.encryptionKey, false, "", "", logger, chunkManager)
sm, err := NewSessionManager(tt.encryptionKey, false, "", "", 0, logger, chunkManager)
if tt.expectError {
if err == nil {
@@ -200,7 +200,7 @@ func TestSessionManagerCreation(t *testing.T) {
func TestSessionManagerPoolBehavior(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", logger, chunkManager)
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -291,7 +291,7 @@ func TestSessionManagerPoolBehavior(t *testing.T) {
func TestSessionManagerErrorHandling(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", logger, chunkManager)
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -390,7 +390,7 @@ func TestSessionManagerCleanup(t *testing.T) {
logger := &MockLogger{}
mockChunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", logger, mockChunkManager)
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", 0, logger, mockChunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -458,7 +458,7 @@ func TestSessionManagerHTTPSBehavior(t *testing.T) {
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef",
tt.forceHTTPS, "", "", logger, chunkManager)
tt.forceHTTPS, "", "", 0, logger, chunkManager)
if tt.expectError {
if err == nil {
@@ -520,7 +520,7 @@ func TestSessionManagerCookieDomain(t *testing.T) {
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef",
false, tt.cookieDomain, "", logger, chunkManager)
false, tt.cookieDomain, "", 0, logger, chunkManager)
if err != nil {
t.Errorf("Unexpected error for %s: %v", tt.description, err)
@@ -549,7 +549,7 @@ func BenchmarkSessionManagerCreation(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
sm, err := NewSessionManager(encryptionKey, false, "", "", logger, chunkManager)
sm, err := NewSessionManager(encryptionKey, false, "", "", 0, logger, chunkManager)
if err != nil {
b.Fatalf("Failed to create session manager: %v", err)
}
@@ -561,7 +561,7 @@ func BenchmarkSessionManagerCreation(b *testing.B) {
func BenchmarkSessionManagerGetSession(b *testing.B) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", logger, chunkManager)
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", 0, logger, chunkManager)
if err != nil {
b.Fatalf("Failed to create session manager: %v", err)
}
@@ -599,7 +599,7 @@ func minInt(a, b int) int {
func TestValidateSessionHealth(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", logger, chunkManager)
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -660,7 +660,7 @@ func TestValidateSessionHealth(t *testing.T) {
func TestValidateTokenFormat(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", logger, chunkManager)
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -727,7 +727,7 @@ func TestValidateTokenFormat(t *testing.T) {
func TestDetectSessionTampering(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", logger, chunkManager)
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef", false, "", "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -812,7 +812,7 @@ func TestGetSessionMetrics(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef",
tt.forceHTTPS, tt.cookieDomain, "", logger, chunkManager)
tt.forceHTTPS, tt.cookieDomain, "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -898,7 +898,7 @@ func TestShouldUseSecureCookies(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef",
tt.forceHTTPS, "", "", logger, chunkManager)
tt.forceHTTPS, "", "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}
@@ -940,7 +940,7 @@ func TestGetSessionOptions(t *testing.T) {
logger := &MockLogger{}
chunkManager := &MockChunkManager{}
sm, err := NewSessionManager("0123456789abcdef0123456789abcdef0123456789abcdef",
false, tt.cookieDomain, "", logger, chunkManager)
false, tt.cookieDomain, "", 0, logger, chunkManager)
if err != nil {
t.Fatalf("Failed to create session manager: %v", err)
}