package session import ( "net/http" "os" "strings" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) // SessionMiddleware extracts session from request and adds to context func SessionMiddleware(pgPool *pgxpool.Pool) gin.HandlerFunc { store := GetSessionStore(pgPool) return func(c *gin.Context) { sessionID := extractSessionID(c) if sessionID != "" { session, err := store.GetSession(c.Request.Context(), sessionID) if err == nil && session != nil { // Add session to context c.Set("session", session) c.Set("session_id", session.SessionID) c.Set("user_id", session.UserID) c.Set("email", session.Email) c.Set("user_type", string(session.UserType)) c.Set("roles", session.Roles) c.Set("permissions", session.Permissions) if session.TenantID != nil { c.Set("tenant_id", *session.TenantID) } } } c.Next() } } // extractSessionID extracts session ID from request func extractSessionID(c *gin.Context) string { // Try Authorization header first authHeader := c.GetHeader("Authorization") if strings.HasPrefix(authHeader, "Bearer ") { return strings.TrimPrefix(authHeader, "Bearer ") } // Try X-Session-ID header if sessionID := c.GetHeader("X-Session-ID"); sessionID != "" { return sessionID } // Try cookie if cookie, err := c.Cookie("session_id"); err == nil { return cookie } return "" } // RequireSession requires a valid session func RequireSession() gin.HandlerFunc { return func(c *gin.Context) { session := GetSession(c) if session == nil { // Check development bypass if os.Getenv("ENVIRONMENT") == "development" { // Set demo session demoSession := getDemoSession() c.Set("session", demoSession) c.Set("session_id", demoSession.SessionID) c.Set("user_id", demoSession.UserID) c.Set("email", demoSession.Email) c.Set("user_type", string(demoSession.UserType)) c.Set("roles", demoSession.Roles) c.Set("permissions", demoSession.Permissions) c.Next() return } c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ "error": "unauthorized", "message": "Authentication required", }) return } c.Next() } } // GetSession retrieves session from context func GetSession(c *gin.Context) *Session { if session, exists := c.Get("session"); exists { if s, ok := session.(*Session); ok { return s } } return nil } // GetSessionUserID retrieves user ID from session context func GetSessionUserID(c *gin.Context) (uuid.UUID, error) { userIDStr, exists := c.Get("user_id") if !exists { return uuid.Nil, nil } return uuid.Parse(userIDStr.(string)) } // GetSessionEmail retrieves email from session context func GetSessionEmail(c *gin.Context) string { email, exists := c.Get("email") if !exists { return "" } return email.(string) } // GetSessionUserType retrieves user type from session context func GetSessionUserType(c *gin.Context) UserType { userType, exists := c.Get("user_type") if !exists { return UserTypeCustomer } return UserType(userType.(string)) } // GetSessionRoles retrieves roles from session context func GetSessionRoles(c *gin.Context) []string { roles, exists := c.Get("roles") if !exists { return nil } if r, ok := roles.([]string); ok { return r } return nil } // GetSessionPermissions retrieves permissions from session context func GetSessionPermissions(c *gin.Context) []string { perms, exists := c.Get("permissions") if !exists { return nil } if p, ok := perms.([]string); ok { return p } return nil } // GetSessionTenantID retrieves tenant ID from session context func GetSessionTenantID(c *gin.Context) *string { tenantID, exists := c.Get("tenant_id") if !exists { return nil } if t, ok := tenantID.(string); ok { return &t } return nil } // getDemoSession returns a demo session for development func getDemoSession() *Session { tenantID := "a0000000-0000-0000-0000-000000000001" ip := "127.0.0.1" ua := "Development" return &Session{ SessionID: "demo-session-id", UserID: "10000000-0000-0000-0000-000000000024", Email: "demo@breakpilot.app", UserType: UserTypeEmployee, Roles: []string{ "admin", "schul_admin", "teacher", }, Permissions: []string{ "grades:read", "grades:write", "attendance:read", "attendance:write", "students:read", "students:write", "reports:generate", "consent:admin", "own_data:read", "users:manage", }, TenantID: &tenantID, IPAddress: &ip, UserAgent: &ua, CreatedAt: time.Now().UTC(), LastActivityAt: time.Now().UTC(), } }