Pulse/internal/api/router_helpers.go
rcourtman 9072b8eaa8 feat: enhance API router with multi-tenant authorization
Router & Middleware:
- Add auth context middleware for user/token extraction
- Add tenant middleware with authorization checking
- Refactor middleware chain ordering for proper isolation
- Add router helpers for common patterns

Authentication & SSO:
- Enhance auth with tenant-aware context
- Update OIDC, SAML, and SSO handlers for multi-tenant
- Add RBAC handler improvements
- Add security enhancements

New Test Coverage:
- API foundation tests
- Auth and authorization tests
- Router state and general tests
- SSO handler CRUD tests
- WebSocket isolation tests
- Resource handler tests
2026-01-24 22:42:23 +00:00

106 lines
3 KiB
Go

package api
import (
"net/http"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
"github.com/rs/zerolog/log"
)
// getMonitor returns the tenant-specific monitor instance for the request.
// It uses the OrgID from the context (injected by TenantMiddleware).
// If no tenant monitor is found, or if not in multi-tenant mode, it returns the default monitor.
func (r *Router) getMonitor(req *http.Request) (*monitoring.Monitor, error) {
if r.mtMonitor == nil {
return r.monitor, nil
}
orgID := GetOrgID(req.Context())
if orgID == "" {
return r.monitor, nil
}
return r.mtMonitor.GetMonitor(orgID)
}
// MultiTenantStateProvider wraps a MultiTenantMonitor to provide state for specific tenants.
type MultiTenantStateProvider struct {
mtMonitor *monitoring.MultiTenantMonitor
defaultMonitor *monitoring.Monitor
}
// NewMultiTenantStateProvider creates a new tenant state provider.
func NewMultiTenantStateProvider(mtm *monitoring.MultiTenantMonitor, defaultM *monitoring.Monitor) *MultiTenantStateProvider {
return &MultiTenantStateProvider{
mtMonitor: mtm,
defaultMonitor: defaultM,
}
}
// GetStateForTenant returns the state for a specific tenant.
func (p *MultiTenantStateProvider) GetStateForTenant(orgID string) models.StateSnapshot {
// Default org uses the default monitor
if orgID == "" || orgID == "default" {
if p.defaultMonitor != nil {
return p.defaultMonitor.GetState()
}
return models.StateSnapshot{}
}
// Try to get tenant-specific monitor
if p.mtMonitor != nil {
monitor, err := p.mtMonitor.GetMonitor(orgID)
if err != nil {
log.Warn().Err(err).Str("org_id", orgID).Msg("Failed to get tenant monitor for state")
// Fall back to default monitor
if p.defaultMonitor != nil {
return p.defaultMonitor.GetState()
}
return models.StateSnapshot{}
}
if monitor != nil {
return monitor.GetState()
}
}
// Fall back to default monitor
if p.defaultMonitor != nil {
return p.defaultMonitor.GetState()
}
return models.StateSnapshot{}
}
// SetMultiTenantMonitor updates the multi-tenant monitor manager.
// Used during reload.
func (r *Router) SetMultiTenantMonitor(mtm *monitoring.MultiTenantMonitor) {
r.mtMonitor = mtm
if r.alertHandlers != nil {
r.alertHandlers.SetMultiTenantMonitor(mtm)
}
if r.notificationHandlers != nil {
r.notificationHandlers.SetMultiTenantMonitor(mtm)
}
if r.dockerAgentHandlers != nil {
r.dockerAgentHandlers.SetMultiTenantMonitor(mtm)
}
if r.hostAgentHandlers != nil {
r.hostAgentHandlers.SetMultiTenantMonitor(mtm)
}
if r.kubernetesAgentHandlers != nil {
r.kubernetesAgentHandlers.SetMultiTenantMonitor(mtm)
}
if r.systemSettingsHandler != nil {
r.systemSettingsHandler.SetMultiTenantMonitor(mtm)
}
if mtm != nil {
if m, err := mtm.GetMonitor("default"); err == nil {
r.monitor = m
}
}
// Wire tenant state provider to resource handlers
if r.resourceHandlers != nil {
r.resourceHandlers.SetTenantStateProvider(NewMultiTenantStateProvider(mtm, r.monitor))
}
}