Skip to content

feat(aws): add IAM to Cloud Explorer - #145

Merged
fredpena merged 5 commits into
floci-io:mainfrom
thomhurst:agent/aws-iam-cloud-explorer
Aug 30, 2026
Merged

feat(aws): add IAM to Cloud Explorer#145
fredpena merged 5 commits into
floci-io:mainfrom
thomhurst:agent/aws-iam-cloud-explorer

Conversation

@thomhurst

@thomhurst thomhurst commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add the generic identity Cloud Explorer category and Identity sidebar entry
  • implement account-scoped AWS IAM user list, create, delete, and inspect operations through the cloud SPI
  • add IAM schema validation, pagination, normalized resource metadata, documentation, and adapter/route coverage

Type of change

  • Bug fix (fix:)
  • New feature / service UI (feat:)
  • Breaking change (feat!: or fix!:)
  • Docs / chore

Area

  • Frontend (packages/frontend)
  • API / Cloud Proxy (packages/api)
  • Cloud Explorer adapter / schema
  • Build / CI / Docker

Verification

  • pnpm lint
  • pnpm type-check
  • pnpm test — 173 tests passed
  • pnpm build
  • HTTP smoke check: AWS services reports identity available and /cloud-explorer/aws/identity returns 200
  • Tested against AWS IAM via Floci core (:4566)

Checklist

  • pnpm lint, pnpm type-check, pnpm test, and pnpm build pass locally
  • New or updated tests added where it makes sense (bun test in packages/api)
  • No fake/mock data added — unwired states stay empty or show an explicit placeholder
  • Commit messages / PR title follow Conventional Commits

Closes #79

image

@thomhurst
thomhurst requested a review from hectorvent as a code owner July 18, 2026 11:53
@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires up AWS IAM user management into the multi-cloud Cloud Explorer, following the established schema-driven SPI pattern end-to-end. It adds the identity service catalog entry, a schema with username and path field validation, the AwsIamAdapter with paginated list/get/create/delete, proper typed-error propagation, and the IAM SDK client in the per-account client set.

  • Backend: AwsIamAdapter implements all four advertised operations, throws typed errors (ValidationError, NotFoundError, ConflictError) for known failure modes, and lets unhandled AWS SDK errors propagate to the route-level mapAwsSdkError mapper — consistent with every other adapter in the codebase.
  • Schema: iamSchema.ts exports a single awsIamSchema() function with a self-consistent username regex ({1,64} length in the pattern itself), separate path validation, and capability declarations that match what the adapter actually implements.
  • Frontend: Only a "Create user" label addition and the iam-user type in the resource union were needed; the generic DynamicResourceView handles everything else from the schema.

Confidence Score: 5/5

  • Safe to merge. The change is a well-scoped addition of a single adapter that follows the existing multi-cloud SPI pattern without modifying any shared infrastructure.
  • All four advertised operations are implemented and tested. Typed errors are thrown for every known failure mode (missing user, conflict on delete, invalid input), and raw SDK errors fall through to the existing route-level mapper. The schema, adapter, registration, and frontend type are consistent with each other and with the rest of the codebase. No regressions to existing adapters are introduced.
  • No files require special attention.

Important Files Changed

Filename Overview
packages/api/src/adapter-aws/AwsIamAdapter.ts Implements CloudServiceAdapter for AWS IAM users (list, get, create, delete) with proper typed error handling, pagination guard, and client-side search filtering. Previous issues with ConflictError and ValidationError are resolved.
packages/api/src/adapter-aws/AwsIamAdapter.test.ts Comprehensive unit tests covering all adapter operations including pagination, search, not-found, conflict, validation, and error mapping. All paths are exercised with a fake client.
packages/api/src/cloud-spi/iamSchema.ts Defines IAM schema with correctly consolidated username pattern (length baked into regex) and separate path validation. Capabilities, columns, and filters follow the SPI contract.
packages/api/src/cloudProxy.ts AwsIamAdapter is correctly registered with the per-account IAM client. No issues.
packages/api/src/cloud-spi/serviceCatalog.ts identity service added to SERVICE_CATALOG under Security group with order:5. iconKey 'iam' will fall back to a generic icon if unmapped, which is acceptable.
packages/frontend/src/components/DynamicResourceView.tsx Only adds a "Create user" label for aws:identity in resourceCreateLabel. No bespoke panel required; the generic DynamicFormRenderer handles the IAM create form correctly.

Sequence Diagram

sequenceDiagram
    participant Browser as Browser (React :4500)
    participant API as Hono API (:4501)
    participant Registry as CloudAdapterRegistry
    participant Adapter as AwsIamAdapter
    participant IAM as AWS IAM SDK (:4566)

    Browser->>API: GET /clouds/aws/identity/schema
    API->>Registry: adapter("aws:identity").schema()
    Registry->>Adapter: schema()
    Adapter-->>Browser: awsIamSchema (fields, columns, capabilities)

    Browser->>API: "GET /clouds/aws/identity/resources?search=alice"
    API->>Registry: "adapter("aws:identity").list({search})"
    Registry->>Adapter: "list({search})"
    loop paginate (max 50 pages)
        Adapter->>IAM: "ListUsersCommand({Marker})"
        IAM-->>Adapter: "{Users[], IsTruncated, Marker}"
    end
    Adapter-->>Browser: CloudResource[] (client-side filtered)

    Browser->>API: "POST /clouds/aws/identity/resources {userName, path}"
    API->>Registry: adapter("aws:identity").create(input)
    Adapter->>Adapter: "validate userName & path"
    Adapter->>IAM: "CreateUserCommand({UserName, Path})"
    IAM-->>Adapter: "{User}"
    Adapter-->>Browser: CloudResource

    Browser->>API: DELETE /clouds/aws/identity/resources/:id
    API->>Registry: adapter("aws:identity").delete(id)
    Adapter->>IAM: "DeleteUserCommand({UserName: id})"
    alt user not found
        IAM-->>Adapter: NoSuchEntityException
        Adapter-->>Browser: 404 NotFoundError
    else user has attached resources
        IAM-->>Adapter: DeleteConflictException
        Adapter-->>Browser: 409 ConflictError (with guidance)
    else success
        IAM-->>Adapter: "{}"
        Adapter-->>Browser: 204
    end
Loading

Reviews (10): Last reviewed commit: "chore: merge upstream main" | Re-trigger Greptile

Comment thread packages/api/src/adapter-aws/AwsIamAdapter.ts
Comment thread packages/api/src/cloud-spi/iamSchema.ts

@hectorvent hectorvent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @thomhurst. This is the only AWS adapter in the repo that actually paginates, and you covered the IsTruncated loop with a real two-page test rather than a claim. Complete registration, honest region: null, route tests included. Strongest wiring of the batch.

Before merge

  • CI just started (first-contribution gate); needs green. Merge order vs #153 is ours; if it lands after, identity collapses into a single catalog row, which is the cheap path.
  • Smallest real fix worth doing now: delete() on a missing user surfaces as 502. IAM's NoSuchEntityException message doesn't contain "not found", so it dodges the normalizer. You already added errorName dispatch; two more lines map it to 404.

Nits: the IAM-specific DeleteConflictException copy sits in the generic normalizer, better thrown typed from the adapter; a max-pages guard on the ListUsers loop; the username and path rules exist in schema and adapter with nothing keeping them in sync; template Type and Area boxes.

Thanks again!

@thomhurst

Copy link
Copy Markdown
Contributor Author

Thanks @hectorvent — all addressed in 1abc1f3:

  • delete() on a missing user is now a 404: NoSuchEntityException is matched by errorName rather than message text, so it no longer dodges the normalizer into a 502.
  • The IAM-specific delete-conflict copy moved out of the shared normalizer and into the adapter as a typed ResourceConflictError, with ResourceNotFoundError for the above.
  • ListUsers pagination now has a page cap so a runtime that always sets IsTruncated cannot spin.
  • The user name and path rules are single-sourced from the schema, so the adapter and schema cannot drift.
  • Committed the missing @aws-sdk/client-iam bun lockfile entry.

Template Type and Area boxes are filled. Merge order vs #153 is yours — if it lands first I'll collapse identity into a single catalog row.

christianvv added a commit to christianvv/floci-ui that referenced this pull request Jul 28, 2026
apigatewaySchemaFor was dead code left over from the pre-floci-io#153
schema-dispatch pattern, where CloudProxyService.schema() called a
per-service *SchemaFor(cloud) wrapper via an if/else chain. That
dispatch no longer exists — schema() now calls adapter.schema()
directly, so only awsApiGatewaySchema() is ever used.

Confirmed by checking iamSchema.ts, written fresh against the current
catalog architecture during floci-io#145's rebase: it exports no equivalent
wrapper, just the cloud-specific schema function consumed directly by
the adapter. The wrapper isn't a convention to preserve, it's leftover
cruft.

Flagged by automated review on floci-io#169.

@hectorvent hectorvent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @thomhurst, this follow-up closes out everything from the last pass. bun.lock now carries the @aws-sdk/client-iam entry (that was the CI failure), NoSuchEntityException surfaces as a proper 404 via a typed error in the adapter, the delete conflict copy moved out of the shared normalizer into the adapter, the ListUsers loop has a page cap, and the user name and path rules are single-sourced from the schema. The merge onto current main also lands identity the new way: one catalog row in the Security group with no frontend nav edits. Nothing left on my list.

One heads up on timing: PR #162 (opened today) also adds an AWS IAM adapter. That collision is on us for not marking #79 as claimed once your PR was open, not on either of you, so I will pick a direction. Your branch being current with main and already through review should make that call straightforward.

CI just needs the workflow runs approved again after the new push; once green this looks ready from my side. Thanks for the fast turnaround!

christianvv added a commit to christianvv/floci-ui that referenced this pull request Jul 29, 2026
apigatewaySchemaFor was dead code left over from the pre-floci-io#153
schema-dispatch pattern, where CloudProxyService.schema() called a
per-service *SchemaFor(cloud) wrapper via an if/else chain. That
dispatch no longer exists — schema() now calls adapter.schema()
directly, so only awsApiGatewaySchema() is ever used.

Confirmed by checking iamSchema.ts, written fresh against the current
catalog architecture during floci-io#145's rebase: it exports no equivalent
wrapper, just the cloud-specific schema function consumed directly by
the adapter. The wrapper isn't a convention to preserve, it's leftover
cruft.

Flagged by automated review on floci-io#169.
@thomhurst
thomhurst force-pushed the agent/aws-iam-cloud-explorer branch from 6cd839c to efd511a Compare August 13, 2026 09:32
christianvv added a commit to christianvv/floci-ui that referenced this pull request Aug 13, 2026
apigatewaySchemaFor was dead code left over from the pre-floci-io#153
schema-dispatch pattern, where CloudProxyService.schema() called a
per-service *SchemaFor(cloud) wrapper via an if/else chain. That
dispatch no longer exists — schema() now calls adapter.schema()
directly, so only awsApiGatewaySchema() is ever used.

Confirmed by checking iamSchema.ts, written fresh against the current
catalog architecture during floci-io#145's rebase: it exports no equivalent
wrapper, just the cloud-specific schema function consumed directly by
the adapter. The wrapper isn't a convention to preserve, it's leftover
cruft.

Flagged by automated review on floci-io#169.
@thomhurst
thomhurst requested a review from fredpena as a code owner August 18, 2026 05:36

@hectorvent hectorvent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the rebases and for typing the empty-create path as a RuntimeError while you were in there; that closes the last bare Error in the adapter. I rebuilt the stack at c52dc26: creating a user returns the ARN and user id, a duplicate is a typed 409, a bad name is a typed 400, and the table and "Create user" button render from the schema. CI is green and the branch is mergeable.

#162 overlaps with this one, and picking between them is mine to do; I will follow up there. No blockers from my side.

@fredpena
fredpena merged commit fc50d19 into floci-io:main Aug 30, 2026
6 checks passed
@hectorvent

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.4.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

fredpena pushed a commit to TheSaifZaman/floci-ui that referenced this pull request Sep 1, 2026
# [0.4.0](floci-io/floci-ui@0.3.0...0.4.0) (2026-09-01)

### Bug Fixes

* **ec2:** include catalog AMIs in launch selector ([floci-io#191](floci-io#191)) ([b72135d](floci-io@b72135d))

### Features

* **aws:** add CloudFormation adapter to Cloud Explorer ([floci-io#184](floci-io#184)) ([f3d6105](floci-io@f3d6105)), closes [floci-io#81](floci-io#81) [floci-io#75](floci-io#75) [floci-io#81](floci-io#81) [floci-io#75](floci-io#75) [floci-io#81](floci-io#81)
* **aws:** add EventBridge explorer ([floci-io#146](floci-io#146)) ([42944e9](floci-io@42944e9)), closes [floci-io#85](floci-io#85)
* **aws:** add IAM to Cloud Explorer ([floci-io#145](floci-io#145)) ([fc50d19](floci-io@fc50d19)), closes [floci-io#79](floci-io#79)
* **aws:** add Secrets Manager resource adapter ([floci-io#193](floci-io#193)) ([4811866](floci-io@4811866))
* **azure:** add databases and split Cosmos NoSQL ([floci-io#149](floci-io#149)) ([2e704f4](floci-io@2e704f4)), closes [floci-io#92](floci-io#92) [floci-io#67](floci-io#67) [floci-io/floci-az#138](floci-io/floci-az#138) [floci-io#143](floci-io#143)
* **azure:** add Service Bus explorer ([floci-io#144](floci-io#144)) ([a9f0d06](floci-io@a9f0d06)), closes [floci-io#89](floci-io#89)
* **eks:** Manage nodegroups and Fargate profiles via Cloud Proxy ([floci-io#194](floci-io#194)) ([3091cc9](floci-io@3091cc9)), closes [floci-io#106](floci-io#106)
* **loadbalancing:** add an AWS Elastic Load Balancing adapter ([floci-io#168](floci-io#168)) ([10d4298](floci-io@10d4298)), closes [floci-io#162](floci-io#162) [floci-io#162](floci-io#162) [floci-io#156](floci-io#156)
* **messaging:** add a messaging category with SQS and Pub/Sub ([floci-io#157](floci-io#157)) ([dfa6d1c](floci-io@dfa6d1c)), closes [floci-io#155](floci-io#155) [floci-io#155](floci-io#155) [floci-io#156](floci-io#156) [floci-io#156](floci-io#156)
* **secretsmanager:** add JSON key-value editor for secret values ([floci-io#195](floci-io#195)) ([8e88961](floci-io@8e88961)), closes [floci-io#151](floci-io#151)
* **ses:** Add AWS SES mailbox to Cloud Explorer ([floci-io#196](floci-io#196)) ([6389a56](floci-io@6389a56)), closes [floci-io#130](floci-io#130)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(aws): IAM in Cloud Explorer

3 participants