Skip to content

feat: add support for workbench custom oauth integrations#47

Draft
statik wants to merge 5 commits intomainfrom
feat-workbench-custom-oauth-integrations
Draft

feat: add support for workbench custom oauth integrations#47
statik wants to merge 5 commits intomainfrom
feat-workbench-custom-oauth-integrations

Conversation

@statik
Copy link
Collaborator

@statik statik commented Jan 23, 2026

Summary

Adds support for Workbench custom OAuth integrations by implementing the oauth-clients.conf configuration file format as documented at https://docs.posit.co/ide/server-pro/integration/custom-oauth.html.

This feature allows users to configure multiple OAuth integrations (Box, Dropbox, Google Drive, etc.) for their Workbench instances, enabling data access from external APIs.

Backwards Compatibility

This change is fully backwards compatible:

  • The OAuthClients field is optional - existing Workbench CRs without it continue to work unchanged
  • Nil OAuthClients is handled as a no-op (verified by unit test)
  • Existing Databricks configurations are unaffected (verified by unit test)
  • No changes to existing API fields or behavior
  • Feature is additive only - no breaking changes

Implementation Details

  • Added WorkbenchOAuthClientConfig struct with fields for name, client-id, client-secret, authorization-url, token-url, token-endpoint-auth-method, and scopes
  • Added OAuthClients map field to WorkbenchSecretIniConfig - map keys become INI section headers
  • Added FetchAndSetOAuthClientSecrets() method to fetch secrets from vault (K8s Secrets or AWS Secrets Manager)
  • Integrated secret fetching into ReconcileWorkbench() reconciliation loop (non-fatal errors)
  • Added OAuth secrets to AWS SecretProviderClass for secret mounting
  • Fixed bug in GenerateConfigMap() where slices in map-based configs were not converted to comma-separated strings
  • Added comprehensive API documentation with kubebuilder validation markers
  • Added required field validation (client-id, authorization-url, token-url)

Test Coverage

Unit Tests (8 total)

  • TestWorkbenchSecretConfig_GenerateSecretData_WithOAuthClients - INI generation with multiple clients
  • TestWorkbenchSecretConfig_GenerateSecretData_WithOAuthClients_EmptyScopes - Empty scopes handling
  • TestWorkbenchSecretConfig_GenerateSecretData_WithMultipleConfigs - OAuth + other configs together
  • TestOAuthClientSecrets - Secret fetching happy path
  • TestOAuthClientSecrets_NilOAuthClients - Nil handling (no-op)
  • TestOAuthClientSecrets_MissingRequiredFields - Validation skips invalid, processes valid
  • TestOAuthClientSecrets_BackwardsCompatibility - Existing configs unaffected

What the tests verify:

  • ✅ INI file format matches Workbench documentation
  • ✅ Scopes rendered as comma-separated strings
  • ✅ Kebab-case field name conversion
  • ✅ Nil OAuthClients is a no-op
  • ✅ Missing required fields are skipped gracefully
  • ✅ Valid integrations processed even when others fail
  • ✅ Existing Databricks configs unaffected

Example Usage

apiVersion: core.posit.team/v1beta1
kind: Workbench
spec:
  secretConfig:
    workbench-secret-ini-config:
      oauth-clients.conf:
        box-integration:
          name: "Box API Access"
          client-id: "your-box-client-id"
          authorization-url: "https://account.box.com/api/oauth2/authorize"
          token-url: "https://api.box.com/oauth2/token"
          scopes: ["read", "write"]

See config/samples/workbench_oauth_integrations.yaml for complete examples.

Files Changed

File Changes
api/core/v1beta1/workbench_config.go OAuth struct + API docs + slice fix
api/core/v1beta1/workbench_config_test.go 4 unit tests
internal/controller/core/workbench.go Secret fetching + validation
internal/controller/core/workbench_test.go 4 controller tests
config/samples/workbench_oauth_integrations.yaml Example CR
config/crd/bases/core.posit.team_workbenches.yaml Regenerated CRD

Closes #46

🤖 Generated with Claude Code

Adds support for configuring custom OAuth integrations in Workbench by implementing the oauth-clients.conf configuration file format.

This feature allows users to configure multiple OAuth integrations (Box, Dropbox, Google Drive, etc.) for their Workbench instances, enabling data access from external APIs.

Changes:
- Add WorkbenchOAuthClientConfig struct to define OAuth client configuration
- Add OAuthClients map field to WorkbenchSecretIniConfig
- Add FetchAndSetOAuthClientSecrets() method to fetch secrets from vault
- Integrate OAuth secret fetching into Workbench reconciliation
- Add OAuth secrets to AWS SecretProviderClass
- Generate CRD manifests and client code

The implementation follows the exact same pattern as the existing Databricks integration:
- Map keys become INI section headers
- Existing reflection-based GenerateConfigMap() handles INI generation automatically
- Secrets fetched using same pattern as Databricks
- File mounted via existing secret-config volume at /mnt/secure-config/rstudio/oauth-clients.conf

Fixes #46

Co-Authored-By: Claude <noreply@anthropic.com>
@statik statik self-assigned this Jan 23, 2026
@claude
Copy link

claude bot commented Jan 23, 2026

Claude finished @statik's task —— View job


Review Complete

  • Read review guidelines
  • Analyze PR diff and changed files
  • Review API changes
  • Review controller changes
  • Submit review with inline comments

Submitted a code review with feedback on:

  • API validation annotations
  • Defensive nil checks in controller
  • Secret naming convention documentation
  • Error handling consistency
  • Testing plan follow-up

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Code Review

This PR follows the existing Databricks integration pattern well. The implementation is clean and consistent with the codebase conventions. A few observations:

API Changes (api/core/v1beta1/workbench_config.go)

Suggestion: Consider adding kubebuilder validation annotations for required fields. Based on the Workbench OAuth docs, client-id, authorization-url, and token-url appear to be required for a functional integration. This would provide early validation feedback rather than runtime errors.

Controller Changes (internal/controller/core/workbench.go)

  1. Defensive nil check (line 76): The nil check only validates OAuthClients but not the full path. Consider checking w.Spec.SecretConfig.WorkbenchSecretIniConfig as well to prevent potential panics.

  2. Secret naming convention (line 86): The dev-oauth-client-secret-%s pattern uses integrationName directly. Consider:

    • Documenting this convention in the CRD description so users understand what secret name to create
    • Sanitizing integrationName to prevent invalid characters in secret names
  3. Inconsistent error handling (line 166 vs 160): OAuth client secret errors are fatal (returns error), while Databricks errors are non-fatal (logs and continues). Is this intentional? If OAuth secrets are always required when configured, the current approach is correct. Otherwise, consider aligning with the Databricks pattern.

  4. DRY opportunity (lines 86 and 327): The secret key generation pattern dev-oauth-client-secret-%s appears in two places. Consider extracting to a helper function for consistency.

Testing

Per the review guidelines checklist, unit and integration tests are not included in this PR. The PR description mentions a testing plan - would be good to either include tests in this PR or track them as follow-up.

Overall

The implementation is straightforward and follows established patterns. The changes are well-scoped and the CRD regeneration looks correct.

Adds comprehensive unit tests for the OAuth integration feature:
- Test INI generation with multiple OAuth clients
- Test kebab-case conversion of field names
- Test comma-separated scopes array formatting
- Test empty scopes handling
- Test interaction with other config types

Also fixes bug in WorkbenchSecretIniConfig.GenerateConfigMap() where
slices in map-based configs were not converted to comma-separated strings.
The fix ensures array fields (like scopes) are properly formatted as
"scopes=read,write" instead of "scopes=[read write]".

Co-Authored-By: Claude <noreply@anthropic.com>
@statik
Copy link
Collaborator Author

statik commented Jan 23, 2026

Unit tests added!

Added 4 comprehensive unit tests for OAuth integration:

  • Multiple OAuth clients with all fields
  • Kebab-case field name conversion
  • Comma-separated scopes formatting
  • Empty scopes handling
  • Integration with other config types

All tests passing. Also fixed a bug in GenerateConfigMap() where slices in map-based configs weren't converted to comma-separated strings.

@statik
Copy link
Collaborator Author

statik commented Jan 23, 2026

🔧 Fixed CI formatting issue

The first build failed due to inconsistent struct field alignment. Fixed the spacing in WorkbenchSecretIniConfig to match the project's formatter expectations.

New build running: https://github.com/posit-dev/team-operator/actions/runs/21274383756

@statik
Copy link
Collaborator Author

statik commented Jan 23, 2026

CI Build Passed!

All checks are passing. The PR is ready for review.

Build: https://github.com/posit-dev/team-operator/actions/runs/21274383756
Duration: 7m 37s

@statik
Copy link
Collaborator Author

statik commented Jan 23, 2026

@ian-flores do you have advice on which cluster/site I should test this adhoc operator build on?

Improvements to the custom OAuth integrations feature:

1. Error handling consistency:
   - OAuth secret fetching errors are now non-fatal (like Databricks)
   - Continues processing other integrations even if one fails
   - A missing secret will not block entire Workbench deployment

2. API documentation:
   - Added comprehensive kubebuilder comments to WorkbenchOAuthClientConfig
   - Documented required vs optional fields
   - Added validation markers (MinLength, Enum)
   - Links to official Workbench documentation

3. Validation for required fields:
   - Validates client-id, authorization-url, and token-url are present
   - Logs informative message listing missing fields
   - Skips integration gracefully if validation fails

4. Example CR:
   - Added config/samples/workbench_oauth_integrations.yaml
   - Shows Box, Google Drive, Dropbox, and custom API examples
   - Includes corresponding Kubernetes Secret example
   - Documents prerequisites and redirect URI configuration

Co-Authored-By: Claude <noreply@anthropic.com>
@statik
Copy link
Collaborator Author

statik commented Jan 23, 2026

Improvements Added

Based on feedback, I've implemented the following enhancements:

1. Error Handling Consistency 🔧

  • OAuth secret fetching errors are now non-fatal (matching the Databricks pattern)
  • If one integration's secret fails to fetch, other integrations continue processing
  • A missing secret won't block the entire Workbench deployment

2. API Documentation 📝

Added comprehensive kubebuilder comments to WorkbenchOAuthClientConfig:

  • Documented all fields (required vs optional)
  • Added validation markers (MinLength, Enum for token-endpoint-auth-method)
  • Links to official Workbench documentation
  • Clear guidance that client-secret is auto-populated from secret store

3. Validation for Required Fields ✅

Per the Workbench documentation, these fields are required:

  • client-id
  • authorization-url
  • token-url

The controller now validates these and logs a helpful message if any are missing:

skipping OAuth integration with missing required fields
  integration=box-integration
  missingFields=[authorization-url, token-url]

4. Example CR 📄

Added config/samples/workbench_oauth_integrations.yaml with:

  • Box, Google Drive, Dropbox, and custom internal API examples
  • Corresponding Kubernetes Secret showing the naming convention
  • Documentation comments for prerequisites and redirect URI configuration

Files changed in this commit:

  • api/core/v1beta1/workbench_config.go - API documentation
  • internal/controller/core/workbench.go - Error handling + validation
  • config/crd/bases/core.posit.team_workbenches.yaml - Regenerated CRD
  • config/samples/workbench_oauth_integrations.yaml - New example

Adds comprehensive controller-level tests for FetchAndSetOAuthClientSecrets:

- TestOAuthClientSecrets: Happy path with multiple OAuth integrations
- TestOAuthClientSecrets_NilOAuthClients: Verifies nil handling (no-op)
- TestOAuthClientSecrets_MissingRequiredFields: Validates required field checking
- TestOAuthClientSecrets_BackwardsCompatibility: Ensures existing configs unaffected

These tests provide confidence that:
- OAuth feature works correctly end-to-end
- Existing Workbench deployments without OAuth are unaffected
- Invalid integrations are skipped gracefully
- Valid integrations are processed even when others fail

Co-Authored-By: Claude <noreply@anthropic.com>
@statik
Copy link
Collaborator Author

statik commented Jan 23, 2026

Additional Tests Added

Added 4 controller-level tests for FetchAndSetOAuthClientSecrets:

Test Purpose
TestOAuthClientSecrets Happy path - multiple OAuth integrations fetch secrets correctly
TestOAuthClientSecrets_NilOAuthClients Nil handling - verifies no-op for existing deployments
TestOAuthClientSecrets_MissingRequiredFields Validation - skips invalid, processes valid integrations
TestOAuthClientSecrets_BackwardsCompatibility Backwards compat - existing Databricks configs unaffected

Total test coverage: 8 unit tests (4 API-level + 4 controller-level)

Also updated the PR description with explicit backwards compatibility notes and test coverage summary.

@ian-flores
Copy link
Collaborator

@ian-flores do you have advice on which cluster/site I should test this adhoc operator build on?

@statik You can use ganso01-staging or either academy01-* for this; they're already migrated.

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.

add support for workbench custom oauth integrations

2 participants