Ctrl K
ComunidadGuías…

pkg/auth/types.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package auth

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"log"
	"os"
	"time"
)

// Config holds authentication configuration.
// Supports three modes: "none" (default), "proxy" (trust reverse-proxy headers),
// and "oidc" (OpenID Connect login flow).
type Config struct {
	Mode      string        // "none" (default), "proxy", "oidc"
	Secret    string        // HMAC signing key for session cookies
	CookieTTL time.Duration // default 4h, sliding

	// Proxy mode
	UserHeader     string // default "X-Forwarded-User"
	GroupsHeader   string // default "X-Forwarded-Groups"
	ProxyLogoutURL string // optional, URL the logout button redirects to so the upstream proxy session is torn down (e.g. oauth2-proxy /oauth2/sign_out)

	// Session revocation (optional, used by backchannel logout)
	Revoker SessionRevoker

	// TokenReviewer resolves the Kubernetes identity of a user token in
	// user-token mode (a SelfSubjectReview against the API server).
	TokenReviewer func(ctx context.Context, token string) (*User, error)

	// OIDC mode
	OIDCIssuer                string
	OIDCInternalIssuer        string
	OIDCAuthorizationURL      string
	OIDCTokenURL              string
	OIDCUserInfoURL           string
	OIDCJWKSURL               string
	OIDCClientID              string
	OIDCClientSecret          string
	OIDCRedirectURL           string
	OIDCUsernameClaim         string   // ID token claim used as the username; empty = email, falling back to sub
	OIDCGroupsClaim           string   // default "groups"
	OIDCScopes                []string // OAuth2 scopes requested at authorization; default ["openid", "profile", "email", "groups"]
	OIDCPostLogoutRedirectURL string   // optional, URL to redirect after IdP logout
	OIDCUsernamePrefix        string   // prefix added to OIDC username for K8s impersonation (e.g., "oidc:")
	OIDCGroupsPrefix          string   // prefix added to OIDC groups for K8s impersonation (e.g., "oidc:")
	OIDCInsecureSkipVerify    bool     // skip TLS verification for OIDC provider (dev/test only)
	OIDCCACert                string   // path to CA certificate file for OIDC provider TLS
	OIDCBackchannelLogout     bool     // enable backchannel logout endpoint
	OIDCEnablePKCE            bool     // enable PKCE (S256) for the OIDC authorization-code flow (opt-in)
}

// SessionRevoker checks whether a session has been revoked (e.g., via OIDC
// backchannel logout). Used by the auth middleware to reject revoked sessions.
type SessionRevoker interface {
	IsRevoked(sid string) bool
}

// User represents an authenticated user
type User struct {
	Username string   `json:"username"`
	Groups   []string `json:"groups"`

	// User-token mode only. Token is the user's Kubernetes bearer token,
	// UID and Extra come from the API server's SelfSubjectReview of it.
	// System marks an allow-listed background identity with no token.
	// HubGroups are the upstream's radar:* groups, kept apart from the
	// Kubernetes identity so Hub-role gates still see them.
	Token     string              `json:"-"`
	UID       string              `json:"-"`
	Extra     map[string][]string `json:"-"`
	System    bool                `json:"-"`
	HubGroups []string            `json:"-"`
}

// Defaults applies default values to config fields that are empty
func (c *Config) Defaults() {
	if c.CookieTTL == 0 {
		c.CookieTTL = 4 * time.Hour // default 4h, sliding — extends on activity
	}
	if c.UserHeader == "" {
		c.UserHeader = "X-Forwarded-User"
	}
	if c.GroupsHeader == "" {
		c.GroupsHeader = "X-Forwarded-Groups"
	}
	if c.OIDCGroupsClaim == "" {
		c.OIDCGroupsClaim = "groups"
	}
	if len(c.OIDCScopes) == 0 {
		c.OIDCScopes = []string{"openid", "profile", "email", "groups"}
	}
	// Fall back to env vars for secrets (used by Helm chart)
	if c.Secret == "" {
		c.Secret = os.Getenv("RADAR_AUTH_SECRET")
	}
	if c.OIDCClientSecret == "" {
		c.OIDCClientSecret = os.Getenv("RADAR_OIDC_CLIENT_SECRET")
	}
	// Auto-generate secret if still empty and auth is enabled
	if c.Secret == "" && c.Enabled() {
		b := make([]byte, 32)
		if _, err := rand.Read(b); err != nil {
			log.Fatalf("[auth] Failed to generate session secret: %v", err)
		}
		c.Secret = hex.EncodeToString(b)
		log.Printf("[auth] Auto-generated session secret (sessions will not survive restarts)")
	}
}

// Enabled returns true if auth mode is not "none"
func (c *Config) Enabled() bool {
	return c.Mode != "" && c.Mode != "none"
}

// contextKey is the context key for the authenticated user
type contextKey struct{}

// UserFromContext retrieves the authenticated user from the request context.
// Returns nil when auth is disabled or the user is not authenticated.
func UserFromContext(ctx context.Context) *User {
	user, _ := ctx.Value(contextKey{}).(*User)
	return user
}

// ContextWithUser returns a new context with the authenticated user set.
func ContextWithUser(ctx context.Context, user *User) context.Context {
	return context.WithValue(ctx, contextKey{}, user)
}