chore: import upstream snapshot with attribution
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
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
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
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterCheckAssignmentDeletionStatusContract } from '@/lib/api/contracts/tools/aws/identity-center-check-assignment-deletion-status'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { checkAssignmentDeletionStatus, createSSOAdminClient } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterCheckAssignmentDeletionStatusAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(
|
||||
awsIdentityCenterCheckAssignmentDeletionStatusContract,
|
||||
request,
|
||||
{
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
}
|
||||
)
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Checking assignment deletion status for request ${params.requestId}`)
|
||||
|
||||
const client = createSSOAdminClient(params)
|
||||
try {
|
||||
const result = await checkAssignmentDeletionStatus(
|
||||
client,
|
||||
params.instanceArn,
|
||||
params.requestId
|
||||
)
|
||||
logger.info(`Assignment deletion status: ${result.status}`)
|
||||
return NextResponse.json({
|
||||
message: `Assignment deletion status: ${result.status}`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to check assignment deletion status:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to check assignment deletion status: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterCheckAssignmentStatusContract } from '@/lib/api/contracts/tools/aws/identity-center-check-assignment-status'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { checkAssignmentCreationStatus, createSSOAdminClient } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterCheckAssignmentStatusAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterCheckAssignmentStatusContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Checking assignment status for request ${params.requestId}`)
|
||||
|
||||
const client = createSSOAdminClient(params)
|
||||
try {
|
||||
const result = await checkAssignmentCreationStatus(
|
||||
client,
|
||||
params.instanceArn,
|
||||
params.requestId
|
||||
)
|
||||
logger.info(`Assignment status: ${result.status}`)
|
||||
return NextResponse.json({
|
||||
message: `Assignment status: ${result.status}`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to check assignment status:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to check assignment status: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
CreateAccountAssignmentCommand,
|
||||
type PrincipalType,
|
||||
type TargetType,
|
||||
} from '@aws-sdk/client-sso-admin'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterCreateAccountAssignmentContract } from '@/lib/api/contracts/tools/aws/identity-center-create-account-assignment'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSSOAdminClient, mapAssignmentStatus } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterCreateAccountAssignmentAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(
|
||||
awsIdentityCenterCreateAccountAssignmentContract,
|
||||
request,
|
||||
{
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
}
|
||||
)
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(
|
||||
`Creating account assignment for ${params.principalType} ${params.principalId} on account ${params.accountId}`
|
||||
)
|
||||
|
||||
const client = createSSOAdminClient(params)
|
||||
try {
|
||||
const command = new CreateAccountAssignmentCommand({
|
||||
InstanceArn: params.instanceArn,
|
||||
TargetId: params.accountId,
|
||||
TargetType: 'AWS_ACCOUNT' as TargetType,
|
||||
PermissionSetArn: params.permissionSetArn,
|
||||
PrincipalType: params.principalType as PrincipalType,
|
||||
PrincipalId: params.principalId,
|
||||
})
|
||||
const response = await client.send(command)
|
||||
const status = response.AccountAssignmentCreationStatus ?? {}
|
||||
const result = mapAssignmentStatus(status)
|
||||
|
||||
logger.info(
|
||||
`Account assignment creation initiated with status ${result.status}, requestId ${result.requestId}`
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Account assignment creation ${result.status === 'SUCCEEDED' ? 'succeeded' : 'initiated'}`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to create account assignment:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to create account assignment: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
DeleteAccountAssignmentCommand,
|
||||
type PrincipalType,
|
||||
type TargetType,
|
||||
} from '@aws-sdk/client-sso-admin'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterDeleteAccountAssignmentContract } from '@/lib/api/contracts/tools/aws/identity-center-delete-account-assignment'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSSOAdminClient, mapAssignmentStatus } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterDeleteAccountAssignmentAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(
|
||||
awsIdentityCenterDeleteAccountAssignmentContract,
|
||||
request,
|
||||
{
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
}
|
||||
)
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(
|
||||
`Deleting account assignment for ${params.principalType} ${params.principalId} on account ${params.accountId}`
|
||||
)
|
||||
|
||||
const client = createSSOAdminClient(params)
|
||||
try {
|
||||
const command = new DeleteAccountAssignmentCommand({
|
||||
InstanceArn: params.instanceArn,
|
||||
TargetId: params.accountId,
|
||||
TargetType: 'AWS_ACCOUNT' as TargetType,
|
||||
PermissionSetArn: params.permissionSetArn,
|
||||
PrincipalType: params.principalType as PrincipalType,
|
||||
PrincipalId: params.principalId,
|
||||
})
|
||||
const response = await client.send(command)
|
||||
const status = response.AccountAssignmentDeletionStatus ?? {}
|
||||
const result = mapAssignmentStatus(status)
|
||||
|
||||
logger.info(
|
||||
`Account assignment deletion initiated with status ${result.status}, requestId ${result.requestId}`
|
||||
)
|
||||
|
||||
return NextResponse.json({
|
||||
message: `Account assignment deletion ${result.status === 'SUCCEEDED' ? 'succeeded' : 'initiated'}`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to delete account assignment:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to delete account assignment: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterDescribeAccountContract } from '@/lib/api/contracts/tools/aws/identity-center-describe-account'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createOrganizationsClient, describeAccount } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterDescribeAccountAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterDescribeAccountContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Describing AWS account ${params.accountId}`)
|
||||
|
||||
const client = createOrganizationsClient(params)
|
||||
try {
|
||||
const result = await describeAccount(client, params.accountId)
|
||||
logger.info(`Successfully described account ${result.name}`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to describe account:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to describe account: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterGetGroupContract } from '@/lib/api/contracts/tools/aws/identity-center-get-group'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIdentityStoreClient, getGroupByDisplayName } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterGetGroupAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterGetGroupContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(
|
||||
`Looking up group "${params.displayName}" in identity store ${params.identityStoreId}`
|
||||
)
|
||||
|
||||
const client = createIdentityStoreClient(params)
|
||||
try {
|
||||
const result = await getGroupByDisplayName(client, params.identityStoreId, params.displayName)
|
||||
logger.info(`Successfully found group ${result.groupId}`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get group:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get group: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterGetUserContract } from '@/lib/api/contracts/tools/aws/identity-center-get-user'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIdentityStoreClient, getUserByEmail } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterGetUserAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterGetUserContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Looking up user by email in identity store ${params.identityStoreId}`)
|
||||
|
||||
const client = createIdentityStoreClient(params)
|
||||
try {
|
||||
const result = await getUserByEmail(client, params.identityStoreId, params.email)
|
||||
logger.info(`Successfully found user ${result.userId}`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get user:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get user: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,54 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterListAccountAssignmentsContract } from '@/lib/api/contracts/tools/aws/identity-center-list-account-assignments'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSSOAdminClient, listAccountAssignmentsForPrincipal } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterListAccountAssignmentsAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(
|
||||
awsIdentityCenterListAccountAssignmentsContract,
|
||||
request,
|
||||
{
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
}
|
||||
)
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing account assignments for ${params.principalType} ${params.principalId}`)
|
||||
|
||||
const client = createSSOAdminClient(params)
|
||||
try {
|
||||
const result = await listAccountAssignmentsForPrincipal(
|
||||
client,
|
||||
params.instanceArn,
|
||||
params.principalId,
|
||||
params.principalType,
|
||||
params.maxResults,
|
||||
params.nextToken
|
||||
)
|
||||
logger.info(`Successfully listed ${result.count} account assignments`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list account assignments:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list account assignments: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterListAccountsContract } from '@/lib/api/contracts/tools/aws/identity-center-list-accounts'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createOrganizationsClient, listAccounts } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterListAccountsAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterListAccountsContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Listing AWS accounts')
|
||||
|
||||
const client = createOrganizationsClient(params)
|
||||
try {
|
||||
const result = await listAccounts(client, params.maxResults, params.nextToken)
|
||||
logger.info(`Successfully listed ${result.count} accounts`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list AWS accounts:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list AWS accounts: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterListGroupsContract } from '@/lib/api/contracts/tools/aws/identity-center-list-groups'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIdentityStoreClient, listGroups } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterListGroupsAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterListGroupsContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing groups in identity store ${params.identityStoreId}`)
|
||||
|
||||
const client = createIdentityStoreClient(params)
|
||||
try {
|
||||
const result = await listGroups(
|
||||
client,
|
||||
params.identityStoreId,
|
||||
params.maxResults,
|
||||
params.nextToken
|
||||
)
|
||||
logger.info(`Successfully listed ${result.count} groups`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list groups:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list groups: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterListInstancesContract } from '@/lib/api/contracts/tools/aws/identity-center-list-instances'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSSOAdminClient, listInstances } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterListInstancesAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterListInstancesContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info('Listing Identity Center instances')
|
||||
|
||||
const client = createSSOAdminClient(params)
|
||||
try {
|
||||
const result = await listInstances(client, params.maxResults, params.nextToken)
|
||||
logger.info(`Successfully listed ${result.count} instances`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list Identity Center instances:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list Identity Center instances: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,48 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIdentityCenterListPermissionSetsContract } from '@/lib/api/contracts/tools/aws/identity-center-list-permission-sets'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createSSOAdminClient, listPermissionSets } from '../utils'
|
||||
|
||||
const logger = createLogger('IdentityCenterListPermissionSetsAPI')
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = await parseToolRequest(awsIdentityCenterListPermissionSetsContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing permission sets for instance ${params.instanceArn}`)
|
||||
|
||||
const client = createSSOAdminClient(params)
|
||||
try {
|
||||
const result = await listPermissionSets(
|
||||
client,
|
||||
params.instanceArn,
|
||||
params.maxResults,
|
||||
params.nextToken
|
||||
)
|
||||
logger.info(`Successfully listed ${result.count} permission sets`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to list permission sets:', error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list permission sets: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,304 @@
|
||||
import {
|
||||
DescribeGroupCommand,
|
||||
DescribeUserCommand,
|
||||
GetGroupIdCommand,
|
||||
GetUserIdCommand,
|
||||
IdentitystoreClient,
|
||||
ListGroupsCommand,
|
||||
} from '@aws-sdk/client-identitystore'
|
||||
import {
|
||||
DescribeAccountCommand,
|
||||
ListAccountsCommand,
|
||||
OrganizationsClient,
|
||||
} from '@aws-sdk/client-organizations'
|
||||
import {
|
||||
type AccountAssignmentOperationStatus,
|
||||
DescribeAccountAssignmentCreationStatusCommand,
|
||||
DescribeAccountAssignmentDeletionStatusCommand,
|
||||
DescribePermissionSetCommand,
|
||||
ListAccountAssignmentsForPrincipalCommand,
|
||||
ListInstancesCommand,
|
||||
ListPermissionSetsCommand,
|
||||
type PrincipalType,
|
||||
SSOAdminClient,
|
||||
} from '@aws-sdk/client-sso-admin'
|
||||
import type { IdentityCenterConnectionConfig } from '@/tools/identity_center/types'
|
||||
|
||||
export function createSSOAdminClient(config: IdentityCenterConnectionConfig): SSOAdminClient {
|
||||
return new SSOAdminClient({
|
||||
region: config.region,
|
||||
credentials: {
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function createIdentityStoreClient(
|
||||
config: IdentityCenterConnectionConfig
|
||||
): IdentitystoreClient {
|
||||
return new IdentitystoreClient({
|
||||
region: config.region,
|
||||
credentials: {
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function createOrganizationsClient(config: IdentityCenterConnectionConfig) {
|
||||
return new OrganizationsClient({
|
||||
region: 'us-east-1', // Organizations API only available in us-east-1
|
||||
credentials: {
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function listInstances(
|
||||
client: SSOAdminClient,
|
||||
maxResults?: number | null,
|
||||
nextToken?: string | null
|
||||
) {
|
||||
const command = new ListInstancesCommand({
|
||||
...(maxResults ? { MaxResults: maxResults } : {}),
|
||||
...(nextToken ? { NextToken: nextToken } : {}),
|
||||
})
|
||||
const response = await client.send(command)
|
||||
const instances = (response.Instances ?? []).map((instance) => ({
|
||||
instanceArn: instance.InstanceArn ?? '',
|
||||
identityStoreId: instance.IdentityStoreId ?? '',
|
||||
name: instance.Name ?? null,
|
||||
status: instance.Status ?? '',
|
||||
statusReason: instance.StatusReason ?? null,
|
||||
ownerAccountId: instance.OwnerAccountId ?? null,
|
||||
createdDate: instance.CreatedDate?.toISOString() ?? null,
|
||||
}))
|
||||
return { instances, nextToken: response.NextToken ?? null, count: instances.length }
|
||||
}
|
||||
|
||||
export async function listAccounts(
|
||||
client: OrganizationsClient,
|
||||
maxResults?: number | null,
|
||||
nextToken?: string | null
|
||||
) {
|
||||
const command = new ListAccountsCommand({
|
||||
...(maxResults ? { MaxResults: maxResults } : {}),
|
||||
...(nextToken ? { NextToken: nextToken } : {}),
|
||||
})
|
||||
const response = await client.send(command)
|
||||
const accounts = (response.Accounts ?? []).map((account) => ({
|
||||
id: account.Id ?? '',
|
||||
arn: account.Arn ?? '',
|
||||
name: account.Name ?? '',
|
||||
email: account.Email ?? '',
|
||||
status: account.State ?? '',
|
||||
joinedTimestamp: account.JoinedTimestamp?.toISOString() ?? null,
|
||||
}))
|
||||
return { accounts, nextToken: response.NextToken ?? null, count: accounts.length }
|
||||
}
|
||||
|
||||
export async function listPermissionSets(
|
||||
client: SSOAdminClient,
|
||||
instanceArn: string,
|
||||
maxResults?: number | null,
|
||||
nextToken?: string | null
|
||||
) {
|
||||
const listCommand = new ListPermissionSetsCommand({
|
||||
InstanceArn: instanceArn,
|
||||
...(maxResults ? { MaxResults: maxResults } : {}),
|
||||
...(nextToken ? { NextToken: nextToken } : {}),
|
||||
})
|
||||
const listResponse = await client.send(listCommand)
|
||||
const permissionSetArns = listResponse.PermissionSets ?? []
|
||||
|
||||
const permissionSets = await Promise.all(
|
||||
permissionSetArns.map(async (arn) => {
|
||||
const describeCommand = new DescribePermissionSetCommand({
|
||||
InstanceArn: instanceArn,
|
||||
PermissionSetArn: arn,
|
||||
})
|
||||
const describeResponse = await client.send(describeCommand)
|
||||
const ps = describeResponse.PermissionSet
|
||||
return {
|
||||
permissionSetArn: ps?.PermissionSetArn ?? arn,
|
||||
name: ps?.Name ?? '',
|
||||
description: ps?.Description ?? null,
|
||||
sessionDuration: ps?.SessionDuration ?? null,
|
||||
createdDate: ps?.CreatedDate?.toISOString() ?? null,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
permissionSets,
|
||||
nextToken: listResponse.NextToken ?? null,
|
||||
count: permissionSets.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserByEmail(
|
||||
ssoClient: IdentitystoreClient,
|
||||
identityStoreId: string,
|
||||
email: string
|
||||
) {
|
||||
const getUserIdCommand = new GetUserIdCommand({
|
||||
IdentityStoreId: identityStoreId,
|
||||
AlternateIdentifier: {
|
||||
UniqueAttribute: {
|
||||
AttributePath: 'emails.value',
|
||||
AttributeValue: email,
|
||||
},
|
||||
},
|
||||
})
|
||||
const getUserIdResponse = await ssoClient.send(getUserIdCommand)
|
||||
const userId = getUserIdResponse.UserId ?? ''
|
||||
|
||||
const describeCommand = new DescribeUserCommand({
|
||||
IdentityStoreId: identityStoreId,
|
||||
UserId: userId,
|
||||
})
|
||||
const describeResponse = await ssoClient.send(describeCommand)
|
||||
|
||||
const primaryEmail =
|
||||
describeResponse.Emails?.find((e) => e.Primary)?.Value ??
|
||||
describeResponse.Emails?.[0]?.Value ??
|
||||
null
|
||||
|
||||
return {
|
||||
userId,
|
||||
userName: describeResponse.UserName ?? '',
|
||||
displayName: describeResponse.DisplayName ?? null,
|
||||
email: primaryEmail,
|
||||
}
|
||||
}
|
||||
|
||||
export function mapAssignmentStatus(status: AccountAssignmentOperationStatus) {
|
||||
return {
|
||||
status: status.Status ?? '',
|
||||
requestId: status.RequestId ?? '',
|
||||
accountId: status.TargetId ?? null,
|
||||
permissionSetArn: status.PermissionSetArn ?? null,
|
||||
principalType: status.PrincipalType ?? null,
|
||||
principalId: status.PrincipalId ?? null,
|
||||
failureReason: status.FailureReason ?? null,
|
||||
createdDate: status.CreatedDate?.toISOString() ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkAssignmentCreationStatus(
|
||||
client: SSOAdminClient,
|
||||
instanceArn: string,
|
||||
requestId: string
|
||||
) {
|
||||
const command = new DescribeAccountAssignmentCreationStatusCommand({
|
||||
InstanceArn: instanceArn,
|
||||
AccountAssignmentCreationRequestId: requestId,
|
||||
})
|
||||
const response = await client.send(command)
|
||||
return mapAssignmentStatus(response.AccountAssignmentCreationStatus ?? {})
|
||||
}
|
||||
|
||||
export async function checkAssignmentDeletionStatus(
|
||||
client: SSOAdminClient,
|
||||
instanceArn: string,
|
||||
requestId: string
|
||||
) {
|
||||
const command = new DescribeAccountAssignmentDeletionStatusCommand({
|
||||
InstanceArn: instanceArn,
|
||||
AccountAssignmentDeletionRequestId: requestId,
|
||||
})
|
||||
const response = await client.send(command)
|
||||
return mapAssignmentStatus(response.AccountAssignmentDeletionStatus ?? {})
|
||||
}
|
||||
|
||||
export async function listGroups(
|
||||
client: IdentitystoreClient,
|
||||
identityStoreId: string,
|
||||
maxResults?: number | null,
|
||||
nextToken?: string | null
|
||||
) {
|
||||
const command = new ListGroupsCommand({
|
||||
IdentityStoreId: identityStoreId,
|
||||
...(maxResults ? { MaxResults: maxResults } : {}),
|
||||
...(nextToken ? { NextToken: nextToken } : {}),
|
||||
})
|
||||
const response = await client.send(command)
|
||||
const groups = (response.Groups ?? []).map((group) => ({
|
||||
groupId: group.GroupId ?? '',
|
||||
displayName: group.DisplayName ?? null,
|
||||
description: group.Description ?? null,
|
||||
externalIds: group.ExternalIds?.map((e) => ({ issuer: e.Issuer ?? '', id: e.Id ?? '' })) ?? [],
|
||||
}))
|
||||
return { groups, nextToken: response.NextToken ?? null, count: groups.length }
|
||||
}
|
||||
|
||||
export async function getGroupByDisplayName(
|
||||
client: IdentitystoreClient,
|
||||
identityStoreId: string,
|
||||
displayName: string
|
||||
) {
|
||||
const getGroupIdCommand = new GetGroupIdCommand({
|
||||
IdentityStoreId: identityStoreId,
|
||||
AlternateIdentifier: {
|
||||
UniqueAttribute: {
|
||||
AttributePath: 'displayName',
|
||||
AttributeValue: displayName,
|
||||
},
|
||||
},
|
||||
})
|
||||
const getGroupIdResponse = await client.send(getGroupIdCommand)
|
||||
const groupId = getGroupIdResponse.GroupId ?? ''
|
||||
|
||||
const describeCommand = new DescribeGroupCommand({
|
||||
IdentityStoreId: identityStoreId,
|
||||
GroupId: groupId,
|
||||
})
|
||||
const describeResponse = await client.send(describeCommand)
|
||||
|
||||
return {
|
||||
groupId,
|
||||
displayName: describeResponse.DisplayName ?? null,
|
||||
description: describeResponse.Description ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function describeAccount(client: OrganizationsClient, accountId: string) {
|
||||
const command = new DescribeAccountCommand({ AccountId: accountId })
|
||||
const response = await client.send(command)
|
||||
const account = response.Account
|
||||
return {
|
||||
id: account?.Id ?? '',
|
||||
arn: account?.Arn ?? '',
|
||||
name: account?.Name ?? '',
|
||||
email: account?.Email ?? '',
|
||||
status: account?.State ?? '',
|
||||
joinedTimestamp: account?.JoinedTimestamp?.toISOString() ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function listAccountAssignmentsForPrincipal(
|
||||
client: SSOAdminClient,
|
||||
instanceArn: string,
|
||||
principalId: string,
|
||||
principalType: string,
|
||||
maxResults?: number | null,
|
||||
nextToken?: string | null
|
||||
) {
|
||||
const command = new ListAccountAssignmentsForPrincipalCommand({
|
||||
InstanceArn: instanceArn,
|
||||
PrincipalId: principalId,
|
||||
PrincipalType: principalType as PrincipalType,
|
||||
...(maxResults ? { MaxResults: maxResults } : {}),
|
||||
...(nextToken ? { NextToken: nextToken } : {}),
|
||||
})
|
||||
const response = await client.send(command)
|
||||
const assignments = (response.AccountAssignments ?? []).map((a) => ({
|
||||
accountId: a.AccountId ?? '',
|
||||
permissionSetArn: a.PermissionSetArn ?? '',
|
||||
principalType: a.PrincipalType ?? '',
|
||||
principalId: a.PrincipalId ?? '',
|
||||
}))
|
||||
return { assignments, nextToken: response.NextToken ?? null, count: assignments.length }
|
||||
}
|
||||
Reference in New Issue
Block a user