Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
2 changes: 2 additions & 0 deletions apps/docs/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9333,6 +9333,8 @@ export function NetSuiteIcon(props: SVGProps<SVGSVGElement>) {
)
}

export const OracleIcon = NetSuiteIcon

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
5 changes: 5 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,11 @@ 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. |
| `--tenancy-ocid <value>` | No | OCI tenancy OCID. |
| `--user-ocid <value>` | No | OCI user OCID. |
| `--fingerprint <value>` | No | OCI API-key fingerprint. |
| `--private-key-passphrase <value>` | No | Write-only OCI private-key passphrase. |
| `--region <value>` | No | OCI home region. |
| `--name <displayName>` | No | Alias for --display-name. |

</CommandTable>
Expand Down
5 changes: 5 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,11 @@ 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. |
| `--tenancy-ocid <value>` | No | OCI tenancy OCID. |
| `--user-ocid <value>` | No | OCI user OCID. |
| `--fingerprint <value>` | No | OCI API-key fingerprint. |
| `--private-key-passphrase <value>` | No | Write-only OCI private-key passphrase. |
| `--region <value>` | No | OCI home region. |
| `--name <displayName>` | No | Alias for --display-name. |

</CommandTable>
Expand Down
30 changes: 30 additions & 0 deletions apps/docs/openapi-v2-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -8210,6 +8210,36 @@
"type": "string",
"minLength": 1,
"maxLength": 255
},
"tenancyOcid": {
"description": "OCI tenancy OCID.",
"type": "string",
"minLength": 1,
"maxLength": 255
},
"userOcid": {
"description": "OCI user OCID.",
"type": "string",
"minLength": 1,
"maxLength": 255
},
"fingerprint": {
"description": "OCI API-key fingerprint.",
"type": "string",
"minLength": 1,
"maxLength": 128
},
"privateKeyPassphrase": {
"description": "Write-only OCI private-key passphrase.",
"writeOnly": true,
"type": "string",
"maxLength": 4096
},
"region": {
"description": "OCI home region.",
"type": "string",
"minLength": 1,
"maxLength": 128
}
},
"additionalProperties": false,
Expand Down
64 changes: 64 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,68 @@ describe('POST /api/credentials', () => {
expect(dbChainMockFns.insert).not.toHaveBeenCalled()
})
})

it('forwards OCI API-key fields without returning secret material', async () => {
mockVerifyAndBuildServiceAccountSecret.mockResolvedValueOnce({
providerId: 'oci-api-key-service-account',
encryptedServiceAccountKey: 'encrypted-oci-blob',
displayName: 'ocid1.user.oc1..principal',
auditMetadata: {
principalKind: 'user',
principalId: 'ocid1.user.oc1..principal',
},
principal: { kind: 'user', id: 'ocid1.user.oc1..principal' },
})
queueTableRows(credential, [])
queueTableRows(credential, [])
queueTableRows(credential, [
{
id: 'credential-oci',
workspaceId: WORKSPACE_ID,
type: 'service_account',
displayName: 'ocid1.user.oc1..principal',
description: null,
unredacted: false,
providerId: 'oci-api-key-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-api-key-service-account',
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
userOcid: 'ocid1.user.oc1..principal',
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
privateKeyPassphrase: ' exact passphrase ',
region: 'us-ashburn-1',
})
)
const body = await response.text()

expect(response.status).toBe(201)
expect(mockVerifyAndBuildServiceAccountSecret).toHaveBeenCalledWith(
'oci-api-key-service-account',
expect.objectContaining({
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
userOcid: 'ocid1.user.oc1..principal',
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
privateKeyPassphrase: ' exact passphrase ',
region: 'us-ashburn-1',
})
)
expect(body).not.toContain('PRIVATE KEY')
expect(body).not.toContain('exact passphrase')
expect(body).not.toContain('encrypted-oci-blob')
})
})
27 changes: 27 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,33 @@ describe('PATCH /api/v2/credentials/[credentialId]', () => {
expect(body).not.toContain('MUST_NOT_LEAK_CIPHERTEXT')
})

it('forwards a complete OCI rotation tuple with an omitted replacement passphrase', async () => {
const request = patchRequest({
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
userOcid: 'ocid1.user.oc1..replacement',
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
privateKey: '-----BEGIN PRIVATE KEY-----\nreplacement\n-----END PRIVATE KEY-----',
region: 'us-ashburn-1',
})
const response = await PATCH(request, context)

expect(response.status).toBe(200)
expect(mocks.update).toHaveBeenCalledWith({
principal: auth.principal,
input: {
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
userOcid: 'ocid1.user.oc1..replacement',
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
privateKey: '-----BEGIN PRIVATE KEY-----\nreplacement\n-----END PRIVATE KEY-----',
region: 'us-ashburn-1',
credentialId: CREDENTIAL_ID,
assertedWorkspaceId: WORKSPACE_ID,
},
request,
})
expect(JSON.stringify(await response.json())).not.toContain('PRIVATE KEY')
})

it('asserts the workspace scope and preserves the credential id', async () => {
const request = patchRequest({ displayName: 'Zoom prod' })
await PATCH(request, context)
Expand Down
44 changes: 44 additions & 0 deletions apps/sim/app/api/v2/credentials/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,55 @@ describe('POST /api/v2/credentials', () => {
authMethod: undefined,
privateKey: undefined,
username: undefined,
tenancyOcid: undefined,
userOcid: undefined,
fingerprint: undefined,
privateKeyPassphrase: undefined,
region: undefined,
},
request,
})
})

it('forwards OCI credential fields from the write-only credentials envelope', async () => {
const request = new NextRequest('http://localhost:3000/api/v2/credentials', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
workspaceId: WORKSPACE_ID,
type: 'service_account',
providerId: 'oci-api-key-service-account',
credentials: JSON.stringify({
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
userOcid: 'ocid1.user.oc1..user',
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
privateKeyPassphrase: ' exact passphrase ',
region: 'us-ashburn-1',
}),
}),
})
const response = await POST(request)
const body = await response.text()

expect(response.status).toBe(201)
expect(mocks.create).toHaveBeenCalledWith({
principal: { kind: 'personal_api_key', userId: 'user-1', keyId: 'key-1' },
input: expect.objectContaining({
providerId: 'oci-api-key-service-account',
tenancyOcid: 'ocid1.tenancy.oc1..tenant',
userOcid: 'ocid1.user.oc1..user',
fingerprint: '00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff',
privateKey: '-----BEGIN PRIVATE KEY-----\nkey\n-----END PRIVATE KEY-----',
privateKeyPassphrase: ' exact passphrase ',
region: 'us-ashburn-1',
}),
request,
})
expect(body).not.toContain('PRIVATE KEY')
expect(body).not.toContain('exact passphrase')
})

it('rejects an unknown service-account provider before the use case', async () => {
const response = await POST(
new NextRequest('http://localhost:3000/api/v2/credentials', {
Expand Down
Loading
Loading