Skip to content

feat: support Slack dual-token credentials with per-role proxy routes#215

Merged
xcoulon merged 8 commits into
codeready-toolchain:masterfrom
xcoulon:slack-support
Jun 25, 2026
Merged

feat: support Slack dual-token credentials with per-role proxy routes#215
xcoulon merged 8 commits into
codeready-toolchain:masterfrom
xcoulon:slack-support

Conversation

@xcoulon

@xcoulon xcoulon commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Add EnvVarSuffix and AllowedPaths fields to channelSecretRole so
multi-secret channels (like Slack) generate one proxy route per role
with distinct env vars (CRED_<NAME>_APP, CRED_<NAME>_BOT). The
app-token route restricts to /api/apps.connections.open while the
bot-token route is a catch-all. Single-role channels are unaffected.

Also add body token replacement for Slack routes: the proxy rewrites the
token field in JSON and form-encoded request bodies, replacing the
placeholder with the real credential from the env var.

Additional changes:

  • add --verbose flag to the proxy
  • add SetVerbose() to Server for goproxy request/response logging
  • move user namespace to test-e2e in e2e tests and add TLS echo
    server helpers (deployTLSEchoServer, patchProxyForEchoServer,
    curlThroughProxy) for end-to-end proxy verification

Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Xavier Coulon xcoulon@redhat.com

Summary by CodeRabbit

  • New Features
    • Added a -verbose option to enable more detailed proxy logging.
    • Enhanced Slack dual-token support with role-based routing and separate proxy environment variables for app vs bot tokens.
    • Added request body token replacement during MITM for Slack token in both JSON and application/x-www-form-urlencoded bodies.
  • Bug Fixes
    • Ensured correct ordering of path-restricted routes before catch-all routes; single-role channels keep existing behavior.
    • Preserved companion passthrough routes (for example, .slack.com).
    • If body rewriting fails, the proxy now returns HTTP 502 with an error response.

Add `EnvVarSuffix` and `AllowedPaths` fields to `channelSecretRole` so
multi-secret channels (like Slack) generate one proxy route per role
with distinct env vars (`CRED_<NAME>_APP`, `CRED_<NAME>_BOT`). The
app-token route restricts to `/api/apps.connections.open` while the
bot-token route is a catch-all. Single-role channels are unaffected.

Also add body token replacement for Slack routes: the proxy rewrites the
`token` field in JSON and form-encoded request bodies, replacing the
placeholder with the real credential from the env var.

Additional changes:
- add `--verbose` flag to the proxy
- add `SetVerbose()` to `Server` for goproxy request/response logging
- move user namespace to `test-e2e` in e2e tests and add TLS echo
  server helpers (`deployTLSEchoServer`, `patchProxyForEchoServer`,
  `curlThroughProxy`) for end-to-end proxy verification

Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
@coderabbitai

coderabbitai Bot commented Jun 25, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 2d63cf34-336c-4df6-94cc-f7846edc5a8a

📥 Commits

Reviewing files that changed from the base of the PR and between a45635a and 6316f81.

📒 Files selected for processing (1)
  • test/e2e/e2e_test.go
🔗 Linked repositories identified

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

  • codeready-toolchain/api (manual)
  • codeready-toolchain/toolchain-common (manual)
  • codeready-toolchain/host-operator (manual)
  • codeready-toolchain/toolchain-e2e (manual)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/e2e/e2e_test.go
📜 Recent review details
🧰 Additional context used
🔀 Multi-repo context codeready-toolchain/host-operator, codeready-toolchain/toolchain-e2e, codeready-toolchain/api, codeready-toolchain/toolchain-common

Linked repositories findings

codeready-toolchain/host-operator

  • No references found for the new Slack multi-token contract (AllowedPaths, EnvVarSuffix, CRED_SLACK_*, SetVerbose, bodyRewriter, apps.connections.open, slack.com) in the searched files. Nothing cross-repo to update surfaced from this repo. [::codeready-toolchain/host-operator::]

codeready-toolchain/toolchain-e2e

  • README.adoc:58-71 and make/test.mk:42-146 show e2e is run against the host/member operator images and supports local repo overrides, but no Slack-specific config is referenced here.
  • No direct references to the new Slack dual-token env vars or routing contract were found in the searched files. [::codeready-toolchain/toolchain-e2e::]

codeready-toolchain/api

  • api/v1alpha1/toolchaincluster_types.go:29-35 defines LocalSecretReference, and the generated OpenAPI/deepcopy files mirror it. No Slack proxy/routing contract types were found here.
  • The only SecretRef hits are existing API fields unrelated to the new proxy changes (api/v1alpha1/spacerequest_types.go:72-74, api/v1alpha1/toolchaincluster_types.go:29). [::codeready-toolchain/api::]

codeready-toolchain/toolchain-common

  • pkg/cluster/service.go:158 and tests around pkg/test/cluster.go / pkg/cluster/service_test.go use ToolchainCluster.Spec.SecretRef, but there were no matches for the new Slack multi-secret routing fields or proxy env var changes.
  • No cross-repo consumer of CRED_SLACK_*, AllowedPaths, or EnvVarSuffix was found. [::codeready-toolchain/toolchain-common::]

codeready-toolchain/toolchain-e2e

  • The repo’s e2e suite already contains test/e2e/e2e_test.go logic for Slack dual-token verification per the shell output, so this repo is the only direct consumer of the new Slack behavior found in the search. [::codeready-toolchain/toolchain-e2e::]

Walkthrough

The PR adds Slack dual-token routing metadata, per-role proxy route and env-var generation, Slack request-body rewriting in the proxy, verbose proxy logging, and matching unit and e2e coverage.

Changes

Slack dual-token routing

Layer / File(s) Summary
Specs and channel contract
openspec/..., internal/controller/claw_channels.go, internal/controller/claw_channels_test.go
channelSecretRole gains AllowedPaths and EnvVarSuffix, Slack roles use those fields, and the spec/proposal/design/task files describe the multi-secret routing model.
Controller route and env expansion
internal/controller/claw_proxy.go, internal/controller/claw_proxy_test.go
Multi-secret Slack credentials expand into per-role routes and env vars, and controller tests cover Slack role selection, route generation, env-var wiring, and single-role regression cases.
Proxy body rewrite and verbose mode
cmd/proxy/main.go, internal/proxy/config.go, internal/proxy/body_rewriter.go, internal/proxy/server.go
Route gains a body rewriter hook, Slack request bodies are rewritten in proxy and gateway handling, and the proxy command can enable verbose logging and server verbosity.
Proxy rewrite tests
internal/proxy/body_rewriter_test.go, internal/proxy/server_test.go
Unit and MITM tests cover Slack token replacement for JSON and form bodies, plus request-body rewriting through the proxy server.
E2E Slack dual-token flow
test/e2e/e2e_test.go
The e2e suite switches namespaces, expands debug capture, updates metrics expectations, and adds Slack dual-token MITM coverage with TLS echo and curl helpers.

Sequence Diagram(s)

sequenceDiagram
  participant ClawController
  participant ProxyServer
  participant SlackBodyTokenReplacer
  participant SlackAPI

  ClawController->>ProxyServer: configure routes and env vars
  ProxyServer->>SlackBodyTokenReplacer: Rewrite(request body)
  SlackBodyTokenReplacer-->>ProxyServer: rewritten body
  ProxyServer->>SlackAPI: forward request
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

feature, test

Suggested reviewers

  • alexeykazakov

Possibly related PRs

  • codeready-toolchain/claw-operator#170: Touches internal/controller/claw_proxy.go route generation and ordering, which this PR extends for per-role Slack routes and AllowedPaths handling.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: Slack dual-token support with per-role proxy routing.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot added documentation Improvements or additions to documentation feature New feature or request test Work that adds, fixes, or maintains automated tests or coverage (unit, integration, e2e, flakiness) labels Jun 25, 2026

@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: 7

🤖 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 @.gitignore:
- Line 44: The ignore rule for proxy is too broad and can match any path
component named proxy, including source under cmd/proxy/. Update the .gitignore
entry to anchor it to the repository root or make it more specific to the
intended build artifact so it no longer hides legitimate tracked files; keep the
change focused on the proxy pattern in .gitignore.

In `@internal/assets/manifests/claw-proxy/proxy-deployment.yaml`:
- Around line 31-34: The base proxy deployment is starting every pod with the
verbose debug flag enabled, which should not ship in the default manifest.
Remove the --verbose entry from the main claw proxy deployment in
proxy-deployment.yaml and place any debug-only flags alongside the existing
commented dump-domains guidance in a separate overlay or temporary patch,
keeping the proxy deployment defaulted to non-debug behavior.

In `@internal/controller/claw_proxy_test.go`:
- Line 1282: The test in claw_proxy_test should assert against the correct env
var name returned by credEnvVarName("slack"), since using CRED_SL leaves the
check too weak. Update the NotContains assertion in the relevant test case to
look for CRED_SLACK instead, so the test fails if the old unsuffixed variable is
still emitted alongside the role-specific ones.

In `@internal/proxy/config.go`:
- Around line 48-49: The config is referencing body-rewriter wiring before the
package defines the needed symbols, causing compile failures; add the missing
BodyRewriter type and ensure NewSlackBodyTokenReplacer is defined or imported in
the proxy package before using them in Route/config wiring. Update the relevant
proxy types and constructor plumbing so injector/bodyRewriter fields and any
Route setup compile cleanly with the new body-rewriter symbols.

In `@internal/proxy/server.go`:
- Around line 60-61: The Slack route setup in server.go is referencing a missing
body rewriter constructor, causing the undefined symbol failure in CI. Add the
missing constructor to internal/proxy or update the call in the route
configuration loop to use the correct exported symbol that creates the Slack
body token rewriter. Make sure the symbol name matches what is available to
cfg.Routes[i].bodyRewriter assignment.

In `@test/e2e/e2e_test.go`:
- Around line 2121-2127: After scaling claw-operator-controller-manager to zero
in the e2e test, wait until the controller pod/process is actually gone before
patching any operator-managed resources. Update the test flow around the
existing kubectl scale call and the subsequent managed ConfigMap/Deployment
mutations so it blocks until the reconciler is fully stopped, preventing the
still-running controller from overwriting the test patches.
- Around line 112-115: The cleanup in e2e_test.go only removes
operatorNamespace, so userNamespace (the persistent test-e2e namespace created
in the setup flow) must also be deleted there. Update the top-level t.Cleanup in
the test setup to clean up both namespaces, using the same namespace variables
referenced around the kubectl create ns userNamespace step, so reruns do not hit
AlreadyExists or reuse stale resources.
🪄 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: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 322fbb9c-ea6a-4868-b5b2-231d921a4895

📥 Commits

Reviewing files that changed from the base of the PR and between 8da980c and 520bc0b.

📒 Files selected for processing (17)
  • .gitignore
  • cmd/proxy/main.go
  • internal/assets/manifests/claw-proxy/proxy-deployment.yaml
  • internal/controller/claw_channels.go
  • internal/controller/claw_channels_test.go
  • internal/controller/claw_proxy.go
  • internal/controller/claw_proxy_test.go
  • internal/proxy/config.go
  • internal/proxy/server.go
  • internal/proxy/server_test.go
  • openspec/changes/archive/2026-06-25-slack-dual-token/.openspec.yaml
  • openspec/changes/archive/2026-06-25-slack-dual-token/design.md
  • openspec/changes/archive/2026-06-25-slack-dual-token/proposal.md
  • openspec/changes/archive/2026-06-25-slack-dual-token/specs/channel-multi-secret-routing/spec.md
  • openspec/changes/archive/2026-06-25-slack-dual-token/tasks.md
  • openspec/specs/channel-multi-secret-routing/spec.md
  • test/e2e/e2e_test.go
🔗 Linked repositories identified

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

  • codeready-toolchain/api (manual)
  • codeready-toolchain/toolchain-common (manual)
  • codeready-toolchain/host-operator (manual)
  • codeready-toolchain/toolchain-e2e (manual)
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
internal/assets/manifests/**/*.{yaml,yml}

📄 CodeRabbit inference engine (CLAUDE.md)

internal/assets/manifests/**/*.{yaml,yml}: Configure pod security with non-root user (uid 65532), restricted seccomp profile, and drop all Linux capabilities
Set readOnlyRootFilesystem: true on proxy and wait-for-proxy containers, but not on init-config or gateway containers which require writable paths for Node.js and AI tools

Files:

  • internal/assets/manifests/claw-proxy/proxy-deployment.yaml
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • internal/assets/manifests/claw-proxy/proxy-deployment.yaml
  • cmd/proxy/main.go
  • openspec/changes/archive/2026-06-25-slack-dual-token/tasks.md
  • openspec/specs/channel-multi-secret-routing/spec.md
  • openspec/changes/archive/2026-06-25-slack-dual-token/specs/channel-multi-secret-routing/spec.md
  • openspec/changes/archive/2026-06-25-slack-dual-token/proposal.md
  • openspec/changes/archive/2026-06-25-slack-dual-token/design.md
  • internal/proxy/server_test.go
  • internal/proxy/config.go
  • internal/proxy/server.go
  • internal/controller/claw_channels_test.go
  • internal/controller/claw_channels.go
  • internal/controller/claw_proxy_test.go
  • internal/controller/claw_proxy.go
  • test/e2e/e2e_test.go
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Include Go license header from hack/boilerplate.go.txt template in all Go source files
Run golangci-lint via make lint and use make lint-fix for auto-fixing, with .golangci.yml configuration including lll and dupl linters enabled
Use make fmt (go fmt) and make vet (go vet) for code formatting and vetting before committing

Files:

  • cmd/proxy/main.go
  • internal/proxy/server_test.go
  • internal/proxy/config.go
  • internal/proxy/server.go
  • internal/controller/claw_channels_test.go
  • internal/controller/claw_channels.go
  • internal/controller/claw_proxy_test.go
  • internal/controller/claw_proxy.go
  • test/e2e/e2e_test.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*_test.go: Use require assertion from testify for fatal setup errors in tests, and assert for value comparisons
Structure tests with Test* function names, use t.Run() for subtests, t.Cleanup() for cleanup, and implement table-driven test patterns
Use waitFor(t, timeout, interval, condition, message) helper for async test assertions with 10s timeout and 250ms poll interval
Create separate test files per resource type (e.g., claw_configmap_test.go, claw_credentials_test.go)

Files:

  • internal/proxy/server_test.go
  • internal/controller/claw_channels_test.go
  • internal/controller/claw_proxy_test.go
  • test/e2e/e2e_test.go
internal/controller/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Set owner references on all created resources via controllerutil.SetControllerReference

Files:

  • internal/controller/claw_channels_test.go
  • internal/controller/claw_channels.go
  • internal/controller/claw_proxy_test.go
  • internal/controller/claw_proxy.go
🪛 GitHub Actions: E2E Tests / 0_E2E Tests.txt
internal/proxy/config.go

[error] 49-49: go vet failed: internal/proxy/config.go:49:15: undefined: BodyRewriter

🪛 GitHub Actions: E2E Tests / E2E Tests
internal/proxy/config.go

[error] 49-49: go vet failed: undefined: BodyRewriter


[error] 1-1: Command failed at step: go vet ./...

🪛 GitHub Actions: Lint / Lint
internal/proxy/config.go

[error] 49-49: golangci-lint failed: undefined: BodyRewriter

🪛 GitHub Actions: Tests / 0_Unit Tests.txt
internal/proxy/config.go

[error] 49-49: go vet failed: internal/proxy/config.go:49:15: undefined: BodyRewriter

🪛 GitHub Actions: Tests / Unit Tests
internal/proxy/config.go

[error] 49-49: go vet failed: undefined: BodyRewriter

🪛 GitHub Check: E2E Tests
internal/proxy/config.go

[failure] 49-49:
undefined: BodyRewriter

internal/proxy/server.go

[failure] 61-61:
undefined: NewSlackBodyTokenReplacer

🪛 GitHub Check: Lint
internal/proxy/server_test.go

[failure] 597-597:
undefined: NewSlackBodyTokenReplacer (typecheck)

internal/proxy/config.go

[failure] 49-49:
undefined: BodyRewriter

internal/proxy/server.go

[failure] 61-61:
undefined: NewSlackBodyTokenReplacer

🪛 GitHub Check: Unit Tests
internal/proxy/config.go

[failure] 49-49:
undefined: BodyRewriter

internal/proxy/server.go

[failure] 61-61:
undefined: NewSlackBodyTokenReplacer

🔇 Additional comments (1)
internal/controller/claw_proxy.go (1)

224-248: 🎯 Functional Correctness

Verify partial Slack role sets are rejected before generating routes.

credentialRoutes always materializes both Slack role routes from knownChannels, but configureProxyForCredentials only injects env vars for roles that actually exist in SecretRef. If validation allows a credential with only appToken or only botToken, the specific route still wins with an unset env var and fails at runtime. Please confirm the CRD/webhook rejects incomplete Slack role sets.

Comment thread .gitignore Outdated
Comment thread internal/assets/manifests/claw-proxy/proxy-deployment.yaml Outdated
Comment thread internal/controller/claw_proxy_test.go Outdated
Comment thread internal/proxy/config.go
Comment thread internal/proxy/server.go
Comment thread test/e2e/e2e_test.go
Comment thread test/e2e/e2e_test.go
xcoulon added 5 commits June 25, 2026 13:26
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
@coderabbitai coderabbitai Bot removed the documentation Improvements or additions to documentation label Jun 25, 2026
xcoulon added 2 commits June 25, 2026 15:47
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
Signed-off-by: Xavier Coulon <xcoulon@redhat.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.74419% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.80%. Comparing base (8da980c) to head (6316f81).

Files with missing lines Patch % Lines
internal/proxy/body_rewriter.go 77.77% 6 Missing and 6 partials ⚠️
internal/proxy/server.go 33.33% 10 Missing and 2 partials ⚠️
internal/controller/claw_proxy.go 89.47% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #215      +/-   ##
==========================================
- Coverage   78.97%   78.80%   -0.18%     
==========================================
  Files          32       33       +1     
  Lines        4176     4284     +108     
==========================================
+ Hits         3298     3376      +78     
- Misses        562      581      +19     
- Partials      316      327      +11     
Files with missing lines Coverage Δ
internal/controller/claw_channels.go 95.00% <ø> (ø)
internal/proxy/config.go 100.00% <ø> (ø)
internal/controller/claw_proxy.go 82.44% <89.47%> (+0.11%) ⬆️
internal/proxy/body_rewriter.go 77.77% <77.77%> (ø)
internal/proxy/server.go 70.43% <33.33%> (-4.13%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@xcoulon xcoulon merged commit d8fee7d into codeready-toolchain:master Jun 25, 2026
5 checks passed
@xcoulon xcoulon deleted the slack-support branch June 25, 2026 16:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request test Work that adds, fixes, or maintains automated tests or coverage (unit, integration, e2e, flakiness)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants