mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(okta): add complete Okta identity management integration (#3685)
* feat(okta): add complete Okta identity management integration Add 18 Okta Management API tools covering user lifecycle (list, get, create, update, activate, deactivate, suspend, unsuspend, reset password, delete) and group management (list, get, create, update, delete, add/remove members, list members). Includes block with conditional UI, icon, registry entries, and generated docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(okta): add manual description section to generated docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(okta): address PR review — SSRF prevention, safe response parsing, consistent sendEmail - Add validateOktaDomain() to prevent SSRF via user-supplied domain param - Fix 9 tools to check response.ok before calling response.json() - Make sendEmail query param explicit in deactivate_user and delete_user Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(okta): only forward boolean switches when explicitly true Switch subBlocks default to OFF (false), which was being forwarded to tools and overriding their default-true behavior for sendEmail and activate params. Now only forward these when explicitly toggled ON. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(okta): use nullish coalescing for boolean switch defaults Block now forwards sendEmail/activate values as-is (including false). Tools use ?? operator so: explicit true/false from switches are respected, undefined (programmatic calls) still defaults to true. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(okta): prevent silent data loss in update operations - update_group: always include description in PUT body (defaults to '') since PUT replaces the full profile object - update_user: use !== undefined checks so empty strings can clear fields via Okta's POST partial update - block: allow empty strings through passthrough loop and use !== undefined for groupDescription mapping Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(okta): move validateOktaDomain to centralized input-validation - Moved validateOktaDomain from tools/okta/types.ts to lib/core/security/input-validation.ts alongside other validation utils - Added .trim() to handle copy-paste whitespace in domain input - Updated all 18 tool files to import from the new location Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d3daab743f
commit
6326353f5c
@@ -6106,6 +6106,19 @@ export function AgentSkillsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function OktaIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 63 63' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M34.6.4l-1.3 16c-.6-.1-1.2-.1-1.9-.1-.8 0-1.6.1-2.3.2l-.7-7.7c0-.2.2-.5.4-.5h1.3L29.5.5c0-.2.2-.5.4-.5h4.3c.3 0 .5.2.4.4zm-10.8.8c-.1-.2-.3-.4-.5-.3l-4 1.5c-.3.1-.4.4-.3.6l3.3 7.1-1.2.5c-.2.1-.3.3-.2.6l3.3 7c1.2-.7 2.5-1.2 3.9-1.5L23.8 1.2zM14 5.7l9.3 13.1c-1.2.8-2.2 1.7-3.1 2.7L14.5 16c-.2-.2-.2-.5 0-.6l1-.8L10 9c-.2-.2-.2-.5 0-.6l3.3-2.7c.2-.3.5-.2.7 0zM6.2 13.2c-.2-.1-.5-.1-.6.1l-2.1 3.7c-.1.2 0 .5.2.6l7.1 3.4-.7 1.1c-.1.2 0 .5.2.6l7.1 3.2c.5-1.3 1.2-2.5 2-3.6L6.2 13.2zM.9 23.3c0-.2.3-.4.5-.3l15.5 4c-.4 1.3-.6 2.7-.7 4.1l-7.8-.6c-.2 0-.4-.2-.4-.5l.2-1.3L.6 28c-.2 0-.4-.2-.4-.5l.7-4.2zM.4 33.8c-.3 0-.4.2-.4.5l.8 4.2c0 .2.3.4.5.3l7.6-2 .2 1.3c0 .2.3.4.5.3l7.5-2.1c-.4-1.3-.7-2.7-.8-4.1L.4 33.8zm2.5 11.1c-.1-.2 0-.5.2-.6l14.5-6.9c.5 1.3 1.3 2.5 2.2 3.6l-6.3 4.5c-.2.1-.5.1-.6-.1L12 44.3l-6.5 4.5c-.2.1-.5.1-.6-.1l-2-3.8zm17.5-3L9.1 53.3c-.2.2-.2.5 0 .6l3.3 2.7c.2.2.5.1.6-.1l4.6-6.4 1 .9c.2.2.5.1.6-.1l4.4-6.4c-1.2-.7-2.3-1.6-3.2-2.6zm-2.2 18.2c-.2-.1-.3-.3-.2-.6L24.6 45c1.2.6 2.6 1.1 3.9 1.4l-2 7.5c-.1.2-.3.4-.5.3l-1.2-.5-2.1 7.6c-.1.2-.3.4-.5.3l-4-1.5zm10.9-13.5l-1.3 16c0 .2.2.5.4.5H33c.2 0 .4-.2.4-.5l-.6-7.8h1.3c.2 0 .4-.2.4-.5l-.7-7.7c-.8.1-1.5.2-2.3.2-.6 0-1.3 0-1.9-.1zm16-43.2c.1-.2 0-.5-.2-.6l-4-1.5c-.2-.1-.5.1-.5.3l-2.1 7.6-1.2-.5c-.2-.1-.5.1-.5.3l-2 7.5c1.4.3 2.7.8 3.9 1.4l6.6-14.5zm8.8 6.3L42.6 21.1c-.9-1-2-1.9-3.2-2.6l4.4-6.4c.1-.2.4-.2.6-.1l1 .9 4.6-6.4c.1-.2.4-.2.6-.1l3.3 2.7c.2.2.2.5 0 .6zM59.9 18.7c.2-.1.3-.4.2-.6L58 14.4c-.1-.2-.4-.3-.6-.1l-6.5 4.5-.7-1.1c-.1-.2-.4-.3-.6-.1L43.3 22c.9 1.1 1.6 2.3 2.2 3.6l14.4-6.9zm2.3 5.8l.7 4.2c0 .2-.1.5-.4.5l-15.9 1.5c-.1-1.4-.4-2.8-.8-4.1l7.5-2.1c.2-.1.5.1.5.3l.2 1.3 7.6-2c.3-.1.5.1.6.4zM61.5 40c.2.1.5-.1.5-.3l.7-4.2c0-.2-.1-.5-.4-.5l-7.8-.7.2-1.3c0-.2-.1-.5-.4-.5l-7.8-.6c0 1.4-.3 2.8-.7 4.1L61.5 40zm-4.1 9.6c-.1.2-.4.3-.6.1l-13.2-9.1c.8-1.1 1.5-2.3 2-3.6l7.1 3.2c.2.1.3.4.2.6L52.2 42l7.1 3.4c.2.1.3.4.2.6l-2.1 3.6zm-17.7-5.4L49 57.3c.1.2.4.2.6.1l3.3-2.7c.2-.2.2-.4 0-.6l-5.5-5.6 1-.8c.2-.2.2-.4 0-.6l-5.5-5.5c1.1.8 0 1.7-1.2 2.4zm0 17.8c-.2.1-.5-.1-.5-.3l-4.2-15.4c1.4-.3 2.7-.8 3.9-1.5l3.3 7c.1.2 0 .5-.2.6l-1.2.5 3.3 7.1c.1.2 0 .5-.2.6L39.7 62z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg' fill='none'>
|
||||
|
||||
@@ -111,6 +111,7 @@ import {
|
||||
Neo4jIcon,
|
||||
NotionIcon,
|
||||
ObsidianIcon,
|
||||
OktaIcon,
|
||||
OnePasswordIcon,
|
||||
OpenAIIcon,
|
||||
OutlookIcon,
|
||||
@@ -282,6 +283,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
neo4j: Neo4jIcon,
|
||||
notion_v2: NotionIcon,
|
||||
obsidian: ObsidianIcon,
|
||||
okta: OktaIcon,
|
||||
onedrive: MicrosoftOneDriveIcon,
|
||||
onepassword: OnePasswordIcon,
|
||||
openai: OpenAIIcon,
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"neo4j",
|
||||
"notion",
|
||||
"obsidian",
|
||||
"okta",
|
||||
"onedrive",
|
||||
"onepassword",
|
||||
"openai",
|
||||
|
||||
@@ -0,0 +1,516 @@
|
||||
---
|
||||
title: Okta
|
||||
description: Manage users and groups in Okta
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="okta"
|
||||
color="#191919"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Okta](https://www.okta.com/) is an identity and access management platform that provides secure authentication, authorization, and user management for organizations.
|
||||
|
||||
With the Okta integration in Sim, you can:
|
||||
|
||||
- **List and search users**: Retrieve users from your Okta org with SCIM search expressions and filters
|
||||
- **Manage user lifecycle**: Create, activate, deactivate, suspend, unsuspend, and delete users
|
||||
- **Update user profiles**: Modify user attributes like name, email, phone, title, and department
|
||||
- **Reset passwords**: Trigger password reset flows with optional email notification
|
||||
- **Manage groups**: Create, update, delete, and list groups in your organization
|
||||
- **Manage group membership**: Add or remove users from groups, and list group members
|
||||
|
||||
In Sim, the Okta integration enables your agents to automate identity management tasks as part of their workflows. This allows for scenarios such as onboarding new employees, offboarding departing users, managing group-based access, auditing user status, and responding to security events by suspending or deactivating accounts.
|
||||
|
||||
## Need Help?
|
||||
|
||||
If you encounter issues with the Okta integration, contact us at [help@sim.ai](mailto:help@sim.ai)
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Okta identity management into your workflow. List, create, update, activate, suspend, and delete users. Reset passwords. Manage groups and group membership.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `okta_list_users`
|
||||
|
||||
List all users in your Okta organization with optional search and filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `search` | string | No | Okta search expression \(e.g., profile.firstName eq "John" or profile.email co "example.com"\) |
|
||||
| `filter` | string | No | Okta filter expression \(e.g., status eq "ACTIVE"\) |
|
||||
| `limit` | number | No | Maximum number of users to return \(default: 200, max: 200\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `users` | array | Array of Okta user objects |
|
||||
| ↳ `id` | string | User ID |
|
||||
| ↳ `status` | string | User status \(ACTIVE, STAGED, PROVISIONED, etc.\) |
|
||||
| ↳ `firstName` | string | First name |
|
||||
| ↳ `lastName` | string | Last name |
|
||||
| ↳ `email` | string | Email address |
|
||||
| ↳ `login` | string | Login \(usually email\) |
|
||||
| ↳ `mobilePhone` | string | Mobile phone |
|
||||
| ↳ `title` | string | Job title |
|
||||
| ↳ `department` | string | Department |
|
||||
| ↳ `created` | string | Creation timestamp |
|
||||
| ↳ `lastLogin` | string | Last login timestamp |
|
||||
| ↳ `lastUpdated` | string | Last update timestamp |
|
||||
| ↳ `activated` | string | Activation timestamp |
|
||||
| ↳ `statusChanged` | string | Status change timestamp |
|
||||
| `count` | number | Number of users returned |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_get_user`
|
||||
|
||||
Get a specific user by ID or login from your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID or login \(email\) to look up |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | User ID |
|
||||
| `status` | string | User status |
|
||||
| `firstName` | string | First name |
|
||||
| `lastName` | string | Last name |
|
||||
| `email` | string | Email address |
|
||||
| `login` | string | Login \(usually email\) |
|
||||
| `mobilePhone` | string | Mobile phone |
|
||||
| `secondEmail` | string | Secondary email |
|
||||
| `displayName` | string | Display name |
|
||||
| `title` | string | Job title |
|
||||
| `department` | string | Department |
|
||||
| `organization` | string | Organization |
|
||||
| `manager` | string | Manager name |
|
||||
| `managerId` | string | Manager ID |
|
||||
| `division` | string | Division |
|
||||
| `employeeNumber` | string | Employee number |
|
||||
| `userType` | string | User type |
|
||||
| `created` | string | Creation timestamp |
|
||||
| `activated` | string | Activation timestamp |
|
||||
| `lastLogin` | string | Last login timestamp |
|
||||
| `lastUpdated` | string | Last update timestamp |
|
||||
| `statusChanged` | string | Status change timestamp |
|
||||
| `passwordChanged` | string | Password change timestamp |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_create_user`
|
||||
|
||||
Create a new user in your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `firstName` | string | Yes | First name of the user |
|
||||
| `lastName` | string | Yes | Last name of the user |
|
||||
| `email` | string | Yes | Email address of the user |
|
||||
| `login` | string | No | Login for the user \(defaults to email if not provided\) |
|
||||
| `password` | string | No | Password for the user \(if not set, user will be emailed to set password\) |
|
||||
| `mobilePhone` | string | No | Mobile phone number |
|
||||
| `title` | string | No | Job title |
|
||||
| `department` | string | No | Department |
|
||||
| `activate` | boolean | No | Whether to activate the user immediately \(default: true\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Created user ID |
|
||||
| `status` | string | User status |
|
||||
| `firstName` | string | First name |
|
||||
| `lastName` | string | Last name |
|
||||
| `email` | string | Email address |
|
||||
| `login` | string | Login |
|
||||
| `created` | string | Creation timestamp |
|
||||
| `lastUpdated` | string | Last update timestamp |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_update_user`
|
||||
|
||||
Update a user profile in your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID or login to update |
|
||||
| `firstName` | string | No | Updated first name |
|
||||
| `lastName` | string | No | Updated last name |
|
||||
| `email` | string | No | Updated email address |
|
||||
| `login` | string | No | Updated login |
|
||||
| `mobilePhone` | string | No | Updated mobile phone number |
|
||||
| `title` | string | No | Updated job title |
|
||||
| `department` | string | No | Updated department |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | User ID |
|
||||
| `status` | string | User status |
|
||||
| `firstName` | string | First name |
|
||||
| `lastName` | string | Last name |
|
||||
| `email` | string | Email address |
|
||||
| `login` | string | Login |
|
||||
| `created` | string | Creation timestamp |
|
||||
| `lastUpdated` | string | Last update timestamp |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_activate_user`
|
||||
|
||||
Activate a user in your Okta organization. Can only be performed on users with STAGED or DEPROVISIONED status. Optionally sends an activation email.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID or login to activate |
|
||||
| `sendEmail` | boolean | No | Send activation email to the user \(default: true\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | Activated user ID |
|
||||
| `activated` | boolean | Whether the user was activated |
|
||||
| `activationUrl` | string | Activation URL \(only returned when sendEmail is false\) |
|
||||
| `activationToken` | string | Activation token \(only returned when sendEmail is false\) |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_deactivate_user`
|
||||
|
||||
Deactivate a user in your Okta organization. This transitions the user to DEPROVISIONED status.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID or login to deactivate |
|
||||
| `sendEmail` | boolean | No | Send deactivation email to admin \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | Deactivated user ID |
|
||||
| `deactivated` | boolean | Whether the user was deactivated |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_suspend_user`
|
||||
|
||||
Suspend a user in your Okta organization. Only users with ACTIVE status can be suspended. Suspended users cannot log in but retain group and app assignments.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID or login to suspend |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | Suspended user ID |
|
||||
| `suspended` | boolean | Whether the user was suspended |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_unsuspend_user`
|
||||
|
||||
Unsuspend a previously suspended user in your Okta organization. Returns the user to ACTIVE status.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID or login to unsuspend |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | Unsuspended user ID |
|
||||
| `unsuspended` | boolean | Whether the user was unsuspended |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_reset_password`
|
||||
|
||||
Generate a one-time token to reset a user password. Can email the reset link to the user or return it directly. Transitions the user to RECOVERY status.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID or login to reset password for |
|
||||
| `sendEmail` | boolean | No | Send password reset email to the user \(default: true\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | User ID |
|
||||
| `resetPasswordUrl` | string | Password reset URL \(only returned when sendEmail is false\) |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_delete_user`
|
||||
|
||||
Permanently delete a user from your Okta organization. Can only be performed on DEPROVISIONED users. If the user is active, this will first deactivate them and a second call is needed to delete.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `userId` | string | Yes | User ID to delete |
|
||||
| `sendEmail` | boolean | No | Send deactivation email to admin \(default: false\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `userId` | string | Deleted user ID |
|
||||
| `deleted` | boolean | Whether the user was deleted |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_list_groups`
|
||||
|
||||
List all groups in your Okta organization with optional search and filtering
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `search` | string | No | Okta search expression for groups \(e.g., profile.name sw "Engineering" or type eq "OKTA_GROUP"\) |
|
||||
| `filter` | string | No | Okta filter expression \(e.g., type eq "OKTA_GROUP"\) |
|
||||
| `limit` | number | No | Maximum number of groups to return \(default: 10000, max: 10000\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `groups` | array | Array of Okta group objects |
|
||||
| ↳ `id` | string | Group ID |
|
||||
| ↳ `name` | string | Group name |
|
||||
| ↳ `description` | string | Group description |
|
||||
| ↳ `type` | string | Group type \(OKTA_GROUP, APP_GROUP, BUILT_IN\) |
|
||||
| ↳ `created` | string | Creation timestamp |
|
||||
| ↳ `lastUpdated` | string | Last update timestamp |
|
||||
| ↳ `lastMembershipUpdated` | string | Last membership change timestamp |
|
||||
| `count` | number | Number of groups returned |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_get_group`
|
||||
|
||||
Get a specific group by ID from your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `groupId` | string | Yes | Group ID to look up |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Group ID |
|
||||
| `name` | string | Group name |
|
||||
| `description` | string | Group description |
|
||||
| `type` | string | Group type |
|
||||
| `created` | string | Creation timestamp |
|
||||
| `lastUpdated` | string | Last update timestamp |
|
||||
| `lastMembershipUpdated` | string | Last membership change timestamp |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_create_group`
|
||||
|
||||
Create a new group in your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `name` | string | Yes | Name of the group |
|
||||
| `description` | string | No | Description of the group |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Created group ID |
|
||||
| `name` | string | Group name |
|
||||
| `description` | string | Group description |
|
||||
| `type` | string | Group type |
|
||||
| `created` | string | Creation timestamp |
|
||||
| `lastUpdated` | string | Last update timestamp |
|
||||
| `lastMembershipUpdated` | string | Last membership change timestamp |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_update_group`
|
||||
|
||||
Update a group profile in your Okta organization. Only groups of OKTA_GROUP type can be updated. All profile properties must be specified (full replacement).
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `groupId` | string | Yes | Group ID to update |
|
||||
| `name` | string | Yes | Updated group name |
|
||||
| `description` | string | No | Updated group description |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Group ID |
|
||||
| `name` | string | Group name |
|
||||
| `description` | string | Group description |
|
||||
| `type` | string | Group type |
|
||||
| `created` | string | Creation timestamp |
|
||||
| `lastUpdated` | string | Last update timestamp |
|
||||
| `lastMembershipUpdated` | string | Last membership change timestamp |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_delete_group`
|
||||
|
||||
Delete a group from your Okta organization. Groups of OKTA_GROUP or APP_GROUP type can be removed.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `groupId` | string | Yes | Group ID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `groupId` | string | Deleted group ID |
|
||||
| `deleted` | boolean | Whether the group was deleted |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_add_user_to_group`
|
||||
|
||||
Add a user to a group in your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `groupId` | string | Yes | Group ID to add the user to |
|
||||
| `userId` | string | Yes | User ID to add to the group |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `groupId` | string | Group ID |
|
||||
| `userId` | string | User ID added to the group |
|
||||
| `added` | boolean | Whether the user was added |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_remove_user_from_group`
|
||||
|
||||
Remove a user from a group in your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `groupId` | string | Yes | Group ID to remove the user from |
|
||||
| `userId` | string | Yes | User ID to remove from the group |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `groupId` | string | Group ID |
|
||||
| `userId` | string | User ID removed from the group |
|
||||
| `removed` | boolean | Whether the user was removed |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `okta_list_group_members`
|
||||
|
||||
List all members of a specific group in your Okta organization
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Okta API token for authentication |
|
||||
| `domain` | string | Yes | Okta domain \(e.g., dev-123456.okta.com\) |
|
||||
| `groupId` | string | Yes | Group ID to list members for |
|
||||
| `limit` | number | No | Maximum number of members to return \(default: 1000, max: 1000\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `members` | array | Array of group member user objects |
|
||||
| ↳ `id` | string | User ID |
|
||||
| ↳ `status` | string | User status |
|
||||
| ↳ `firstName` | string | First name |
|
||||
| ↳ `lastName` | string | Last name |
|
||||
| ↳ `email` | string | Email address |
|
||||
| ↳ `login` | string | Login |
|
||||
| ↳ `mobilePhone` | string | Mobile phone |
|
||||
| ↳ `title` | string | Job title |
|
||||
| ↳ `department` | string | Department |
|
||||
| ↳ `created` | string | Creation timestamp |
|
||||
| ↳ `lastLogin` | string | Last login timestamp |
|
||||
| ↳ `lastUpdated` | string | Last update timestamp |
|
||||
| ↳ `activated` | string | Activation timestamp |
|
||||
| ↳ `statusChanged` | string | Status change timestamp |
|
||||
| `count` | number | Number of members returned |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
AsanaIcon,
|
||||
AshbyIcon,
|
||||
AttioIcon,
|
||||
BoxCompanyIcon,
|
||||
BrainIcon,
|
||||
BrandfetchIcon,
|
||||
BrowserUseIcon,
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
DevinIcon,
|
||||
DiscordIcon,
|
||||
DocumentIcon,
|
||||
DocuSignIcon,
|
||||
DropboxIcon,
|
||||
DsPyIcon,
|
||||
DubIcon,
|
||||
@@ -107,6 +109,7 @@ import {
|
||||
Neo4jIcon,
|
||||
NotionIcon,
|
||||
ObsidianIcon,
|
||||
OktaIcon,
|
||||
OnePasswordIcon,
|
||||
OpenAIIcon,
|
||||
OutlookIcon,
|
||||
@@ -162,6 +165,7 @@ import {
|
||||
WhatsAppIcon,
|
||||
WikipediaIcon,
|
||||
WordpressIcon,
|
||||
WorkdayIcon,
|
||||
xIcon,
|
||||
YouTubeIcon,
|
||||
ZendeskIcon,
|
||||
@@ -184,6 +188,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
asana: AsanaIcon,
|
||||
ashby: AshbyIcon,
|
||||
attio: AttioIcon,
|
||||
box: BoxCompanyIcon,
|
||||
brandfetch: BrandfetchIcon,
|
||||
browser_use: BrowserUseIcon,
|
||||
calcom: CalComIcon,
|
||||
@@ -198,6 +203,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
datadog: DatadogIcon,
|
||||
devin: DevinIcon,
|
||||
discord: DiscordIcon,
|
||||
docusign: DocuSignIcon,
|
||||
dropbox: DropboxIcon,
|
||||
dspy: DsPyIcon,
|
||||
dub: DubIcon,
|
||||
@@ -273,6 +279,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
neo4j: Neo4jIcon,
|
||||
notion_v2: NotionIcon,
|
||||
obsidian: ObsidianIcon,
|
||||
okta: OktaIcon,
|
||||
onedrive: MicrosoftOneDriveIcon,
|
||||
onepassword: OnePasswordIcon,
|
||||
openai: OpenAIIcon,
|
||||
@@ -331,6 +338,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
whatsapp: WhatsAppIcon,
|
||||
wikipedia: WikipediaIcon,
|
||||
wordpress: WordpressIcon,
|
||||
workday: WorkdayIcon,
|
||||
x: xIcon,
|
||||
youtube: YouTubeIcon,
|
||||
zendesk: ZendeskIcon,
|
||||
|
||||
@@ -683,7 +683,7 @@
|
||||
"slug": "ashby",
|
||||
"name": "Ashby",
|
||||
"description": "Manage candidates, jobs, and applications in Ashby",
|
||||
"longDescription": "Integrate Ashby into the workflow. Can list, search, create, and update candidates, list and get job details, create notes, list notes, list and get applications, create applications, and list offers.",
|
||||
"longDescription": "Integrate Ashby into the workflow. Manage candidates (list, get, create, update, search, tag), applications (list, get, create, change stage), jobs (list, get), job postings (list, get), offers (list, get), notes (list, create), interviews (list), and reference data (sources, tags, archive reasons, custom fields, departments, locations, openings, users).",
|
||||
"bgColor": "#5D4ED6",
|
||||
"iconName": "AshbyIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/ashby",
|
||||
@@ -739,9 +739,69 @@
|
||||
{
|
||||
"name": "List Offers",
|
||||
"description": "Lists all offers with their latest version in an Ashby organization."
|
||||
},
|
||||
{
|
||||
"name": "Change Application Stage",
|
||||
"description": "Moves an application to a different interview stage. Requires an archive reason when moving to an Archived stage."
|
||||
},
|
||||
{
|
||||
"name": "Add Candidate Tag",
|
||||
"description": "Adds a tag to a candidate in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "Remove Candidate Tag",
|
||||
"description": "Removes a tag from a candidate in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "Get Offer",
|
||||
"description": "Retrieves full details about a single offer by its ID."
|
||||
},
|
||||
{
|
||||
"name": "List Sources",
|
||||
"description": "Lists all candidate sources configured in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "List Candidate Tags",
|
||||
"description": "Lists all candidate tags configured in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "List Archive Reasons",
|
||||
"description": "Lists all archive reasons configured in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "List Custom Fields",
|
||||
"description": "Lists all custom field definitions configured in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "List Departments",
|
||||
"description": "Lists all departments in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "List Locations",
|
||||
"description": "Lists all locations configured in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "List Job Postings",
|
||||
"description": "Lists all job postings in Ashby."
|
||||
},
|
||||
{
|
||||
"name": "Get Job Posting",
|
||||
"description": "Retrieves full details about a single job posting by its ID."
|
||||
},
|
||||
{
|
||||
"name": "List Openings",
|
||||
"description": "Lists all openings in Ashby with pagination."
|
||||
},
|
||||
{
|
||||
"name": "List Users",
|
||||
"description": "Lists all users in Ashby with pagination."
|
||||
},
|
||||
{
|
||||
"name": "List Interviews",
|
||||
"description": "Lists interview schedules in Ashby, optionally filtered by application or interview stage."
|
||||
}
|
||||
],
|
||||
"operationCount": 13,
|
||||
"operationCount": 28,
|
||||
"triggers": [
|
||||
{
|
||||
"id": "ashby_application_submit",
|
||||
@@ -1062,6 +1122,83 @@
|
||||
"authType": "none",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "box",
|
||||
"slug": "box",
|
||||
"name": "Box",
|
||||
"description": "Manage files, folders, and e-signatures with Box",
|
||||
"longDescription": "Integrate Box into your workflow to manage files, folders, and e-signatures. Upload and download files, search content, create folders, send documents for e-signature, track signing status, and more.",
|
||||
"bgColor": "#FFFFFF",
|
||||
"iconName": "BoxCompanyIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/box",
|
||||
"operations": [
|
||||
{
|
||||
"name": "Upload File",
|
||||
"description": "Upload a file to a Box folder"
|
||||
},
|
||||
{
|
||||
"name": "Download File",
|
||||
"description": "Download a file from Box"
|
||||
},
|
||||
{
|
||||
"name": "Get File Info",
|
||||
"description": "Get detailed information about a file in Box"
|
||||
},
|
||||
{
|
||||
"name": "List Folder Items",
|
||||
"description": "List files and folders in a Box folder"
|
||||
},
|
||||
{
|
||||
"name": "Create Folder",
|
||||
"description": "Create a new folder in Box"
|
||||
},
|
||||
{
|
||||
"name": "Delete File",
|
||||
"description": "Delete a file from Box"
|
||||
},
|
||||
{
|
||||
"name": "Delete Folder",
|
||||
"description": "Delete a folder from Box"
|
||||
},
|
||||
{
|
||||
"name": "Copy File",
|
||||
"description": "Copy a file to another folder in Box"
|
||||
},
|
||||
{
|
||||
"name": "Search",
|
||||
"description": "Search for files and folders in Box"
|
||||
},
|
||||
{
|
||||
"name": "Update File",
|
||||
"description": "Update file info in Box (rename, move, change description, add tags)"
|
||||
},
|
||||
{
|
||||
"name": "Create Sign Request",
|
||||
"description": "Create a new Box Sign request to send documents for e-signature"
|
||||
},
|
||||
{
|
||||
"name": "Get Sign Request",
|
||||
"description": "Get the details and status of a Box Sign request"
|
||||
},
|
||||
{
|
||||
"name": "List Sign Requests",
|
||||
"description": "List all Box Sign requests"
|
||||
},
|
||||
{
|
||||
"name": "Cancel Sign Request",
|
||||
"description": "Cancel a pending Box Sign request"
|
||||
},
|
||||
{
|
||||
"name": "Resend Sign Request",
|
||||
"description": "Resend a Box Sign request to signers who have not yet signed"
|
||||
}
|
||||
],
|
||||
"operationCount": 15,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "oauth",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "brandfetch",
|
||||
"slug": "brandfetch",
|
||||
@@ -2117,6 +2254,55 @@
|
||||
"authType": "none",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "docusign",
|
||||
"slug": "docusign",
|
||||
"name": "DocuSign",
|
||||
"description": "Send documents for e-signature via DocuSign",
|
||||
"longDescription": "Create and send envelopes for e-signature, use templates, check signing status, download signed documents, and manage recipients with DocuSign.",
|
||||
"bgColor": "#FFFFFF",
|
||||
"iconName": "DocuSignIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/docusign",
|
||||
"operations": [
|
||||
{
|
||||
"name": "Send Envelope",
|
||||
"description": "Create and send a DocuSign envelope with a document for e-signature"
|
||||
},
|
||||
{
|
||||
"name": "Send from Template",
|
||||
"description": "Create and send a DocuSign envelope using a pre-built template"
|
||||
},
|
||||
{
|
||||
"name": "Get Envelope",
|
||||
"description": "Get the details and status of a DocuSign envelope"
|
||||
},
|
||||
{
|
||||
"name": "List Envelopes",
|
||||
"description": "List envelopes from your DocuSign account with optional filters"
|
||||
},
|
||||
{
|
||||
"name": "Void Envelope",
|
||||
"description": "Void (cancel) a sent DocuSign envelope that has not yet been completed"
|
||||
},
|
||||
{
|
||||
"name": "Download Document",
|
||||
"description": "Download a signed document from a completed DocuSign envelope"
|
||||
},
|
||||
{
|
||||
"name": "List Templates",
|
||||
"description": "List available templates in your DocuSign account"
|
||||
},
|
||||
{
|
||||
"name": "List Recipients",
|
||||
"description": "Get the recipient status details for a DocuSign envelope"
|
||||
}
|
||||
],
|
||||
"operationCount": 8,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "oauth",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "dropbox",
|
||||
"slug": "dropbox",
|
||||
@@ -7208,6 +7394,95 @@
|
||||
"authType": "api-key",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "okta",
|
||||
"slug": "okta",
|
||||
"name": "Okta",
|
||||
"description": "Manage users and groups in Okta",
|
||||
"longDescription": "Integrate Okta identity management into your workflow. List, create, update, activate, suspend, and delete users. Reset passwords. Manage groups and group membership.",
|
||||
"bgColor": "#191919",
|
||||
"iconName": "OktaIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/okta",
|
||||
"operations": [
|
||||
{
|
||||
"name": "List Users",
|
||||
"description": "List all users in your Okta organization with optional search and filtering"
|
||||
},
|
||||
{
|
||||
"name": "Get User",
|
||||
"description": "Get a specific user by ID or login from your Okta organization"
|
||||
},
|
||||
{
|
||||
"name": "Create User",
|
||||
"description": "Create a new user in your Okta organization"
|
||||
},
|
||||
{
|
||||
"name": "Update User",
|
||||
"description": "Update a user profile in your Okta organization"
|
||||
},
|
||||
{
|
||||
"name": "Activate User",
|
||||
"description": "Activate a user in your Okta organization. Can only be performed on users with STAGED or DEPROVISIONED status. Optionally sends an activation email."
|
||||
},
|
||||
{
|
||||
"name": "Deactivate User",
|
||||
"description": "Deactivate a user in your Okta organization. This transitions the user to DEPROVISIONED status."
|
||||
},
|
||||
{
|
||||
"name": "Suspend User",
|
||||
"description": "Suspend a user in your Okta organization. Only users with ACTIVE status can be suspended. Suspended users cannot log in but retain group and app assignments."
|
||||
},
|
||||
{
|
||||
"name": "Unsuspend User",
|
||||
"description": "Unsuspend a previously suspended user in your Okta organization. Returns the user to ACTIVE status."
|
||||
},
|
||||
{
|
||||
"name": "Reset Password",
|
||||
"description": "Generate a one-time token to reset a user password. Can email the reset link to the user or return it directly. Transitions the user to RECOVERY status."
|
||||
},
|
||||
{
|
||||
"name": "Delete User",
|
||||
"description": "Permanently delete a user from your Okta organization. Can only be performed on DEPROVISIONED users. If the user is active, this will first deactivate them and a second call is needed to delete."
|
||||
},
|
||||
{
|
||||
"name": "List Groups",
|
||||
"description": "List all groups in your Okta organization with optional search and filtering"
|
||||
},
|
||||
{
|
||||
"name": "Get Group",
|
||||
"description": "Get a specific group by ID from your Okta organization"
|
||||
},
|
||||
{
|
||||
"name": "Create Group",
|
||||
"description": "Create a new group in your Okta organization"
|
||||
},
|
||||
{
|
||||
"name": "Update Group",
|
||||
"description": "Update a group profile in your Okta organization. Only groups of OKTA_GROUP type can be updated. All profile properties must be specified (full replacement)."
|
||||
},
|
||||
{
|
||||
"name": "Delete Group",
|
||||
"description": "Delete a group from your Okta organization. Groups of OKTA_GROUP or APP_GROUP type can be removed."
|
||||
},
|
||||
{
|
||||
"name": "Add User to Group",
|
||||
"description": "Add a user to a group in your Okta organization"
|
||||
},
|
||||
{
|
||||
"name": "Remove User from Group",
|
||||
"description": "Remove a user from a group in your Okta organization"
|
||||
},
|
||||
{
|
||||
"name": "List Group Members",
|
||||
"description": "List all members of a specific group in your Okta organization"
|
||||
}
|
||||
],
|
||||
"operationCount": 18,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "api-key",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "onedrive",
|
||||
"slug": "onedrive",
|
||||
@@ -10302,6 +10577,63 @@
|
||||
"authType": "oauth",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "workday",
|
||||
"slug": "workday",
|
||||
"name": "Workday",
|
||||
"description": "Manage workers, hiring, onboarding, and HR operations in Workday",
|
||||
"longDescription": "Integrate Workday HRIS into your workflow. Create pre-hires, hire employees, manage worker profiles, assign onboarding plans, handle job changes, retrieve compensation data, and process terminations.",
|
||||
"bgColor": "#F5F0EB",
|
||||
"iconName": "WorkdayIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/workday",
|
||||
"operations": [
|
||||
{
|
||||
"name": "Get Worker",
|
||||
"description": "Retrieve a specific worker profile including personal, employment, and organization data."
|
||||
},
|
||||
{
|
||||
"name": "List Workers",
|
||||
"description": "List or search workers with optional filtering and pagination."
|
||||
},
|
||||
{
|
||||
"name": "Create Pre-Hire",
|
||||
"description": "Create a new pre-hire (applicant) record in Workday. This is typically the first step before hiring an employee."
|
||||
},
|
||||
{
|
||||
"name": "Hire Employee",
|
||||
"description": "Hire a pre-hire into an employee position. Converts an applicant into an active employee record with position, start date, and manager assignment."
|
||||
},
|
||||
{
|
||||
"name": "Update Worker",
|
||||
"description": "Update fields on an existing worker record in Workday."
|
||||
},
|
||||
{
|
||||
"name": "Assign Onboarding Plan",
|
||||
"description": "Create or update an onboarding plan assignment for a worker. Sets up onboarding stages and manages the assignment lifecycle."
|
||||
},
|
||||
{
|
||||
"name": "Get Organizations",
|
||||
"description": "Retrieve organizations, departments, and cost centers from Workday."
|
||||
},
|
||||
{
|
||||
"name": "Change Job",
|
||||
"description": "Perform a job change for a worker including transfers, promotions, demotions, and lateral moves."
|
||||
},
|
||||
{
|
||||
"name": "Get Compensation",
|
||||
"description": "Retrieve compensation plan details for a specific worker."
|
||||
},
|
||||
{
|
||||
"name": "Terminate Worker",
|
||||
"description": "Initiate a worker termination in Workday. Triggers the Terminate Employee business process."
|
||||
}
|
||||
],
|
||||
"operationCount": 10,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "none",
|
||||
"category": "tools"
|
||||
},
|
||||
{
|
||||
"type": "x",
|
||||
"slug": "x",
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
import { OktaIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import type { OktaResponse } from '@/tools/okta/types'
|
||||
|
||||
export const OktaBlock: BlockConfig<OktaResponse> = {
|
||||
type: 'okta',
|
||||
name: 'Okta',
|
||||
description: 'Manage users and groups in Okta',
|
||||
longDescription:
|
||||
'Integrate Okta identity management into your workflow. List, create, update, activate, suspend, and delete users. Reset passwords. Manage groups and group membership.',
|
||||
docsLink: 'https://docs.sim.ai/tools/okta',
|
||||
category: 'tools',
|
||||
bgColor: '#191919',
|
||||
icon: OktaIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'List Users', id: 'okta_list_users' },
|
||||
{ label: 'Get User', id: 'okta_get_user' },
|
||||
{ label: 'Create User', id: 'okta_create_user' },
|
||||
{ label: 'Update User', id: 'okta_update_user' },
|
||||
{ label: 'Activate User', id: 'okta_activate_user' },
|
||||
{ label: 'Deactivate User', id: 'okta_deactivate_user' },
|
||||
{ label: 'Suspend User', id: 'okta_suspend_user' },
|
||||
{ label: 'Unsuspend User', id: 'okta_unsuspend_user' },
|
||||
{ label: 'Reset Password', id: 'okta_reset_password' },
|
||||
{ label: 'Delete User', id: 'okta_delete_user' },
|
||||
{ label: 'List Groups', id: 'okta_list_groups' },
|
||||
{ label: 'Get Group', id: 'okta_get_group' },
|
||||
{ label: 'Create Group', id: 'okta_create_group' },
|
||||
{ label: 'Update Group', id: 'okta_update_group' },
|
||||
{ label: 'Delete Group', id: 'okta_delete_group' },
|
||||
{ label: 'Add User to Group', id: 'okta_add_user_to_group' },
|
||||
{ label: 'Remove User from Group', id: 'okta_remove_user_from_group' },
|
||||
{ label: 'List Group Members', id: 'okta_list_group_members' },
|
||||
],
|
||||
value: () => 'okta_list_users',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Token',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Enter your Okta API token',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'domain',
|
||||
title: 'Okta Domain',
|
||||
type: 'short-input',
|
||||
placeholder: 'dev-123456.okta.com',
|
||||
required: true,
|
||||
},
|
||||
// Search/Filter params (list operations)
|
||||
{
|
||||
id: 'search',
|
||||
title: 'Search',
|
||||
type: 'short-input',
|
||||
placeholder: 'profile.firstName eq "John"',
|
||||
condition: { field: 'operation', value: ['okta_list_users', 'okta_list_groups'] },
|
||||
},
|
||||
{
|
||||
id: 'filter',
|
||||
title: 'Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'status eq "ACTIVE"',
|
||||
condition: { field: 'operation', value: ['okta_list_users', 'okta_list_groups'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// User ID (shared across user operations that need it)
|
||||
{
|
||||
id: 'userId',
|
||||
title: 'User ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'User ID or login (email)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'okta_get_user',
|
||||
'okta_update_user',
|
||||
'okta_activate_user',
|
||||
'okta_deactivate_user',
|
||||
'okta_suspend_user',
|
||||
'okta_unsuspend_user',
|
||||
'okta_reset_password',
|
||||
'okta_delete_user',
|
||||
'okta_add_user_to_group',
|
||||
'okta_remove_user_from_group',
|
||||
],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'okta_get_user',
|
||||
'okta_update_user',
|
||||
'okta_activate_user',
|
||||
'okta_deactivate_user',
|
||||
'okta_suspend_user',
|
||||
'okta_unsuspend_user',
|
||||
'okta_reset_password',
|
||||
'okta_delete_user',
|
||||
'okta_add_user_to_group',
|
||||
'okta_remove_user_from_group',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Group ID (shared across group operations that need it)
|
||||
{
|
||||
id: 'groupId',
|
||||
title: 'Group ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Okta group ID',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'okta_get_group',
|
||||
'okta_update_group',
|
||||
'okta_delete_group',
|
||||
'okta_add_user_to_group',
|
||||
'okta_remove_user_from_group',
|
||||
'okta_list_group_members',
|
||||
],
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'okta_get_group',
|
||||
'okta_update_group',
|
||||
'okta_delete_group',
|
||||
'okta_add_user_to_group',
|
||||
'okta_remove_user_from_group',
|
||||
'okta_list_group_members',
|
||||
],
|
||||
},
|
||||
},
|
||||
// Create/Update User profile params
|
||||
{
|
||||
id: 'firstName',
|
||||
title: 'First Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'John',
|
||||
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
|
||||
required: { field: 'operation', value: 'okta_create_user' },
|
||||
},
|
||||
{
|
||||
id: 'lastName',
|
||||
title: 'Last Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Doe',
|
||||
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
|
||||
required: { field: 'operation', value: 'okta_create_user' },
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
title: 'Email',
|
||||
type: 'short-input',
|
||||
placeholder: 'john.doe@example.com',
|
||||
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
|
||||
required: { field: 'operation', value: 'okta_create_user' },
|
||||
},
|
||||
{
|
||||
id: 'login',
|
||||
title: 'Login',
|
||||
type: 'short-input',
|
||||
placeholder: 'john.doe@example.com (defaults to email)',
|
||||
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'password',
|
||||
title: 'Password',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Set user password',
|
||||
condition: { field: 'operation', value: 'okta_create_user' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'mobilePhone',
|
||||
title: 'Mobile Phone',
|
||||
type: 'short-input',
|
||||
placeholder: '+1234567890',
|
||||
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Job Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Software Engineer',
|
||||
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'department',
|
||||
title: 'Department',
|
||||
type: 'short-input',
|
||||
placeholder: 'Engineering',
|
||||
condition: { field: 'operation', value: ['okta_create_user', 'okta_update_user'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'activate',
|
||||
title: 'Activate Immediately',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'okta_create_user' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Group name (for create/update group)
|
||||
{
|
||||
id: 'groupName',
|
||||
title: 'Group Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Engineering Team',
|
||||
condition: { field: 'operation', value: ['okta_create_group', 'okta_update_group'] },
|
||||
required: { field: 'operation', value: ['okta_create_group', 'okta_update_group'] },
|
||||
},
|
||||
{
|
||||
id: 'groupDescription',
|
||||
title: 'Group Description',
|
||||
type: 'short-input',
|
||||
placeholder: 'Description for the group',
|
||||
condition: { field: 'operation', value: ['okta_create_group', 'okta_update_group'] },
|
||||
},
|
||||
// Send email option (activate, reset password, delete)
|
||||
{
|
||||
id: 'sendEmail',
|
||||
title: 'Send Email',
|
||||
type: 'switch',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'okta_activate_user',
|
||||
'okta_deactivate_user',
|
||||
'okta_reset_password',
|
||||
'okta_delete_user',
|
||||
],
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Pagination
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Max results to return',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['okta_list_users', 'okta_list_groups', 'okta_list_group_members'],
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'okta_list_users',
|
||||
'okta_get_user',
|
||||
'okta_create_user',
|
||||
'okta_update_user',
|
||||
'okta_activate_user',
|
||||
'okta_deactivate_user',
|
||||
'okta_suspend_user',
|
||||
'okta_unsuspend_user',
|
||||
'okta_reset_password',
|
||||
'okta_delete_user',
|
||||
'okta_list_groups',
|
||||
'okta_get_group',
|
||||
'okta_create_group',
|
||||
'okta_update_group',
|
||||
'okta_delete_group',
|
||||
'okta_add_user_to_group',
|
||||
'okta_remove_user_from_group',
|
||||
'okta_list_group_members',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => params.operation as string,
|
||||
params: (params) => {
|
||||
const result: Record<string, unknown> = {
|
||||
apiKey: params.apiKey,
|
||||
domain: params.domain,
|
||||
}
|
||||
|
||||
if (params.limit) result.limit = Number(params.limit)
|
||||
|
||||
// Map group-specific UI fields to tool param names
|
||||
if (params.groupName) result.name = params.groupName
|
||||
if (params.groupDescription !== undefined) result.description = params.groupDescription
|
||||
|
||||
// Pass through all other non-empty params
|
||||
// Allow empty strings so users can clear fields (e.g. update_user partial updates)
|
||||
const skipKeys = new Set([
|
||||
'operation',
|
||||
'apiKey',
|
||||
'domain',
|
||||
'limit',
|
||||
'groupName',
|
||||
'groupDescription',
|
||||
])
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (!skipKeys.has(key) && value !== undefined && value !== null) {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Okta API token' },
|
||||
domain: { type: 'string', description: 'Okta domain' },
|
||||
userId: { type: 'string', description: 'User ID or login' },
|
||||
groupId: { type: 'string', description: 'Group ID' },
|
||||
search: { type: 'string', description: 'Search expression' },
|
||||
filter: { type: 'string', description: 'Filter expression' },
|
||||
limit: { type: 'number', description: 'Max results to return' },
|
||||
firstName: { type: 'string', description: 'First name' },
|
||||
lastName: { type: 'string', description: 'Last name' },
|
||||
email: { type: 'string', description: 'Email address' },
|
||||
login: { type: 'string', description: 'Login (defaults to email)' },
|
||||
password: { type: 'string', description: 'User password' },
|
||||
mobilePhone: { type: 'string', description: 'Mobile phone number' },
|
||||
title: { type: 'string', description: 'Job title' },
|
||||
department: { type: 'string', description: 'Department' },
|
||||
activate: { type: 'boolean', description: 'Activate user immediately on creation' },
|
||||
groupName: { type: 'string', description: 'Group name' },
|
||||
groupDescription: { type: 'string', description: 'Group description' },
|
||||
sendEmail: { type: 'boolean', description: 'Whether to send email notification' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
users: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of user objects (id, status, firstName, lastName, email, login, mobilePhone, title, department, created, lastLogin, lastUpdated)',
|
||||
},
|
||||
members: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of group member user objects (id, status, firstName, lastName, email, login, mobilePhone, title, department, created, lastLogin, lastUpdated)',
|
||||
},
|
||||
groups: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of group objects (id, name, description, type, created, lastUpdated, lastMembershipUpdated)',
|
||||
},
|
||||
id: { type: 'string', description: 'Resource ID' },
|
||||
status: { type: 'string', description: 'User status' },
|
||||
firstName: { type: 'string', description: 'First name' },
|
||||
lastName: { type: 'string', description: 'Last name' },
|
||||
email: { type: 'string', description: 'Email address' },
|
||||
login: { type: 'string', description: 'Login' },
|
||||
name: { type: 'string', description: 'Group name' },
|
||||
description: { type: 'string', description: 'Group description' },
|
||||
type: { type: 'string', description: 'Group type' },
|
||||
count: { type: 'number', description: 'Number of results' },
|
||||
added: { type: 'boolean', description: 'Whether user was added to group' },
|
||||
removed: { type: 'boolean', description: 'Whether user was removed from group' },
|
||||
deactivated: { type: 'boolean', description: 'Whether user was deactivated' },
|
||||
suspended: { type: 'boolean', description: 'Whether user was suspended' },
|
||||
unsuspended: { type: 'boolean', description: 'Whether user was unsuspended' },
|
||||
activated: { type: 'boolean', description: 'Whether user was activated' },
|
||||
deleted: { type: 'boolean', description: 'Whether resource was deleted' },
|
||||
activationUrl: { type: 'string', description: 'Activation URL (when sendEmail is false)' },
|
||||
activationToken: { type: 'string', description: 'Activation token (when sendEmail is false)' },
|
||||
resetPasswordUrl: {
|
||||
type: 'string',
|
||||
description: 'Password reset URL (when sendEmail is false)',
|
||||
},
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -122,6 +122,7 @@ import { Neo4jBlock } from '@/blocks/blocks/neo4j'
|
||||
import { NoteBlock } from '@/blocks/blocks/note'
|
||||
import { NotionBlock, NotionV2Block } from '@/blocks/blocks/notion'
|
||||
import { ObsidianBlock } from '@/blocks/blocks/obsidian'
|
||||
import { OktaBlock } from '@/blocks/blocks/okta'
|
||||
import { OneDriveBlock } from '@/blocks/blocks/onedrive'
|
||||
import { OnePasswordBlock } from '@/blocks/blocks/onepassword'
|
||||
import { OpenAIBlock } from '@/blocks/blocks/openai'
|
||||
@@ -340,6 +341,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
notion: NotionBlock,
|
||||
notion_v2: NotionV2Block,
|
||||
obsidian: ObsidianBlock,
|
||||
okta: OktaBlock,
|
||||
onepassword: OnePasswordBlock,
|
||||
onedrive: OneDriveBlock,
|
||||
openai: OpenAIBlock,
|
||||
|
||||
@@ -6106,6 +6106,19 @@ export function AgentSkillsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function OktaIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 63 63' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M34.6.4l-1.3 16c-.6-.1-1.2-.1-1.9-.1-.8 0-1.6.1-2.3.2l-.7-7.7c0-.2.2-.5.4-.5h1.3L29.5.5c0-.2.2-.5.4-.5h4.3c.3 0 .5.2.4.4zm-10.8.8c-.1-.2-.3-.4-.5-.3l-4 1.5c-.3.1-.4.4-.3.6l3.3 7.1-1.2.5c-.2.1-.3.3-.2.6l3.3 7c1.2-.7 2.5-1.2 3.9-1.5L23.8 1.2zM14 5.7l9.3 13.1c-1.2.8-2.2 1.7-3.1 2.7L14.5 16c-.2-.2-.2-.5 0-.6l1-.8L10 9c-.2-.2-.2-.5 0-.6l3.3-2.7c.2-.3.5-.2.7 0zM6.2 13.2c-.2-.1-.5-.1-.6.1l-2.1 3.7c-.1.2 0 .5.2.6l7.1 3.4-.7 1.1c-.1.2 0 .5.2.6l7.1 3.2c.5-1.3 1.2-2.5 2-3.6L6.2 13.2zM.9 23.3c0-.2.3-.4.5-.3l15.5 4c-.4 1.3-.6 2.7-.7 4.1l-7.8-.6c-.2 0-.4-.2-.4-.5l.2-1.3L.6 28c-.2 0-.4-.2-.4-.5l.7-4.2zM.4 33.8c-.3 0-.4.2-.4.5l.8 4.2c0 .2.3.4.5.3l7.6-2 .2 1.3c0 .2.3.4.5.3l7.5-2.1c-.4-1.3-.7-2.7-.8-4.1L.4 33.8zm2.5 11.1c-.1-.2 0-.5.2-.6l14.5-6.9c.5 1.3 1.3 2.5 2.2 3.6l-6.3 4.5c-.2.1-.5.1-.6-.1L12 44.3l-6.5 4.5c-.2.1-.5.1-.6-.1l-2-3.8zm17.5-3L9.1 53.3c-.2.2-.2.5 0 .6l3.3 2.7c.2.2.5.1.6-.1l4.6-6.4 1 .9c.2.2.5.1.6-.1l4.4-6.4c-1.2-.7-2.3-1.6-3.2-2.6zm-2.2 18.2c-.2-.1-.3-.3-.2-.6L24.6 45c1.2.6 2.6 1.1 3.9 1.4l-2 7.5c-.1.2-.3.4-.5.3l-1.2-.5-2.1 7.6c-.1.2-.3.4-.5.3l-4-1.5zm10.9-13.5l-1.3 16c0 .2.2.5.4.5H33c.2 0 .4-.2.4-.5l-.6-7.8h1.3c.2 0 .4-.2.4-.5l-.7-7.7c-.8.1-1.5.2-2.3.2-.6 0-1.3 0-1.9-.1zm16-43.2c.1-.2 0-.5-.2-.6l-4-1.5c-.2-.1-.5.1-.5.3l-2.1 7.6-1.2-.5c-.2-.1-.5.1-.5.3l-2 7.5c1.4.3 2.7.8 3.9 1.4l6.6-14.5zm8.8 6.3L42.6 21.1c-.9-1-2-1.9-3.2-2.6l4.4-6.4c.1-.2.4-.2.6-.1l1 .9 4.6-6.4c.1-.2.4-.2.6-.1l3.3 2.7c.2.2.2.5 0 .6zM59.9 18.7c.2-.1.3-.4.2-.6L58 14.4c-.1-.2-.4-.3-.6-.1l-6.5 4.5-.7-1.1c-.1-.2-.4-.3-.6-.1L43.3 22c.9 1.1 1.6 2.3 2.2 3.6l14.4-6.9zm2.3 5.8l.7 4.2c0 .2-.1.5-.4.5l-15.9 1.5c-.1-1.4-.4-2.8-.8-4.1l7.5-2.1c.2-.1.5.1.5.3l.2 1.3 7.6-2c.3-.1.5.1.6.4zM61.5 40c.2.1.5-.1.5-.3l.7-4.2c0-.2-.1-.5-.4-.5l-7.8-.7.2-1.3c0-.2-.1-.5-.4-.5l-7.8-.6c0 1.4-.3 2.8-.7 4.1L61.5 40zm-4.1 9.6c-.1.2-.4.3-.6.1l-13.2-9.1c.8-1.1 1.5-2.3 2-3.6l7.1 3.2c.2.1.3.4.2.6L52.2 42l7.1 3.4c.2.1.3.4.2.6l-2.1 3.6zm-17.7-5.4L49 57.3c.1.2.4.2.6.1l3.3-2.7c.2-.2.2-.4 0-.6l-5.5-5.6 1-.8c.2-.2.2-.4 0-.6l-5.5-5.5c1.1.8 0 1.7-1.2 2.4zm0 17.8c-.2.1-.5-.1-.5-.3l-4.2-15.4c1.4-.3 2.7-.8 3.9-1.5l3.3 7c.1.2 0 .5-.2.6l-1.2.5 3.3 7.1c.1.2 0 .5-.2.6L39.7 62z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg' fill='none'>
|
||||
|
||||
@@ -1192,3 +1192,33 @@ export function validateCallbackUrl(url: string): boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const OKTA_DOMAIN_PATTERN =
|
||||
/^[a-zA-Z0-9][a-zA-Z0-9-]*\.(okta|okta-gov|okta-emea|oktapreview|trexcloud)\.com$/
|
||||
|
||||
/**
|
||||
* Validates and sanitizes an Okta domain to prevent SSRF.
|
||||
* Ensures the domain matches a known Okta domain suffix.
|
||||
*
|
||||
* @param rawDomain - The raw domain string (may include protocol, trailing slash, or whitespace)
|
||||
* @returns The cleaned, validated domain string
|
||||
* @throws Error if the domain does not match a known Okta domain suffix
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const domain = validateOktaDomain(params.domain)
|
||||
* // Returns: "dev-123456.okta.com"
|
||||
* ```
|
||||
*/
|
||||
export function validateOktaDomain(rawDomain: string): string {
|
||||
const domain = rawDomain
|
||||
.trim()
|
||||
.replace(/^https?:\/\//, '')
|
||||
.replace(/\/$/, '')
|
||||
if (!OKTA_DOMAIN_PATTERN.test(domain)) {
|
||||
throw new Error(
|
||||
`Invalid Okta domain: "${domain}". Must be a valid Okta domain (e.g., dev-123456.okta.com)`
|
||||
)
|
||||
}
|
||||
return domain
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaActivateUserParams,
|
||||
OktaActivateUserResponse,
|
||||
OktaApiError,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaActivateUser')
|
||||
|
||||
export const oktaActivateUserTool: ToolConfig<OktaActivateUserParams, OktaActivateUserResponse> = {
|
||||
id: 'okta_activate_user',
|
||||
name: 'Activate User in Okta',
|
||||
description:
|
||||
'Activate a user in your Okta organization. Can only be performed on users with STAGED or DEPROVISIONED status. Optionally sends an activation email.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID or login to activate',
|
||||
},
|
||||
sendEmail: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Send activation email to the user (default: true)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const sendEmail = params.sendEmail ?? true
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/activate?sendEmail=${sendEmail}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to activate user in Okta')
|
||||
}
|
||||
|
||||
let activationUrl: string | null = null
|
||||
let activationToken: string | null = null
|
||||
try {
|
||||
const data = await response.json()
|
||||
activationUrl = data.activationUrl ?? null
|
||||
activationToken = data.activationToken ?? null
|
||||
} catch {
|
||||
// empty body when sendEmail=true
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
userId: params?.userId ?? '',
|
||||
activated: true,
|
||||
activationUrl,
|
||||
activationToken,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
userId: { type: 'string', description: 'Activated user ID' },
|
||||
activated: { type: 'boolean', description: 'Whether the user was activated' },
|
||||
activationUrl: {
|
||||
type: 'string',
|
||||
description: 'Activation URL (only returned when sendEmail is false)',
|
||||
optional: true,
|
||||
},
|
||||
activationToken: {
|
||||
type: 'string',
|
||||
description: 'Activation token (only returned when sendEmail is false)',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaAddUserToGroupParams,
|
||||
OktaAddUserToGroupResponse,
|
||||
OktaApiError,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaAddUserToGroup')
|
||||
|
||||
export const oktaAddUserToGroupTool: ToolConfig<
|
||||
OktaAddUserToGroupParams,
|
||||
OktaAddUserToGroupResponse
|
||||
> = {
|
||||
id: 'okta_add_user_to_group',
|
||||
name: 'Add User to Group in Okta',
|
||||
description: 'Add a user to a group in your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Group ID to add the user to',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID to add to the group',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}/users/${encodeURIComponent(params.userId)}`
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to add user to group in Okta')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
groupId: params?.groupId ?? '',
|
||||
userId: params?.userId ?? '',
|
||||
added: true,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
groupId: { type: 'string', description: 'Group ID' },
|
||||
userId: { type: 'string', description: 'User ID added to the group' },
|
||||
added: { type: 'boolean', description: 'Whether the user was added' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaCreateGroupParams,
|
||||
OktaCreateGroupResponse,
|
||||
OktaGroup,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaCreateGroup')
|
||||
|
||||
export const oktaCreateGroupTool: ToolConfig<OktaCreateGroupParams, OktaCreateGroupResponse> = {
|
||||
id: 'okta_create_group',
|
||||
name: 'Create Group in Okta',
|
||||
description: 'Create a new group in your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the group',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the group',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/groups`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const profile: Record<string, string> = { name: params.name }
|
||||
if (params.description) profile.description = params.description
|
||||
return { profile }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to create group in Okta')
|
||||
}
|
||||
|
||||
const group: OktaGroup = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: group.id,
|
||||
name: group.profile?.name ?? '',
|
||||
description: group.profile?.description ?? null,
|
||||
type: group.type,
|
||||
created: group.created,
|
||||
lastUpdated: group.lastUpdated,
|
||||
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Created group ID' },
|
||||
name: { type: 'string', description: 'Group name' },
|
||||
description: { type: 'string', description: 'Group description', optional: true },
|
||||
type: { type: 'string', description: 'Group type' },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
lastMembershipUpdated: {
|
||||
type: 'string',
|
||||
description: 'Last membership change timestamp',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaCreateUserParams,
|
||||
OktaCreateUserResponse,
|
||||
OktaUser,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaCreateUser')
|
||||
|
||||
export const oktaCreateUserTool: ToolConfig<OktaCreateUserParams, OktaCreateUserResponse> = {
|
||||
id: 'okta_create_user',
|
||||
name: 'Create User in Okta',
|
||||
description: 'Create a new user in your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'First name of the user',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Last name of the user',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email address of the user',
|
||||
},
|
||||
login: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Login for the user (defaults to email if not provided)',
|
||||
},
|
||||
password: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Password for the user (if not set, user will be emailed to set password)',
|
||||
},
|
||||
mobilePhone: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Mobile phone number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Job title',
|
||||
},
|
||||
department: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Department',
|
||||
},
|
||||
activate: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to activate the user immediately (default: true)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const activate = params.activate ?? true
|
||||
return `https://${domain}/api/v1/users?activate=${activate}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const profile: Record<string, string> = {
|
||||
firstName: params.firstName,
|
||||
lastName: params.lastName,
|
||||
email: params.email,
|
||||
login: params.login || params.email,
|
||||
}
|
||||
|
||||
if (params.mobilePhone) profile.mobilePhone = params.mobilePhone
|
||||
if (params.title) profile.title = params.title
|
||||
if (params.department) profile.department = params.department
|
||||
|
||||
const body: Record<string, unknown> = { profile }
|
||||
|
||||
if (params.password) {
|
||||
body.credentials = {
|
||||
password: { value: params.password },
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to create user in Okta')
|
||||
}
|
||||
|
||||
const user: OktaUser = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: user.id,
|
||||
status: user.status,
|
||||
firstName: user.profile?.firstName ?? null,
|
||||
lastName: user.profile?.lastName ?? null,
|
||||
email: user.profile?.email ?? null,
|
||||
login: user.profile?.login ?? null,
|
||||
created: user.created,
|
||||
lastUpdated: user.lastUpdated,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Created user ID' },
|
||||
status: { type: 'string', description: 'User status' },
|
||||
firstName: { type: 'string', description: 'First name', optional: true },
|
||||
lastName: { type: 'string', description: 'Last name', optional: true },
|
||||
email: { type: 'string', description: 'Email address', optional: true },
|
||||
login: { type: 'string', description: 'Login', optional: true },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaDeactivateUserParams,
|
||||
OktaDeactivateUserResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaDeactivateUser')
|
||||
|
||||
export const oktaDeactivateUserTool: ToolConfig<
|
||||
OktaDeactivateUserParams,
|
||||
OktaDeactivateUserResponse
|
||||
> = {
|
||||
id: 'okta_deactivate_user',
|
||||
name: 'Deactivate User in Okta',
|
||||
description:
|
||||
'Deactivate a user in your Okta organization. This transitions the user to DEPROVISIONED status.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID or login to deactivate',
|
||||
},
|
||||
sendEmail: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Send deactivation email to admin (default: false)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const sendEmail = params.sendEmail === true
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/deactivate?sendEmail=${sendEmail}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body on some error codes
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to deactivate user in Okta')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
userId: params?.userId ?? '',
|
||||
deactivated: true,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
userId: { type: 'string', description: 'Deactivated user ID' },
|
||||
deactivated: { type: 'boolean', description: 'Whether the user was deactivated' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaDeleteGroupParams,
|
||||
OktaDeleteGroupResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaDeleteGroup')
|
||||
|
||||
export const oktaDeleteGroupTool: ToolConfig<OktaDeleteGroupParams, OktaDeleteGroupResponse> = {
|
||||
id: 'okta_delete_group',
|
||||
name: 'Delete Group from Okta',
|
||||
description:
|
||||
'Delete a group from your Okta organization. Groups of OKTA_GROUP or APP_GROUP type can be removed.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Group ID to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to delete group from Okta')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
groupId: params?.groupId ?? '',
|
||||
deleted: true,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
groupId: { type: 'string', description: 'Deleted group ID' },
|
||||
deleted: { type: 'boolean', description: 'Whether the group was deleted' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type { OktaApiError, OktaDeleteUserParams, OktaDeleteUserResponse } from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaDeleteUser')
|
||||
|
||||
export const oktaDeleteUserTool: ToolConfig<OktaDeleteUserParams, OktaDeleteUserResponse> = {
|
||||
id: 'okta_delete_user',
|
||||
name: 'Delete User from Okta',
|
||||
description:
|
||||
'Permanently delete a user from your Okta organization. Can only be performed on DEPROVISIONED users. If the user is active, this will first deactivate them and a second call is needed to delete.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID to delete',
|
||||
},
|
||||
sendEmail: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Send deactivation email to admin (default: false)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const sendEmail = params.sendEmail === true
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}?sendEmail=${sendEmail}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to delete user from Okta')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
userId: params?.userId ?? '',
|
||||
deleted: true,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
userId: { type: 'string', description: 'Deleted user ID' },
|
||||
deleted: { type: 'boolean', description: 'Whether the user was deleted' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaGetGroupParams,
|
||||
OktaGetGroupResponse,
|
||||
OktaGroup,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaGetGroup')
|
||||
|
||||
export const oktaGetGroupTool: ToolConfig<OktaGetGroupParams, OktaGetGroupResponse> = {
|
||||
id: 'okta_get_group',
|
||||
name: 'Get Group from Okta',
|
||||
description: 'Get a specific group by ID from your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Group ID to look up',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to get group from Okta')
|
||||
}
|
||||
|
||||
const group: OktaGroup = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: group.id,
|
||||
name: group.profile?.name ?? '',
|
||||
description: group.profile?.description ?? null,
|
||||
type: group.type,
|
||||
created: group.created,
|
||||
lastUpdated: group.lastUpdated,
|
||||
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Group ID' },
|
||||
name: { type: 'string', description: 'Group name' },
|
||||
description: { type: 'string', description: 'Group description', optional: true },
|
||||
type: { type: 'string', description: 'Group type' },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
lastMembershipUpdated: {
|
||||
type: 'string',
|
||||
description: 'Last membership change timestamp',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaGetUserParams,
|
||||
OktaGetUserResponse,
|
||||
OktaUser,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaGetUser')
|
||||
|
||||
export const oktaGetUserTool: ToolConfig<OktaGetUserParams, OktaGetUserResponse> = {
|
||||
id: 'okta_get_user',
|
||||
name: 'Get User from Okta',
|
||||
description: 'Get a specific user by ID or login from your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID or login (email) to look up',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to get user from Okta')
|
||||
}
|
||||
|
||||
const user: OktaUser = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: user.id,
|
||||
status: user.status,
|
||||
firstName: user.profile?.firstName ?? null,
|
||||
lastName: user.profile?.lastName ?? null,
|
||||
email: user.profile?.email ?? null,
|
||||
login: user.profile?.login ?? null,
|
||||
mobilePhone: user.profile?.mobilePhone ?? null,
|
||||
secondEmail: user.profile?.secondEmail ?? null,
|
||||
displayName: user.profile?.displayName ?? null,
|
||||
title: user.profile?.title ?? null,
|
||||
department: user.profile?.department ?? null,
|
||||
organization: user.profile?.organization ?? null,
|
||||
manager: user.profile?.manager ?? null,
|
||||
managerId: user.profile?.managerId ?? null,
|
||||
division: user.profile?.division ?? null,
|
||||
employeeNumber: user.profile?.employeeNumber ?? null,
|
||||
userType: user.profile?.userType ?? null,
|
||||
created: user.created,
|
||||
activated: user.activated ?? null,
|
||||
lastLogin: user.lastLogin ?? null,
|
||||
lastUpdated: user.lastUpdated,
|
||||
statusChanged: user.statusChanged ?? null,
|
||||
passwordChanged: user.passwordChanged ?? null,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
status: { type: 'string', description: 'User status' },
|
||||
firstName: { type: 'string', description: 'First name', optional: true },
|
||||
lastName: { type: 'string', description: 'Last name', optional: true },
|
||||
email: { type: 'string', description: 'Email address', optional: true },
|
||||
login: { type: 'string', description: 'Login (usually email)', optional: true },
|
||||
mobilePhone: { type: 'string', description: 'Mobile phone', optional: true },
|
||||
secondEmail: { type: 'string', description: 'Secondary email', optional: true },
|
||||
displayName: { type: 'string', description: 'Display name', optional: true },
|
||||
title: { type: 'string', description: 'Job title', optional: true },
|
||||
department: { type: 'string', description: 'Department', optional: true },
|
||||
organization: { type: 'string', description: 'Organization', optional: true },
|
||||
manager: { type: 'string', description: 'Manager name', optional: true },
|
||||
managerId: { type: 'string', description: 'Manager ID', optional: true },
|
||||
division: { type: 'string', description: 'Division', optional: true },
|
||||
employeeNumber: { type: 'string', description: 'Employee number', optional: true },
|
||||
userType: { type: 'string', description: 'User type', optional: true },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
activated: { type: 'string', description: 'Activation timestamp', optional: true },
|
||||
lastLogin: { type: 'string', description: 'Last login timestamp', optional: true },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
statusChanged: { type: 'string', description: 'Status change timestamp', optional: true },
|
||||
passwordChanged: { type: 'string', description: 'Password change timestamp', optional: true },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export { oktaActivateUserTool } from './activate_user'
|
||||
export { oktaAddUserToGroupTool } from './add_user_to_group'
|
||||
export { oktaCreateGroupTool } from './create_group'
|
||||
export { oktaCreateUserTool } from './create_user'
|
||||
export { oktaDeactivateUserTool } from './deactivate_user'
|
||||
export { oktaDeleteGroupTool } from './delete_group'
|
||||
export { oktaDeleteUserTool } from './delete_user'
|
||||
export { oktaGetGroupTool } from './get_group'
|
||||
export { oktaGetUserTool } from './get_user'
|
||||
export { oktaListGroupMembersTool } from './list_group_members'
|
||||
export { oktaListGroupsTool } from './list_groups'
|
||||
export { oktaListUsersTool } from './list_users'
|
||||
export { oktaRemoveUserFromGroupTool } from './remove_user_from_group'
|
||||
export { oktaResetPasswordTool } from './reset_password'
|
||||
export { oktaSuspendUserTool } from './suspend_user'
|
||||
export * from './types'
|
||||
export { oktaUnsuspendUserTool } from './unsuspend_user'
|
||||
export { oktaUpdateGroupTool } from './update_group'
|
||||
export { oktaUpdateUserTool } from './update_user'
|
||||
@@ -0,0 +1,140 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaListGroupMembersParams,
|
||||
OktaListGroupMembersResponse,
|
||||
OktaUser,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaListGroupMembers')
|
||||
|
||||
export const oktaListGroupMembersTool: ToolConfig<
|
||||
OktaListGroupMembersParams,
|
||||
OktaListGroupMembersResponse
|
||||
> = {
|
||||
id: 'okta_list_group_members',
|
||||
name: 'List Group Members from Okta',
|
||||
description: 'List all members of a specific group in your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Group ID to list members for',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of members to return (default: 1000, max: 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.limit) queryParams.append('limit', params.limit.toString())
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
const base = `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}/users`
|
||||
return queryString ? `${base}?${queryString}` : base
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to list group members from Okta')
|
||||
}
|
||||
|
||||
const data: OktaUser[] = await response.json()
|
||||
|
||||
const members = data.map((user) => ({
|
||||
id: user.id,
|
||||
status: user.status,
|
||||
firstName: user.profile?.firstName ?? null,
|
||||
lastName: user.profile?.lastName ?? null,
|
||||
email: user.profile?.email ?? null,
|
||||
login: user.profile?.login ?? null,
|
||||
mobilePhone: user.profile?.mobilePhone ?? null,
|
||||
title: user.profile?.title ?? null,
|
||||
department: user.profile?.department ?? null,
|
||||
created: user.created,
|
||||
lastLogin: user.lastLogin ?? null,
|
||||
lastUpdated: user.lastUpdated,
|
||||
activated: user.activated ?? null,
|
||||
statusChanged: user.statusChanged ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
members,
|
||||
count: members.length,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
members: {
|
||||
type: 'array',
|
||||
description: 'Array of group member user objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
status: { type: 'string', description: 'User status' },
|
||||
firstName: { type: 'string', description: 'First name', optional: true },
|
||||
lastName: { type: 'string', description: 'Last name', optional: true },
|
||||
email: { type: 'string', description: 'Email address', optional: true },
|
||||
login: { type: 'string', description: 'Login', optional: true },
|
||||
mobilePhone: { type: 'string', description: 'Mobile phone', optional: true },
|
||||
title: { type: 'string', description: 'Job title', optional: true },
|
||||
department: { type: 'string', description: 'Department', optional: true },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastLogin: { type: 'string', description: 'Last login timestamp', optional: true },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
activated: { type: 'string', description: 'Activation timestamp', optional: true },
|
||||
statusChanged: {
|
||||
type: 'string',
|
||||
description: 'Status change timestamp',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of members returned' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaGroup,
|
||||
OktaListGroupsParams,
|
||||
OktaListGroupsResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaListGroups')
|
||||
|
||||
export const oktaListGroupsTool: ToolConfig<OktaListGroupsParams, OktaListGroupsResponse> = {
|
||||
id: 'okta_list_groups',
|
||||
name: 'List Groups from Okta',
|
||||
description: 'List all groups in your Okta organization with optional search and filtering',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Okta search expression for groups (e.g., profile.name sw "Engineering" or type eq "OKTA_GROUP")',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Okta filter expression (e.g., type eq "OKTA_GROUP")',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of groups to return (default: 10000, max: 10000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.search) queryParams.append('search', params.search)
|
||||
if (params.filter) queryParams.append('filter', params.filter)
|
||||
if (params.limit) queryParams.append('limit', params.limit.toString())
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString
|
||||
? `https://${domain}/api/v1/groups?${queryString}`
|
||||
: `https://${domain}/api/v1/groups`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to list groups from Okta')
|
||||
}
|
||||
|
||||
const data: OktaGroup[] = await response.json()
|
||||
|
||||
const groups = data.map((group) => ({
|
||||
id: group.id,
|
||||
name: group.profile?.name ?? '',
|
||||
description: group.profile?.description ?? null,
|
||||
type: group.type,
|
||||
created: group.created,
|
||||
lastUpdated: group.lastUpdated,
|
||||
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
groups,
|
||||
count: groups.length,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
groups: {
|
||||
type: 'array',
|
||||
description: 'Array of Okta group objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Group ID' },
|
||||
name: { type: 'string', description: 'Group name' },
|
||||
description: { type: 'string', description: 'Group description', optional: true },
|
||||
type: { type: 'string', description: 'Group type (OKTA_GROUP, APP_GROUP, BUILT_IN)' },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
lastMembershipUpdated: {
|
||||
type: 'string',
|
||||
description: 'Last membership change timestamp',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of groups returned' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaListUsersParams,
|
||||
OktaListUsersResponse,
|
||||
OktaUser,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaListUsers')
|
||||
|
||||
export const oktaListUsersTool: ToolConfig<OktaListUsersParams, OktaListUsersResponse> = {
|
||||
id: 'okta_list_users',
|
||||
name: 'List Users from Okta',
|
||||
description: 'List all users in your Okta organization with optional search and filtering',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Okta search expression (e.g., profile.firstName eq "John" or profile.email co "example.com")',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Okta filter expression (e.g., status eq "ACTIVE")',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of users to return (default: 200, max: 200)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.search) queryParams.append('search', params.search)
|
||||
if (params.filter) queryParams.append('filter', params.filter)
|
||||
if (params.limit) queryParams.append('limit', params.limit.toString())
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString
|
||||
? `https://${domain}/api/v1/users?${queryString}`
|
||||
: `https://${domain}/api/v1/users`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to list users from Okta')
|
||||
}
|
||||
|
||||
const data: OktaUser[] = await response.json()
|
||||
|
||||
const users = data.map((user) => ({
|
||||
id: user.id,
|
||||
status: user.status,
|
||||
firstName: user.profile?.firstName ?? null,
|
||||
lastName: user.profile?.lastName ?? null,
|
||||
email: user.profile?.email ?? null,
|
||||
login: user.profile?.login ?? null,
|
||||
mobilePhone: user.profile?.mobilePhone ?? null,
|
||||
title: user.profile?.title ?? null,
|
||||
department: user.profile?.department ?? null,
|
||||
created: user.created,
|
||||
lastLogin: user.lastLogin ?? null,
|
||||
lastUpdated: user.lastUpdated,
|
||||
activated: user.activated ?? null,
|
||||
statusChanged: user.statusChanged ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
users,
|
||||
count: users.length,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
users: {
|
||||
type: 'array',
|
||||
description: 'Array of Okta user objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'User status (ACTIVE, STAGED, PROVISIONED, etc.)',
|
||||
},
|
||||
firstName: { type: 'string', description: 'First name', optional: true },
|
||||
lastName: { type: 'string', description: 'Last name', optional: true },
|
||||
email: { type: 'string', description: 'Email address', optional: true },
|
||||
login: { type: 'string', description: 'Login (usually email)', optional: true },
|
||||
mobilePhone: { type: 'string', description: 'Mobile phone', optional: true },
|
||||
title: { type: 'string', description: 'Job title', optional: true },
|
||||
department: { type: 'string', description: 'Department', optional: true },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastLogin: { type: 'string', description: 'Last login timestamp', optional: true },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
activated: { type: 'string', description: 'Activation timestamp', optional: true },
|
||||
statusChanged: { type: 'string', description: 'Status change timestamp', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of users returned' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaRemoveUserFromGroupParams,
|
||||
OktaRemoveUserFromGroupResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaRemoveUserFromGroup')
|
||||
|
||||
export const oktaRemoveUserFromGroupTool: ToolConfig<
|
||||
OktaRemoveUserFromGroupParams,
|
||||
OktaRemoveUserFromGroupResponse
|
||||
> = {
|
||||
id: 'okta_remove_user_from_group',
|
||||
name: 'Remove User from Group in Okta',
|
||||
description: 'Remove a user from a group in your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Group ID to remove the user from',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID to remove from the group',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}/users/${encodeURIComponent(params.userId)}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to remove user from group in Okta')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
groupId: params?.groupId ?? '',
|
||||
userId: params?.userId ?? '',
|
||||
removed: true,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
groupId: { type: 'string', description: 'Group ID' },
|
||||
userId: { type: 'string', description: 'User ID removed from the group' },
|
||||
removed: { type: 'boolean', description: 'Whether the user was removed' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaResetPasswordParams,
|
||||
OktaResetPasswordResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaResetPassword')
|
||||
|
||||
export const oktaResetPasswordTool: ToolConfig<OktaResetPasswordParams, OktaResetPasswordResponse> =
|
||||
{
|
||||
id: 'okta_reset_password',
|
||||
name: 'Reset Password in Okta',
|
||||
description:
|
||||
'Generate a one-time token to reset a user password. Can email the reset link to the user or return it directly. Transitions the user to RECOVERY status.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID or login to reset password for',
|
||||
},
|
||||
sendEmail: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Send password reset email to the user (default: true)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
const sendEmail = params.sendEmail ?? true
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/reset_password?sendEmail=${sendEmail}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to reset password in Okta')
|
||||
}
|
||||
|
||||
let resetPasswordUrl: string | null = null
|
||||
try {
|
||||
const data = await response.json()
|
||||
resetPasswordUrl = data.resetPasswordUrl ?? null
|
||||
} catch {
|
||||
// empty body when sendEmail=true
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
userId: params?.userId ?? '',
|
||||
resetPasswordUrl,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
userId: { type: 'string', description: 'User ID' },
|
||||
resetPasswordUrl: {
|
||||
type: 'string',
|
||||
description: 'Password reset URL (only returned when sendEmail is false)',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaSuspendUserParams,
|
||||
OktaSuspendUserResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaSuspendUser')
|
||||
|
||||
export const oktaSuspendUserTool: ToolConfig<OktaSuspendUserParams, OktaSuspendUserResponse> = {
|
||||
id: 'okta_suspend_user',
|
||||
name: 'Suspend User in Okta',
|
||||
description:
|
||||
'Suspend a user in your Okta organization. Only users with ACTIVE status can be suspended. Suspended users cannot log in but retain group and app assignments.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID or login to suspend',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/suspend`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to suspend user in Okta')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
userId: params?.userId ?? '',
|
||||
suspended: true,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
userId: { type: 'string', description: 'Suspended user ID' },
|
||||
suspended: { type: 'boolean', description: 'Whether the user was suspended' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Okta API error response
|
||||
*/
|
||||
export interface OktaApiError {
|
||||
errorCode?: string
|
||||
errorSummary?: string
|
||||
errorCauses?: { errorSummary: string }[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Common params for all Okta tools
|
||||
*/
|
||||
export interface OktaBaseParams {
|
||||
apiKey: string
|
||||
domain: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Okta User profile object from the API
|
||||
*/
|
||||
export interface OktaUserProfile {
|
||||
firstName?: string | null
|
||||
lastName?: string | null
|
||||
email?: string | null
|
||||
login?: string | null
|
||||
mobilePhone?: string | null
|
||||
secondEmail?: string | null
|
||||
displayName?: string | null
|
||||
nickName?: string | null
|
||||
title?: string | null
|
||||
department?: string | null
|
||||
organization?: string | null
|
||||
manager?: string | null
|
||||
managerId?: string | null
|
||||
division?: string | null
|
||||
costCenter?: string | null
|
||||
employeeNumber?: string | null
|
||||
userType?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Okta User object from the API
|
||||
*/
|
||||
export interface OktaUser {
|
||||
id: string
|
||||
status: string
|
||||
created: string
|
||||
activated: string | null
|
||||
statusChanged: string | null
|
||||
lastLogin: string | null
|
||||
lastUpdated: string
|
||||
passwordChanged: string | null
|
||||
type: { id: string }
|
||||
profile: OktaUserProfile
|
||||
}
|
||||
|
||||
/**
|
||||
* Okta Group profile from the API
|
||||
*/
|
||||
export interface OktaGroupProfile {
|
||||
name: string
|
||||
description?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Okta Group object from the API
|
||||
*/
|
||||
export interface OktaGroup {
|
||||
id: string
|
||||
created: string
|
||||
lastUpdated: string
|
||||
lastMembershipUpdated: string | null
|
||||
type: string
|
||||
profile: OktaGroupProfile
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformed user output
|
||||
*/
|
||||
export interface OktaUserOutput {
|
||||
id: string
|
||||
status: string
|
||||
firstName: string | null
|
||||
lastName: string | null
|
||||
email: string | null
|
||||
login: string | null
|
||||
mobilePhone: string | null
|
||||
title: string | null
|
||||
department: string | null
|
||||
created: string
|
||||
lastLogin: string | null
|
||||
lastUpdated: string
|
||||
activated: string | null
|
||||
statusChanged: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Transformed group output
|
||||
*/
|
||||
export interface OktaGroupOutput {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
type: string
|
||||
created: string
|
||||
lastUpdated: string
|
||||
lastMembershipUpdated: string | null
|
||||
}
|
||||
|
||||
// List Users
|
||||
export interface OktaListUsersParams extends OktaBaseParams {
|
||||
search?: string
|
||||
filter?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface OktaListUsersResponse extends ToolResponse {
|
||||
output: {
|
||||
users: OktaUserOutput[]
|
||||
count: number
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Get User
|
||||
export interface OktaGetUserParams extends OktaBaseParams {
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface OktaGetUserResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
status: string
|
||||
firstName: string | null
|
||||
lastName: string | null
|
||||
email: string | null
|
||||
login: string | null
|
||||
mobilePhone: string | null
|
||||
secondEmail: string | null
|
||||
displayName: string | null
|
||||
title: string | null
|
||||
department: string | null
|
||||
organization: string | null
|
||||
manager: string | null
|
||||
managerId: string | null
|
||||
division: string | null
|
||||
employeeNumber: string | null
|
||||
userType: string | null
|
||||
created: string
|
||||
activated: string | null
|
||||
lastLogin: string | null
|
||||
lastUpdated: string
|
||||
statusChanged: string | null
|
||||
passwordChanged: string | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Create User
|
||||
export interface OktaCreateUserParams extends OktaBaseParams {
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
login?: string
|
||||
password?: string
|
||||
mobilePhone?: string
|
||||
title?: string
|
||||
department?: string
|
||||
activate?: boolean
|
||||
}
|
||||
|
||||
export interface OktaCreateUserResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
status: string
|
||||
firstName: string | null
|
||||
lastName: string | null
|
||||
email: string | null
|
||||
login: string | null
|
||||
created: string
|
||||
lastUpdated: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Update User
|
||||
export interface OktaUpdateUserParams extends OktaBaseParams {
|
||||
userId: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
email?: string
|
||||
login?: string
|
||||
mobilePhone?: string
|
||||
title?: string
|
||||
department?: string
|
||||
}
|
||||
|
||||
export interface OktaUpdateUserResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
status: string
|
||||
firstName: string | null
|
||||
lastName: string | null
|
||||
email: string | null
|
||||
login: string | null
|
||||
created: string
|
||||
lastUpdated: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Deactivate User
|
||||
export interface OktaDeactivateUserParams extends OktaBaseParams {
|
||||
userId: string
|
||||
sendEmail?: boolean
|
||||
}
|
||||
|
||||
export interface OktaDeactivateUserResponse extends ToolResponse {
|
||||
output: {
|
||||
userId: string
|
||||
deactivated: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// List Groups
|
||||
export interface OktaListGroupsParams extends OktaBaseParams {
|
||||
search?: string
|
||||
filter?: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface OktaListGroupsResponse extends ToolResponse {
|
||||
output: {
|
||||
groups: OktaGroupOutput[]
|
||||
count: number
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Get Group
|
||||
export interface OktaGetGroupParams extends OktaBaseParams {
|
||||
groupId: string
|
||||
}
|
||||
|
||||
export interface OktaGetGroupResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
type: string
|
||||
created: string
|
||||
lastUpdated: string
|
||||
lastMembershipUpdated: string | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Add User to Group
|
||||
export interface OktaAddUserToGroupParams extends OktaBaseParams {
|
||||
groupId: string
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface OktaAddUserToGroupResponse extends ToolResponse {
|
||||
output: {
|
||||
groupId: string
|
||||
userId: string
|
||||
added: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Remove User from Group
|
||||
export interface OktaRemoveUserFromGroupParams extends OktaBaseParams {
|
||||
groupId: string
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface OktaRemoveUserFromGroupResponse extends ToolResponse {
|
||||
output: {
|
||||
groupId: string
|
||||
userId: string
|
||||
removed: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// List Group Members
|
||||
export interface OktaListGroupMembersParams extends OktaBaseParams {
|
||||
groupId: string
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface OktaListGroupMembersResponse extends ToolResponse {
|
||||
output: {
|
||||
members: OktaUserOutput[]
|
||||
count: number
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Suspend User
|
||||
export interface OktaSuspendUserParams extends OktaBaseParams {
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface OktaSuspendUserResponse extends ToolResponse {
|
||||
output: {
|
||||
userId: string
|
||||
suspended: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Unsuspend User
|
||||
export interface OktaUnsuspendUserParams extends OktaBaseParams {
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface OktaUnsuspendUserResponse extends ToolResponse {
|
||||
output: {
|
||||
userId: string
|
||||
unsuspended: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Activate User
|
||||
export interface OktaActivateUserParams extends OktaBaseParams {
|
||||
userId: string
|
||||
sendEmail?: boolean
|
||||
}
|
||||
|
||||
export interface OktaActivateUserResponse extends ToolResponse {
|
||||
output: {
|
||||
userId: string
|
||||
activated: boolean
|
||||
activationUrl: string | null
|
||||
activationToken: string | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Reset Password
|
||||
export interface OktaResetPasswordParams extends OktaBaseParams {
|
||||
userId: string
|
||||
sendEmail?: boolean
|
||||
}
|
||||
|
||||
export interface OktaResetPasswordResponse extends ToolResponse {
|
||||
output: {
|
||||
userId: string
|
||||
resetPasswordUrl: string | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Delete User
|
||||
export interface OktaDeleteUserParams extends OktaBaseParams {
|
||||
userId: string
|
||||
sendEmail?: boolean
|
||||
}
|
||||
|
||||
export interface OktaDeleteUserResponse extends ToolResponse {
|
||||
output: {
|
||||
userId: string
|
||||
deleted: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Create Group
|
||||
export interface OktaCreateGroupParams extends OktaBaseParams {
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface OktaCreateGroupResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
type: string
|
||||
created: string
|
||||
lastUpdated: string
|
||||
lastMembershipUpdated: string | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Update Group
|
||||
export interface OktaUpdateGroupParams extends OktaBaseParams {
|
||||
groupId: string
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface OktaUpdateGroupResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
type: string
|
||||
created: string
|
||||
lastUpdated: string
|
||||
lastMembershipUpdated: string | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Group
|
||||
export interface OktaDeleteGroupParams extends OktaBaseParams {
|
||||
groupId: string
|
||||
}
|
||||
|
||||
export interface OktaDeleteGroupResponse extends ToolResponse {
|
||||
output: {
|
||||
groupId: string
|
||||
deleted: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
// Generic response type for the block
|
||||
export type OktaResponse =
|
||||
| OktaListUsersResponse
|
||||
| OktaGetUserResponse
|
||||
| OktaCreateUserResponse
|
||||
| OktaUpdateUserResponse
|
||||
| OktaDeactivateUserResponse
|
||||
| OktaSuspendUserResponse
|
||||
| OktaUnsuspendUserResponse
|
||||
| OktaActivateUserResponse
|
||||
| OktaResetPasswordResponse
|
||||
| OktaDeleteUserResponse
|
||||
| OktaListGroupsResponse
|
||||
| OktaGetGroupResponse
|
||||
| OktaCreateGroupResponse
|
||||
| OktaUpdateGroupResponse
|
||||
| OktaDeleteGroupResponse
|
||||
| OktaAddUserToGroupResponse
|
||||
| OktaRemoveUserFromGroupResponse
|
||||
| OktaListGroupMembersResponse
|
||||
@@ -0,0 +1,81 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaUnsuspendUserParams,
|
||||
OktaUnsuspendUserResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaUnsuspendUser')
|
||||
|
||||
export const oktaUnsuspendUserTool: ToolConfig<OktaUnsuspendUserParams, OktaUnsuspendUserResponse> =
|
||||
{
|
||||
id: 'okta_unsuspend_user',
|
||||
name: 'Unsuspend User in Okta',
|
||||
description:
|
||||
'Unsuspend a previously suspended user in your Okta organization. Returns the user to ACTIVE status.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID or login to unsuspend',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}/lifecycle/unsuspend`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// empty response body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to unsuspend user in Okta')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
userId: params?.userId ?? '',
|
||||
unsuspended: true,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
userId: { type: 'string', description: 'Unsuspended user ID' },
|
||||
unsuspended: { type: 'boolean', description: 'Whether the user was unsuspended' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaGroup,
|
||||
OktaUpdateGroupParams,
|
||||
OktaUpdateGroupResponse,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaUpdateGroup')
|
||||
|
||||
export const oktaUpdateGroupTool: ToolConfig<OktaUpdateGroupParams, OktaUpdateGroupResponse> = {
|
||||
id: 'okta_update_group',
|
||||
name: 'Update Group in Okta',
|
||||
description:
|
||||
'Update a group profile in your Okta organization. Only groups of OKTA_GROUP type can be updated. All profile properties must be specified (full replacement).',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Group ID to update',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated group name',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated group description',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/groups/${encodeURIComponent(params.groupId)}`
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
profile: {
|
||||
name: params.name,
|
||||
description: params.description ?? '',
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to update group in Okta')
|
||||
}
|
||||
|
||||
const group: OktaGroup = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: group.id,
|
||||
name: group.profile?.name ?? '',
|
||||
description: group.profile?.description ?? null,
|
||||
type: group.type,
|
||||
created: group.created,
|
||||
lastUpdated: group.lastUpdated,
|
||||
lastMembershipUpdated: group.lastMembershipUpdated ?? null,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Group ID' },
|
||||
name: { type: 'string', description: 'Group name' },
|
||||
description: { type: 'string', description: 'Group description', optional: true },
|
||||
type: { type: 'string', description: 'Group type' },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
lastMembershipUpdated: {
|
||||
type: 'string',
|
||||
description: 'Last membership change timestamp',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { validateOktaDomain } from '@/lib/core/security/input-validation'
|
||||
import type {
|
||||
OktaApiError,
|
||||
OktaUpdateUserParams,
|
||||
OktaUpdateUserResponse,
|
||||
OktaUser,
|
||||
} from '@/tools/okta/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('OktaUpdateUser')
|
||||
|
||||
export const oktaUpdateUserTool: ToolConfig<OktaUpdateUserParams, OktaUpdateUserResponse> = {
|
||||
id: 'okta_update_user',
|
||||
name: 'Update User in Okta',
|
||||
description: 'Update a user profile in your Okta organization',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta API token for authentication',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Okta domain (e.g., dev-123456.okta.com)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID or login to update',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated first name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated last name',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated email address',
|
||||
},
|
||||
login: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated login',
|
||||
},
|
||||
mobilePhone: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated mobile phone number',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated job title',
|
||||
},
|
||||
department: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated department',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const domain = validateOktaDomain(params.domain)
|
||||
return `https://${domain}/api/v1/users/${encodeURIComponent(params.userId)}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `SSWS ${params.apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const profile: Record<string, string> = {}
|
||||
|
||||
if (params.firstName !== undefined) profile.firstName = params.firstName
|
||||
if (params.lastName !== undefined) profile.lastName = params.lastName
|
||||
if (params.email !== undefined) profile.email = params.email
|
||||
if (params.login !== undefined) profile.login = params.login
|
||||
if (params.mobilePhone !== undefined) profile.mobilePhone = params.mobilePhone
|
||||
if (params.title !== undefined) profile.title = params.title
|
||||
if (params.department !== undefined) profile.department = params.department
|
||||
|
||||
return { profile }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
let error: OktaApiError = {}
|
||||
try {
|
||||
error = await response.json()
|
||||
} catch {
|
||||
// non-JSON error body
|
||||
}
|
||||
logger.error('Okta API request failed', { data: error, status: response.status })
|
||||
throw new Error(error.errorSummary || 'Failed to update user in Okta')
|
||||
}
|
||||
|
||||
const user: OktaUser = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: user.id,
|
||||
status: user.status,
|
||||
firstName: user.profile?.firstName ?? null,
|
||||
lastName: user.profile?.lastName ?? null,
|
||||
email: user.profile?.email ?? null,
|
||||
login: user.profile?.login ?? null,
|
||||
created: user.created,
|
||||
lastUpdated: user.lastUpdated,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
status: { type: 'string', description: 'User status' },
|
||||
firstName: { type: 'string', description: 'First name', optional: true },
|
||||
lastName: { type: 'string', description: 'Last name', optional: true },
|
||||
email: { type: 'string', description: 'Email address', optional: true },
|
||||
login: { type: 'string', description: 'Login', optional: true },
|
||||
created: { type: 'string', description: 'Creation timestamp' },
|
||||
lastUpdated: { type: 'string', description: 'Last update timestamp' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -1574,6 +1574,26 @@ import {
|
||||
obsidianPatchNoteTool,
|
||||
obsidianSearchTool,
|
||||
} from '@/tools/obsidian'
|
||||
import {
|
||||
oktaActivateUserTool,
|
||||
oktaAddUserToGroupTool,
|
||||
oktaCreateGroupTool,
|
||||
oktaCreateUserTool,
|
||||
oktaDeactivateUserTool,
|
||||
oktaDeleteGroupTool,
|
||||
oktaDeleteUserTool,
|
||||
oktaGetGroupTool,
|
||||
oktaGetUserTool,
|
||||
oktaListGroupMembersTool,
|
||||
oktaListGroupsTool,
|
||||
oktaListUsersTool,
|
||||
oktaRemoveUserFromGroupTool,
|
||||
oktaResetPasswordTool,
|
||||
oktaSuspendUserTool,
|
||||
oktaUnsuspendUserTool,
|
||||
oktaUpdateGroupTool,
|
||||
oktaUpdateUserTool,
|
||||
} from '@/tools/okta'
|
||||
import {
|
||||
onedriveCreateFolderTool,
|
||||
onedriveDeleteTool,
|
||||
@@ -2920,6 +2940,24 @@ export const tools: Record<string, ToolConfig> = {
|
||||
obsidian_patch_active: obsidianPatchActiveTool,
|
||||
obsidian_patch_note: obsidianPatchNoteTool,
|
||||
obsidian_search: obsidianSearchTool,
|
||||
okta_list_users: oktaListUsersTool,
|
||||
okta_get_user: oktaGetUserTool,
|
||||
okta_create_user: oktaCreateUserTool,
|
||||
okta_update_user: oktaUpdateUserTool,
|
||||
okta_activate_user: oktaActivateUserTool,
|
||||
okta_deactivate_user: oktaDeactivateUserTool,
|
||||
okta_suspend_user: oktaSuspendUserTool,
|
||||
okta_unsuspend_user: oktaUnsuspendUserTool,
|
||||
okta_reset_password: oktaResetPasswordTool,
|
||||
okta_delete_user: oktaDeleteUserTool,
|
||||
okta_list_groups: oktaListGroupsTool,
|
||||
okta_get_group: oktaGetGroupTool,
|
||||
okta_create_group: oktaCreateGroupTool,
|
||||
okta_update_group: oktaUpdateGroupTool,
|
||||
okta_delete_group: oktaDeleteGroupTool,
|
||||
okta_add_user_to_group: oktaAddUserToGroupTool,
|
||||
okta_remove_user_from_group: oktaRemoveUserFromGroupTool,
|
||||
okta_list_group_members: oktaListGroupMembersTool,
|
||||
onepassword_list_vaults: onepasswordListVaultsTool,
|
||||
onepassword_get_vault: onepasswordGetVaultTool,
|
||||
onepassword_list_items: onepasswordListItemsTool,
|
||||
|
||||
Reference in New Issue
Block a user