Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions cmd/thv-operator/api/v1alpha1/mcpauthzconfig_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// SPDX-FileCopyrightText: Copyright 2025 Stacklok, Inc.
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)

// Condition type and reasons for MCPAuthzConfig status (RFC-0023)
const (
// ConditionTypeAuthzConfigValid indicates whether the MCPAuthzConfig configuration is valid
ConditionTypeAuthzConfigValid = ConditionTypeValid

// ConditionReasonAuthzConfigValid indicates spec validation passed
ConditionReasonAuthzConfigValid = "ConfigValid"

// ConditionReasonAuthzConfigInvalid indicates spec validation failed
ConditionReasonAuthzConfigInvalid = "ConfigInvalid"
)

// MCPAuthzConfigSpec defines the desired state of MCPAuthzConfig.
// MCPAuthzConfig resources are namespace-scoped and can only be referenced by
// MCPServer, MCPRemoteProxy, or VirtualMCPServer resources in the same namespace.
type MCPAuthzConfigSpec struct {
// Type identifies the authorizer backend (e.g., "cedarv1", "httpv1").
// Must match a registered authorizer type in the factory registry.
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
Type string `json:"type"`

// Config contains the backend-specific authorization configuration.
// The structure depends on the Type field:
// - cedarv1: policies ([]string), entities_json (string), primary_upstream_provider (string), group_claim_name (string)
// - httpv1: http ({url, timeout, insecure_skip_verify}), context ({include_args, include_operation}), claim_mapping (string)
// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Type=object
Config runtime.RawExtension `json:"config"`
}

// MCPAuthzConfigStatus defines the observed state of MCPAuthzConfig
type MCPAuthzConfigStatus struct {
// Conditions represent the latest available observations of the MCPAuthzConfig's state
// +listType=map
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`

// ObservedGeneration is the most recent generation observed for this MCPAuthzConfig.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// ConfigHash is a hash of the current configuration for change detection
// +optional
ConfigHash string `json:"configHash,omitempty"`

// ReferencingWorkloads is a list of workload resources that reference this MCPAuthzConfig.
// Each entry identifies the workload by kind and name.
// +listType=map
// +listMapKey=name
// +optional
ReferencingWorkloads []WorkloadReference `json:"referencingWorkloads,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=authzcfg,categories=toolhive
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Valid",type=string,JSONPath=`.status.conditions[?(@.type=='Valid')].status`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// MCPAuthzConfig is the Schema for the mcpauthzconfigs API.
// MCPAuthzConfig resources are namespace-scoped and can only be referenced by
// MCPServer, MCPRemoteProxy, or VirtualMCPServer resources within the same namespace.
// Cross-namespace references are not supported for security and isolation reasons.
type MCPAuthzConfig struct {
metav1.TypeMeta `json:",inline"` // nolint:revive
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec MCPAuthzConfigSpec `json:"spec,omitempty"`
Status MCPAuthzConfigStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// MCPAuthzConfigList contains a list of MCPAuthzConfig
type MCPAuthzConfigList struct {
metav1.TypeMeta `json:",inline"` // nolint:revive
metav1.ListMeta `json:"metadata,omitempty"`
Items []MCPAuthzConfig `json:"items"`
}

// MCPAuthzConfigReference references a shared MCPAuthzConfig resource.
// The referenced MCPAuthzConfig must be in the same namespace as the referencing workload.
type MCPAuthzConfigReference struct {
// Name is the name of the MCPAuthzConfig resource in the same namespace.
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
Name string `json:"name"`
}

func init() {
SchemeBuilder.Register(&MCPAuthzConfig{}, &MCPAuthzConfigList{})
}
15 changes: 14 additions & 1 deletion cmd/thv-operator/api/v1alpha1/mcpremoteproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type HeaderFromSecret struct {
// MCPRemoteProxySpec defines the desired state of MCPRemoteProxy
//
// +kubebuilder:validation:XValidation:rule="!(has(self.oidcConfig) && has(self.oidcConfigRef))",message="oidcConfig and oidcConfigRef are mutually exclusive; use oidcConfigRef to reference a shared MCPOIDCConfig"
// +kubebuilder:validation:XValidation:rule="!(has(self.authzConfig) && has(self.authzConfigRef))",message="authzConfig and authzConfigRef are mutually exclusive; use authzConfigRef to reference a shared MCPAuthzConfig"
//
//nolint:lll // CEL validation rules exceed line length limit
type MCPRemoteProxySpec struct {
Expand Down Expand Up @@ -87,10 +88,18 @@ type MCPRemoteProxySpec struct {
// +optional
HeaderForward *HeaderForwardConfig `json:"headerForward,omitempty"`

// AuthzConfig defines authorization policy configuration for the proxy
// AuthzConfig defines authorization policy configuration for the proxy.
// Deprecated: Use AuthzConfigRef to reference a shared MCPAuthzConfig resource instead.
// AuthzConfig and AuthzConfigRef are mutually exclusive.
// +optional
AuthzConfig *AuthzConfigRef `json:"authzConfig,omitempty"`

// AuthzConfigRef references a shared MCPAuthzConfig resource for authorization.
// The referenced MCPAuthzConfig must exist in the same namespace as this MCPRemoteProxy.
// Mutually exclusive with authzConfig.
// +optional
AuthzConfigRef *MCPAuthzConfigReference `json:"authzConfigRef,omitempty"`

// Audit defines audit logging configuration for the proxy
// +optional
Audit *AuditConfig `json:"audit,omitempty"`
Expand Down Expand Up @@ -187,6 +196,10 @@ type MCPRemoteProxyStatus struct {
// +optional
OIDCConfigHash string `json:"oidcConfigHash,omitempty"`

// AuthzConfigHash is the hash of the referenced MCPAuthzConfig spec for change detection
// +optional
AuthzConfigHash string `json:"authzConfigHash,omitempty"`

// Message provides additional information about the current phase
// +optional
Message string `json:"message,omitempty"`
Expand Down
32 changes: 31 additions & 1 deletion cmd/thv-operator/api/v1alpha1/mcpserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,23 @@ const (
ConditionReasonOIDCConfigRefError = "OIDCConfigRefError"
)

// Condition type for MCPAuthzConfig reference validation
const (
// ConditionAuthzConfigRefValidated indicates whether the AuthzConfigRef is valid
ConditionAuthzConfigRefValidated = "AuthzConfigRefValidated"
)

const (
// ConditionReasonAuthzConfigRefValid indicates the referenced MCPAuthzConfig is valid and ready
ConditionReasonAuthzConfigRefValid = "AuthzConfigRefValid"

// ConditionReasonAuthzConfigRefNotFound indicates the referenced MCPAuthzConfig was not found
ConditionReasonAuthzConfigRefNotFound = "AuthzConfigRefNotFound"

// ConditionReasonAuthzConfigRefNotValid indicates the referenced MCPAuthzConfig is not valid
ConditionReasonAuthzConfigRefNotValid = "AuthzConfigRefNotValid"
)

const (
// ConditionReasonCABundleRefValid indicates the CABundleRef is valid and the ConfigMap exists
ConditionReasonCABundleRefValid = "CABundleRefValid"
Expand Down Expand Up @@ -190,6 +207,7 @@ const SessionStorageProviderRedis = "redis"
// MCPServerSpec defines the desired state of MCPServer
//
// +kubebuilder:validation:XValidation:rule="!(has(self.oidcConfig) && has(self.oidcConfigRef))",message="oidcConfig and oidcConfigRef are mutually exclusive; use oidcConfigRef to reference a shared MCPOIDCConfig"
// +kubebuilder:validation:XValidation:rule="!(has(self.authzConfig) && has(self.authzConfigRef))",message="authzConfig and authzConfigRef are mutually exclusive; use authzConfigRef to reference a shared MCPAuthzConfig"
// +kubebuilder:validation:XValidation:rule="!(has(self.telemetry) && has(self.telemetryConfigRef))",message="telemetry and telemetryConfigRef are mutually exclusive; migrate to telemetryConfigRef"
// +kubebuilder:validation:XValidation:rule="!has(self.rateLimiting) || (has(self.sessionStorage) && self.sessionStorage.provider == 'redis')",message="rateLimiting requires sessionStorage with provider 'redis'"
// +kubebuilder:validation:XValidation:rule="!(has(self.rateLimiting) && has(self.rateLimiting.perUser)) || has(self.oidcConfig) || has(self.oidcConfigRef) || has(self.externalAuthConfigRef)",message="rateLimiting.perUser requires authentication (oidcConfig, oidcConfigRef, or externalAuthConfigRef)"
Expand Down Expand Up @@ -290,10 +308,18 @@ type MCPServerSpec struct {
// +optional
OIDCConfigRef *MCPOIDCConfigReference `json:"oidcConfigRef,omitempty"`

// AuthzConfig defines authorization policy configuration for the MCP server
// AuthzConfig defines authorization policy configuration for the MCP server.
// Deprecated: Use AuthzConfigRef to reference a shared MCPAuthzConfig resource instead.
// AuthzConfig and AuthzConfigRef are mutually exclusive.
// +optional
AuthzConfig *AuthzConfigRef `json:"authzConfig,omitempty"`

// AuthzConfigRef references a shared MCPAuthzConfig resource for authorization.
// The referenced MCPAuthzConfig must exist in the same namespace as this MCPServer.
// Mutually exclusive with authzConfig.
// +optional
AuthzConfigRef *MCPAuthzConfigReference `json:"authzConfigRef,omitempty"`

// Audit defines audit logging configuration for the MCP server
// +optional
Audit *AuditConfig `json:"audit,omitempty"`
Expand Down Expand Up @@ -1061,6 +1087,10 @@ type MCPServerStatus struct {
// +optional
TelemetryConfigHash string `json:"telemetryConfigHash,omitempty"`

// AuthzConfigHash is the hash of the referenced MCPAuthzConfig spec for change detection
// +optional
AuthzConfigHash string `json:"authzConfigHash,omitempty"`

// URL is the URL where the MCP server can be accessed
// +optional
URL string `json:"url,omitempty"`
Expand Down
17 changes: 15 additions & 2 deletions cmd/thv-operator/api/v1alpha1/virtualmcpserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ type EmbeddingServerRef struct {
//
// +kubebuilder:validation:XValidation:rule="self.type == 'oidc' ? (has(self.oidcConfig) || has(self.oidcConfigRef)) : true",message="spec.incomingAuth.oidcConfig or oidcConfigRef is required when type is oidc"
// +kubebuilder:validation:XValidation:rule="!(has(self.oidcConfig) && has(self.oidcConfigRef))",message="oidcConfig and oidcConfigRef are mutually exclusive; use oidcConfigRef to reference a shared MCPOIDCConfig"
// +kubebuilder:validation:XValidation:rule="!(has(self.authzConfig) && has(self.authzConfigRef))",message="authzConfig and authzConfigRef are mutually exclusive; use authzConfigRef to reference a shared MCPAuthzConfig"
//
//nolint:lll // CEL validation rules exceed line length limit
type IncomingAuthConfig struct {
Expand All @@ -133,10 +134,17 @@ type IncomingAuthConfig struct {
// +optional
OIDCConfigRef *MCPOIDCConfigReference `json:"oidcConfigRef,omitempty"`

// AuthzConfig defines authorization policy configuration
// Reuses MCPServer authz patterns
// AuthzConfig defines authorization policy configuration.
// Deprecated: Use AuthzConfigRef to reference a shared MCPAuthzConfig resource instead.
// AuthzConfig and AuthzConfigRef are mutually exclusive.
// +optional
AuthzConfig *AuthzConfigRef `json:"authzConfig,omitempty"`

// AuthzConfigRef references a shared MCPAuthzConfig resource for authorization.
// The referenced MCPAuthzConfig must exist in the same namespace as this VirtualMCPServer.
// Mutually exclusive with authzConfig.
// +optional
AuthzConfigRef *MCPAuthzConfigReference `json:"authzConfigRef,omitempty"`
}

// OutgoingAuthConfig configures authentication from Virtual MCP to backend MCPServers
Expand Down Expand Up @@ -228,6 +236,11 @@ type VirtualMCPServerStatus struct {
// Only populated when IncomingAuth.OIDCConfigRef is set.
// +optional
OIDCConfigHash string `json:"oidcConfigHash,omitempty"`

// AuthzConfigHash is the hash of the referenced MCPAuthzConfig spec for change detection.
// Only populated when IncomingAuth.AuthzConfigRef is set.
// +optional
AuthzConfigHash string `json:"authzConfigHash,omitempty"`
}

// VirtualMCPServerPhase represents the lifecycle phase of a VirtualMCPServer
Expand Down
Loading
Loading