fix(cli): scope migrated private/protected S3 access to the caller identity#14953
fix(cli): scope migrated private/protected S3 access to the caller identity#14953ahmedhamouda78 wants to merge 4 commits into
Conversation
…entity
gen2-migration emitted Gen1 authenticated access on private/{entity_id}/* and
protected/{entity_id}/* using allow.authenticated, so the {entity_id} token was
not bound to the requesting user.
Map Gen1 authenticated access on private/ and protected/ to
allow.entity('identity') so {entity_id} resolves to the caller's Cognito
identity, matching the per-user scoping of the Gen1 configuration. protected/
retains allow.authenticated read access; public/* is unchanged.
Adds a test covering per-user scoping and updates the affected inline snapshots.
…er access mapping
Regenerate the storage/resource.ts post.generate snapshots for the migration
sample apps to reflect the updated mapping: Gen1 authenticated access on
private/ and protected/ now renders as allow.entity('identity'), with
allow.authenticated read retained on protected/. public/* is unchanged.
- Scope the test guard to the private/protected segments and assert no authenticated write/delete there. The previous negative regex stopped at the first ']' (inside the entity permission array) and never reached the authenticated call, so it could not catch a regression. - Make the pure render helpers (createAllowPattern, createEntityPattern, createResourcePattern) static. - Explain the intentional authenticated-read overlap on protected/ when the auth permission set is read-only. - Drop a redundant test comment.
sarayev
left a comment
There was a problem hiding this comment.
Review — Approve with comments ✅
Correct security fix: gen2-migration generate was emitting allow.authenticated.to([...]) for private/{entity_id}/* and protected/{entity_id}/*, granting every authenticated user the same access and defeating per-user {entity_id} scoping. Mapping them to allow.entity('identity').to([...]) (keeping allow.authenticated.to(['read']) on protected/ for Gen1 cross-user read) is right. Separate AST nodes per path + private static helpers are clean; all 8 fixture snapshots + 3 inline snapshots updated consistently.
Should-fix (test quality, not correctness)
s3.generator.test.tsnew test — use a full snapshot for positive assertions. ThetoMatch()regex +normalized.slice(indexOf(...))positives conflict with the repo guideline (toMatchInlineSnapshot()on complete output) and the slice-to-EOF is fragile (captures postRefactor/applyEscapeHatches; breaks if path ordering changes). Convert positives to onetoMatchInlineSnapshot()onwrittenFile('resource.ts'); keep thenot.toMatchnegative guards as-is.- Missing edge case: write-only auth on
protected/(authAccess: ['CREATE_AND_UPDATE']). Code unconditionally appendsallow.authenticated.to(['read'])toprotected/{entity_id}/*— correct Gen1 semantics but untested; add a test locking owner=entity('identity').to(['write'])+ public=authenticated.to(['read']).
Nits
s3.renderer.tscreateEntityPattern: multi-line JSDoc on aprivate statichelper → use a//comment per the style guide.buildAccessProperty: 3 inline comment blocks could consolidate into one.
Core logic correct and regression-safe for existing scenarios. Please run the full split e2e + all PR checks and get them green before merge.
- Convert the per-user scoping test's positive assertions to a full
toMatchInlineSnapshot() on the generated resource.ts (per repo
guideline); keep the scoped not.toMatch write/delete guards.
- Add a write-only auth edge case (authAccess CREATE_AND_UPDATE) locking
protected/ = entity('identity').to(['write']) + authenticated.to(['read']).
- s3.renderer: use // on the createEntityPattern helper and consolidate the
buildAccessProperty auth-mapping comments into one block.
|
@sarayev thanks for the thorough review. Pushed 1. Full inline snapshot for the per-user test — Replaced the 2. Write-only 3. 4. No production logic changed — all test-quality and comment-style. Local verification: |
sarayev
left a comment
There was a problem hiding this comment.
Thanks for addressing the feedback — approving, this looks good to me.
One non-blocking thing for a future pass (don't need to fix here): for read-only auth on protected/, the allow.entity('identity').to(['read']) rule is fully subsumed by the allow.authenticated.to(['read']) that also gets emitted, so it's effectively a redundant rule that slightly inflates the generated policy. It's a deliberate uniformity trade-off (and documented in the comment), just worth keeping in mind.
Description of changes
The
gen2-migration generatestorage generator rendered Gen1 authenticatedaccess on the per-user paths
private/{entity_id}/*andprotected/{entity_id}/*usingallow.authenticated. With that rule the{entity_id}token is not bound to the requesting user, so the generateddefineStorageaccess did not match the per-user scoping of the originalGen1 configuration.
This change maps Gen1 authenticated access on
private/andprotected/toallow.entity('identity'), so{entity_id}resolves to the caller's ownCognito identity — matching the Gen1 behavior.
protected/additionallykeeps
allow.authenticatedread access (Gen1 protected semantics), andpublic/*is unchanged. Group and function access mappings are unchanged.Files:
packages/amplify-cli/.../generate/amplify/storage/s3.renderer.ts— access-rule mapping + a smallentity('identity')render helper.packages/amplify-cli/.../__tests__/.../storage/s3.generator.test.ts— new coverage + updated inline snapshots.Issue #, if available
Description of how you validated changes
yarn jest s3.generator— 12 tests pass, snapshots updated.allow.entity('identity')and never grant write/delete via
allow.authenticated.authenticated-role IAM policy scopes
private/protectedobject access tothe caller's Cognito identity.
Checklist
yarn testpassesBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.