Skip to content

Commit 5f7ab5a

Browse files
Bill Leoutsakoswaleedlatif1
authored andcommitted
feat(oci): add validated native integration foundation
1 parent 684b228 commit 5f7ab5a

34 files changed

Lines changed: 4643 additions & 5 deletions

‎apps/docs/components/icons.tsx‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9524,7 +9524,7 @@ export function NewRelicIcon(props: SVGProps<SVGSVGElement>) {
95249524
)
95259525
}
95269526

9527-
export function NetSuiteIcon(props: SVGProps<SVGSVGElement>) {
9527+
export function OracleIcon(props: SVGProps<SVGSVGElement>) {
95289528
return (
95299529
<svg {...props} viewBox='0 0 93.9 59.4' xmlns='http://www.w3.org/2000/svg'>
95309530
<path
@@ -9535,6 +9535,10 @@ export function NetSuiteIcon(props: SVGProps<SVGSVGElement>) {
95359535
)
95369536
}
95379537

9538+
export function NetSuiteIcon(props: SVGProps<SVGSVGElement>) {
9539+
return <OracleIcon {...props} />
9540+
}
9541+
95389542
export function WizaIcon(props: SVGProps<SVGSVGElement>) {
95399543
return (
95409544
<svg {...props} viewBox='0 0 51 49' fill='none' xmlns='http://www.w3.org/2000/svg'>

‎apps/docs/components/ui/icon-mapping.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ import {
201201
OktaIcon,
202202
OnePasswordIcon,
203203
OpenAIIcon,
204+
OracleIcon,
204205
OtterIcon,
205206
OutlookIcon,
206207
PackageSearchIcon,
@@ -547,6 +548,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
547548
notion: NotionIcon,
548549
notion_v2: NotionIcon,
549550
obsidian: ObsidianIcon,
551+
oci: OracleIcon,
550552
okta: OktaIcon,
551553
onedrive: MicrosoftOneDriveIcon,
552554
onepassword: OnePasswordIcon,

‎apps/docs/content/docs/cli/credentials.mdx‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ Update Credential (OAuth login or personal API key required)
202202
| `--auth-method <value>` | No | Provider authentication method. |
203203
| `--private-key <value>` | No | Write-only PEM private key. |
204204
| `--username <value>` | No | Provider run-as username. |
205+
| `--tenancy-ocid <value>` | No | OCI tenancy OCID. |
206+
| `--user-ocid <value>` | No | OCI user OCID. |
207+
| `--fingerprint <value>` | No | OCI API-key fingerprint. |
208+
| `--private-key-passphrase <value>` | No | Write-only OCI private-key passphrase. |
209+
| `--region <value>` | No | OCI home region. |
205210
| `--name <displayName>` | No | Alias for --display-name. |
206211

207212
</CommandTable>

‎apps/docs/content/docs/cli/reference.mdx‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,11 @@ sim credentials update <credentialId> [options]
635635
| `--auth-method <value>` | No | Provider authentication method. |
636636
| `--private-key <value>` | No | Write-only PEM private key. |
637637
| `--username <value>` | No | Provider run-as username. |
638+
| `--tenancy-ocid <value>` | No | OCI tenancy OCID. |
639+
| `--user-ocid <value>` | No | OCI user OCID. |
640+
| `--fingerprint <value>` | No | OCI API-key fingerprint. |
641+
| `--private-key-passphrase <value>` | No | Write-only OCI private-key passphrase. |
642+
| `--region <value>` | No | OCI home region. |
638643
| `--name <displayName>` | No | Alias for --display-name. |
639644

640645
</CommandTable>

‎apps/docs/openapi-v2-resources.json‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15333,6 +15333,36 @@
1533315333
"type": "string",
1533415334
"minLength": 1,
1533515335
"maxLength": 255
15336+
},
15337+
"tenancyOcid": {
15338+
"description": "OCI tenancy OCID.",
15339+
"type": "string",
15340+
"minLength": 1,
15341+
"maxLength": 255
15342+
},
15343+
"userOcid": {
15344+
"description": "OCI user OCID.",
15345+
"type": "string",
15346+
"minLength": 1,
15347+
"maxLength": 255
15348+
},
15349+
"fingerprint": {
15350+
"description": "OCI API-key fingerprint.",
15351+
"type": "string",
15352+
"minLength": 1,
15353+
"maxLength": 128
15354+
},
15355+
"privateKeyPassphrase": {
15356+
"description": "Write-only OCI private-key passphrase.",
15357+
"writeOnly": true,
15358+
"type": "string",
15359+
"maxLength": 4096
15360+
},
15361+
"region": {
15362+
"description": "OCI home region.",
15363+
"type": "string",
15364+
"minLength": 1,
15365+
"maxLength": 128
1533615366
}
1533715367
},
1533815368
"additionalProperties": false,

‎apps/sim/app/api/credentials/route.test.ts‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,4 +345,68 @@ describe('POST /api/credentials', () => {
345345
expect(dbChainMockFns.insert).not.toHaveBeenCalled()
346346
})
347347
})
348+
349+
it('forwards OCI API-key fields without returning secret material', async () => {
350+
mockVerifyAndBuildServiceAccountSecret.mockResolvedValueOnce({
351+
providerId: 'oci-api-key-service-account',
352+
encryptedServiceAccountKey: 'encrypted-oci-blob',
353+
displayName: 'ocid1.user.oc1..principal',
354+
auditMetadata: {
355+
principalKind: 'user',
356+
principalId: 'ocid1.user.oc1..principal',
357+
},
358+
principal: { kind: 'user', id: 'ocid1.user.oc1..principal' },
359+
})
360+
queueTableRows(credential, [])
361+
queueTableRows(credential, [])
362+
queueTableRows(credential, [
363+
{
364+
id: 'credential-oci',
365+
workspaceId: WORKSPACE_ID,
366+
type: 'service_account',
367+
displayName: 'ocid1.user.oc1..principal',
368+
description: null,
369+
unredacted: false,
370+
providerId: 'oci-api-key-service-account',
371+
accountId: null,
372+
envKey: null,
373+
envOwnerUserId: null,
374+
encryptedServiceAccountKey: 'encrypted-oci-blob',
375+
createdBy: 'user-1',
376+
createdAt: new Date('2026-08-11T00:00:00.000Z'),
377+
updatedAt: new Date('2026-08-11T00:00:00.000Z'),
378+
},
379+
])
380+
381+
const response = await POST(
382+
createMockRequest('POST', {
383+
workspaceId: WORKSPACE_ID,
384+
type: 'service_account',
385+
providerId: 'oci-api-key-service-account',
386+
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
387+
userOcid: 'ocid1.user.oc1..principal',
388+
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
389+
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
390+
privateKeyPassphrase: ' exact passphrase ',
391+
region: 'us-ashburn-1',
392+
})
393+
)
394+
const body = await response.text()
395+
396+
expect(response.status).toBe(201)
397+
expect(mockVerifyAndBuildServiceAccountSecret).toHaveBeenCalledWith(
398+
'oci-api-key-service-account',
399+
expect.objectContaining({
400+
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
401+
userOcid: 'ocid1.user.oc1..principal',
402+
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
403+
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
404+
privateKeyPassphrase: ' exact passphrase ',
405+
region: 'us-ashburn-1',
406+
})
407+
)
408+
expect(body).not.toContain('PRIVATE KEY')
409+
expect(body).not.toContain('exact passphrase')
410+
expect(body).not.toContain('encrypted-oci-blob')
411+
})
348412
})

‎apps/sim/app/api/v2/credentials/[credentialId]/route.test.ts‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,33 @@ describe('PATCH /api/v2/credentials/[credentialId]', () => {
121121
expect(body).not.toContain('MUST_NOT_LEAK_CIPHERTEXT')
122122
})
123123

124+
it('forwards a complete OCI rotation tuple with an omitted replacement passphrase', async () => {
125+
const request = patchRequest({
126+
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
127+
userOcid: 'ocid1.user.oc1..replacement',
128+
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
129+
privateKey: '-----BEGIN PRIVATE KEY-----\nreplacement\n-----END PRIVATE KEY-----',
130+
region: 'us-ashburn-1',
131+
})
132+
const response = await PATCH(request, context)
133+
134+
expect(response.status).toBe(200)
135+
expect(mocks.update).toHaveBeenCalledWith({
136+
principal: auth.principal,
137+
input: {
138+
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
139+
userOcid: 'ocid1.user.oc1..replacement',
140+
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
141+
privateKey: '-----BEGIN PRIVATE KEY-----\nreplacement\n-----END PRIVATE KEY-----',
142+
region: 'us-ashburn-1',
143+
credentialId: CREDENTIAL_ID,
144+
assertedWorkspaceId: WORKSPACE_ID,
145+
},
146+
request,
147+
})
148+
expect(JSON.stringify(await response.json())).not.toContain('PRIVATE KEY')
149+
})
150+
124151
it('clears a description with an explicit null and leaves an omitted field alone', async () => {
125152
await PATCH(patchRequest({ description: null }), context)
126153

‎apps/sim/app/api/v2/credentials/route.test.ts‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,55 @@ describe('POST /api/v2/credentials', () => {
203203
authMethod: undefined,
204204
privateKey: undefined,
205205
username: undefined,
206+
tenancyOcid: undefined,
207+
userOcid: undefined,
208+
fingerprint: undefined,
209+
privateKeyPassphrase: undefined,
210+
region: undefined,
206211
},
207212
request,
208213
})
209214
})
210215

216+
it('forwards OCI credential fields from the write-only credentials envelope', async () => {
217+
const request = new NextRequest('http://localhost:3000/api/v2/credentials', {
218+
method: 'POST',
219+
headers: { 'content-type': 'application/json' },
220+
body: JSON.stringify({
221+
workspaceId: WORKSPACE_ID,
222+
type: 'service_account',
223+
providerId: 'oci-api-key-service-account',
224+
credentials: JSON.stringify({
225+
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
226+
userOcid: 'ocid1.user.oc1..user',
227+
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
228+
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
229+
privateKeyPassphrase: ' exact passphrase ',
230+
region: 'us-ashburn-1',
231+
}),
232+
}),
233+
})
234+
const response = await POST(request)
235+
const body = await response.text()
236+
237+
expect(response.status).toBe(201)
238+
expect(mocks.create).toHaveBeenCalledWith({
239+
principal: { kind: 'personal_api_key', userId: 'user-1', keyId: 'key-1' },
240+
input: expect.objectContaining({
241+
providerId: 'oci-api-key-service-account',
242+
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
243+
userOcid: 'ocid1.user.oc1..user',
244+
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
245+
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
246+
privateKeyPassphrase: ' exact passphrase ',
247+
region: 'us-ashburn-1',
248+
}),
249+
request,
250+
})
251+
expect(body).not.toContain('PRIVATE KEY')
252+
expect(body).not.toContain('exact passphrase')
253+
})
254+
211255
/**
212256
* `credentials create slack-custom-bot` used to fail twice over: discovery
213257
* demanded a client-generated id, and a caller who then supplied a slug was

0 commit comments

Comments
 (0)