Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion apps/docs/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9276,7 +9276,7 @@ export function NewRelicIcon(props: SVGProps<SVGSVGElement>) {
)
}

export function NetSuiteIcon(props: SVGProps<SVGSVGElement>) {
function OracleOvalMark(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 93.9 59.4' xmlns='http://www.w3.org/2000/svg'>
<path
Expand All @@ -9287,6 +9287,15 @@ export function NetSuiteIcon(props: SVGProps<SVGSVGElement>) {
)
}

export function NetSuiteIcon(props: SVGProps<SVGSVGElement>) {
return <OracleOvalMark {...props} />
}

/** Oracle brand mark reused for the OCI Object Storage integration. */
export function OciObjectStorageIcon(props: SVGProps<SVGSVGElement>) {
return <OracleOvalMark {...props} />
}

export function WizaIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 51 49' fill='none' xmlns='http://www.w3.org/2000/svg'>
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/components/ui/icon-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ import {
NewRelicIcon,
NotionIcon,
ObsidianIcon,
OciObjectStorageIcon,
OktaIcon,
OnePasswordIcon,
OpenAIIcon,
Expand Down Expand Up @@ -476,6 +477,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
notion: NotionIcon,
notion_v2: NotionIcon,
obsidian: ObsidianIcon,
oci_object_storage: OciObjectStorageIcon,
okta: OktaIcon,
onedrive: MicrosoftOneDriveIcon,
onepassword: OnePasswordIcon,
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/content/docs/cli/credentials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ Update Credential (personal API key required)
| `--auth-method <value>` | No | Provider authentication method. |
| `--private-key <value>` | No | Write-only PEM private key. |
| `--username <value>` | No | Provider run-as username. |
| `--access-key-id <value>` | No | Write-only OCI Customer Secret Key access identifier. |
| `--secret-access-key <value>` | No | Write-only OCI Customer Secret Key secret. |
| `--namespace <value>` | No | OCI Object Storage tenancy namespace. |
| `--region <value>` | No | Public commercial OCI region identifier. |
| `--name <displayName>` | No | Alias for --display-name. |

</CommandTable>
Expand Down
4 changes: 4 additions & 0 deletions apps/docs/content/docs/cli/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,10 @@ sim credentials update <credentialId> [options]
| `--auth-method <value>` | No | Provider authentication method. |
| `--private-key <value>` | No | Write-only PEM private key. |
| `--username <value>` | No | Provider run-as username. |
| `--access-key-id <value>` | No | Write-only OCI Customer Secret Key access identifier. |
| `--secret-access-key <value>` | No | Write-only OCI Customer Secret Key secret. |
| `--namespace <value>` | No | OCI Object Storage tenancy namespace. |
| `--region <value>` | No | Public commercial OCI region identifier. |
| `--name <displayName>` | No | Alias for --display-name. |

</CommandTable>
Expand Down
1 change: 1 addition & 0 deletions apps/docs/content/docs/integrations/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
"notion",
"notion-service-account",
"obsidian",
"oci_object_storage",
"okta",
"onedrive",
"onepassword",
Expand Down
184 changes: 184 additions & 0 deletions apps/docs/content/docs/integrations/oci_object_storage.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
title: OCI Object Storage
description: List, upload, download, inspect, and delete objects in Oracle Cloud
---

import { BlockInfoCard } from "@/components/ui/block-info-card"

<BlockInfoCard
type="oci_object_storage"
color="#FFFFFF"
/>

## Usage Instructions

Connect an Oracle Cloud Infrastructure Customer Secret Key to work with real Object Storage buckets and objects through Oracle’s S3 Compatibility API. Includes bucket and object listing, uploads, downloads, metadata inspection, and deletion, with a 100 MiB Sim transfer limit. Minimum IAM permissions are BUCKET_INSPECT, OBJECT_INSPECT, OBJECT_READ, OBJECT_CREATE, OBJECT_OVERWRITE, and OBJECT_DELETE; customer-managed encryption keys require additional Vault permissions.



## Actions

### OCI Object Storage List Buckets

List the buckets visible to a connected OCI Customer Secret Key

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `oauthCredential` | string | Yes | Connected OCI Object Storage Customer Secret Key credential |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `buckets` | array | OCI Object Storage buckets visible in the connected region |
| ↳ `name` | string | Bucket name |
| ↳ `creationDate` | string | Bucket creation time in ISO 8601 format |
| `owner` | object | Oracle owner identity returned by GetService |
| ↳ `id` | string | Owner identifier |
| ↳ `displayName` | string | Owner display name |

### OCI Object Storage List Objects

List one page of objects and common prefixes in an OCI bucket

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `oauthCredential` | string | Yes | Connected OCI Object Storage Customer Secret Key credential |
| `bucketName` | string | Yes | OCI Object Storage bucket name |
| `prefix` | string | No | Return only object keys beginning with this prefix |
| `delimiter` | string | No | Use / to group keys into common prefixes |
| `maxKeys` | number | No | Maximum objects and prefixes to return \(1-1000\) |
| `startAfter` | string | No | Begin listing after this object key |
| `continuationToken` | string | No | Opaque token returned by the previous truncated page |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `bucket` | string | Bucket that was listed |
| `objects` | array | Objects in this page |
| ↳ `key` | string | Object key |
| ↳ `size` | number | Object size in bytes |
| ↳ `lastModified` | string | Last modification time in ISO 8601 format |
| ↳ `etag` | string | Entity tag |
| ↳ `storageClass` | string | Object storage class |
| `commonPrefixes` | array | Grouped key prefixes when a delimiter is supplied |
| `keyCount` | number | Number of results in this page |
| `maxKeys` | number | Page size applied by Oracle |
| `isTruncated` | boolean | Whether another page is available |
| `nextContinuationToken` | string | Opaque token for the next page |
| `continuationToken` | string | Opaque token used for this page |
| `startAfter` | string | Start-after key used |
| `prefix` | string | Prefix applied to this listing |
| `delimiter` | string | Delimiter applied to this listing |

### OCI Object Storage Upload Object

Upload one file or inline text object, replacing an existing key

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `oauthCredential` | string | Yes | Connected OCI Object Storage Customer Secret Key credential |
| `bucketName` | string | Yes | Destination OCI bucket name |
| `objectKey` | string | Yes | Destination object key, including any prefix |
| `file` | file | No | Authorized Sim file to upload |
| `content` | string | No | Inline text to upload instead of a file |
| `contentType` | string | No | Object Content-Type; inferred from the file when omitted |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `bucket` | string | Destination bucket name |
| `key` | string | Uploaded object key |
| `size` | number | Uploaded size in bytes |
| `contentType` | string | Uploaded Content-Type |
| `etag` | string | Entity tag returned by Oracle |
| `checksumSha256` | string | Base64 SHA-256 checksum when returned by Oracle |
| `requestId` | string | Oracle request identifier |

### OCI Object Storage Download Object

Download an OCI object of up to 100 MiB into the workflow file system

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `oauthCredential` | string | Yes | Connected OCI Object Storage Customer Secret Key credential |
| `bucketName` | string | Yes | Source OCI bucket name |
| `objectKey` | string | Yes | Object key to download |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `file` | file | Downloaded file stored in execution files |
| `bucket` | string | Source bucket name |
| `key` | string | Downloaded object key |
| `contentLength` | number | Downloaded size in bytes |
| `contentType` | string | Object Content-Type |
| `etag` | string | Entity tag |
| `lastModified` | string | Last modification time in ISO 8601 format |
| `metadata` | json | User-defined object metadata |
| `requestId` | string | Oracle request identifier |

### OCI Object Storage Inspect Object Metadata

Read documented metadata for an OCI object without downloading its body

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `oauthCredential` | string | Yes | Connected OCI Object Storage Customer Secret Key credential |
| `bucketName` | string | Yes | OCI bucket name |
| `objectKey` | string | Yes | Object key to inspect |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `bucket` | string | Bucket name |
| `key` | string | Object key |
| `contentLength` | number | Object size in bytes |
| `contentType` | string | Object Content-Type |
| `contentEncoding` | string | Content-Encoding |
| `contentLanguage` | string | Content-Language |
| `cacheControl` | string | Cache-Control value |
| `contentDisposition` | string | Content-Disposition |
| `etag` | string | Entity tag |
| `lastModified` | string | Last modification time in ISO 8601 format |
| `storageClass` | string | Object storage class |
| `metadata` | json | User-defined object metadata |
| `checksumSha256` | string | Base64 SHA-256 checksum when returned by Oracle |
| `requestId` | string | Oracle request identifier |

### OCI Object Storage Delete Object

Delete one object from an OCI bucket

#### Input

| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `oauthCredential` | string | Yes | Connected OCI Object Storage Customer Secret Key credential |
| `bucketName` | string | Yes | OCI bucket name |
| `objectKey` | string | Yes | Object key to delete |

#### Output

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `deleted` | boolean | Whether Oracle accepted the deletion |
| `bucket` | string | Bucket name |
| `key` | string | Deleted object key |
| `requestId` | string | Oracle request identifier |


26 changes: 26 additions & 0 deletions apps/docs/openapi-v2-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -8210,6 +8210,32 @@
"type": "string",
"minLength": 1,
"maxLength": 255
},
"accessKeyId": {
"description": "Write-only OCI Customer Secret Key access identifier.",
"writeOnly": true,
"type": "string",
"minLength": 1,
"maxLength": 512
},
"secretAccessKey": {
"description": "Write-only OCI Customer Secret Key secret.",
"writeOnly": true,
"type": "string",
"minLength": 1,
"maxLength": 1024
},
"namespace": {
"description": "OCI Object Storage tenancy namespace.",
"type": "string",
"minLength": 1,
"maxLength": 63
},
"region": {
"description": "Public commercial OCI region identifier.",
"type": "string",
"minLength": 1,
"maxLength": 64
}
},
"additionalProperties": false,
Expand Down
58 changes: 58 additions & 0 deletions apps/sim/app/api/credentials/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,4 +550,62 @@ describe('POST /api/credentials', () => {
expect(dbChainMockFns.insert).not.toHaveBeenCalled()
})
})

describe('OCI Object Storage service accounts', () => {
it('forwards all four fields on create and never returns the encrypted secret', async () => {
mockVerifyAndBuildServiceAccountSecret.mockResolvedValueOnce({
providerId: 'oci-object-storage-service-account',
encryptedServiceAccountKey: 'encrypted-oci-blob',
displayName: 'Storage Automation — us-ashburn-1',
auditMetadata: { ociNamespace: 'namespace1', ociRegion: 'us-ashburn-1' },
principal: { kind: 'user', id: 'ocid1.user.oc1..owner' },
})
queueTableRows(credential, [])
queueTableRows(credential, [])
queueTableRows(credential, [
{
id: 'credential-oci',
workspaceId: WORKSPACE_ID,
type: 'service_account',
displayName: 'Storage Automation — us-ashburn-1',
description: null,
unredacted: false,
providerId: 'oci-object-storage-service-account',
accountId: null,
envKey: null,
envOwnerUserId: null,
encryptedServiceAccountKey: 'encrypted-oci-blob',
createdBy: 'user-1',
createdAt: new Date('2026-08-11T00:00:00.000Z'),
updatedAt: new Date('2026-08-11T00:00:00.000Z'),
},
])

const response = await POST(
createMockRequest('POST', {
workspaceId: WORKSPACE_ID,
type: 'service_account',
providerId: 'oci-object-storage-service-account',
accessKeyId: 'access-key-canary',
secretAccessKey: 'secret-key-canary',
namespace: 'namespace1',
region: 'us-ashburn-1',
})
)
const body = await response.json()

expect(response.status).toBe(201)
expect(body.credential).not.toHaveProperty('encryptedServiceAccountKey')
expect(JSON.stringify(body)).not.toContain('key-canary')
expect(mockVerifyAndBuildServiceAccountSecret).toHaveBeenCalledWith(
'oci-object-storage-service-account',
expect.objectContaining({
accessKeyId: 'access-key-canary',
secretAccessKey: 'secret-key-canary',
namespace: 'namespace1',
region: 'us-ashburn-1',
})
)
})
})
})
24 changes: 24 additions & 0 deletions apps/sim/app/api/v2/credentials/[credentialId]/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,30 @@ describe('PATCH /api/v2/credentials/[credentialId]', () => {
expect(body).not.toContain('MUST_NOT_LEAK_CIPHERTEXT')
})

it('forwards all OCI Customer Secret Key fields without returning them', async () => {
const secretFields = {
accessKeyId: 'access-key-canary',
secretAccessKey: 'secret-key-canary',
namespace: 'namespace1',
region: 'us-ashburn-1',
}
const response = await PATCH(patchRequest(secretFields), context)

expect(response.status).toBe(200)
expect(mocks.update).toHaveBeenCalledWith({
principal: auth.principal,
input: {
...secretFields,
credentialId: CREDENTIAL_ID,
assertedWorkspaceId: WORKSPACE_ID,
},
request: expect.any(NextRequest),
})
const body = await response.text()
expect(body).not.toContain('key-canary')
expect(body).not.toContain('namespace1')
})

it('asserts the workspace scope and preserves the credential id', async () => {
const request = patchRequest({ displayName: 'Zoom prod' })
await PATCH(request, context)
Expand Down
Loading
Loading