chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+109
View File
@@ -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.trim())}/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' },
},
}
+90
View File
@@ -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.trim())}/users/${encodeURIComponent(params.userId.trim())}`
},
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' },
},
}
+106
View File
@@ -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' },
},
}
+164
View File
@@ -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' },
},
}
+90
View File
@@ -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.trim())}/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' },
},
}
+80
View File
@@ -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.trim())}`
},
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' },
},
}
+83
View File
@@ -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.trim())}?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' },
},
}
+95
View File
@@ -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.trim())}`
},
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' },
},
}
+123
View File
@@ -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.trim())}`
},
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' },
},
}
+19
View File
@@ -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'
+140
View File
@@ -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.trim())}/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' },
},
}
+133
View File
@@ -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' },
},
}
+146
View File
@@ -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.trim())}/users/${encodeURIComponent(params.userId.trim())}`
},
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' },
},
}
+100
View File
@@ -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.trim())}/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' },
},
}
+80
View File
@@ -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.trim())}/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' },
},
}
+446
View File
@@ -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
*/
interface OktaBaseParams {
apiKey: string
domain: string
}
/**
* Okta User profile object from the API
*/
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
*/
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
*/
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
*/
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
+81
View File
@@ -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.trim())}/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' },
},
}
+114
View File
@@ -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.trim())}`
},
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' },
},
}
+148
View File
@@ -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.trim())}`
},
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' },
},
}