Skip to content

feat(auth): resolve principal up front and attach to AwsRequest#391

Merged
vieiralucas merged 3 commits intomainfrom
worktree-sigv4-iam-batch4-principal
Apr 14, 2026
Merged

feat(auth): resolve principal up front and attach to AwsRequest#391
vieiralucas merged 3 commits intomainfrom
worktree-sigv4-iam-batch4-principal

Conversation

@vieiralucas
Copy link
Copy Markdown
Member

@vieiralucas vieiralucas commented Apr 14, 2026

Summary

Batch 4 of 9. Introduces the Principal type and plumbs the resolved caller identity through AwsRequest so service handlers can make identity decisions without re-parsing the Authorization header. Prereq for batch 5/6 where the IAM evaluator needs to know the caller.

  • New Principal struct + PrincipalType enum in fakecloud-core::auth (User / AssumedRole / FederatedUser / Root) with an is_root helper and an ARN classifier.
  • ResolvedCredential now carries a Principal value instead of flat arn/user_id/account_id fields; accessor methods preserve batch 3's call sites.
  • IamCredentialResolver classifies the principal type from the stored ARN shape so STS temporary credentials from AssumeRole/AssumeRoleWithSAML/AssumeRoleWithWebIdentity surface as AssumedRole, and GetFederationToken credentials surface as FederatedUser.
  • AwsRequest.principal: Option<Principal> populated by dispatch via a single up-front resolver call. SigV4 verification reuses the same lookup instead of calling resolve() twice.
  • StsService::get_caller_identity refactored to use req.principal first, removing ~30 lines of duplicated AKID-walking logic.
  • account_id always sourced from the credential itself (Feature proposal: multi-account isolation within a single fakecloud instance #381 note) — multi-account isolation becomes a state-partitioning change rather than a cross-cutting rewrite.

Decisions

  • Added accessor methods (principal_arn() / user_id() / account_id()) on ResolvedCredential rather than duplicating the fields, keeping the struct honest and batch 3 call sites unchanged.
  • PrincipalType::from_arn is best-effort and falls back to Root for unparseable ARNs. This matches how AWS treats unauthenticated callers — they resolve to the account root.
  • The ~18 unchanged service crates had their test helpers and internal AwsRequest proxies bulk-updated to supply principal: None / req.principal.clone(). No behavior change for those services.

Test plan

  • cargo test -p fakecloud-core — 46 unit tests including 3 new auth:: cases (PrincipalType::from_arn, Principal::is_root, ResolvedCredential accessor forwarding)
  • cargo test -p fakecloud-iam credential_resolver — 3 tests including a new classifies_sts_assumed_role_principal
  • cargo test -p fakecloud-e2e --test iam — 57 tests, GetCallerIdentity still round-trips IAM users and STS temp credentials via the new req.principal path
  • cargo test -p fakecloud-e2e --test sigv4_verification — 6 tests, SigV4 path uses the shared resolver lookup
  • cargo test -p fakecloud-conformance — no regressions
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo fmt --check clean

Summary by cubic

Resolve the caller’s identity once per request and attach a Principal to AwsRequest, so handlers can make identity-based decisions without re-parsing Authorization. Harden ARN classification so unknown shapes are never treated as root.

  • New Features

    • Added Principal and PrincipalType to fakecloud-core::auth with is_root and an ARN classifier; introduced Unknown so unrecognized ARNs aren’t treated as root.
    • ResolvedCredential now carries a Principal; added principal_arn(), user_id(), and account_id() accessors.
    • AwsRequest.principal: Option<Principal> is set by dispatch; account ID is sourced from the credential.
    • IamCredentialResolver classifies principals (User / AssumedRole / FederatedUser / Root) and returns Unknown for unrecognized shapes; Root requires an explicit :root ARN.
  • Refactors

    • SigV4 verification reuses the resolved credential instead of resolving twice.
    • StsService::get_caller_identity reads req.principal and falls back to account root only when needed.
    • Updated service test helpers to pass principal: None; no behavior changes.
    • Ran rustfmt; no behavior changes.

Written for commit 4e84e52. Summary will update on new commits.

Introduces the Principal type and plumbs the resolved caller identity
through AwsRequest so service handlers can make identity decisions
without re-parsing the Authorization header.

- New Principal struct + PrincipalType enum in fakecloud-core::auth
  (User / AssumedRole / FederatedUser / Root) with an is_root helper
  and PrincipalType::from_arn classifier.
- ResolvedCredential now carries a Principal value instead of flat
  arn/user_id/account_id fields; accessor methods preserve batch 3's
  call sites.
- IamCredentialResolver classifies the principal type from the stored
  ARN shape so STS temporary credentials surface as AssumedRole /
  FederatedUser automatically.
- AwsRequest.principal: Option<Principal> populated by dispatch via a
  single up-front resolver call. SigV4 verification reuses the same
  lookup instead of calling resolve() twice.
- STS GetCallerIdentity refactored to use req.principal first, falling
  back to the account-root identity only when the caller is the root
  bypass or didn't present credentials. Removes ~30 lines of
  duplicated AKID-walking logic.
- Account id is always sourced from the credential itself (#381
  note), priming multi-account isolation without implementing it.

Tests:
- 3 new auth:: unit tests (PrincipalType::from_arn classifier,
  Principal::is_root, ResolvedCredential accessor forwarding).
- 1 new credential_resolver:: test classifying STS assumed-role
  temp credentials.
- All existing STS e2e tests green via the refactored handler.
- All existing SigV4 verification e2e tests green.
- 22 unchanged service crates had their test helpers bulk-updated
  to supply principal: None / req.principal.clone().
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 25 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/fakecloud-core/src/auth.rs">

<violation number="1" location="crates/fakecloud-core/src/auth.rs:56">
P2: Fallback to `PrincipalType::Root` for unrecognized ARNs makes malformed or unexpected ARNs act as root, which can bypass IAM evaluation. Root should only be returned for explicit `...:root` ARNs.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/fakecloud-core/src/auth.rs Outdated
Identified by cubic on PR #391: PrincipalType::from_arn used Root as
the catch-all fallback, but Principal::is_root short-circuits IAM
enforcement. A malformed or unexpected ARN would silently grant root
permissions during policy evaluation.

- Added PrincipalType::Unknown for ARNs that don't match any
  well-known shape.
- from_arn now requires an explicit ':root' suffix to return Root.
  Garbage / empty / unrecognized ARNs return Unknown, which is not
  treated as root by is_root() and so flows through the policy
  evaluator like any other non-root principal.
- New regression test asserting empty / garbage / weird-IAM ARNs
  resolve to Unknown and that an Unknown Principal is not is_root().
@vieiralucas vieiralucas merged commit e302274 into main Apr 14, 2026
35 checks passed
@vieiralucas vieiralucas deleted the worktree-sigv4-iam-batch4-principal branch April 14, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant