feat: IBM COS connector with HMAC and S3 Connector#1077
feat: IBM COS connector with HMAC and S3 Connector#1077edwinjosechittilappilly merged 35 commits intomainfrom
Conversation
Introduce a full Amazon S3 / S3-compatible connector and integrate it into the app. Backend: add new S3 connector implementation and auth helpers (src/connectors/aws_s3), register API routes for defaults/configure/list/bucket-status, wire S3 into connection manager and connector registry, and include AWS S3 in sync_all. Frontend: add S3 settings form/dialog, React Query hooks (defaults, configure, bucket status), connect S3 UI into connector cards, cloud picker, and upload flow with an S3 bucket view and direct-sync support. CLI/TUI: add S3-related env fields and config prompts. Misc: small UI icon usages and query invalidation added to keep state in sync.
Add env-var fallback getters for S3 credentials and clearer errors: implement get_client_id and get_client_secret to read from config or AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY and raise ValueError when missing. Frontend fixes: treat a connected connector as available in the connector card UI, and surface S3 bucket loading errors on the upload page by including the query error in the response and rendering a descriptive error message when bucket fetch fails.
Connector card: use isConnected alone for active state, treat a connector as configured when isConnected or connector.available, and show a "Configure" action (with Settings2 icon) when onConfigure is provided; also keep existing loading state. S3 settings dialog: import useEffect and add an effect to sync buckets and selectedBuckets when defaults.bucket_names load asynchronously so defaults are applied after dialog mount. AWS logo: replace fill="currentColor" with an explicit color (#232F3E) for consistent rendering.
|
Lates file: Figma IBM cloud storage icons by theme: |
…g user_id parameter
This reverts commit 9fee2e8.
There was a problem hiding this comment.
These changes mirror those in #1090 and are tied to the the IBM_AUTH_ENABLED env
|
@mfortman11 can you also resolve the conflicts too? |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 50 out of 52 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| auth_mode: "iam" | "hmac"; | ||
| endpoint: string; | ||
| // IAM | ||
| api_key: string; | ||
| service_instance_id: string; | ||
| // HMAC | ||
| hmac_access_key: string; | ||
| hmac_secret_key: string; | ||
| } | ||
|
|
||
| interface IBMCOSSettingsFormProps { | ||
| /** Available buckets after a successful test — null means not yet tested */ | ||
| buckets: string[] | null; | ||
| selectedBuckets: string[]; | ||
| onSelectedBucketsChange: (buckets: string[]) => void; | ||
| isFetchingBuckets: boolean; | ||
| bucketsError: string | null; | ||
| onTestConnection: () => void; | ||
| apiKeySet?: boolean; | ||
| hmacAccessKeySet?: boolean; | ||
| hmacSecretKeySet?: boolean; | ||
| formError?: string | null; | ||
| /** When true, IAM tab is greyed out and HMAC is the only selectable option */ | ||
| disableIam?: boolean; | ||
| } | ||
|
|
||
| export function IBMCOSSettingsForm({ | ||
| buckets, | ||
| selectedBuckets, | ||
| onSelectedBucketsChange, | ||
| isFetchingBuckets, | ||
| bucketsError, | ||
| onTestConnection, | ||
| apiKeySet, | ||
| hmacAccessKeySet, | ||
| hmacSecretKeySet, | ||
| formError, | ||
| disableIam = false, | ||
| }: IBMCOSSettingsFormProps) { | ||
| const { | ||
| register, | ||
| control, | ||
| formState: { errors }, | ||
| } = useFormContext<IBMCOSFormData>(); |
| def get_file_extension(mimetype: str) -> str: | ||
| """Get file extension based on MIME type""" | ||
| """Get file extension based on MIME type. Returns None if the type is unknown.""" | ||
| mime_to_ext = { |
| try: | ||
| cfg = connection.config | ||
| cos = create_ibm_cos_resource(cfg) | ||
| all_buckets = [b.name for b in cos.buckets.all()] | ||
| except Exception: |
| "name": S3Connector.CONNECTOR_NAME, | ||
| "description": S3Connector.CONNECTOR_DESCRIPTION, | ||
| "icon": S3Connector.CONNECTOR_ICON, | ||
| "available": os.environ.get("IBM_AUTH_ENABLED", "").lower() in ("1", "true", "yes"), | ||
| }, |
| refetch, | ||
| } = useIBMCOSBucketStatusQuery(connector.connectionId, { enabled: true }); | ||
| return ( | ||
| <BucketView | ||
| connector={connector} | ||
| buckets={buckets} | ||
| isLoading={isLoading} |
| async def authenticate(self) -> bool: | ||
| """Validate credentials by listing accessible buckets.""" | ||
| try: | ||
| resource = self._get_resource() | ||
| list(resource.buckets.all()) |
| class IBMCOSConfigureBody(BaseModel): | ||
| auth_mode: str # "iam" or "hmac" | ||
| endpoint: str |
| "name": IBMCOSConnector.CONNECTOR_NAME, | ||
| "description": IBMCOSConnector.CONNECTOR_DESCRIPTION, | ||
| "icon": IBMCOSConnector.CONNECTOR_ICON, | ||
| "available": os.environ.get("IBM_AUTH_ENABLED", "").lower() in ("1", "true", "yes"), | ||
| }, |
| access_key: string; | ||
| secret_key: string; | ||
| endpoint_url: string; | ||
| region: string; | ||
| } | ||
|
|
||
| interface S3SettingsFormProps { | ||
| /** Available buckets after a successful test — null means not yet tested */ | ||
| buckets: string[] | null; | ||
| selectedBuckets: string[]; | ||
| onSelectedBucketsChange: (buckets: string[]) => void; | ||
| isFetchingBuckets: boolean; | ||
| bucketsError: string | null; | ||
| onTestConnection: () => void; | ||
| accessKeySet?: boolean; | ||
| secretKeySet?: boolean; | ||
| formError?: string | null; | ||
| } | ||
|
|
||
| export function S3SettingsForm({ | ||
| buckets, | ||
| selectedBuckets, | ||
| onSelectedBucketsChange, | ||
| isFetchingBuckets, | ||
| bucketsError, | ||
| onTestConnection, | ||
| accessKeySet, | ||
| secretKeySet, | ||
| formError, | ||
| }: S3SettingsFormProps) { | ||
| const { register } = useFormContext<S3FormData>(); | ||
|
|
||
| const toggleBucket = (name: string, checked: boolean) => { | ||
| if (checked) { | ||
| onSelectedBucketsChange([...selectedBuckets, name]); | ||
| } else { | ||
| onSelectedBucketsChange(selectedBuckets.filter((b) => b !== name)); | ||
| } | ||
| }; | ||
|
|
||
| const toggleAll = (checked: boolean) => { | ||
| onSelectedBucketsChange(checked ? (buckets ?? []) : []); | ||
| }; |
| CLIENT_ID_ENV_VAR = "IBM_COS_API_KEY" | ||
| CLIENT_SECRET_ENV_VAR = "IBM_COS_SERVICE_INSTANCE_ID" | ||
|
|
||
| def get_client_id(self) -> str: |
There was a problem hiding this comment.
We'll need to deal with API returning credentials to client, this is a credential disclosure bug.
The status payload includes connector.get_client_id() take a look in src/api/connectors.py(352) the JSON returns the raw api_key, so simply loading the connectors page fetches the secret via frontend/app/api/queries/useGetConnectorsQuery.ts line 66). T
ricofurtado
left a comment
There was a problem hiding this comment.
I believe it is safe to merge. Have take some notes we should rework.
|
|
||
| # Cloud connector types to sync | ||
| cloud_connector_types = ["google_drive", "onedrive", "sharepoint"] | ||
| cloud_connector_types = ["google_drive", "onedrive", "sharepoint", "ibm_cos", "aws_s3"] |
There was a problem hiding this comment.
The new connectors will be skipped if there are not files in Opensearch right? Does it need sync_all / bucket_filter for initial ingest?

This pull request adds support for configuring and managing AWS S3 and IBM Cloud Object Storage (COS) connectors through new settings dialogs and API hooks, improving the user experience for direct-auth connectors. It introduces new React Query hooks for fetching and updating connector configuration and status, updates the connector card UI to support configuration dialogs, and adds icon support for these connectors.
Connector configuration and management enhancements:
useS3ConfigureMutation.ts,useIBMCOSConfigureMutation.ts). [1] [2]useS3DefaultsQuery.ts,useIBMCOSDefaultsQuery.ts,useS3BucketStatusQuery.ts,useIBMCOSBucketStatusQuery.ts,useIBMCOSBucketsQuery.ts). [1] [2] [3] [4] [5]UI and UX improvements:
connector-card.tsx,connector-cards.tsx). [1] [2] [3] [4] [5] [6] [7]knowledge/page.tsx,connector-cards.tsx). [1] [2] [3]Connector sync and feedback improvements:
useSyncConnector.ts).useConnectConnectorMutation.ts).Screen.Recording.2026-03-10.at.1.39.55.AM.mov
Command to run MinIO: