Skip to content
Merged
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
4 changes: 4 additions & 0 deletions backend/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ type ClientType string
const (
ClientTypeUI ClientType = "ui"
ClientTypeCLI ClientType = "cli"

// UIIdentity is the well-known identity value that the UI sends in bind requests.
// The backend resolves it to the actual identity derived from the authenticated session.
UIIdentity = "ui-identity"
)

type AuthContext struct {
Expand Down
22 changes: 17 additions & 5 deletions backend/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,26 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) {
return
}

// TODO: Move to validating admission.
if bindRequest.Spec.ClusterIdentity.Identity == "" {
logger.Error(fmt.Errorf("missing cluster identity"), "invalid bind request")
writeErrorResponse(w, http.StatusBadRequest, kubebindv1alpha2.ErrorCodeBadRequest, "Missing cluster identity in bind request", "The cluster identity must be provided in the bind request")
// Identity is always required. CLI provides the cluster identity (kube-system UID),
// and the UI sends the well-known "ui-identity" value.
identity := bindRequest.Spec.ClusterIdentity.Identity
if identity == "" {
writeErrorResponse(w, http.StatusBadRequest, kubebindv1alpha2.ErrorCodeBadRequest, "Missing cluster identity", "spec.clusterIdentity.identity is required")
return
}

handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, params.ConsumerID, params.ClusterID)
// Resolve the UI sentinel to a real identity derived from the authenticated session.
if identity == auth.UIIdentity {
identity = state.Token.Issuer + "/" + state.Token.Subject
logger.Info("Resolved ui-identity from session", "identity", identity)
}

consumerID := params.ConsumerID
if consumerID == "" {
consumerID = identity
}

handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, consumerID, params.ClusterID)
if err != nil {
logger.Error(err, "failed to handle resources")
statusCode, code, details := mapErrorToCode(err)
Expand Down
7 changes: 4 additions & 3 deletions test/e2e/bind/happy-case_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,11 @@ func testHappyCase(
}

t.Logf("Creating consumer workspace and starting konnector")
consumer1Config, consumer1Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t), framework.WithName("%s-consumer-%s", name, suffix))
consumer1Config, consumer1Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t),
framework.WithGenerateName("%s-consumer-%s-", name, suffix))
consumer2Config, consumer2Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t),
framework.WithGenerateName("%s-consumer-%s-", name, suffix))
framework.StartKonnector(t, consumer1Config, "--kubeconfig="+consumer1Kubeconfig, "--server-address=:0")

consumer2Config, consumer2Kubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t), framework.WithName("%s-consumer-%s", name, suffix))
framework.StartKonnector(t, consumer2Config, "--kubeconfig="+consumer2Kubeconfig, "--server-address=:0")

serviceGVR := schema.GroupVersionResource{Group: "wildwest.dev", Version: "v1alpha1", Resource: "cowboys"}
Expand Down
13 changes: 4 additions & 9 deletions web/src/views/Resources.vue
Original file line number Diff line number Diff line change
Expand Up @@ -321,15 +321,10 @@ const handleBind = async (templateName: string, bindingName: string) => {
const bindUrl = buildApiUrl('/bind')

// Create the binding request
// Use consumerId if available (CLI flow), otherwise use sessionId as cluster identity
// Read from Vue Router's route.query instead of window.location
const sessionIdFromRoute = route.query.session_id as string || ''
const clusterIdentity = consumerId.value || sessionIdFromRoute

if (!clusterIdentity) {
showAlertModal('Missing cluster identity. Please ensure you have authenticated properly.', 'Binding Failed', 'error')
return
}
// CLI flow: use consumerId (kube-system namespace UID)
// UI flow: use the well-known "ui-identity" sentinel - the backend resolves
// it to the actual identity from the authenticated OIDC session.
const clusterIdentity = consumerId.value || 'ui-identity'

const bindingRequest: BindableResourcesRequest = {
metadata: {
Expand Down
Loading