feat(authz): bind decisions to exact actions
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 37s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02e47-6aac-7ee1-914d-0584c75d3c81
This commit is contained in:
tegwick 2026-08-23 13:18:26 +02:00
parent 7323dd1a60
commit c473f1971d
28 changed files with 644 additions and 12 deletions

View file

@ -98,6 +98,9 @@ func (a *Authenticator) authorize(ctx context.Context, authorization string, sys
}
identity, err := a.reviewer.Review(ctx, strings.TrimSpace(token))
if err != nil {
if errors.Is(err, ErrUnauthenticated) {
return err
}
return fmt.Errorf("%w: %v", ErrUnavailable, err)
}
if strings.TrimSpace(identity.Username) == "" || !contains(identity.Audiences, a.audience) {

View file

@ -3,6 +3,7 @@ package callerauth
import (
"context"
"errors"
"fmt"
"strings"
"testing"
)
@ -53,6 +54,11 @@ func TestAuthenticatorRejectsMissingTokenAndReviewerFailure(t *testing.T) {
if err := unavailable.Authorize(context.Background(), "Bearer token", []string{"tenant-engine"}); !errors.Is(err, ErrUnavailable) {
t.Fatalf("reviewer error = %v; want unavailable", err)
}
rejected, _ := New(ModeEnforce, fakeReviewer{err: fmt.Errorf("%w: invalid bearer token", ErrUnauthenticated)}, "flex-auth", bindings, nil)
if err := rejected.Authorize(context.Background(), "Bearer malformed", []string{"tenant-engine"}); !errors.Is(err, ErrUnauthenticated) {
t.Fatalf("rejected token error = %v; want unauthenticated", err)
}
}
func TestAuthenticatorWarnModePermitsButRecordsFailure(t *testing.T) {

View file

@ -101,7 +101,7 @@ func (r *KubernetesTokenReviewer) Review(ctx context.Context, callerToken string
return Identity{}, fmt.Errorf("decode TokenReview: %w", err)
}
if review.Status.Error != "" {
return Identity{}, fmt.Errorf("TokenReview: %s", review.Status.Error)
return Identity{}, fmt.Errorf("%w: TokenReview: %s", ErrUnauthenticated, review.Status.Error)
}
if !review.Status.Authenticated {
return Identity{}, nil

View file

@ -0,0 +1,46 @@
package callerauth
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
)
func TestKubernetesTokenReviewerClassifiesRejectedTokenAsUnauthenticated(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/apis/authentication.k8s.io/v1/tokenreviews" {
t.Fatalf("TokenReview path = %q", r.URL.Path)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
_, _ = w.Write([]byte(`{
"apiVersion": "authentication.k8s.io/v1",
"kind": "TokenReview",
"status": {
"authenticated": false,
"error": "invalid bearer token"
}
}`))
}))
defer server.Close()
tokenFile := filepath.Join(t.TempDir(), "reviewer-token")
if err := os.WriteFile(tokenFile, []byte("reviewer-token\n"), 0o600); err != nil {
t.Fatal(err)
}
reviewer := &KubernetesTokenReviewer{
endpoint: server.URL + "/apis/authentication.k8s.io/v1/tokenreviews",
audience: "flex-auth",
reviewerTokenFile: tokenFile,
client: server.Client(),
}
_, err := reviewer.Review(context.Background(), "malformed")
if !errors.Is(err, ErrUnauthenticated) {
t.Fatalf("Review error = %v; want unauthenticated", err)
}
}