Documentation
¶
Overview ¶
Package httpserver provides HTTP API handlers for HTCondor operations.
Package httpserver provides HTTP API handlers for HTCondor operations.
Index ¶
- Constants
- Variables
- func AuthenticatedViaAPIKey(ctx context.Context) bool
- func ConfigureSecurityForCollectorPing(token, serverName string) (*security.SecurityConfig, error)
- func ConfigureSecurityForToken(token string) (*security.SecurityConfig, error)
- func ConfigureSecurityForTokenWithCache(token string, sessionCache *security.SessionCache) (*security.SecurityConfig, error)
- func ConfigureSecurityForTokenWithCacheAndFallback(token string, sessionCache *security.SessionCache, allowFSFallback bool) (*security.SecurityConfig, error)
- func ContainsScope(ctx context.Context, scope string) bool
- func DefaultIDPSession(username string) *openid.DefaultSession
- func DefaultOpenIDConnectSession(username string) *openid.DefaultSession
- func GenerateSigningKey() ([]byte, error)
- func GetScheddWithToken(ctx context.Context, schedd *htcondor.Schedd) (*htcondor.Schedd, error)
- func GetSecurityConfigFromToken(ctx context.Context) (*security.SecurityConfig, error)
- func GetTokenFromContext(ctx context.Context) (string, bool)
- func WithToken(ctx context.Context, token string) context.Context
- type AdminClient
- type AdminCondorConfigEntry
- type AdminCondorConfigResponse
- type AdminLogsResponse
- type AdminToken
- type AdvertiseRequest
- type AdvertiseResponse
- type AuthMeResponse
- type CollectorAdsResponse
- type Config
- type DashboardResponse
- type DeviceAuthorizationResponse
- type DeviceCodeHandler
- func (h *DeviceCodeHandler) HandleDeviceAccessRequest(ctx context.Context, deviceCode string, session fosite.Session) (fosite.Requester, error)
- func (h *DeviceCodeHandler) HandleDeviceAuthorizationRequest(ctx context.Context, client fosite.Client, scopes []string) (*DeviceAuthorizationResponse, error)
- type ErrorResponse
- type Handler
- func (h *Handler) GetOAuth2Provider() *OAuth2Provider
- func (h *Handler) GetSchedd() *htcondor.Schedd
- func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (h *Handler) SetupRoutes(setupFunc func(*http.ServeMux))
- func (h *Handler) Start(ctx context.Context, ln net.Listener, protocol string) error
- func (h *Handler) Stop(ctx context.Context) error
- func (h *Handler) UpdateOAuth2RedirectURL(redirectURL string)
- func (h *Handler) UpdateSchedd(newAddress string)
- type HandlerConfig
- type HistoryListResponse
- type IDPProvider
- type IDPProviderOptions
- type IDPStorage
- func (s *IDPStorage) AuthenticateUser(ctx context.Context, username, password string) error
- func (s *IDPStorage) ClientAssertionJWTValid(ctx context.Context, jti string) error
- func (s *IDPStorage) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *IDPStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *IDPStorage) CreateClient(ctx context.Context, client *fosite.DefaultClient) error
- func (s *IDPStorage) CreateOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) error
- func (s *IDPStorage) CreatePKCERequestSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *IDPStorage) CreateRefreshTokenSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *IDPStorage) CreateSession(ctx context.Context, username string) (string, error)
- func (s *IDPStorage) CreateUser(ctx context.Context, username, password, state string) error
- func (s *IDPStorage) DeleteAccessTokenSession(ctx context.Context, signature string) error
- func (s *IDPStorage) DeleteOpenIDConnectSession(ctx context.Context, signature string) error
- func (s *IDPStorage) DeletePKCERequestSession(ctx context.Context, signature string) error
- func (s *IDPStorage) DeleteRefreshTokenSession(ctx context.Context, signature string) error
- func (s *IDPStorage) DeleteSession(ctx context.Context, sessionID string) error
- func (s *IDPStorage) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *IDPStorage) GetAuthorizeCodeSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *IDPStorage) GetClient(ctx context.Context, clientID string) (fosite.Client, error)
- func (s *IDPStorage) GetOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) (fosite.Requester, error)
- func (s *IDPStorage) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *IDPStorage) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *IDPStorage) GetSession(ctx context.Context, sessionID string) (string, error)
- func (s *IDPStorage) GetUserState(ctx context.Context, username string) (string, error)
- func (s *IDPStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) error
- func (s *IDPStorage) LoadHMACSecret(ctx context.Context) ([]byte, error)
- func (s *IDPStorage) LoadRSAKey(ctx context.Context) (string, error)
- func (s *IDPStorage) RevokeAccessToken(ctx context.Context, requestID string) error
- func (s *IDPStorage) RevokeRefreshToken(ctx context.Context, requestID string) error
- func (s *IDPStorage) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestID string, _ string) error
- func (s *IDPStorage) SaveHMACSecret(ctx context.Context, secret []byte) error
- func (s *IDPStorage) SaveRSAKey(ctx context.Context, privateKeyPEM string) error
- func (s *IDPStorage) SetClientAssertionJWT(ctx context.Context, jti string, exp time.Time) error
- func (s *IDPStorage) SetSealer(sealer *seal.Sealer)
- func (s *IDPStorage) UserExists(ctx context.Context, username string) (bool, error)
- type InteractiveCreateTerminalRequest
- type InteractiveCreateTerminalResponse
- type InteractiveTerminalSummary
- type JobActionFunc
- type JobEditRequest
- type JobListResponse
- type JobLogResponse
- type JobSubmitRequest
- type JobSubmitResponse
- type JupyterCreateRequest
- type JupyterCreateResponse
- type JupyterInstanceSummary
- type LoginRateLimiter
- type OAuth2Provider
- func (p *OAuth2Provider) Close() error
- func (p *OAuth2Provider) GetProvider() fosite.OAuth2Provider
- func (p *OAuth2Provider) GetStorage() *OAuth2Storage
- func (p *OAuth2Provider) GetStrategy() *compose.CommonStrategy
- func (p *OAuth2Provider) IntrospectToken(ctx context.Context, token string) (fosite.Session, error)
- func (p *OAuth2Provider) UpdateIssuer(issuer string)
- type OAuth2ProviderOptions
- type OAuth2StateEntry
- type OAuth2StateStore
- func (s *OAuth2StateStore) GenerateState() (string, error)
- func (s *OAuth2StateStore) Get(state string) (fosite.AuthorizeRequester, bool)
- func (s *OAuth2StateStore) GetWithURL(state string) (fosite.AuthorizeRequester, string, bool)
- func (s *OAuth2StateStore) GetWithUsername(state string) (fosite.AuthorizeRequester, string, []string, bool)
- func (s *OAuth2StateStore) Remove(state string)
- func (s *OAuth2StateStore) Start(ctx context.Context)
- func (s *OAuth2StateStore) Store(state string, ar fosite.AuthorizeRequester)
- func (s *OAuth2StateStore) StoreWithURL(state string, ar fosite.AuthorizeRequester, originalURL string)
- func (s *OAuth2StateStore) StoreWithUsername(state string, ar fosite.AuthorizeRequester, originalURL, username string, ...)
- func (s *OAuth2StateStore) Wait()
- type OAuth2Storage
- func (s *OAuth2Storage) ApproveDeviceCodeSession(ctx context.Context, userCode string, subject string, session fosite.Session) error
- func (s *OAuth2Storage) ApproveDeviceCodeSessionWithScopes(ctx context.Context, userCode string, subject string, session fosite.Session, ...) error
- func (s *OAuth2Storage) ClientAssertionJWTValid(ctx context.Context, jti string) error
- func (s *OAuth2Storage) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *OAuth2Storage) CreateAuthorizeCodeSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *OAuth2Storage) CreateClient(ctx context.Context, client *fosite.DefaultClient) error
- func (s *OAuth2Storage) CreateDeviceCodeSession(ctx context.Context, deviceCode string, userCode string, ...) error
- func (s *OAuth2Storage) CreateOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) error
- func (s *OAuth2Storage) CreatePKCERequestSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *OAuth2Storage) CreateRefreshTokenSession(ctx context.Context, signature string, request fosite.Requester) error
- func (s *OAuth2Storage) DeleteAccessTokenSession(ctx context.Context, signature string) error
- func (s *OAuth2Storage) DeleteOpenIDConnectSession(ctx context.Context, signature string) error
- func (s *OAuth2Storage) DeletePKCERequestSession(ctx context.Context, signature string) error
- func (s *OAuth2Storage) DeleteRefreshTokenSession(ctx context.Context, signature string) error
- func (s *OAuth2Storage) DenyDeviceCodeSession(ctx context.Context, userCode string) error
- func (s *OAuth2Storage) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *OAuth2Storage) GetAuthorizeCodeSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *OAuth2Storage) GetClient(ctx context.Context, clientID string) (fosite.Client, error)
- func (s *OAuth2Storage) GetDB() *sql.DB
- func (s *OAuth2Storage) GetDeviceCodeSession(ctx context.Context, deviceCode string, session fosite.Session) (fosite.Requester, error)
- func (s *OAuth2Storage) GetDeviceCodeSessionByUserCode(ctx context.Context, userCode string) (string, fosite.Requester, error)
- func (s *OAuth2Storage) GetOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) (fosite.Requester, error)
- func (s *OAuth2Storage) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *OAuth2Storage) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
- func (s *OAuth2Storage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) error
- func (s *OAuth2Storage) InvalidateDeviceCodeSession(ctx context.Context, deviceCode string) error
- func (s *OAuth2Storage) LoadHMACSecret(ctx context.Context) ([]byte, error)
- func (s *OAuth2Storage) LoadRSAKey(ctx context.Context) (string, error)
- func (s *OAuth2Storage) RevokeAccessToken(ctx context.Context, requestID string) error
- func (s *OAuth2Storage) RevokeRefreshToken(ctx context.Context, requestID string) error
- func (s *OAuth2Storage) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestID string, _ string) error
- func (s *OAuth2Storage) SaveHMACSecret(ctx context.Context, secret []byte) error
- func (s *OAuth2Storage) SaveRSAKey(ctx context.Context, privateKeyPEM string) error
- func (s *OAuth2Storage) SetClientAssertionJWT(ctx context.Context, jti string, exp time.Time) error
- func (s *OAuth2Storage) SetSealer(sealer *seal.Sealer)
- func (s *OAuth2Storage) UpdateDeviceCodePolling(ctx context.Context, deviceCode string) error
- type PeekResponse
- type PeekedStreamResponse
- type PingResponse
- type Server
- type SessionData
- type SessionStore
- type ShareOutputRequest
- type ShareOutputResponse
- type TokenCache
- func (tc *TokenCache) Add(token string) (*TokenCacheEntry, error)
- func (tc *TokenCache) AddValidated(token, username string, expiration time.Time) (*TokenCacheEntry, error)
- func (tc *TokenCache) Get(token string) (*TokenCacheEntry, bool)
- func (tc *TokenCache) MarkValidated(token, authoritativeUsername string)
- func (tc *TokenCache) Remove(token string)
- func (tc *TokenCache) Size() int
- func (tc *TokenCache) ValidatedUsername(token string) string
- type TokenCacheEntry
- type UserInfo
- type VersionResponse
- type WhoAmIResponse
Constants ¶
const ( InteractiveWatchdogPollSec = 30 InteractiveWatchdogFreshnessSec = 120 )
Watchdog timing.
The script polls every InteractiveWatchdogPollSec; if .heartbeat is older than InteractiveWatchdogFreshnessSec it exits. The webapp side sends heartbeats every interactiveHeartbeatIntervalSec (handlers_ssh.go) when the user has typed within interactiveHeartbeatIdleWindowSec.
Defaults give a ~120s eviction window once the user goes idle, with 30s polling on each end. Tuned for "tab forgotten" not "user thinking".
Variables ¶
var ( ErrAuthorizationPending = &fosite.RFC6749Error{ ErrorField: "authorization_pending", DescriptionField: "The authorization request is still pending", CodeField: http.StatusBadRequest, } ErrSlowDown = &fosite.RFC6749Error{ ErrorField: "slow_down", DescriptionField: "Client is polling too frequently", CodeField: http.StatusBadRequest, } ErrExpiredToken = &fosite.RFC6749Error{ ErrorField: "expired_token", DescriptionField: "The device code has expired", CodeField: http.StatusBadRequest, } )
Device flow error codes (RFC 8628)
Functions ¶
func AuthenticatedViaAPIKey ¶ added in v0.0.12
AuthenticatedViaAPIKey reports whether ctx was set up by the API-key auth path (rather than a JWT, OAuth2 token, or browser session). Some authorization decisions only make sense for API keys (e.g. "API keys must have an explicit scope; sessions don't").
func ConfigureSecurityForCollectorPing ¶ added in v0.0.11
func ConfigureSecurityForCollectorPing(token, serverName string) (*security.SecurityConfig, error)
ConfigureSecurityForCollectorPing builds a SecurityConfig used solely by the periodic collector ping. The collector ping is read-only — we just need *some* mutually agreeable handshake — so this offers both TOKEN and SSL. That's useful when the daemon's token does not match the collector's IssuerKeys (an issuer rotation, a misconfigured TrustDomain, etc.): SSL keeps /readyz green via a path that has nothing to do with JWT signing. The schedd path — which DOES need the token's identity for authz — keeps using TOKEN only.
SSL is always offered (even with no client cert/key on disk) because many collectors permit anonymous SSL: the client only verifies the server's cert and connects as ANONYMOUS@…, which is enough for a read-only ping. Cedar's SSL auth handles empty CertFile/KeyFile as "no client cert presented" and empty CAFile as "use the system trust store" — see cedar/security/ssl_auth.go and cmd/ssl-test/main.go.
serverName is used by cedar's SSL handshake for hostname/SAN verification. Without it, cedar falls back to the literal string "unknown" and verification fails ("certificate is valid for host.example.com, ..., not unknown"). Pass the bare hostname of the collector address — see hostFromCondorAddress in handler.go.
`token` may be empty; in that case only SSL is offered.
func ConfigureSecurityForToken ¶
func ConfigureSecurityForToken(token string) (*security.SecurityConfig, error)
ConfigureSecurityForToken configures security settings to use the provided token This is a helper function to set up cedar's security configuration for TOKEN authentication
func ConfigureSecurityForTokenWithCache ¶ added in v0.0.3
func ConfigureSecurityForTokenWithCache(token string, sessionCache *security.SessionCache) (*security.SecurityConfig, error)
ConfigureSecurityForTokenWithCache configures security settings with an optional session cache If sessionCache is nil, the global cache will be used
func ConfigureSecurityForTokenWithCacheAndFallback ¶ added in v0.0.4
func ConfigureSecurityForTokenWithCacheAndFallback(token string, sessionCache *security.SessionCache, allowFSFallback bool) (*security.SecurityConfig, error)
ConfigureSecurityForTokenWithCacheAndFallback configures security settings with optional session cache and optional FS authentication fallback.
allowFSFallback semantics:
true (user-header mode): the token is generated locally per request and not signed with anything the schedd recognises, so we APPEND FS as a fallback for use against a same-host schedd where the OS-user identity is acceptable.
false (session/JWT mode): the token IS signed by us with the pool's signing key, the schedd validates it, and its `sub` claim is the user we want recorded as the job Owner. We therefore REMOVE FS from the offered methods, so the schedd can't pick it during negotiation. (Cedar's negotiation walks the server's preference order and selects the first method also offered by the client; HTCondor's default lists FS first, so leaving FS in the client's list lets FS win on a same-host schedd, and the schedd then records the OS user instead of the token's identity. We saw this in session_integration_test.go: jobs submitted via session cookie were owned by `vscode` — the test runner's UID — not by the JWT subject `testuser@trust.domain`.)
Authentication methods otherwise come from SEC_CLIENT_AUTHENTICATION_METHODS / SEC_DEFAULT_AUTHENTICATION_METHODS in the loaded HTCondor configuration. This was previously a hardcoded `[TOKEN]` list, which broke any pool that expects SSL alongside IDTOKENS.
Implementation: delegates to htcondor.NewClientSecurityConfig for the configured-methods-aware base, then applies the FS rule above. Other call sites (file_transfer, schedd_ssh, mcpserver) use NewClientSecurityConfig directly; the httpserver-only allowFSFallback knob lives here so we don't drag it into the root package's API.
func ContainsScope ¶ added in v0.0.12
ContainsScope reports whether the request's API key was minted with the named scope. Returns false when the request was NOT authenticated via an API key (or was authenticated via one with different scopes). Use this in handlers that want to opt into API-key access.
func DefaultIDPSession ¶ added in v0.0.4
func DefaultIDPSession(username string) *openid.DefaultSession
DefaultIDPSession creates a default OpenID Connect session for IDP
func DefaultOpenIDConnectSession ¶ added in v0.0.3
func DefaultOpenIDConnectSession(username string) *openid.DefaultSession
DefaultOpenIDConnectSession creates a default OpenID Connect session
func GenerateSigningKey ¶
GenerateSigningKey generates a new signing key for token generation Returns the key content as bytes
func GetScheddWithToken ¶
GetScheddWithToken creates a schedd connection configured with token authentication This wraps the schedd to use token authentication from context
func GetSecurityConfigFromToken ¶
func GetSecurityConfigFromToken(ctx context.Context) (*security.SecurityConfig, error)
GetSecurityConfigFromToken retrieves the token from context and creates a SecurityConfig This is a convenience function for HTTP handlers to convert context token to SecurityConfig
func GetTokenFromContext ¶
GetTokenFromContext retrieves the token from the context
Types ¶
type AdminClient ¶ added in v0.0.11
type AdminClient struct {
ID string `json:"id"`
RedirectURIs []string `json:"redirect_uris,omitempty"`
GrantTypes []string `json:"grant_types,omitempty"`
ResponseTypes []string `json:"response_types,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Public bool `json:"public"`
CreatedAt time.Time `json:"created_at"`
}
AdminClient is the SPA-facing shape for an OAuth2 client. We mirror only the fields useful for an "audit + cleanup" UI; secrets are never returned (they're hashed in storage anyway, but we still strip them out of the response shape on principle).
type AdminCondorConfigEntry ¶ added in v0.0.12
type AdminCondorConfigEntry struct {
Key string `json:"key"`
Value string `json:"value,omitempty"`
Redacted bool `json:"redacted,omitempty"`
}
AdminCondorConfigEntry is one (key, value) row from the HTCondor config that we surface to the admin info page. Sensitive values (matched by sensitiveCondorKeyPattern) come back with Redacted=true and an empty Value so the admin can SEE the key is set without the raw value rendering on screen — defense-in-depth even though HTCondor convention is that secrets are paths, not literals.
type AdminCondorConfigResponse ¶ added in v0.0.12
type AdminCondorConfigResponse struct {
Configured bool `json:"configured"`
Entries []AdminCondorConfigEntry `json:"entries"`
}
AdminCondorConfigResponse is the full readout. Configured=false means no HTCondor config object was wired into this server; treat as "feature unavailable on this deployment" client-side.
type AdminLogsResponse ¶ added in v0.0.11
type AdminLogsResponse struct {
Enabled bool `json:"enabled"`
Entries []logging.BufferEntry `json:"entries"`
}
AdminLogsResponse wraps the buffer entries with a hint when the buffer hasn't been initialized — the SPA shows a different empty state for "no logs yet" vs "feature not wired up".
type AdminToken ¶ added in v0.0.11
type AdminToken struct {
Kind string `json:"kind"` // "access" or "refresh"
SignaturePrefix string `json:"signature_prefix"`
ClientID string `json:"client_id"`
Subject string `json:"subject,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Active bool `json:"active"`
RequestedAt time.Time `json:"requested_at"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
}
AdminToken is the SPA-facing shape for an OAuth2 access/refresh token row. We never expose the raw token signature — only its prefix as a fingerprint, so admins can correlate against logs without being able to use the token themselves.
type AdvertiseRequest ¶ added in v0.0.4
type AdvertiseRequest struct {
Ad *classad.ClassAd `json:"ad,omitempty"` // Single ad (JSON body)
Command string `json:"command,omitempty"` // Optional UPDATE command (e.g., "UPDATE_STARTD_AD")
WithAck bool `json:"with_ack,omitempty"` // Request acknowledgment
}
AdvertiseRequest represents a request to advertise to the collector
type AdvertiseResponse ¶ added in v0.0.4
type AdvertiseResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Succeeded int `json:"succeeded"` // Number of ads successfully advertised
Failed int `json:"failed"` // Number of ads that failed
Errors []string `json:"errors,omitempty"` // Error messages for failed ads
}
AdvertiseResponse represents the response from advertise
type AuthMeResponse ¶ added in v0.0.11
type AuthMeResponse struct {
Authenticated bool `json:"authenticated"`
Username string `json:"username,omitempty"`
Groups []string `json:"groups,omitempty"`
IsAdmin bool `json:"is_admin"`
}
AuthMeResponse describes the currently-authenticated browser session.
The Web UI uses this as its single source of truth for "is the user logged in", who they are, and whether to render admin pages. It is intentionally looser than /api/v1/whoami: it always returns 200 (with Authenticated=false when there is no session) so the SPA can render a landing page without going through error-handling.
type CollectorAdsResponse ¶ added in v0.0.3
CollectorAdsResponse represents collector ads listing response
type Config ¶
type Config struct {
ListenAddr string // Address to listen on (e.g., ":8080")
ScheddName string // Schedd name
ScheddAddr string // Schedd address (e.g., "127.0.0.1:9618"). If empty, discovered from collector.
UserHeader string // HTTP header to extract username from (optional)
// UserHeaderTrustedProxies is the CIDR list from which UserHeader
// is honored. See HandlerConfig.UserHeaderTrustedProxies for
// full docs and the security rationale. Configurable via
// HTTP_API_USER_HEADER_TRUSTED_PROXIES.
UserHeaderTrustedProxies []string
// UserHeaderTrustAnyUnsafe disables the trusted-proxy gate and
// honors UserHeader from any source. Demo / test only — see
// HandlerConfig.UserHeaderTrustAnyUnsafe. Configurable via
// HTTP_API_USER_HEADER_TRUST_ANY.
UserHeaderTrustAnyUnsafe bool
SigningKeyPath string // Path to token signing key (optional, for token generation)
TrustDomain string // Trust domain for token issuer (optional; only used if UserHeader is set)
UIDDomain string // UID domain for generated token username (optional; only used if UserHeader is set)
HTTPBaseURL string // Base URL for HTTP API (e.g., "http://localhost:8080") for generating file download links in MCP responses
TLSCertFile string // Path to TLS certificate file (optional, enables HTTPS)
TLSKeyFile string // Path to TLS key file (optional, enables HTTPS)
TLSCACertFile string // Path to TLS CA certificate file (optional, for trusting self-signed certs)
ReadTimeout time.Duration // HTTP read timeout (default: 30s)
WriteTimeout time.Duration // HTTP write timeout (default: 30s)
IdleTimeout time.Duration // HTTP idle timeout (default: 120s)
Collector *htcondor.Collector // Collector for metrics (optional)
EnableMetrics bool // Enable /metrics endpoint (default: true if Collector is set)
MetricsCacheTTL time.Duration // Metrics cache TTL (default: 10s)
// MetricsPublic disables the API-key auth gate on /metrics.
// Configurable via HTTP_API_METRICS_PUBLIC; see HandlerConfig.
MetricsPublic bool
Logger *logging.Logger // Logger instance (optional, creates default if nil)
JupyterWorkDir string // Per-instance scratch dir for JupyterLab submission artifacts; default <TempDir>/htcondor-api-jupyter
// InteractiveExtraSubmit is an optional verbatim block of extra
// HTCondor submit-file directives merged into every
// interactive-terminal and Jupyter job. See
// HandlerConfig.InteractiveExtraSubmit for the trust model and
// full documentation. Configurable via
// HTTP_API_INTERACTIVE_EXTRA_SUBMIT.
InteractiveExtraSubmit string
// Batch-submission template paths.
TemplateGlobalPath string // Optional YAML file with operator-curated templates
// TemplateUserStoreDBPath is deprecated; the templates store
// shares the unified DBPath. Kept so existing callers compile.
TemplateUserStoreDBPath string //nolint:unused // back-compat; ignored.
EnableMCP bool // Enable MCP endpoints with OAuth2 (default: false)
// DBPath is the unified SQLite database file. See HandlerConfig.DBPath.
DBPath string
// KEKFilePath enables envelope encryption for long-lived secrets
// in the DB. See HandlerConfig.KEKFilePath.
KEKFilePath string
// OAuth2DBPath is the legacy name for DBPath; kept for back-compat.
OAuth2DBPath string
OAuth2Issuer string // OAuth2 issuer URL (default: listen address)
OAuth2ClientID string // OAuth2 client ID for SSO (optional)
OAuth2ClientSecret string // OAuth2 client secret for SSO (optional)
OAuth2AuthURL string // OAuth2 authorization URL for SSO (optional)
OAuth2TokenURL string // OAuth2 token URL for SSO (optional)
OAuth2RedirectURL string // OAuth2 redirect URL for SSO (optional)
OAuth2UserInfoURL string // OAuth2 user info endpoint for SSO (optional)
OAuth2Scopes []string // OAuth2 scopes to request (default: ["openid", "profile", "email"])
OAuth2UsernameClaim string // Claim name for username in token (default: "sub")
OAuth2GroupsClaim string // Claim name for groups in user info (default: "groups")
// OAuth2AccessTokenLifespan / OAuth2RefreshTokenLifespan control how long the
// embedded MCP issuer's tokens are valid. Zero means "use the package default"
// (1h access, 30d refresh). RefreshTokenLifespan must be >= AccessTokenLifespan.
OAuth2AccessTokenLifespan time.Duration
OAuth2RefreshTokenLifespan time.Duration
MCPAccessGroup string // Group required for any MCP access (empty = all authenticated)
MCPReadGroup string // Group required for read operations (empty = all have read)
MCPWriteGroup string // Group required for write operations (empty = all have write)
MCPInstructions string // Server-level instructions provided to all MCP agents (e.g., AP-specific guidance)
WebUIAdminGroup string // Group required for Web UI admin pages (empty disables admin UI). Configurable via HTTP_API_WEBUI_ADMIN_GROUP.
EnableIDP bool // Enable built-in IDP (always enabled in demo mode)
// IDPDBPath is deprecated; the IDP shares the unified DBPath.
IDPDBPath string //nolint:unused // back-compat; ignored.
IDPIssuer string // IDP issuer URL (default: listen address)
// IDPAccessTokenLifespan / IDPRefreshTokenLifespan: see OAuth2*Lifespan above.
IDPAccessTokenLifespan time.Duration
IDPRefreshTokenLifespan time.Duration
SessionTTL time.Duration // HTTP session TTL (default: 24h)
HTCondorConfig *config.Config // HTCondor configuration (optional, used for LOCAL_DIR default)
PingInterval time.Duration // Interval for periodic daemon pings (default: 1 minute, 0 = disabled)
StreamBufferSize int // Buffer size for streaming queries (default: 100)
StreamWriteTimeout time.Duration // Write timeout for streaming queries (default: 5s)
Token string // Token for daemon authentication (optional)
Credd htcondor.CreddClient // Optional credd client; defaults to in-memory implementation
// LLMAPIKeyFile is the path to a file holding the Anthropic API
// key. Empty disables the chat endpoint. See HandlerConfig.
LLMAPIKeyFile string
// LLMAPIURL is an optional override for the upstream Anthropic
// endpoint, useful when the operator runs an LLM gateway. Empty
// = direct to api.anthropic.com.
LLMAPIURL string
// LLMModel overrides the default model id. Empty = package default.
LLMModel string
// LLMOperatorInstructionsFile is the path to a file with extra
// system-prompt rules the operator wants the chat assistant to
// follow on every turn. Empty disables. See HandlerConfig for
// the file-mode requirement and rationale.
LLMOperatorInstructionsFile string
}
Config holds server configuration
type DashboardResponse ¶ added in v0.0.11
type DashboardResponse struct {
Username string `json:"username"`
JobsByStatus map[string]int `json:"jobs_by_status"`
JobsTotal int `json:"jobs_total"`
}
DashboardResponse summarizes the user's queue at the AP. It is a minimal shape on purpose; we'll grow it (transfer history, recent completions, user-level quota) in PR (b)/(c) once the SPA has the basics.
type DeviceAuthorizationResponse ¶ added in v0.0.4
type DeviceAuthorizationResponse struct {
DeviceCode string `json:"device_code"`
UserCode string `json:"user_code"`
VerificationURI string `json:"verification_uri"`
VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval,omitempty"`
}
DeviceAuthorizationResponse represents the response from device authorization endpoint
type DeviceCodeHandler ¶ added in v0.0.4
type DeviceCodeHandler struct {
// contains filtered or unexported fields
}
DeviceCodeHandler implements the OAuth 2.0 Device Authorization Grant (RFC 8628)
func NewDeviceCodeHandler ¶ added in v0.0.4
func NewDeviceCodeHandler(storage *OAuth2Storage, config *fosite.Config) *DeviceCodeHandler
NewDeviceCodeHandler creates a new device code handler
func (*DeviceCodeHandler) HandleDeviceAccessRequest ¶ added in v0.0.4
func (h *DeviceCodeHandler) HandleDeviceAccessRequest(ctx context.Context, deviceCode string, session fosite.Session) (fosite.Requester, error)
HandleDeviceAccessRequest handles token requests with device_code grant type
func (*DeviceCodeHandler) HandleDeviceAuthorizationRequest ¶ added in v0.0.4
func (h *DeviceCodeHandler) HandleDeviceAuthorizationRequest(ctx context.Context, client fosite.Client, scopes []string) (*DeviceAuthorizationResponse, error)
HandleDeviceAuthorizationRequest handles the device authorization endpoint
type ErrorResponse ¶
type ErrorResponse struct {
Error string `json:"error"`
Message string `json:"message,omitempty"`
Code int `json:"code"`
}
ErrorResponse represents an error response body
type Handler ¶ added in v0.0.7
type Handler struct {
// contains filtered or unexported fields
}
Handler represents the HTTP API handler that can be embedded in any HTTP server
func NewHandler ¶ added in v0.0.7
func NewHandler(cfg HandlerConfig) (*Handler, error)
NewHandler creates a new HTTP API handler that can be embedded in any HTTP server
func (*Handler) GetOAuth2Provider ¶ added in v0.0.7
func (h *Handler) GetOAuth2Provider() *OAuth2Provider
GetOAuth2Provider returns the OAuth2 provider (for testing)
func (*Handler) GetSchedd ¶ added in v0.0.7
GetSchedd returns the current schedd instance (thread-safe)
func (*Handler) ServeHTTP ¶ added in v0.0.7
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler interface.
Every request flows through the metrics middleware first so the HTTP request counters / duration histogram / in-flight gauge cover every route uniformly — not just the ones we remembered to wrap in setupRoutes. The middleware short-circuits /metrics itself to avoid Prometheus scrapes self-instrumenting.
Security headers are emitted on every response (see applySecurityHeaders). Setting them at the top wrapper guarantees no route can opt out by accident — handlers further down the stack can still override (e.g. relaxing frame-ancestors for an embeddable widget) but the secure defaults are present until explicitly changed.
The response is also wrapped in a status-capturing writer so we can call tokenCache.MarkValidated when a request bearing a JWT returns 2xx. This is the "lazy validation" pattern: there is no local way to verify the JWT signature (the only authoritative validator is the schedd's CEDAR handshake), so we defer to "the handler completed successfully" as evidence that the schedd accepted the token — and only at that point trust the token's `sub` claim for identity decisions like ownedByMe filtering.
func (*Handler) SetupRoutes ¶ added in v0.0.7
SetupRoutes sets up the HTTP routes on the handler's multiplexer This should be called by Server.NewServer or by users who create a Handler directly
func (*Handler) Start ¶ added in v0.0.7
Start initializes the handler and starts background goroutines. The provided context controls the handler's lifetime - when the context is cancelled, the handler will gracefully shut down all background goroutines.
This method should be called by Server.Start() or Server.StartTLS() before serving requests.
func (*Handler) Stop ¶ added in v0.0.7
Stop gracefully stops all background goroutines and closes providers. This method is called when the handler's context is cancelled (via Server.Shutdown). The background goroutines are responsible for watching their context and exiting when done.
func (*Handler) UpdateOAuth2RedirectURL ¶ added in v0.0.7
UpdateOAuth2RedirectURL updates the OAuth2 redirect URL for SSO integration
func (*Handler) UpdateSchedd ¶ added in v0.0.7
UpdateSchedd updates the schedd instance with a new address (thread-safe). On change, logs both addresses, the age of the previous address, and — when both addresses are shared-port — the old and new sock= IDs so it's obvious whether a schedd restart drove the update (sock= changed) versus a network-level address shift (host:port changed but sock= stable).
Always sets scheddAddrLastConfirmedAt to now: the caller has just talked to the collector successfully, regardless of whether the address differs from the cached value.
type HandlerConfig ¶ added in v0.0.7
type HandlerConfig struct {
ScheddName string // Schedd name
ScheddAddr string // Schedd address (e.g., "127.0.0.1:9618"). If empty, discovered from collector.
// InteractiveExtraSubmit holds extra HTCondor submit-file
// directives merged into the submit file produced for each
// interactive-terminal and JupyterLab job. The string value is
// spliced in verbatim just before the `queue` directive, so it
// can override or extend anything the builder emits (typical
// use: pin an accounting group, add +ProjectName, set a
// site-wide `requirements` fragment, force `concurrency_limits`,
// etc.).
//
// Trust model: the value comes from operator-only configuration
// (HTCondor config or env), so it's inserted into every submit
// file verbatim — no whitelist, no quoting. Treat this as the
// operator's hook into job admission policy, equivalent in
// privilege to writing the schedd's site_local config.
//
// Multi-line content is supported via HTCondor config syntax (a
// quoted multi-line value, or backslash continuations). Empty
// disables the feature. Configurable via
// HTTP_API_INTERACTIVE_EXTRA_SUBMIT.
InteractiveExtraSubmit string
UserHeader string // HTTP header to extract username from (optional)
// UserHeaderTrustedProxies is a list of CIDRs from which UserHeader
// is honored. When UserHeader is set, this list MUST be non-empty
// (or UserHeaderTrustAnyUnsafe must be true) — otherwise the
// header is silently ignored, treating the request as
// unauthenticated. Configure via HTTP_API_USER_HEADER_TRUSTED_PROXIES
// (comma-separated CIDRs, e.g. "127.0.0.1/32,::1/128,10.0.0.0/8")
// or programmatically.
UserHeaderTrustedProxies []string
// UserHeaderTrustAnyUnsafe disables the trusted-proxy check and
// accepts UserHeader from any source. This is the demo / test
// mode only — it is unsafe in any production deployment because
// it lets any client spoof identity by setting the header.
// Configure via HTTP_API_USER_HEADER_TRUST_ANY=1 (loud warning
// at startup).
UserHeaderTrustAnyUnsafe bool
SigningKeyPath string // Path to token signing key (optional, for token generation)
TrustDomain string // Trust domain for token issuer (optional; only used if UserHeader is set)
UIDDomain string // UID domain for generated token username (optional; only used if UserHeader is set)
HTTPBaseURL string // Base URL for HTTP API (e.g., "http://localhost:8080") for generating file download links in MCP responses
TLSCACertFile string // Path to TLS CA certificate file (optional, for trusting self-signed certs)
Collector *htcondor.Collector // Collector for metrics (optional)
EnableMetrics bool // Enable /metrics endpoint (default: true if Collector is set)
MetricsCacheTTL time.Duration // Metrics cache TTL (default: 10s)
// MetricsPublic disables the API-key auth gate on /metrics. Use
// only when network ACLs already isolate the endpoint (e.g. a
// private listening address or a sidecar proxy). Default: false
// — an admin must mint an API key with the `metrics` scope and
// configure Prometheus to send it as a Bearer token.
MetricsPublic bool
Logger *logging.Logger // Logger instance (optional, creates default if nil)
EnableMCP bool // Enable MCP endpoints with OAuth2 (default: false)
// DBPath is the unified SQLite database file backing OAuth2/MCP
// storage, the embedded IDP, browser sessions, and user-saved
// batch-submission templates. Defaults to LOCAL_DIR/htcondor-api.db
// (or /var/lib/condor/htcondor-api.db when LOCAL_DIR is unset).
// Configure via HTTP_API_DB_PATH.
DBPath string
// KEKFilePath is the path to a file holding the master Key
// Encryption Key used to envelope-encrypt long-lived secrets in
// the application database (the OAuth2 / IDP issuer's RSA
// signing key, fosite's HMAC GlobalSecret). Configured via
// HTTP_API_KEK_FILE — the FILE PATH lives in HTCondor config,
// the KEY BYTES never do (HTCondor treats config values as
// public).
//
// Empty disables encryption: secrets are stored in plaintext as
// before. When set, the file must contain exactly 32 raw bytes
// or a 32-byte hex string and must be 0600/0400. See
// httpserver/appdb/seal for the design.
KEKFilePath string
// OAuth2DBPath is a deprecated alias for DBPath kept so existing
// in-process embedders (and the test suite) keep compiling without
// a wholesale rename. NewHandler honors it only when DBPath is
// empty. The cmd-line wrapper does NOT bridge HTTP_API_OAUTH2_DB_PATH
// into this field — pointing the unified DB at a pre-unification
// oauth.db is a guaranteed crash-loop (the legacy schema conflicts
// with goose 0001_init.sql), and the wrapper logs a deprecation
// warning instead. New code should set DBPath.
OAuth2DBPath string
OAuth2Issuer string // OAuth2 issuer URL (default: listen address)
OAuth2ClientID string // OAuth2 client ID for SSO (optional)
OAuth2ClientSecret string // OAuth2 client secret for SSO (optional)
OAuth2AuthURL string // OAuth2 authorization URL for SSO (optional)
OAuth2TokenURL string // OAuth2 token URL for SSO (optional)
OAuth2RedirectURL string // OAuth2 redirect URL for SSO (optional)
OAuth2UserInfoURL string // OAuth2 user info endpoint for SSO (optional)
OAuth2Scopes []string // OAuth2 scopes to request (default: ["openid", "profile", "email"])
OAuth2UsernameClaim string // Claim name for username in token (default: "sub")
OAuth2GroupsClaim string // Claim name for groups in user info (default: "groups")
// OAuth2AccessTokenLifespan is how long an access token issued by the embedded
// MCP issuer is valid. Defaults to 1 hour if zero.
OAuth2AccessTokenLifespan time.Duration
// OAuth2RefreshTokenLifespan is how long a refresh token issued by the embedded
// MCP issuer is valid. Defaults to 30 days if zero. Must be >= OAuth2AccessTokenLifespan;
// otherwise refresh grants will fail before the access token expires (see PelicanPlatform/pelican#3389).
OAuth2RefreshTokenLifespan time.Duration
MCPAccessGroup string // Group required for any MCP access (empty = all authenticated)
MCPReadGroup string // Group required for read operations (empty = all have read)
MCPWriteGroup string // Group required for write operations (empty = all have write)
MCPInstructions string // Server-level instructions provided to all MCP agents (e.g., AP-specific guidance)
WebUIAdminGroup string // Group required for Web UI admin pages (empty disables admin UI). Configurable via HTTP_API_WEBUI_ADMIN_GROUP.
EnableIDP bool // Enable built-in IDP (always enabled in demo mode)
// IDPDBPath is deprecated; the IDP shares the unified DBPath.
// Retained as an unused field so existing callers keep compiling
// during the transition.
IDPDBPath string //nolint:unused // kept for back-compat; ignored by NewHandler.
IDPIssuer string // IDP issuer URL (default: listen address)
// IDPAccessTokenLifespan / IDPRefreshTokenLifespan: see OAuth2*Lifespan above. Zero
// uses the same defaults (1h / 30d).
IDPAccessTokenLifespan time.Duration
IDPRefreshTokenLifespan time.Duration
SessionTTL time.Duration // HTTP session TTL (default: 24h)
HTCondorConfig *config.Config // HTCondor configuration (optional, used for LOCAL_DIR default)
PingInterval time.Duration // Interval for periodic daemon pings (default: 1 minute, 0 = disabled)
StreamBufferSize int // Buffer size for streaming queries (default: 100)
StreamWriteTimeout time.Duration // Write timeout for streaming queries (default: 5s)
Token string // Token for daemon authentication (optional)
Credd htcondor.CreddClient // Optional credd client; defaults to in-memory implementation
// LLMAPIKeyFile is the path to a file holding the Anthropic API
// key used by the chat endpoint at /api/v1/chat. Empty disables
// the chat feature. The bytes never live in HTCondor config —
// only this file path does.
LLMAPIKeyFile string
// LLMAPIURL overrides the upstream Anthropic Messages endpoint.
// Use to point at a self-hosted LLM gateway / cache. Empty falls
// back to chat.DefaultAnthropicURL.
LLMAPIURL string
// LLMModel overrides the default Anthropic model id. Empty falls
// back to chat.DefaultAnthropicModel.
LLMModel string
// LLMOperatorInstructionsFile is the path to a file containing
// extra system-prompt rules the operator wants appended to every
// chat turn (e.g. "users in this pool may not request more than
// 64 GiB of memory; suggest fewer if asked"). Empty path = no
// addendum.
//
// File mode is enforced 0600/0400 on load — same rationale as the
// API-key file: the operator may put policy text here that they
// don't want every local user to read. Loaded once at startup; a
// hot-reload would need a server restart.
LLMOperatorInstructionsFile string
// JupyterWorkDir is where the embedded helper binary (materialized
// from package jupyterhelperbin) and per-instance scratch artifacts
// (token files) are staged. Files persist for the lifetime of the
// job since HTCondor reads transfer_input_files at job-startup time.
// Defaults to <os.TempDir>/htcondor-api-jupyter.
JupyterWorkDir string
// TemplateGlobalPath is an optional YAML file with operator-curated
// batch-submission templates. Empty disables. Built-in templates
// always ship.
TemplateGlobalPath string
// TemplateUserStoreDBPath is deprecated; the templates store now
// shares the unified DBPath. Retained so existing callers
// keep compiling; ignored by NewHandler.
TemplateUserStoreDBPath string //nolint:unused // kept for back-compat; ignored by NewHandler.
}
HandlerConfig holds handler configuration
type HistoryListResponse ¶ added in v0.0.4
HistoryListResponse represents a history listing response
type IDPProvider ¶ added in v0.0.4
type IDPProvider struct {
// contains filtered or unexported fields
}
IDPProvider manages OAuth2 operations for the built-in IDP
func NewIDPProvider ¶ added in v0.0.4
func NewIDPProvider(opts IDPProviderOptions) (*IDPProvider, error)
NewIDPProvider creates a new IDP provider with SQLite storage. Both AccessTokenLifespan and RefreshTokenLifespan in opts must be > 0; otherwise an error is returned. See OAuth2ProviderOptions for the rationale.
func (*IDPProvider) Close ¶ added in v0.0.4
func (p *IDPProvider) Close() error
Close is now a no-op: the IDP provider does not own the underlying *sql.DB anymore. The Handler that opened the unified app DB is responsible for closing it on shutdown.
func (*IDPProvider) GetProvider ¶ added in v0.0.4
func (p *IDPProvider) GetProvider() fosite.OAuth2Provider
GetProvider returns the underlying fosite OAuth2Provider
func (*IDPProvider) GetStorage ¶ added in v0.0.4
func (p *IDPProvider) GetStorage() *IDPStorage
GetStorage returns the IDP storage
func (*IDPProvider) GetStrategy ¶ added in v0.0.4
func (p *IDPProvider) GetStrategy() *compose.CommonStrategy
GetStrategy returns the OAuth2 strategy
func (*IDPProvider) UpdateIssuer ¶ added in v0.0.4
func (p *IDPProvider) UpdateIssuer(issuer string)
UpdateIssuer updates the issuer URL in the OAuth2 config
type IDPProviderOptions ¶ added in v0.0.11
type IDPProviderOptions struct {
DB *sql.DB
Issuer string
AccessTokenLifespan time.Duration
RefreshTokenLifespan time.Duration
// Sealer envelope-encrypts the IDP's RSA private key + HMAC
// GlobalSecret. See OAuth2ProviderOptions.Sealer.
Sealer *seal.Sealer
}
IDPProviderOptions configures lifespans and other tunables for the IDP provider. DB is the unified application database (see appdb); the provider does not own its lifecycle.
type IDPStorage ¶ added in v0.0.4
type IDPStorage struct {
// contains filtered or unexported fields
}
IDPStorage implements fosite storage interfaces using the unified application database. The IDP's tables (idp_*) live alongside the OAuth2/MCP tables in the same SQLite file managed by appdb.
See OAuth2Storage for the sealer field's role.
func NewIDPStorage ¶ added in v0.0.4
func NewIDPStorage(db *sql.DB) *IDPStorage
NewIDPStorage wraps an already-opened, already-migrated DB. Schema is owned by the appdb migrations; this struct only holds the query helpers. The caller retains DB ownership.
func (*IDPStorage) AuthenticateUser ¶ added in v0.0.4
func (s *IDPStorage) AuthenticateUser(ctx context.Context, username, password string) error
AuthenticateUser verifies username and password
func (*IDPStorage) ClientAssertionJWTValid ¶ added in v0.0.4
func (s *IDPStorage) ClientAssertionJWTValid(ctx context.Context, jti string) error
ClientAssertionJWTValid implements fosite.ClientAssertionJWTValid interface
func (*IDPStorage) CreateAccessTokenSession ¶ added in v0.0.4
func (s *IDPStorage) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) error
CreateAccessTokenSession stores an access token session
func (*IDPStorage) CreateAuthorizeCodeSession ¶ added in v0.0.4
func (s *IDPStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, request fosite.Requester) error
CreateAuthorizeCodeSession stores an authorization code session
func (*IDPStorage) CreateClient ¶ added in v0.0.4
func (s *IDPStorage) CreateClient(ctx context.Context, client *fosite.DefaultClient) error
CreateClient creates a new OAuth2 client
func (*IDPStorage) CreateOpenIDConnectSession ¶ added in v0.0.4
func (s *IDPStorage) CreateOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) error
CreateOpenIDConnectSession implements openid.OpenIDConnectRequestStorage interface
func (*IDPStorage) CreatePKCERequestSession ¶ added in v0.0.4
func (s *IDPStorage) CreatePKCERequestSession(ctx context.Context, signature string, request fosite.Requester) error
CreatePKCERequestSession stores a PKCE request session
func (*IDPStorage) CreateRefreshTokenSession ¶ added in v0.0.4
func (s *IDPStorage) CreateRefreshTokenSession(ctx context.Context, signature string, request fosite.Requester) error
CreateRefreshTokenSession stores a refresh token session
func (*IDPStorage) CreateSession ¶ added in v0.0.4
CreateSession creates a new session for the given username
func (*IDPStorage) CreateUser ¶ added in v0.0.4
func (s *IDPStorage) CreateUser(ctx context.Context, username, password, state string) error
CreateUser creates a new user with hashed password and specified state
func (*IDPStorage) DeleteAccessTokenSession ¶ added in v0.0.4
func (s *IDPStorage) DeleteAccessTokenSession(ctx context.Context, signature string) error
DeleteAccessTokenSession deletes an access token session
func (*IDPStorage) DeleteOpenIDConnectSession ¶ added in v0.0.4
func (s *IDPStorage) DeleteOpenIDConnectSession(ctx context.Context, signature string) error
DeleteOpenIDConnectSession implements openid.OpenIDConnectRequestStorage interface
func (*IDPStorage) DeletePKCERequestSession ¶ added in v0.0.4
func (s *IDPStorage) DeletePKCERequestSession(ctx context.Context, signature string) error
DeletePKCERequestSession deletes a PKCE request session
func (*IDPStorage) DeleteRefreshTokenSession ¶ added in v0.0.4
func (s *IDPStorage) DeleteRefreshTokenSession(ctx context.Context, signature string) error
DeleteRefreshTokenSession deletes a refresh token session
func (*IDPStorage) DeleteSession ¶ added in v0.0.4
func (s *IDPStorage) DeleteSession(ctx context.Context, sessionID string) error
DeleteSession deletes a session
func (*IDPStorage) GetAccessTokenSession ¶ added in v0.0.4
func (s *IDPStorage) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetAccessTokenSession retrieves an access token session
func (*IDPStorage) GetAuthorizeCodeSession ¶ added in v0.0.4
func (s *IDPStorage) GetAuthorizeCodeSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetAuthorizeCodeSession retrieves an authorization code session
func (*IDPStorage) GetOpenIDConnectSession ¶ added in v0.0.4
func (s *IDPStorage) GetOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) (fosite.Requester, error)
GetOpenIDConnectSession implements openid.OpenIDConnectRequestStorage interface
func (*IDPStorage) GetPKCERequestSession ¶ added in v0.0.4
func (s *IDPStorage) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetPKCERequestSession retrieves a PKCE request session
func (*IDPStorage) GetRefreshTokenSession ¶ added in v0.0.4
func (s *IDPStorage) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetRefreshTokenSession retrieves a refresh token session
func (*IDPStorage) GetSession ¶ added in v0.0.4
GetSession retrieves the username for a given session ID
func (*IDPStorage) GetUserState ¶ added in v0.0.4
GetUserState retrieves the state of a user
func (*IDPStorage) InvalidateAuthorizeCodeSession ¶ added in v0.0.4
func (s *IDPStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) error
InvalidateAuthorizeCodeSession invalidates an authorization code
func (*IDPStorage) LoadHMACSecret ¶ added in v0.0.4
func (s *IDPStorage) LoadHMACSecret(ctx context.Context) ([]byte, error)
LoadHMACSecret loads the HMAC secret. See OAuth2Storage.LoadHMACSecret.
func (*IDPStorage) LoadRSAKey ¶ added in v0.0.4
func (s *IDPStorage) LoadRSAKey(ctx context.Context) (string, error)
LoadRSAKey loads the RSA private key. See OAuth2Storage.LoadRSAKey.
func (*IDPStorage) RevokeAccessToken ¶ added in v0.0.4
func (s *IDPStorage) RevokeAccessToken(ctx context.Context, requestID string) error
RevokeAccessToken revokes an access token
func (*IDPStorage) RevokeRefreshToken ¶ added in v0.0.4
func (s *IDPStorage) RevokeRefreshToken(ctx context.Context, requestID string) error
RevokeRefreshToken revokes a refresh token
func (*IDPStorage) RevokeRefreshTokenMaybeGracePeriod ¶ added in v0.0.4
func (s *IDPStorage) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestID string, _ string) error
RevokeRefreshTokenMaybeGracePeriod implements fosite.TokenRevocationStorage interface
func (*IDPStorage) SaveHMACSecret ¶ added in v0.0.4
func (s *IDPStorage) SaveHMACSecret(ctx context.Context, secret []byte) error
SaveHMACSecret stores the HMAC secret. See OAuth2Storage.SaveHMACSecret.
func (*IDPStorage) SaveRSAKey ¶ added in v0.0.4
func (s *IDPStorage) SaveRSAKey(ctx context.Context, privateKeyPEM string) error
SaveRSAKey stores the RSA private key. Mirrors OAuth2Storage's implementation — see SaveRSAKey there for the encryption rationale.
func (*IDPStorage) SetClientAssertionJWT ¶ added in v0.0.4
SetClientAssertionJWT implements fosite.SetClientAssertionJWT interface
func (*IDPStorage) SetSealer ¶ added in v0.0.11
func (s *IDPStorage) SetSealer(sealer *seal.Sealer)
SetSealer enables envelope encryption for the long-lived secret columns (RSA private key, HMAC secret). See OAuth2Storage.SetSealer.
func (*IDPStorage) UserExists ¶ added in v0.0.4
UserExists checks if a user exists
type InteractiveCreateTerminalRequest ¶ added in v0.0.11
type InteractiveCreateTerminalRequest struct {
Cpus int `json:"cpus,omitempty"`
MemoryMB int `json:"memory_mb,omitempty"`
DiskMB int `json:"disk_mb,omitempty"`
// GPU fields. Mirrored verbatim into request_gpus and the
// gpus_minimum_* / cuda_version / require_gpus submit lines.
// Gpus == 0 disables the entire GPU section in the submit file.
Gpus int `json:"gpus,omitempty"`
GpusMinimumCapability string `json:"gpus_minimum_capability,omitempty"`
GpusMinimumMemory int `json:"gpus_minimum_memory,omitempty"`
GpusMinimumRuntime string `json:"gpus_minimum_runtime,omitempty"`
CudaVersion string `json:"cuda_version,omitempty"`
RequireGpus string `json:"require_gpus,omitempty"`
}
InteractiveCreateTerminalRequest is the optional JSON body of POST /api/v1/interactive/terminal. All fields are optional; the server fills sensible defaults.
type InteractiveCreateTerminalResponse ¶ added in v0.0.11
type InteractiveCreateTerminalResponse struct {
InstanceID string `json:"instance_id"`
ClusterID int `json:"cluster_id"`
ProcID int `json:"proc_id"`
JobID string `json:"job_id"` // "cluster.proc" — convenience for the SPA
BatchName string `json:"batch_name"`
}
InteractiveCreateTerminalResponse is the JSON returned on success.
type InteractiveTerminalSummary ¶ added in v0.0.11
type InteractiveTerminalSummary struct {
InstanceID string `json:"instance_id"`
JobID string `json:"job_id"`
ClusterID int `json:"cluster_id"`
ProcID int `json:"proc_id"`
BatchName string `json:"batch_name"`
JobStatus int `json:"job_status"`
JobCurrentStartExecutingDate int64 `json:"job_current_start_executing_date,omitempty"`
HoldReasonCode int `json:"hold_reason_code,omitempty"`
HoldReason string `json:"hold_reason,omitempty"`
SubmittedAt string `json:"submitted_at,omitempty"` // RFC3339 from QDate
}
InteractiveTerminalSummary is the SPA-facing shape of one terminal session. Returned by GET /api/v1/interactive/terminal.
JobCurrentStartExecutingDate is the schedd's "executable actually started running" timestamp; combined with JobStatus the SPA's shared status module distinguishes "queued" from "transferring input" from "executing".
type JobActionFunc ¶ added in v0.0.3
type JobActionFunc func(ctx context.Context, constraint, reason string) (*htcondor.JobActionResults, error)
JobActionFunc is a function that performs a job action (hold, release, etc.)
type JobEditRequest ¶ added in v0.0.4
type JobEditRequest struct {
Attributes map[string]interface{} `json:"attributes"` // Attributes to update
}
JobEditRequest represents a job edit request
type JobListResponse ¶
JobListResponse represents a job listing response
type JobLogResponse ¶ added in v0.0.11
type JobLogResponse struct {
JobID string `json:"jobId"`
Filename string `json:"filename"`
Truncated bool `json:"truncated"`
Events []userlog.Event `json:"events"`
}
JobLogResponse is the JSON shape returned by GET /api/v1/jobs/{id}/log. It mirrors the stdout/stderr endpoints — explicit fetch, no streaming.
type JobSubmitRequest ¶
type JobSubmitRequest struct {
SubmitFile string `json:"submit_file"` // Submit file content
}
JobSubmitRequest represents a job submission request
type JobSubmitResponse ¶
type JobSubmitResponse struct {
ClusterID int `json:"cluster_id"`
JobIDs []string `json:"job_ids"` // Array of "cluster.proc" strings
}
JobSubmitResponse represents a job submission response
type JupyterCreateRequest ¶ added in v0.0.11
type JupyterCreateRequest struct {
// Image is the Docker image to launch. Default
// quay.io/jupyter/scipy-notebook:latest.
Image string `json:"image"`
// Cpus is the requested core count. Default 2.
Cpus int `json:"cpus"`
// MemoryMB is the requested RAM in mebibytes. Default 4096.
MemoryMB int `json:"memory_mb"`
// DiskMB is the requested scratch disk in mebibytes. Default 4096.
DiskMB int `json:"disk_mb"`
// GPU fields. Mirrored verbatim into request_gpus and the
// gpus_minimum_* / cuda_version / require_gpus submit lines.
// Gpus == 0 disables the entire GPU section in the submit file.
Gpus int `json:"gpus,omitempty"`
GpusMinimumCapability string `json:"gpus_minimum_capability,omitempty"`
GpusMinimumMemory int `json:"gpus_minimum_memory,omitempty"`
GpusMinimumRuntime string `json:"gpus_minimum_runtime,omitempty"`
CudaVersion string `json:"cuda_version,omitempty"`
RequireGpus string `json:"require_gpus,omitempty"`
}
JupyterCreateRequest is the optional JSON body of POST /jupyter/instances. All fields have sensible defaults so a bare {} is a valid request.
type JupyterCreateResponse ¶ added in v0.0.11
type JupyterCreateResponse struct {
InstanceID string `json:"instance_id"`
ClusterID string `json:"cluster_id"`
// ProxyPath is where the browser should eventually point its iframe
// (only useful once the helper has connected back; the SSE stream
// from /events tells you when).
ProxyPath string `json:"proxy_path"`
}
JupyterCreateResponse is the JSON returned by POST /jupyter/instances.
type JupyterInstanceSummary ¶ added in v0.0.11
type JupyterInstanceSummary struct {
InstanceID string `json:"instance_id"`
ClusterID string `json:"cluster_id,omitempty"`
Image string `json:"image,omitempty"`
Owner string `json:"owner"`
CreatedAt string `json:"created_at"`
Connected bool `json:"connected"` // helper has dialed back
ProxyPath string `json:"proxy_path"`
EventsPath string `json:"events_path"`
JobStatus int `json:"job_status,omitempty"`
JobCurrentStartExecutingDate int64 `json:"job_current_start_executing_date,omitempty"`
HoldReasonCode int `json:"hold_reason_code,omitempty"`
HoldReason string `json:"hold_reason,omitempty"`
}
JupyterInstanceSummary is the SPA-facing shape returned by both GET /api/v1/jupyter/instances (list) and GET /api/v1/jupyter/instances/{id} (single). The proxy_path is what the iframe should mount; the events_path drives the SSE stream.
The job_* fields are populated from a single bulk schedd query (handleJupyterListInstances) so the list view can run the same status-interpretation logic the detail page uses, without a round-trip per row. Empty when the schedd query failed or the cluster is gone — the SPA falls back to a "loading"/"connected only" view in that case.
type LoginRateLimiter ¶ added in v0.0.4
type LoginRateLimiter struct {
// contains filtered or unexported fields
}
LoginRateLimiter manages rate limiting for login attempts per IP address
func NewLoginRateLimiter ¶ added in v0.0.4
func NewLoginRateLimiter(r rate.Limit, b int) *LoginRateLimiter
NewLoginRateLimiter creates a new login rate limiter rate: maximum requests per second per IP burst: maximum burst size per IP
func (*LoginRateLimiter) Allow ¶ added in v0.0.4
func (l *LoginRateLimiter) Allow(ip string) bool
Allow checks if a login attempt from the given IP is allowed
type OAuth2Provider ¶ added in v0.0.3
type OAuth2Provider struct {
// contains filtered or unexported fields
}
OAuth2Provider manages OAuth2 operations
func NewOAuth2Provider ¶ added in v0.0.3
func NewOAuth2Provider(opts OAuth2ProviderOptions) (*OAuth2Provider, error)
NewOAuth2Provider creates a new OAuth2 provider with SQLite storage. Both AccessTokenLifespan and RefreshTokenLifespan in opts must be > 0; otherwise an error is returned. This is intentional: silent fallback to fosite's defaults (1h access, 30d refresh) has bitten downstream projects when callers forget to pass them through, so callers must opt in explicitly.
func (*OAuth2Provider) Close ¶ added in v0.0.3
func (p *OAuth2Provider) Close() error
Close is now a no-op: the OAuth2 provider does not own the underlying *sql.DB anymore. The Handler that opened the unified app DB is responsible for closing it on shutdown. Method retained so callers that defer p.Close() during refactors don't break.
func (*OAuth2Provider) GetProvider ¶ added in v0.0.3
func (p *OAuth2Provider) GetProvider() fosite.OAuth2Provider
GetProvider returns the underlying fosite OAuth2Provider
func (*OAuth2Provider) GetStorage ¶ added in v0.0.3
func (p *OAuth2Provider) GetStorage() *OAuth2Storage
GetStorage returns the OAuth2 storage
func (*OAuth2Provider) GetStrategy ¶ added in v0.0.4
func (p *OAuth2Provider) GetStrategy() *compose.CommonStrategy
GetStrategy returns the OAuth2 strategy
func (*OAuth2Provider) IntrospectToken ¶ added in v0.0.4
IntrospectToken validates an access token and returns the session
func (*OAuth2Provider) UpdateIssuer ¶ added in v0.0.4
func (p *OAuth2Provider) UpdateIssuer(issuer string)
UpdateIssuer updates the issuer URL in the configuration This is useful when using port 0 and getting the actual port after server start
type OAuth2ProviderOptions ¶ added in v0.0.11
type OAuth2ProviderOptions struct {
DB *sql.DB
Issuer string
AccessTokenLifespan time.Duration
RefreshTokenLifespan time.Duration
// Sealer envelope-encrypts long-lived secrets in the DB (the
// issuer's RSA private key, fosite's HMAC GlobalSecret). When
// non-nil, the storage adapter pulls/pushes ciphertext + wrapped
// DEK on the corresponding load/save calls. Nil = plaintext.
Sealer *seal.Sealer
}
OAuth2ProviderOptions configures lifespans and other tunables for the OAuth2 provider. Lifespans must be > 0; callers are expected to validate or default before constructing. DB is the unified application database (see appdb); the provider does not own its lifecycle.
type OAuth2StateEntry ¶ added in v0.0.3
type OAuth2StateEntry struct {
AuthorizeRequest fosite.AuthorizeRequester
Timestamp time.Time
OriginalURL string // Original URL to redirect back to after authentication
Username string // Authenticated username for consent flow
Groups []string // User groups for scope filtering in consent flow
}
OAuth2StateEntry represents a stored OAuth2 authorization state
type OAuth2StateStore ¶ added in v0.0.3
type OAuth2StateStore struct {
// contains filtered or unexported fields
}
OAuth2StateStore manages OAuth2 state parameters for the authorization flow
func NewOAuth2StateStore ¶ added in v0.0.3
func NewOAuth2StateStore() *OAuth2StateStore
NewOAuth2StateStore creates a new OAuth2 state store Call Start() to begin the cleanup goroutine
func (*OAuth2StateStore) GenerateState ¶ added in v0.0.3
func (s *OAuth2StateStore) GenerateState() (string, error)
GenerateState generates a secure random state parameter
func (*OAuth2StateStore) Get ¶ added in v0.0.3
func (s *OAuth2StateStore) Get(state string) (fosite.AuthorizeRequester, bool)
Get retrieves and removes an authorize request for the given state
func (*OAuth2StateStore) GetWithURL ¶ added in v0.0.4
func (s *OAuth2StateStore) GetWithURL(state string) (fosite.AuthorizeRequester, string, bool)
GetWithURL retrieves and removes an authorize request for the given state along with the original URL
func (*OAuth2StateStore) GetWithUsername ¶ added in v0.0.4
func (s *OAuth2StateStore) GetWithUsername(state string) (fosite.AuthorizeRequester, string, []string, bool)
GetWithUsername retrieves an authorize request for the given state along with username and groups (without removing)
func (*OAuth2StateStore) Remove ¶ added in v0.0.4
func (s *OAuth2StateStore) Remove(state string)
Remove removes an entry for the given state
func (*OAuth2StateStore) Start ¶ added in v0.0.7
func (s *OAuth2StateStore) Start(ctx context.Context)
Start begins the cleanup goroutine
func (*OAuth2StateStore) Store ¶ added in v0.0.3
func (s *OAuth2StateStore) Store(state string, ar fosite.AuthorizeRequester)
Store stores an authorize request with the given state
func (*OAuth2StateStore) StoreWithURL ¶ added in v0.0.4
func (s *OAuth2StateStore) StoreWithURL(state string, ar fosite.AuthorizeRequester, originalURL string)
StoreWithURL stores an authorize request with the given state and original URL
func (*OAuth2StateStore) StoreWithUsername ¶ added in v0.0.4
func (s *OAuth2StateStore) StoreWithUsername(state string, ar fosite.AuthorizeRequester, originalURL, username string, groups ...[]string)
StoreWithUsername stores an authorize request with the given state, original URL, and username
func (*OAuth2StateStore) Wait ¶ added in v0.0.7
func (s *OAuth2StateStore) Wait()
Wait waits for the cleanup goroutine to finish
type OAuth2Storage ¶ added in v0.0.3
type OAuth2Storage struct {
// contains filtered or unexported fields
}
OAuth2Storage implements fosite storage interfaces using the unified application database. The schema is owned by appdb's migrations — this struct is purely a thin set of query helpers around an already- migrated *sql.DB.
The optional `sealer` field is the application's envelope-encryption gate. When non-nil, SaveRSAKey / SaveHMACSecret store ciphertext + wrapped DEK; LoadRSAKey / LoadHMACSecret transparently decrypt rows whose DEK column is populated. When nil, the storage falls back to the pre-encryption plaintext behavior — back-compat for deployments that haven't configured a KEK yet.
func NewOAuth2Storage ¶ added in v0.0.3
func NewOAuth2Storage(db *sql.DB) *OAuth2Storage
NewOAuth2Storage wraps an already-opened DB in the OAuth2 storage helpers. Schema creation is no longer this struct's responsibility — see httpserver/appdb. The caller retains ownership of the DB (don't call Close() here on shutdown).
The returned storage starts in plaintext mode; call SetSealer if a KEK has been loaded.
func (*OAuth2Storage) ApproveDeviceCodeSession ¶ added in v0.0.4
func (s *OAuth2Storage) ApproveDeviceCodeSession(ctx context.Context, userCode string, subject string, session fosite.Session) error
ApproveDeviceCodeSession approves a device code (user authorized the device)
func (*OAuth2Storage) ApproveDeviceCodeSessionWithScopes ¶ added in v0.0.12
func (s *OAuth2Storage) ApproveDeviceCodeSessionWithScopes(ctx context.Context, userCode string, subject string, session fosite.Session, grantedScopes []string) error
ApproveDeviceCodeSessionWithScopes is like ApproveDeviceCodeSession but also overrides the device code's granted_scopes column with the supplied subset. Use this when the consent UI showed the user per-scope checkboxes and the user declined some — the resulting access token must reflect the user-approved intersection, not the originally-requested set.
Pass nil grantedScopes to leave the existing granted_scopes untouched (equivalent to ApproveDeviceCodeSession). Pass an empty (non-nil) slice to record "user explicitly approved zero scopes" — useful as a sentinel; fosite will refuse to mint a token for a no-scope grant, but we want the audit log to show the user's choice.
func (*OAuth2Storage) ClientAssertionJWTValid ¶ added in v0.0.3
func (s *OAuth2Storage) ClientAssertionJWTValid(ctx context.Context, jti string) error
ClientAssertionJWTValid implements fosite.ClientAssertionJWTValid interface This checks if a JWT ID (JTI) has already been used to prevent replay attacks
func (*OAuth2Storage) CreateAccessTokenSession ¶ added in v0.0.3
func (s *OAuth2Storage) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) error
CreateAccessTokenSession stores an access token session
func (*OAuth2Storage) CreateAuthorizeCodeSession ¶ added in v0.0.3
func (s *OAuth2Storage) CreateAuthorizeCodeSession(ctx context.Context, signature string, request fosite.Requester) error
CreateAuthorizeCodeSession stores an authorization code session
func (*OAuth2Storage) CreateClient ¶ added in v0.0.3
func (s *OAuth2Storage) CreateClient(ctx context.Context, client *fosite.DefaultClient) error
CreateClient creates a new OAuth2 client
func (*OAuth2Storage) CreateDeviceCodeSession ¶ added in v0.0.4
func (s *OAuth2Storage) CreateDeviceCodeSession(ctx context.Context, deviceCode string, userCode string, request fosite.Requester, expiresAt time.Time) error
CreateDeviceCodeSession creates a new device code session
func (*OAuth2Storage) CreateOpenIDConnectSession ¶ added in v0.0.3
func (s *OAuth2Storage) CreateOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) error
CreateOpenIDConnectSession implements openid.OpenIDConnectRequestStorage interface
func (*OAuth2Storage) CreatePKCERequestSession ¶ added in v0.0.4
func (s *OAuth2Storage) CreatePKCERequestSession(ctx context.Context, signature string, request fosite.Requester) error
CreatePKCERequestSession stores a PKCE request session
func (*OAuth2Storage) CreateRefreshTokenSession ¶ added in v0.0.3
func (s *OAuth2Storage) CreateRefreshTokenSession(ctx context.Context, signature string, request fosite.Requester) error
CreateRefreshTokenSession stores a refresh token session
func (*OAuth2Storage) DeleteAccessTokenSession ¶ added in v0.0.3
func (s *OAuth2Storage) DeleteAccessTokenSession(ctx context.Context, signature string) error
DeleteAccessTokenSession deletes an access token session
func (*OAuth2Storage) DeleteOpenIDConnectSession ¶ added in v0.0.3
func (s *OAuth2Storage) DeleteOpenIDConnectSession(ctx context.Context, signature string) error
DeleteOpenIDConnectSession implements openid.OpenIDConnectRequestStorage interface
func (*OAuth2Storage) DeletePKCERequestSession ¶ added in v0.0.4
func (s *OAuth2Storage) DeletePKCERequestSession(ctx context.Context, signature string) error
DeletePKCERequestSession deletes a PKCE request session
func (*OAuth2Storage) DeleteRefreshTokenSession ¶ added in v0.0.3
func (s *OAuth2Storage) DeleteRefreshTokenSession(ctx context.Context, signature string) error
DeleteRefreshTokenSession deletes a refresh token session
func (*OAuth2Storage) DenyDeviceCodeSession ¶ added in v0.0.4
func (s *OAuth2Storage) DenyDeviceCodeSession(ctx context.Context, userCode string) error
DenyDeviceCodeSession denies a device code (user rejected the device)
func (*OAuth2Storage) GetAccessTokenSession ¶ added in v0.0.3
func (s *OAuth2Storage) GetAccessTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetAccessTokenSession retrieves an access token session
func (*OAuth2Storage) GetAuthorizeCodeSession ¶ added in v0.0.3
func (s *OAuth2Storage) GetAuthorizeCodeSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetAuthorizeCodeSession retrieves an authorization code session
func (*OAuth2Storage) GetDB ¶ added in v0.0.4
func (s *OAuth2Storage) GetDB() *sql.DB
GetDB returns the underlying database connection. Kept on the struct because tests and the SessionStore wiring still reach for it.
func (*OAuth2Storage) GetDeviceCodeSession ¶ added in v0.0.4
func (s *OAuth2Storage) GetDeviceCodeSession(ctx context.Context, deviceCode string, session fosite.Session) (fosite.Requester, error)
GetDeviceCodeSession retrieves a device code session by device code
func (*OAuth2Storage) GetDeviceCodeSessionByUserCode ¶ added in v0.0.4
func (s *OAuth2Storage) GetDeviceCodeSessionByUserCode(ctx context.Context, userCode string) (string, fosite.Requester, error)
GetDeviceCodeSessionByUserCode retrieves a device code session by user code
func (*OAuth2Storage) GetOpenIDConnectSession ¶ added in v0.0.3
func (s *OAuth2Storage) GetOpenIDConnectSession(ctx context.Context, signature string, requester fosite.Requester) (fosite.Requester, error)
GetOpenIDConnectSession implements openid.OpenIDConnectRequestStorage interface
func (*OAuth2Storage) GetPKCERequestSession ¶ added in v0.0.4
func (s *OAuth2Storage) GetPKCERequestSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetPKCERequestSession retrieves a PKCE request session
func (*OAuth2Storage) GetRefreshTokenSession ¶ added in v0.0.3
func (s *OAuth2Storage) GetRefreshTokenSession(ctx context.Context, signature string, session fosite.Session) (fosite.Requester, error)
GetRefreshTokenSession retrieves a refresh token session
func (*OAuth2Storage) InvalidateAuthorizeCodeSession ¶ added in v0.0.3
func (s *OAuth2Storage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) error
InvalidateAuthorizeCodeSession invalidates an authorization code
func (*OAuth2Storage) InvalidateDeviceCodeSession ¶ added in v0.0.4
func (s *OAuth2Storage) InvalidateDeviceCodeSession(ctx context.Context, deviceCode string) error
InvalidateDeviceCodeSession invalidates a device code after it's been used
func (*OAuth2Storage) LoadHMACSecret ¶ added in v0.0.3
func (s *OAuth2Storage) LoadHMACSecret(ctx context.Context) ([]byte, error)
LoadHMACSecret loads the HMAC secret. See LoadRSAKey for the encryption-vs-plaintext branching.
func (*OAuth2Storage) LoadRSAKey ¶ added in v0.0.3
func (s *OAuth2Storage) LoadRSAKey(ctx context.Context) (string, error)
LoadRSAKey loads the RSA private key. Falls back to plaintext when the row's DEK column is NULL — that's the pre-encryption format and the format used when no KEK is configured. When a DEK is present but no sealer is configured (KEK was removed without rotating data), returns an explicit error rather than handing back ciphertext or silently regenerating the key.
func (*OAuth2Storage) RevokeAccessToken ¶ added in v0.0.3
func (s *OAuth2Storage) RevokeAccessToken(ctx context.Context, requestID string) error
RevokeAccessToken revokes an access token
func (*OAuth2Storage) RevokeRefreshToken ¶ added in v0.0.3
func (s *OAuth2Storage) RevokeRefreshToken(ctx context.Context, requestID string) error
RevokeRefreshToken revokes a refresh token
func (*OAuth2Storage) RevokeRefreshTokenMaybeGracePeriod ¶ added in v0.0.3
func (s *OAuth2Storage) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestID string, _ string) error
RevokeRefreshTokenMaybeGracePeriod implements fosite.TokenRevocationStorage interface This handles refresh token revocation. The signature parameter allows for grace period implementation but for simplicity we immediately revoke the token by request ID
func (*OAuth2Storage) SaveHMACSecret ¶ added in v0.0.3
func (s *OAuth2Storage) SaveHMACSecret(ctx context.Context, secret []byte) error
SaveHMACSecret stores the HMAC secret. See SaveRSAKey for the encryption-vs-plaintext branching.
func (*OAuth2Storage) SaveRSAKey ¶ added in v0.0.3
func (s *OAuth2Storage) SaveRSAKey(ctx context.Context, privateKeyPEM string) error
SaveRSAKey stores the RSA private key. When a sealer is set the PEM bytes are encrypted under a fresh per-row DEK (itself wrapped by the DB-instance KEK); when no sealer is configured the PEM is written verbatim — same on-disk shape as the pre-KEK schema, kept for back-compat with deployments that haven't enabled encryption.
func (*OAuth2Storage) SetClientAssertionJWT ¶ added in v0.0.3
SetClientAssertionJWT implements fosite.SetClientAssertionJWT interface This stores the JTI (JWT ID) with expiration to prevent replay attacks
func (*OAuth2Storage) SetSealer ¶ added in v0.0.11
func (s *OAuth2Storage) SetSealer(sealer *seal.Sealer)
SetSealer enables envelope encryption for the long-lived secret columns (RSA private key, HMAC secret). Calling with nil restores plaintext mode. Callers should set this once at startup, before SaveRSAKey / SaveHMACSecret have a chance to fire — the startup-backfill path in NewHandler does exactly that.
func (*OAuth2Storage) UpdateDeviceCodePolling ¶ added in v0.0.4
func (s *OAuth2Storage) UpdateDeviceCodePolling(ctx context.Context, deviceCode string) error
UpdateDeviceCodePolling updates the last polled timestamp for rate limiting
type PeekResponse ¶ added in v0.0.12
type PeekResponse struct {
Stdout *PeekedStreamResponse `json:"stdout,omitempty"`
Stderr *PeekedStreamResponse `json:"stderr,omitempty"`
}
PeekResponse mirrors htcondor.PeekResult on the wire. Fields that weren't requested (or that the starter elected not to return) are omitted entirely so the SPA can detect "stream wasn't transferred" without inferring from a zero-length string.
type PeekedStreamResponse ¶ added in v0.0.12
PeekedStreamResponse is the JSON shape returned for one of the requested streams. `bytes` is the raw text the starter sent (the caller is responsible for handling NUL/binary content if it shows up — stdout/stderr are nearly always UTF-8). `offset` is the absolute file offset *after* this read; pass it back as `stdout_offset` / `stderr_offset` on the next call to follow.
type PingResponse ¶ added in v0.0.4
type PingResponse struct {
Daemon string `json:"daemon"` // "collector" or "schedd"
AuthMethod string `json:"auth_method"` // Authentication method used
User string `json:"user"` // Authenticated username
SessionID string `json:"session_id"` // Session identifier
ValidCommands string `json:"valid_commands"` // Commands authorized
Encryption bool `json:"encryption"` // Whether encryption is enabled
Authentication bool `json:"authentication"` // Whether authentication is enabled
Authorized bool `json:"authorized,omitempty"` // Whether authorized for requested permission (if permission checked)
Permission string `json:"permission,omitempty"` // Permission level checked (if any)
}
PingResponse represents a ping response for a daemon
type Server ¶
type Server struct {
*Handler // Embedded handler for business logic
// contains filtered or unexported fields
}
Server represents the HTTP API server
func (*Server) GetAddr ¶ added in v0.0.3
GetAddr returns the actual listening address of the server. Returns empty string if the server hasn't started yet.
func (*Server) ServeListener ¶ added in v0.0.11
ServeListener runs the API server on a caller-supplied net.Listener. scheme controls which protocol the request URLs are advertised under ("http" or "https") — use "https" if the caller has configured httpServer.TLSConfig, "http" otherwise.
This is the entry point used when condor_master spawns us as a managed daemon and we accept forwarded connections from condor_shared_port via a sharedport.Listener instead of binding our own TCP port. The handler bootstrap (issuer URL, OAuth2 setup) is the same as Start/StartTLS; the only difference is the kind of listener we hand to http.Server.Serve.
type SessionData ¶ added in v0.0.4
type SessionData struct {
Username string // Authenticated username
Groups []string // User groups from IDP (for scope filtering)
CreatedAt time.Time // When the session was created
ExpiresAt time.Time // When the session expires
}
SessionData represents the data stored in a session.
Note: this struct used to carry a Token field that was reserved for per-session HTCondor token storage. The column was never written to and the field is gone — the schema migration in 0002_envelope_encryption.sql drops `http_sessions.token` to remove the unused secret-shaped column. Per-user tokens, when needed, flow through the OAuth2 / IDP storage tables instead.
type SessionStore ¶ added in v0.0.4
type SessionStore struct {
// contains filtered or unexported fields
}
SessionStore manages HTTP sessions with SQLite persistence
func NewSessionStore ¶ added in v0.0.4
NewSessionStore creates a new session store with database persistence The db parameter should be the same database connection used by OAuth2Storage
func (*SessionStore) Cleanup ¶ added in v0.0.4
func (s *SessionStore) Cleanup()
Cleanup removes expired sessions
func (*SessionStore) Create ¶ added in v0.0.4
func (s *SessionStore) Create(username string, groups ...[]string) (string, *SessionData, error)
Create creates a new session for the given username and groups
func (*SessionStore) Delete ¶ added in v0.0.4
func (s *SessionStore) Delete(sessionID string)
Delete removes a session
func (*SessionStore) Get ¶ added in v0.0.4
func (s *SessionStore) Get(sessionID string) *SessionData
Get retrieves a session by ID Returns nil if session doesn't exist or has expired
func (*SessionStore) Size ¶ added in v0.0.4
func (s *SessionStore) Size() int
Size returns the number of active sessions
type ShareOutputRequest ¶ added in v0.0.11
type ShareOutputRequest struct {
}
ShareOutputRequest is the body for POST /api/v1/jobs/{id}/output/share.
type ShareOutputResponse ¶ added in v0.0.11
type ShareOutputResponse struct {
}
ShareOutputResponse is what the SPA gets back. Owner is echoed for UX so the share preview can label the URL with "downloads as <owner>".
type TokenCache ¶ added in v0.0.3
type TokenCache struct {
// contains filtered or unexported fields
}
TokenCache manages validated tokens and their associated session caches
func NewTokenCache ¶ added in v0.0.3
func NewTokenCache() *TokenCache
NewTokenCache creates a new token cache
func (*TokenCache) Add ¶ added in v0.0.3
func (tc *TokenCache) Add(token string) (*TokenCacheEntry, error)
Add adds a validated token to the cache with a session cache If the token is already in the cache, returns the existing entry Automatically schedules cleanup when the token expires
func (*TokenCache) AddValidated ¶ added in v0.0.4
func (tc *TokenCache) AddValidated(token, username string, expiration time.Time) (*TokenCacheEntry, error)
AddValidated adds a pre-validated token (e.g. opaque token) to the cache
func (*TokenCache) Get ¶ added in v0.0.3
func (tc *TokenCache) Get(token string) (*TokenCacheEntry, bool)
Get retrieves a token cache entry if it exists and is not expired
func (*TokenCache) MarkValidated ¶ added in v0.0.12
func (tc *TokenCache) MarkValidated(token, authoritativeUsername string)
MarkValidated promotes a cached token to "validated" status, meaning a schedd op has authenticated successfully with it. Callers may optionally pass an authoritativeUsername observed from the schedd handshake — if non-empty and different from the JWT-claimed username, the entry is updated to the schedd-authoritative value (this protects against any case where the unverified sub claim disagreed with the schedd's interpretation).
Idempotent: safe to call repeatedly per request.
func (*TokenCache) Remove ¶ added in v0.0.3
func (tc *TokenCache) Remove(token string)
Remove removes a token from the cache and cancels its cleanup timer
func (*TokenCache) Size ¶ added in v0.0.3
func (tc *TokenCache) Size() int
Size returns the number of cached tokens
func (*TokenCache) ValidatedUsername ¶ added in v0.0.12
func (tc *TokenCache) ValidatedUsername(token string) string
ValidatedUsername returns the username for a token only if it has been marked validated via a successful schedd handshake. Use this in code paths that must rely on authoritative identity (job-owner filtering, share-URL minting, audit logs). For loose use cases (rate-limit bucket key) the Get-and-read-Username pattern is fine.
Returns "" if the token is unknown, expired, or not yet validated.
type TokenCacheEntry ¶ added in v0.0.3
type TokenCacheEntry struct {
Token string
Username string // sub from the JWT — unverified until Validated == true
Validated bool // true once a schedd op authenticated successfully with this token
Expiration time.Time
SessionCache *security.SessionCache
// contains filtered or unexported fields
}
TokenCacheEntry represents a cached token with its expiration and associated session cache.
Identity-trust note: Username is parsed from the JWT WITHOUT verifying the signature (we have no local way to verify — the only authoritative validator is the schedd's CEDAR handshake, which happens later when we make a schedd call). Until that handshake succeeds, the Username reflects whatever the client put in the token's `sub` claim and MUST NOT be used as authoritative identity (e.g. for filtering jobs to "owned by me", recording the Owner when minting a share URL, or any other authorization decision).
Validated reports whether at least one schedd op has succeeded with this token. Code paths that need authoritative identity should gate on Validated; code paths that only need a stable bucket key (rate-limit per-token / per-username) can use Username directly.
type UserInfo ¶ added in v0.0.3
type UserInfo struct {
Subject string `json:"sub"`
Email string `json:"email"`
Name string `json:"name"`
Groups interface{} `json:"groups"` // Can be []string or string
Claims map[string]interface{} // Additional claims
}
UserInfo represents user information from the IDP
type VersionResponse ¶ added in v0.0.11
VersionResponse represents a build-info response.
type WhoAmIResponse ¶ added in v0.0.4
type WhoAmIResponse struct {
Authenticated bool `json:"authenticated"`
User string `json:"user,omitempty"` // Omit if not authenticated
}
WhoAmIResponse represents a whoami response
Source Files
¶
- apikey_auth.go
- apikey_store.go
- auth.go
- device_code_handler.go
- diagnostics.go
- handler.go
- handlers.go
- handlers_admin.go
- handlers_admin_api_keys.go
- handlers_chat.go
- handlers_chat_doc_tools.go
- handlers_chat_job_detail_tools.go
- handlers_chat_slots_tool.go
- handlers_chat_submit_tools.go
- handlers_chat_tools.go
- handlers_credd.go
- handlers_history.go
- handlers_interactive.go
- handlers_jupyter.go
- handlers_log.go
- handlers_match_analysis.go
- handlers_peek.go
- handlers_share.go
- handlers_ssh.go
- handlers_templates.go
- handlers_webui.go
- idp_handlers.go
- idp_provider.go
- idp_storage.go
- login_rate_limiter.go
- mcp_handlers.go
- metrics.go
- oauth2_provider.go
- oauth2_sso.go
- oauth2_state.go
- oauth2_storage.go
- openapi.go
- ping_health.go
- routes.go
- seal_setup.go
- server.go
- session.go
- submit_string_validate.go
- tarvalidate.go
- test_helpers.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package apikey implements the wire format and crypto for HTTP API authentication tokens this server issues for non-interactive callers (Prometheus, scripts, CI).
|
Package apikey implements the wire format and crypto for HTTP API authentication tokens this server issues for non-interactive callers (Prometheus, scripts, CI). |
|
Package appdb owns the single SQLite database the HTTP API server uses for OAuth2/MCP storage, the embedded IDP, browser sessions, and user-saved batch-submission templates.
|
Package appdb owns the single SQLite database the HTTP API server uses for OAuth2/MCP storage, the embedded IDP, browser sessions, and user-saved batch-submission templates. |
|
seal
Package seal provides envelope encryption for sensitive columns in the unified application database.
|
Package seal provides envelope encryption for sensitive columns in the unified application database. |
|
Package chat provides the LLM-backed chat endpoint that powers the "Ask about your jobs" surface in the SPA.
|
Package chat provides the LLM-backed chat endpoint that powers the "Ask about your jobs" surface in the SPA. |
|
Package jupyterhelperbin without the embed_jupyter_helper tag is a stub that reports "not embedded" — this is the default for `go build ./...` and for dev workflows that don't want a long Makefile dance every time the api binary is rebuilt.
|
Package jupyterhelperbin without the embed_jupyter_helper tag is a stub that reports "not embedded" — this is the default for `go build ./...` and for dev workflows that don't want a long Makefile dance every time the api binary is rebuilt. |
|
Package webui provides the embedded Next.js static export.
|
Package webui provides the embedded Next.js static export. |