Files
Pulse/internal/api/session_oidc_test.go
rcourtman 668cdf3393 feat(license): add audit_logging, advanced_sso, advanced_reporting to Pro tier
Major changes:
- Add audit_logging, advanced_sso, advanced_reporting features to Pro tier
- Persist session username for RBAC authorization after restart
- Add hot-dev auto-detection for pulse-pro binary (enables SQLite audit logging)

Frontend improvements:
- Replace isEnterprise() with hasFeature() for granular feature gating
- Update AuditLogPanel, OIDCPanel, RolesPanel, UserAssignmentsPanel, AISettings
- Update AuditWebhookPanel to use hasFeature('audit_logging')

Backend changes:
- Session store now persists and restores username field
- Update CreateSession/CreateOIDCSession to accept username parameter
- GetSessionUsername falls back to persisted username after restart

Testing:
- Update license_test.go to reflect Pro tier feature changes
- Update session tests for new username parameter
2026-01-10 12:55:02 +00:00

201 lines
5.7 KiB
Go

package api
import (
"os"
"path/filepath"
"testing"
"time"
)
func TestCreateOIDCSession(t *testing.T) {
// Create a temporary directory for test sessions
tmpDir, err := os.MkdirTemp("", "pulse-session-test-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
store := NewSessionStore(tmpDir)
// Create an OIDC session with token info
token := "test-session-token-123"
oidcInfo := &OIDCTokenInfo{
RefreshToken: "test-refresh-token",
AccessTokenExp: time.Now().Add(1 * time.Hour),
Issuer: "https://example.com",
ClientID: "test-client-id",
}
store.CreateOIDCSession(token, 24*time.Hour, "TestAgent", "127.0.0.1", "testuser", oidcInfo)
// Verify session was created
if !store.ValidateSession(token) {
t.Error("Created OIDC session should be valid")
}
// Verify OIDC token info was stored
session := store.GetSession(token)
if session == nil {
t.Fatal("GetSession returned nil for valid session")
}
if session.OIDCRefreshToken != oidcInfo.RefreshToken {
t.Errorf("RefreshToken mismatch: got %q, want %q", session.OIDCRefreshToken, oidcInfo.RefreshToken)
}
if session.OIDCIssuer != oidcInfo.Issuer {
t.Errorf("Issuer mismatch: got %q, want %q", session.OIDCIssuer, oidcInfo.Issuer)
}
if session.OIDCClientID != oidcInfo.ClientID {
t.Errorf("ClientID mismatch: got %q, want %q", session.OIDCClientID, oidcInfo.ClientID)
}
if session.OIDCAccessTokenExp.IsZero() {
t.Error("AccessTokenExp should not be zero")
}
}
func TestUpdateOIDCTokens(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "pulse-session-test-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
store := NewSessionStore(tmpDir)
token := "test-session-token-456"
originalExpiry := time.Now().Add(1 * time.Hour)
oidcInfo := &OIDCTokenInfo{
RefreshToken: "original-refresh-token",
AccessTokenExp: originalExpiry,
Issuer: "https://example.com",
ClientID: "test-client-id",
}
store.CreateOIDCSession(token, 24*time.Hour, "TestAgent", "127.0.0.1", "testuser", oidcInfo)
// Update the tokens (simulating a refresh)
newExpiry := time.Now().Add(2 * time.Hour)
store.UpdateOIDCTokens(token, "new-refresh-token", newExpiry)
// Verify tokens were updated
session := store.GetSession(token)
if session == nil {
t.Fatal("GetSession returned nil for valid session")
}
if session.OIDCRefreshToken != "new-refresh-token" {
t.Errorf("RefreshToken should be updated: got %q, want %q", session.OIDCRefreshToken, "new-refresh-token")
}
if !session.OIDCAccessTokenExp.After(originalExpiry) {
t.Error("AccessTokenExp should be updated to new expiry")
}
}
func TestOIDCSessionPersistence(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "pulse-session-test-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
token := "test-session-token-persist"
oidcInfo := &OIDCTokenInfo{
RefreshToken: "persist-refresh-token",
AccessTokenExp: time.Now().Add(1 * time.Hour),
Issuer: "https://example.com",
ClientID: "test-client-id",
}
// Create session in first store instance
store1 := NewSessionStore(tmpDir)
store1.CreateOIDCSession(token, 24*time.Hour, "TestAgent", "127.0.0.1", "testuser", oidcInfo)
// Verify file was written
sessionFile := filepath.Join(tmpDir, "sessions.json")
if _, err := os.Stat(sessionFile); os.IsNotExist(err) {
t.Fatal("Session file should exist after creating session")
}
// Create new store instance (simulates restart)
store2 := NewSessionStore(tmpDir)
// Verify session was loaded with OIDC info
session := store2.GetSession(token)
if session == nil {
t.Fatal("Session should be restored after reload")
}
if session.OIDCRefreshToken != oidcInfo.RefreshToken {
t.Errorf("RefreshToken should persist: got %q, want %q", session.OIDCRefreshToken, oidcInfo.RefreshToken)
}
if session.OIDCIssuer != oidcInfo.Issuer {
t.Errorf("Issuer should persist: got %q, want %q", session.OIDCIssuer, oidcInfo.Issuer)
}
}
func TestCreateOIDCSession_NilTokenInfo(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "pulse-session-test-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
store := NewSessionStore(tmpDir)
// Create OIDC session with nil token info (no refresh token available)
token := "test-session-no-refresh"
store.CreateOIDCSession(token, 24*time.Hour, "TestAgent", "127.0.0.1", "testuser", nil)
// Verify session was created
if !store.ValidateSession(token) {
t.Error("Session should be valid even without OIDC tokens")
}
session := store.GetSession(token)
if session == nil {
t.Fatal("GetSession returned nil")
}
if session.OIDCRefreshToken != "" {
t.Errorf("RefreshToken should be empty when nil info passed: got %q", session.OIDCRefreshToken)
}
}
func TestInvalidateSession(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "pulse-session-test-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
store := NewSessionStore(tmpDir)
token := "test-session-invalidate"
oidcInfo := &OIDCTokenInfo{
RefreshToken: "refresh-token",
AccessTokenExp: time.Now().Add(1 * time.Hour),
Issuer: "https://example.com",
ClientID: "test-client-id",
}
store.CreateOIDCSession(token, 24*time.Hour, "TestAgent", "127.0.0.1", "testuser", oidcInfo)
// Verify session exists
if !store.ValidateSession(token) {
t.Error("Session should be valid before invalidation")
}
// Invalidate the session
store.InvalidateSession(token)
// Verify session no longer exists
if store.ValidateSession(token) {
t.Error("Session should be invalid after invalidation")
}
}