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,50 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIamAddUserToGroupContract } from '@/lib/api/contracts/tools/aws/iam-add-user-to-group'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { addUserToGroup, createIAMClient } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMAddUserToGroupAPI')
|
||||
|
||||
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(awsIamAddUserToGroupContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Adding user "${params.userName}" to group "${params.groupName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await addUserToGroup(client, params.userName, params.groupName)
|
||||
logger.info(`Successfully added user "${params.userName}" to group "${params.groupName}"`)
|
||||
return NextResponse.json({
|
||||
message: `User "${params.userName}" added to group "${params.groupName}"`,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to add user to group:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to add user to group: ${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 { awsIamAttachRolePolicyContract } from '@/lib/api/contracts/tools/aws/iam-attach-role-policy'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { attachRolePolicy, createIAMClient } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMAttachRolePolicyAPI')
|
||||
|
||||
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(awsIamAttachRolePolicyContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Attaching policy to IAM role "${params.roleName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await attachRolePolicy(client, params.roleName, params.policyArn)
|
||||
logger.info(`Successfully attached policy to IAM role "${params.roleName}"`)
|
||||
return NextResponse.json({
|
||||
message: `Policy "${params.policyArn}" attached to role "${params.roleName}"`,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to attach role policy:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to attach role policy: ${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 { awsIamAttachUserPolicyContract } from '@/lib/api/contracts/tools/aws/iam-attach-user-policy'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { attachUserPolicy, createIAMClient } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMAttachUserPolicyAPI')
|
||||
|
||||
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(awsIamAttachUserPolicyContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Attaching policy to IAM user "${params.userName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await attachUserPolicy(client, params.userName, params.policyArn)
|
||||
logger.info(`Successfully attached policy to IAM user "${params.userName}"`)
|
||||
return NextResponse.json({
|
||||
message: `Policy "${params.policyArn}" attached to user "${params.userName}"`,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to attach user policy:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to attach user policy: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIamCreateAccessKeyContract } from '@/lib/api/contracts/tools/aws/iam-create-access-key'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createAccessKey, createIAMClient } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMCreateAccessKeyAPI')
|
||||
|
||||
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(awsIamCreateAccessKeyContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Creating IAM access key`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await createAccessKey(client, params.userName)
|
||||
logger.info(`Successfully created access key for user "${result.userName}"`)
|
||||
return NextResponse.json({
|
||||
message: `Access key created for user "${result.userName}"`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to create access key:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to create access key: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,58 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIamCreateRoleContract } from '@/lib/api/contracts/tools/aws/iam-create-role'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, createRole } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMCreateRoleAPI')
|
||||
|
||||
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(awsIamCreateRoleContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Creating IAM role "${params.roleName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await createRole(
|
||||
client,
|
||||
params.roleName,
|
||||
params.assumeRolePolicyDocument,
|
||||
params.description,
|
||||
params.path,
|
||||
params.maxSessionDuration
|
||||
)
|
||||
logger.info(`Successfully created IAM role "${result.roleName}"`)
|
||||
return NextResponse.json({
|
||||
message: `Role "${result.roleName}" created successfully`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to create IAM role:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to create IAM role: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIamCreateUserContract } from '@/lib/api/contracts/tools/aws/iam-create-user'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, createUser } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMCreateUserAPI')
|
||||
|
||||
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(awsIamCreateUserContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Creating IAM user "${params.userName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await createUser(client, params.userName, params.path)
|
||||
logger.info(`Successfully created IAM user "${result.userName}"`)
|
||||
return NextResponse.json({
|
||||
message: `User "${result.userName}" created successfully`,
|
||||
...result,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to create IAM user:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to create IAM user: ${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 { awsIamDeleteAccessKeyContract } from '@/lib/api/contracts/tools/aws/iam-delete-access-key'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, deleteAccessKey } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMDeleteAccessKeyAPI')
|
||||
|
||||
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(awsIamDeleteAccessKeyContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Deleting IAM access key "${params.accessKeyIdToDelete}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await deleteAccessKey(client, params.accessKeyIdToDelete, params.userName)
|
||||
logger.info(`Successfully deleted access key "${params.accessKeyIdToDelete}"`)
|
||||
return NextResponse.json({ message: `Access key "${params.accessKeyIdToDelete}" deleted` })
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to delete access key:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to delete access key: ${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 { awsIamDeleteRoleContract } from '@/lib/api/contracts/tools/aws/iam-delete-role'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, deleteRole } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMDeleteRoleAPI')
|
||||
|
||||
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(awsIamDeleteRoleContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Deleting IAM role "${params.roleName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await deleteRole(client, params.roleName)
|
||||
logger.info(`Successfully deleted IAM role "${params.roleName}"`)
|
||||
return NextResponse.json({ message: `Role "${params.roleName}" deleted successfully` })
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to delete IAM role:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to delete IAM role: ${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 { awsIamDeleteUserContract } from '@/lib/api/contracts/tools/aws/iam-delete-user'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, deleteUser } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMDeleteUserAPI')
|
||||
|
||||
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(awsIamDeleteUserContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Deleting IAM user "${params.userName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await deleteUser(client, params.userName)
|
||||
logger.info(`Successfully deleted IAM user "${params.userName}"`)
|
||||
return NextResponse.json({ message: `User "${params.userName}" deleted successfully` })
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to delete IAM user:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to delete IAM user: ${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 { awsIamDetachRolePolicyContract } from '@/lib/api/contracts/tools/aws/iam-detach-role-policy'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, detachRolePolicy } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMDetachRolePolicyAPI')
|
||||
|
||||
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(awsIamDetachRolePolicyContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Detaching policy from IAM role "${params.roleName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await detachRolePolicy(client, params.roleName, params.policyArn)
|
||||
logger.info(`Successfully detached policy from IAM role "${params.roleName}"`)
|
||||
return NextResponse.json({
|
||||
message: `Policy "${params.policyArn}" detached from role "${params.roleName}"`,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to detach role policy:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to detach role policy: ${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 { awsIamDetachUserPolicyContract } from '@/lib/api/contracts/tools/aws/iam-detach-user-policy'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, detachUserPolicy } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMDetachUserPolicyAPI')
|
||||
|
||||
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(awsIamDetachUserPolicyContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Detaching policy from IAM user "${params.userName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await detachUserPolicy(client, params.userName, params.policyArn)
|
||||
logger.info(`Successfully detached policy from IAM user "${params.userName}"`)
|
||||
return NextResponse.json({
|
||||
message: `Policy "${params.policyArn}" detached from user "${params.userName}"`,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to detach user policy:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to detach user policy: ${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 { awsIamGetRoleContract } from '@/lib/api/contracts/tools/aws/iam-get-role'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, getRole } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMGetRoleAPI')
|
||||
|
||||
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(awsIamGetRoleContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Getting IAM role "${params.roleName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await getRole(client, params.roleName)
|
||||
logger.info(`Successfully retrieved IAM role "${params.roleName}"`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to get IAM role:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get IAM role: ${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 { awsIamGetUserContract } from '@/lib/api/contracts/tools/aws/iam-get-user'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, getUser } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMGetUserAPI')
|
||||
|
||||
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(awsIamGetUserContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Getting IAM user "${params.userName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await getUser(client, params.userName)
|
||||
logger.info(`Successfully retrieved IAM user "${params.userName ?? 'caller'}"`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to get IAM user:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to get IAM 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 { awsIamListAttachedRolePoliciesContract } from '@/lib/api/contracts/tools/aws/iam-list-attached-role-policies'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, listAttachedRolePolicies } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMListAttachedRolePoliciesAPI')
|
||||
|
||||
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(awsIamListAttachedRolePoliciesContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing policies attached to IAM role "${params.roleName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listAttachedRolePolicies(
|
||||
client,
|
||||
params.roleName,
|
||||
params.pathPrefix,
|
||||
params.maxItems,
|
||||
params.marker
|
||||
)
|
||||
logger.info(`Found ${result.count} policies attached to role "${params.roleName}"`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to list attached role policies:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list attached role policies: ${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 { awsIamListAttachedUserPoliciesContract } from '@/lib/api/contracts/tools/aws/iam-list-attached-user-policies'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, listAttachedUserPolicies } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMListAttachedUserPoliciesAPI')
|
||||
|
||||
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(awsIamListAttachedUserPoliciesContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing policies attached to IAM user "${params.userName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listAttachedUserPolicies(
|
||||
client,
|
||||
params.userName,
|
||||
params.pathPrefix,
|
||||
params.maxItems,
|
||||
params.marker
|
||||
)
|
||||
logger.info(`Found ${result.count} policies attached to user "${params.userName}"`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to list attached user policies:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list attached user policies: ${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 { awsIamListGroupsContract } from '@/lib/api/contracts/tools/aws/iam-list-groups'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, listGroups } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMListGroupsAPI')
|
||||
|
||||
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(awsIamListGroupsContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing IAM groups`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listGroups(client, params.pathPrefix, params.maxItems, params.marker)
|
||||
logger.info(`Successfully listed ${result.count} IAM groups`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to list IAM groups:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list IAM groups: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIamListPoliciesContract } from '@/lib/api/contracts/tools/aws/iam-list-policies'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, listPolicies } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMListPoliciesAPI')
|
||||
|
||||
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(awsIamListPoliciesContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing IAM policies`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listPolicies(
|
||||
client,
|
||||
params.scope,
|
||||
params.onlyAttached,
|
||||
params.pathPrefix,
|
||||
params.maxItems,
|
||||
params.marker
|
||||
)
|
||||
logger.info(`Successfully listed ${result.count} IAM policies`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to list IAM policies:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list IAM policies: ${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 { awsIamListRolesContract } from '@/lib/api/contracts/tools/aws/iam-list-roles'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, listRoles } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMListRolesAPI')
|
||||
|
||||
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(awsIamListRolesContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing IAM roles`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listRoles(client, params.pathPrefix, params.maxItems, params.marker)
|
||||
logger.info(`Successfully listed ${result.count} IAM roles`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to list IAM roles:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list IAM roles: ${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 { awsIamListUsersContract } from '@/lib/api/contracts/tools/aws/iam-list-users'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, listUsers } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMListUsersAPI')
|
||||
|
||||
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(awsIamListUsersContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Listing IAM users`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await listUsers(client, params.pathPrefix, params.maxItems, params.marker)
|
||||
logger.info(`Successfully listed ${result.count} IAM users`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to list IAM users:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to list IAM users: ${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 { awsIamRemoveUserFromGroupContract } from '@/lib/api/contracts/tools/aws/iam-remove-user-from-group'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, removeUserFromGroup } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMRemoveUserFromGroupAPI')
|
||||
|
||||
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(awsIamRemoveUserFromGroupContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(`Removing user "${params.userName}" from group "${params.groupName}"`)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
await removeUserFromGroup(client, params.userName, params.groupName)
|
||||
logger.info(`Successfully removed user "${params.userName}" from group "${params.groupName}"`)
|
||||
return NextResponse.json({
|
||||
message: `User "${params.userName}" removed from group "${params.groupName}"`,
|
||||
})
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to remove user from group:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to remove user from group: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { awsIamSimulatePrincipalPolicyContract } from '@/lib/api/contracts/tools/aws/iam-simulate-principal-policy'
|
||||
import { parseToolRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { createIAMClient, simulatePrincipalPolicy } from '../utils'
|
||||
|
||||
const logger = createLogger('IAMSimulatePrincipalPolicyAPI')
|
||||
|
||||
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(awsIamSimulatePrincipalPolicyContract, request, {
|
||||
errorFormat: 'details',
|
||||
logger,
|
||||
})
|
||||
if (!parsed.success) return parsed.response
|
||||
const params = parsed.data.body
|
||||
|
||||
logger.info(
|
||||
`Simulating principal policy for "${params.policySourceArn}" on actions: ${params.actionNames}`
|
||||
)
|
||||
|
||||
const client = createIAMClient({
|
||||
region: params.region,
|
||||
accessKeyId: params.accessKeyId,
|
||||
secretAccessKey: params.secretAccessKey,
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await simulatePrincipalPolicy(
|
||||
client,
|
||||
params.policySourceArn,
|
||||
params.actionNames,
|
||||
params.resourceArns,
|
||||
params.maxResults,
|
||||
params.marker
|
||||
)
|
||||
logger.info(`Simulation complete: ${result.count} results`)
|
||||
return NextResponse.json(result)
|
||||
} finally {
|
||||
client.destroy()
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to simulate principal policy:`, error)
|
||||
return NextResponse.json(
|
||||
{ error: `Failed to simulate principal policy: ${toError(error).message}` },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,453 @@
|
||||
import type {
|
||||
AttachedPolicy,
|
||||
Group,
|
||||
Policy,
|
||||
PolicyScopeType,
|
||||
Role,
|
||||
User,
|
||||
} from '@aws-sdk/client-iam'
|
||||
import {
|
||||
AddUserToGroupCommand,
|
||||
AttachRolePolicyCommand,
|
||||
AttachUserPolicyCommand,
|
||||
CreateAccessKeyCommand,
|
||||
CreateRoleCommand,
|
||||
CreateUserCommand,
|
||||
DeleteAccessKeyCommand,
|
||||
DeleteRoleCommand,
|
||||
DeleteUserCommand,
|
||||
DetachRolePolicyCommand,
|
||||
DetachUserPolicyCommand,
|
||||
GetRoleCommand,
|
||||
GetUserCommand,
|
||||
IAMClient,
|
||||
ListAttachedRolePoliciesCommand,
|
||||
ListAttachedUserPoliciesCommand,
|
||||
ListGroupsCommand,
|
||||
ListPoliciesCommand,
|
||||
ListRolesCommand,
|
||||
ListUsersCommand,
|
||||
RemoveUserFromGroupCommand,
|
||||
SimulatePrincipalPolicyCommand,
|
||||
} from '@aws-sdk/client-iam'
|
||||
import type { IAMConnectionConfig } from '@/tools/iam/types'
|
||||
|
||||
export function createIAMClient(config: IAMConnectionConfig): IAMClient {
|
||||
return new IAMClient({
|
||||
region: config.region,
|
||||
credentials: {
|
||||
accessKeyId: config.accessKeyId,
|
||||
secretAccessKey: config.secretAccessKey,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function listUsers(
|
||||
client: IAMClient,
|
||||
pathPrefix?: string | null,
|
||||
maxItems?: number | null,
|
||||
marker?: string | null
|
||||
) {
|
||||
const command = new ListUsersCommand({
|
||||
...(pathPrefix ? { PathPrefix: pathPrefix } : {}),
|
||||
...(maxItems ? { MaxItems: maxItems } : {}),
|
||||
...(marker ? { Marker: marker } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const users = (response.Users ?? []).map((user: User) => ({
|
||||
userName: user.UserName ?? '',
|
||||
userId: user.UserId ?? '',
|
||||
arn: user.Arn ?? '',
|
||||
path: user.Path ?? '',
|
||||
createDate: user.CreateDate?.toISOString() ?? null,
|
||||
passwordLastUsed: user.PasswordLastUsed?.toISOString() ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
users,
|
||||
isTruncated: response.IsTruncated ?? false,
|
||||
marker: response.Marker ?? null,
|
||||
count: users.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUser(client: IAMClient, userName?: string | null) {
|
||||
const command = new GetUserCommand(userName ? { UserName: userName } : {})
|
||||
const response = await client.send(command)
|
||||
const user = response.User
|
||||
|
||||
return {
|
||||
userName: user?.UserName ?? '',
|
||||
userId: user?.UserId ?? '',
|
||||
arn: user?.Arn ?? '',
|
||||
path: user?.Path ?? '',
|
||||
createDate: user?.CreateDate?.toISOString() ?? null,
|
||||
passwordLastUsed: user?.PasswordLastUsed?.toISOString() ?? null,
|
||||
permissionsBoundaryArn: user?.PermissionsBoundary?.PermissionsBoundaryArn ?? null,
|
||||
tags: user?.Tags?.map((t) => ({ key: t.Key ?? '', value: t.Value ?? '' })) ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
export async function createUser(client: IAMClient, userName: string, path?: string | null) {
|
||||
const command = new CreateUserCommand({
|
||||
UserName: userName,
|
||||
...(path ? { Path: path } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const user = response.User
|
||||
|
||||
return {
|
||||
userName: user?.UserName ?? '',
|
||||
userId: user?.UserId ?? '',
|
||||
arn: user?.Arn ?? '',
|
||||
path: user?.Path ?? '',
|
||||
createDate: user?.CreateDate?.toISOString() ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteUser(client: IAMClient, userName: string) {
|
||||
const command = new DeleteUserCommand({ UserName: userName })
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function listRoles(
|
||||
client: IAMClient,
|
||||
pathPrefix?: string | null,
|
||||
maxItems?: number | null,
|
||||
marker?: string | null
|
||||
) {
|
||||
const command = new ListRolesCommand({
|
||||
...(pathPrefix ? { PathPrefix: pathPrefix } : {}),
|
||||
...(maxItems ? { MaxItems: maxItems } : {}),
|
||||
...(marker ? { Marker: marker } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const roles = (response.Roles ?? []).map((role: Role) => ({
|
||||
roleName: role.RoleName ?? '',
|
||||
roleId: role.RoleId ?? '',
|
||||
arn: role.Arn ?? '',
|
||||
path: role.Path ?? '',
|
||||
createDate: role.CreateDate?.toISOString() ?? null,
|
||||
description: role.Description ?? null,
|
||||
maxSessionDuration: role.MaxSessionDuration ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
roles,
|
||||
isTruncated: response.IsTruncated ?? false,
|
||||
marker: response.Marker ?? null,
|
||||
count: roles.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getRole(client: IAMClient, roleName: string) {
|
||||
const command = new GetRoleCommand({ RoleName: roleName })
|
||||
const response = await client.send(command)
|
||||
const role = response.Role
|
||||
|
||||
let policyDocument: string | null = null
|
||||
if (role?.AssumeRolePolicyDocument) {
|
||||
try {
|
||||
policyDocument = decodeURIComponent(role.AssumeRolePolicyDocument)
|
||||
} catch {
|
||||
policyDocument = role.AssumeRolePolicyDocument
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
roleName: role?.RoleName ?? '',
|
||||
roleId: role?.RoleId ?? '',
|
||||
arn: role?.Arn ?? '',
|
||||
path: role?.Path ?? '',
|
||||
createDate: role?.CreateDate?.toISOString() ?? null,
|
||||
description: role?.Description ?? null,
|
||||
maxSessionDuration: role?.MaxSessionDuration ?? null,
|
||||
assumeRolePolicyDocument: policyDocument,
|
||||
roleLastUsedDate: role?.RoleLastUsed?.LastUsedDate?.toISOString() ?? null,
|
||||
roleLastUsedRegion: role?.RoleLastUsed?.Region ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function createRole(
|
||||
client: IAMClient,
|
||||
roleName: string,
|
||||
assumeRolePolicyDocument: string,
|
||||
description?: string | null,
|
||||
path?: string | null,
|
||||
maxSessionDuration?: number | null
|
||||
) {
|
||||
const command = new CreateRoleCommand({
|
||||
RoleName: roleName,
|
||||
AssumeRolePolicyDocument: assumeRolePolicyDocument,
|
||||
...(description ? { Description: description } : {}),
|
||||
...(path ? { Path: path } : {}),
|
||||
...(maxSessionDuration ? { MaxSessionDuration: maxSessionDuration } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const role = response.Role
|
||||
|
||||
return {
|
||||
roleName: role?.RoleName ?? '',
|
||||
roleId: role?.RoleId ?? '',
|
||||
arn: role?.Arn ?? '',
|
||||
path: role?.Path ?? '',
|
||||
createDate: role?.CreateDate?.toISOString() ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteRole(client: IAMClient, roleName: string) {
|
||||
const command = new DeleteRoleCommand({ RoleName: roleName })
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function attachUserPolicy(client: IAMClient, userName: string, policyArn: string) {
|
||||
const command = new AttachUserPolicyCommand({
|
||||
UserName: userName,
|
||||
PolicyArn: policyArn,
|
||||
})
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function detachUserPolicy(client: IAMClient, userName: string, policyArn: string) {
|
||||
const command = new DetachUserPolicyCommand({
|
||||
UserName: userName,
|
||||
PolicyArn: policyArn,
|
||||
})
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function attachRolePolicy(client: IAMClient, roleName: string, policyArn: string) {
|
||||
const command = new AttachRolePolicyCommand({
|
||||
RoleName: roleName,
|
||||
PolicyArn: policyArn,
|
||||
})
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function detachRolePolicy(client: IAMClient, roleName: string, policyArn: string) {
|
||||
const command = new DetachRolePolicyCommand({
|
||||
RoleName: roleName,
|
||||
PolicyArn: policyArn,
|
||||
})
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function listPolicies(
|
||||
client: IAMClient,
|
||||
scope?: string | null,
|
||||
onlyAttached?: boolean | null,
|
||||
pathPrefix?: string | null,
|
||||
maxItems?: number | null,
|
||||
marker?: string | null
|
||||
) {
|
||||
const command = new ListPoliciesCommand({
|
||||
...(scope ? { Scope: scope as PolicyScopeType } : {}),
|
||||
...(onlyAttached != null ? { OnlyAttached: onlyAttached } : {}),
|
||||
...(pathPrefix ? { PathPrefix: pathPrefix } : {}),
|
||||
...(maxItems ? { MaxItems: maxItems } : {}),
|
||||
...(marker ? { Marker: marker } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const policies = (response.Policies ?? []).map((policy: Policy) => ({
|
||||
policyName: policy.PolicyName ?? '',
|
||||
policyId: policy.PolicyId ?? '',
|
||||
arn: policy.Arn ?? '',
|
||||
path: policy.Path ?? '',
|
||||
attachmentCount: policy.AttachmentCount ?? 0,
|
||||
isAttachable: policy.IsAttachable ?? false,
|
||||
createDate: policy.CreateDate?.toISOString() ?? null,
|
||||
updateDate: policy.UpdateDate?.toISOString() ?? null,
|
||||
description: policy.Description ?? null,
|
||||
defaultVersionId: policy.DefaultVersionId ?? null,
|
||||
permissionsBoundaryUsageCount: policy.PermissionsBoundaryUsageCount ?? 0,
|
||||
}))
|
||||
|
||||
return {
|
||||
policies,
|
||||
isTruncated: response.IsTruncated ?? false,
|
||||
marker: response.Marker ?? null,
|
||||
count: policies.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function createAccessKey(client: IAMClient, userName?: string | null) {
|
||||
const command = new CreateAccessKeyCommand({
|
||||
...(userName ? { UserName: userName } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const key = response.AccessKey
|
||||
|
||||
return {
|
||||
accessKeyId: key?.AccessKeyId ?? '',
|
||||
secretAccessKey: key?.SecretAccessKey ?? '',
|
||||
userName: key?.UserName ?? '',
|
||||
status: key?.Status ?? '',
|
||||
createDate: key?.CreateDate?.toISOString() ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteAccessKey(
|
||||
client: IAMClient,
|
||||
accessKeyIdToDelete: string,
|
||||
userName?: string | null
|
||||
) {
|
||||
const command = new DeleteAccessKeyCommand({
|
||||
AccessKeyId: accessKeyIdToDelete,
|
||||
...(userName ? { UserName: userName } : {}),
|
||||
})
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function listGroups(
|
||||
client: IAMClient,
|
||||
pathPrefix?: string | null,
|
||||
maxItems?: number | null,
|
||||
marker?: string | null
|
||||
) {
|
||||
const command = new ListGroupsCommand({
|
||||
...(pathPrefix ? { PathPrefix: pathPrefix } : {}),
|
||||
...(maxItems ? { MaxItems: maxItems } : {}),
|
||||
...(marker ? { Marker: marker } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const groups = (response.Groups ?? []).map((group: Group) => ({
|
||||
groupName: group.GroupName ?? '',
|
||||
groupId: group.GroupId ?? '',
|
||||
arn: group.Arn ?? '',
|
||||
path: group.Path ?? '',
|
||||
createDate: group.CreateDate?.toISOString() ?? null,
|
||||
}))
|
||||
|
||||
return {
|
||||
groups,
|
||||
isTruncated: response.IsTruncated ?? false,
|
||||
marker: response.Marker ?? null,
|
||||
count: groups.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function addUserToGroup(client: IAMClient, userName: string, groupName: string) {
|
||||
const command = new AddUserToGroupCommand({
|
||||
UserName: userName,
|
||||
GroupName: groupName,
|
||||
})
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function removeUserFromGroup(client: IAMClient, userName: string, groupName: string) {
|
||||
const command = new RemoveUserFromGroupCommand({
|
||||
UserName: userName,
|
||||
GroupName: groupName,
|
||||
})
|
||||
await client.send(command)
|
||||
}
|
||||
|
||||
export async function listAttachedRolePolicies(
|
||||
client: IAMClient,
|
||||
roleName: string,
|
||||
pathPrefix?: string | null,
|
||||
maxItems?: number | null,
|
||||
marker?: string | null
|
||||
) {
|
||||
const command = new ListAttachedRolePoliciesCommand({
|
||||
RoleName: roleName,
|
||||
...(pathPrefix ? { PathPrefix: pathPrefix } : {}),
|
||||
...(maxItems ? { MaxItems: maxItems } : {}),
|
||||
...(marker ? { Marker: marker } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const attachedPolicies = (response.AttachedPolicies ?? []).map((p: AttachedPolicy) => ({
|
||||
policyName: p.PolicyName ?? '',
|
||||
policyArn: p.PolicyArn ?? '',
|
||||
}))
|
||||
|
||||
return {
|
||||
attachedPolicies,
|
||||
isTruncated: response.IsTruncated ?? false,
|
||||
marker: response.Marker ?? null,
|
||||
count: attachedPolicies.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function listAttachedUserPolicies(
|
||||
client: IAMClient,
|
||||
userName: string,
|
||||
pathPrefix?: string | null,
|
||||
maxItems?: number | null,
|
||||
marker?: string | null
|
||||
) {
|
||||
const command = new ListAttachedUserPoliciesCommand({
|
||||
UserName: userName,
|
||||
...(pathPrefix ? { PathPrefix: pathPrefix } : {}),
|
||||
...(maxItems ? { MaxItems: maxItems } : {}),
|
||||
...(marker ? { Marker: marker } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const attachedPolicies = (response.AttachedPolicies ?? []).map((p: AttachedPolicy) => ({
|
||||
policyName: p.PolicyName ?? '',
|
||||
policyArn: p.PolicyArn ?? '',
|
||||
}))
|
||||
|
||||
return {
|
||||
attachedPolicies,
|
||||
isTruncated: response.IsTruncated ?? false,
|
||||
marker: response.Marker ?? null,
|
||||
count: attachedPolicies.length,
|
||||
}
|
||||
}
|
||||
|
||||
export async function simulatePrincipalPolicy(
|
||||
client: IAMClient,
|
||||
policySourceArn: string,
|
||||
actionNames: string,
|
||||
resourceArns?: string | null,
|
||||
maxResults?: number | null,
|
||||
marker?: string | null
|
||||
) {
|
||||
const actions = actionNames
|
||||
.split(',')
|
||||
.map((a) => a.trim())
|
||||
.filter(Boolean)
|
||||
const resources = resourceArns
|
||||
? resourceArns
|
||||
.split(',')
|
||||
.map((r) => r.trim())
|
||||
.filter(Boolean)
|
||||
: ['*']
|
||||
|
||||
const command = new SimulatePrincipalPolicyCommand({
|
||||
PolicySourceArn: policySourceArn,
|
||||
ActionNames: actions,
|
||||
ResourceArns: resources,
|
||||
...(maxResults ? { MaxItems: maxResults } : {}),
|
||||
...(marker ? { Marker: marker } : {}),
|
||||
})
|
||||
|
||||
const response = await client.send(command)
|
||||
const evaluationResults = (response.EvaluationResults ?? []).map((r) => ({
|
||||
evalActionName: r.EvalActionName ?? '',
|
||||
evalResourceName: r.EvalResourceName ?? '',
|
||||
evalDecision: r.EvalDecision ?? '',
|
||||
matchedStatements: (r.MatchedStatements ?? []).map((s) => ({
|
||||
sourcePolicyId: s.SourcePolicyId ?? '',
|
||||
sourcePolicyType: s.SourcePolicyType ?? '',
|
||||
})),
|
||||
missingContextValues: (r.MissingContextValues ?? []).map((v) => String(v)),
|
||||
}))
|
||||
|
||||
return {
|
||||
evaluationResults,
|
||||
isTruncated: response.IsTruncated ?? false,
|
||||
marker: response.Marker ?? null,
|
||||
count: evaluationResults.length,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user