Skip to content

HYPERFLEET-1147 - feat: add caller identity support for audit attribution#120

Open
kuudori wants to merge 1 commit into
openshift-hyperfleet:mainfrom
kuudori:HYPERFLEET-1147-caller-identity
Open

HYPERFLEET-1147 - feat: add caller identity support for audit attribution#120
kuudori wants to merge 1 commit into
openshift-hyperfleet:mainfrom
kuudori:HYPERFLEET-1147-caller-identity

Conversation

@kuudori

@kuudori kuudori commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Inject caller identity headers into all E2E API requests so tests work against an API with identity enforcement enabled (production default).

  • Add IdentityConfig (header/value/token) to config with CLI flags, env vars (HYPERFLEET_IDENTITY_*), and config.yaml support
  • Inject identity via OpenAPI RequestEditorFn in helper.newHelper()
  • Add HaveAuditIdentity matcher and ExpectedIdentity() helper
  • Add created_by assertion in cluster creation, deleted_by in deletion
  • Deploy API with identity_header enabled in local kind setup
  • Update AGENTS.md, getting-started, and local-kind-setup docs

Summary

Test Plan

  • Unit tests added/updated
  • make test-all passes
  • make lint passes
  • Helm chart changes validated with make test-helm (if applicable)
  • Deployed to a development cluster and verified (if Helm/config changes)
  • E2E tests passed (if cross-component or major changes)

@openshift-ci openshift-ci Bot requested review from ciaranRoche and rh-amarin June 8, 2026 23:49
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds JWT caller-identity configuration for E2E tests, acquires tokens through Kubernetes TokenRequest, and injects bearer tokens into HyperFleet API requests. It adds configuration validation and defaults, documents setup, and introduces conditional audit assertions for cluster creation and deletion using a new HaveAuditIdentity matcher. The HyperFleet client accepts additional OpenAPI options for request editing.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant E2EHelper
  participant KubernetesAPI
  participant HyperFleetAPI
  participant ClusterTests
  E2EHelper->>KubernetesAPI: Request service-account JWT
  KubernetesAPI-->>E2EHelper: Return token
  E2EHelper->>HyperFleetAPI: Send API request with Bearer token
  HyperFleetAPI-->>ClusterTests: Return cluster audit fields
  ClusterTests->>ClusterTests: Assert created_by or deleted_by identity
Loading
🚥 Pre-merge checks | ✅ 9 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
No Pii Or Sensitive Data In Logs ⚠️ Warning CWE-532: cfg.Display() always logs identity_expected, which can be an email/other PII, and the suite calls it unconditionally at startup. Remove identity_expected from the config log, or redact it; keep only non-sensitive presence/absence signals.
✅ Passed checks (9 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: adding caller identity support for audit attribution.
Description check ✅ Passed The description is aligned with the changeset and covers identity injection, audit matchers, and docs updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Sec-02: Secrets In Log Output ✅ Passed No changed non-test log statement emits token/password/credential/secret fields or interpolated values; no CWE-532 exposure found.
No Hardcoded Secrets ✅ Passed No hardcoded secrets found: tokens are acquired at runtime via TokenRequest and injected dynamically; no embedded creds or long base64 literals in changed files (CWE-798).
No Weak Cryptography ✅ Passed No MD5/DES/RC4/SHA1, ECB, custom crypto, or secret/HMAC comparisons were added; no CWE-327/CWE-328/CWE-208 issue.
No Injection Vectors ✅ Passed No CWE-78/89/79/502 sink added: no new SQL/template/yaml unmarshalling; exec.CommandContext uses fixed helm args and validated/trusted inputs.
No Privileged Containers ✅ Passed No CWE-269 privileged-container flags were added in changed manifests/templates/Dockerfiles; search over touched YAML found none, and no Dockerfile or Helm template changes introduced them.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/hyperfleet-e2e/main.go`:
- Around line 33-34: Add a new CLI flag binding for the identity token so
token-only usage is supported: declare or reuse the variable identityToken and
register it with pfs.StringVar(&identityToken, "identity-token", "", "Caller
identity token (alternative to identity-header/identity-value)"), and add the
same binding in the other flagset where identityHeader/identityValue are
registered (the other pfs.StringVar calls referenced in the comment). Update any
flag parsing logic that constructs request identity to prefer identityToken when
set (instead of header/value).

In `@cmd/hyperfleet-e2e/test/cmd.go`:
- Around line 60-67: The viper.BindPFlag calls for binding parent flags are
ignoring returned errors; update the block that calls viper.BindPFlag (for
config.API.URL, config.Log.Level, config.Log.Format, config.Log.Output,
config.Identity.Header, config.Identity.Value using parentFlags.Lookup("..."))
to check each error and handle it (e.g., return the error from the surrounding
function or log/fatal with context) instead of discarding; ensure you capture
the returned error from each viper.BindPFlag call and include a clear message
referencing the flag/key when reporting the failure.

In `@e2e/cluster/delete.go`:
- Around line 56-58: When h.ExpectedIdentity() returns a non-empty expected
identity, don't just assert deletedCluster.DeletedBy is non-nil — assert it
equals the expected identity for end-to-end attribution. Update the test in
delete.go to (1) fetch expected := h.ExpectedIdentity(), (2) ensure
deletedCluster.DeletedBy is present, and then (3) compare the DeletedBy value
against expected (use the appropriate field/representation on
deletedCluster.DeletedBy) so the assertion fails if the API attributes deletion
to the wrong caller.

In `@pkg/config/config.go`:
- Around line 497-499: The log currently emits c.Identity.Value as
"identity_value" in plaintext using valueOrNotSet; change this to a redacted
form (reuse or add a helper like redactValue similar to redactToken) so
identity_value does not reveal emails/identifiers in logs; update the call in
the block that builds the map (replace valueOrNotSet(c.Identity.Value) with a
redaction helper) and ensure redactToken(c.Identity.Token) remains unchanged.
- Around line 462-465: In Validate(), after the existing header/value presence
check (the block referencing c.Identity.Header and c.Identity.Value), add a
format validation for c.Identity.Value so malformed audit identities fail fast;
call or implement a small validator (e.g., parseAuditIdentity(...) or a tailored
regexp) and return a descriptive error from Validate() if it doesn't match the
expected audit-identity format (include the invalid value in the fmt.Errorf
message) so config loading fails early rather than at API runtime.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: f6bf6e8a-4652-469d-9a0d-4495a6cd0f70

📥 Commits

Reviewing files that changed from the base of the PR and between 0e5ebcc and e286b25.

📒 Files selected for processing (14)
  • AGENTS.md
  • cmd/hyperfleet-e2e/main.go
  • cmd/hyperfleet-e2e/test/cmd.go
  • configs/config.yaml
  • deploy-scripts/lib/api.sh
  • docs/getting-started.md
  • docs/local-kind-setup.md
  • e2e/cluster/creation.go
  • e2e/cluster/delete.go
  • pkg/client/client.go
  • pkg/config/config.go
  • pkg/helper/helper.go
  • pkg/helper/matchers.go
  • pkg/helper/suite.go

Comment thread cmd/hyperfleet-e2e/main.go Outdated
Comment thread cmd/hyperfleet-e2e/test/cmd.go Outdated
Comment thread e2e/cluster/delete.go
Comment thread pkg/config/config.go Outdated
Comment thread pkg/config/config.go Outdated
@kuudori kuudori force-pushed the HYPERFLEET-1147-caller-identity branch from e286b25 to 5d8c6b8 Compare June 9, 2026 00:00
@kuudori kuudori force-pushed the HYPERFLEET-1147-caller-identity branch from 5d8c6b8 to 9fe3db5 Compare June 24, 2026 18:21
@openshift-ci

openshift-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign rafabene for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kuudori kuudori force-pushed the HYPERFLEET-1147-caller-identity branch from 9fe3db5 to 17ae4a9 Compare June 25, 2026 20:04
@kuudori

kuudori commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

/test e2e-deployment-validation

@kuudori kuudori force-pushed the HYPERFLEET-1147-caller-identity branch from 17ae4a9 to 671d18f Compare July 10, 2026 00:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/config/config.go (1)

492-501: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inline defaults 3600 / "hyperfleet-api" duplicate configs/config.yaml and bypass the Default* constant convention.

These values are declared in two places (here and configs/config.yaml lines 149-150). A change in one silently diverges from the other. Every other default in this file references a Default* constant, and the project treats pkg/config/defaults.go as the single source of truth for config defaults.

♻️ Hoist to named constants in pkg/config/defaults.go
// pkg/config/defaults.go
const (
	DefaultTokenRequestExpirationSeconds int64  = 3600
	DefaultTokenRequestAudience          string = "hyperfleet-api"
)
 	if c.Identity.TokenRequest.IsEnabled() {
 		if c.Identity.TokenRequest.ExpirationSeconds == 0 {
-			c.Identity.TokenRequest.ExpirationSeconds = 3600
+			c.Identity.TokenRequest.ExpirationSeconds = DefaultTokenRequestExpirationSeconds
 		}
 		if c.Identity.TokenRequest.Audience == "" {
-			c.Identity.TokenRequest.Audience = "hyperfleet-api"
+			c.Identity.TokenRequest.Audience = DefaultTokenRequestAudience
 		}
 	}
As per coding guidelines: "Config defaults | `pkg/config/defaults.go`" and QUAL-02 (magic numbers/strings should be named constants).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/config/config.go` around lines 492 - 501, Replace the inline token
request defaults in the config defaulting logic with named constants. Add
DefaultTokenRequestExpirationSeconds and DefaultTokenRequestAudience to the
existing constants in defaults.go, then update the Identity.TokenRequest default
assignments in the relevant configuration function to reference them instead of
3600 and "hyperfleet-api".

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/getting-started.md`:
- Around line 41-49: The JWT configuration in the getting-started documentation
should be conditional on identity enforcement being enabled, not presented as
universally required. Update the JWT setup section to state the Kubernetes
prerequisites: the test runner needs cluster credentials and permission to
create a TokenRequest for the configured service account; clarify that these
exports are unnecessary when enforcement is disabled. Also document whether the
e2e-deployment-validation path enables identity enforcement before requiring
this configuration.

In `@pkg/helper/suite.go`:
- Around line 85-92: Prevent bearer-token injection for non-TLS endpoints in the
client-option setup near cfg.Identity.Token and openapi.WithRequestEditorFn:
validate cfg.API.URL uses HTTPS before attaching the Authorization header, and
reject the configuration or skip token injection for HTTP URLs.

---

Nitpick comments:
In `@pkg/config/config.go`:
- Around line 492-501: Replace the inline token request defaults in the config
defaulting logic with named constants. Add DefaultTokenRequestExpirationSeconds
and DefaultTokenRequestAudience to the existing constants in defaults.go, then
update the Identity.TokenRequest default assignments in the relevant
configuration function to reference them instead of 3600 and "hyperfleet-api".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 25b9d127-b733-484a-8c38-5fc91ba4a118

📥 Commits

Reviewing files that changed from the base of the PR and between 17ae4a9 and 671d18f.

📒 Files selected for processing (12)
  • AGENTS.md
  • cmd/hyperfleet-e2e/main.go
  • configs/config.yaml
  • docs/getting-started.md
  • e2e/cluster/creation.go
  • e2e/cluster/delete.go
  • pkg/client/client.go
  • pkg/client/kubernetes/client.go
  • pkg/config/config.go
  • pkg/helper/helper.go
  • pkg/helper/matchers.go
  • pkg/helper/suite.go
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift-hyperfleet/architecture (manual)
  • openshift-hyperfleet/hyperfleet-api (manual)
  • openshift-hyperfleet/hyperfleet-sentinel (manual)
  • openshift-hyperfleet/hyperfleet-adapter (manual)
  • openshift-hyperfleet/hyperfleet-broker (manual)
💤 Files with no reviewable changes (1)
  • cmd/hyperfleet-e2e/main.go
✅ Files skipped from review due to trivial changes (1)
  • AGENTS.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/helper/helper.go
  • e2e/cluster/creation.go
  • e2e/cluster/delete.go
  • pkg/client/client.go
  • pkg/helper/matchers.go

Comment thread docs/getting-started.md Outdated
Comment thread pkg/helper/suite.go
Comment on lines +85 to +92
var opts []openapi.ClientOption
if token := cfg.Identity.Token(); token != "" {
opts = append(opts, openapi.WithRequestEditorFn(
func(_ context.Context, req *http.Request) error {
req.Header.Set("Authorization", "Bearer "+token)
return nil
}))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP 'API\.URL' pkg/ cmd/
rg -nP 'https?://|url\.Parse|Scheme' pkg/config/config.go pkg/client/client.go pkg/helper/suite.go

Repository: openshift-hyperfleet/hyperfleet-e2e

Length of output: 743


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## pkg/config/config.go (around validation + URL helpers)\n'
sed -n '480,640p' pkg/config/config.go

printf '\n## pkg/helper/suite.go (around client construction)\n'
sed -n '1,130p' pkg/helper/suite.go

printf '\n## command wiring for api-url\n'
sed -n '1,120p' cmd/hyperfleet-e2e/test/cmd.go

Repository: openshift-hyperfleet/hyperfleet-e2e

Length of output: 12264


Refuse bearer tokens on non-TLS API URLs (CWE-319)

pkg/helper/suite.go:85-92 unconditionally adds Authorization: Bearer ... for every request. If cfg.API.URL is http://, the JWT is sent in cleartext. Reject non-HTTPS API URLs before attaching the header, or skip token injection unless TLS is in use.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/helper/suite.go` around lines 85 - 92, Prevent bearer-token injection for
non-TLS endpoints in the client-option setup near cfg.Identity.Token and
openapi.WithRequestEditorFn: validate cfg.API.URL uses HTTPS before attaching
the Authorization header, and reject the configuration or skip token injection
for HTTP URLs.

- Add TokenRequest-based JWT acquisition using K8s ServiceAccount tokens
- Add CreateToken() method on K8s client via TokenRequest API
- Add IdentityConfig with tokenRequest config (SA name, namespace, audience, expiration)
- Add expectedIdentity field for audit assertion (created_by, deleted_by)
- Add HaveAuditIdentity matcher and ExpectedIdentity() helper
- Add audit assertions in cluster creation and deletion tests
- Inject Bearer token via openapi.WithRequestEditorFn on all API requests
- Accept ClientOption variadic in NewHyperFleetClient for request editors
- Update config.yaml, AGENTS.md, and getting-started docs
@kuudori kuudori force-pushed the HYPERFLEET-1147-caller-identity branch from 671d18f to 76c422d Compare July 10, 2026 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant