Release: Phase 17 — Admin API + JWT roles + pagination (#60) - #76
Merged
Conversation
…hase 17) Access tokens now carry a `roles` claim (default ["USER"]); the auth filter maps each role to a ROLE_* GrantedAuthority so method security can gate on it. @EnableMethodSecurity turns on @PreAuthorize. Users gain a denormalised comma-separated `roles` column (Flyway V14), which also seeds one admin (admin@shopsphere.local, roles USER,ADMIN) — dev-only password, documented for rotation. Absent/empty roles normalise to ["USER"] so pre-existing tokens still authenticate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POST/PUT/DELETE /api/v1/admin/products, class-level @PreAuthorize("hasRole('ADMIN')"). Authenticated non-admin → 403, anonymous → 401 (security chain), seeded admin → create/edit/delete. AdminProductIT covers all four. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GET /api/v1/products takes optional Pageable; no params → first page of 20 sorted by name (backward-compatible default). Oversized ?size= is clamped to 100 via spring.data.web.pageable.max-page-size, not rejected. Response is a stable PagedResponse envelope (content + page metadata) defined explicitly rather than serialising Spring's PageImpl. ProductsEndpointIT updated to the envelope + page-size/clamp cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e 17) Records the roles model (token claim + ROLE_* authorities; denormalised comma-separated column over a join table, YAGNI), the guarded /admin product controller (403 vs 401), and the pagination envelope (stable DTO over PageImpl; oversize clamped not rejected). Honest limits: token-carried roles → up to 15-min revocation lag; seeded admin password is dev-only. Image-upload guard deferred with Phase 16. Cites DDD/PoEAA/APoSD/XP/PragProg. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 17: authorization (roles), an operator-only product API, and pagination on the public product list. No change to the shopper-facing read/checkout flows.
What shipped
rolesclaim (default["USER"]); the auth filter maps each to aROLE_*authority;@EnableMethodSecurityenables@PreAuthorize. Users gain a denormalised comma-separatedrolescolumn (FlywayV14), which also seeds one admin (admin@shopsphere.local,USER,ADMIN; dev-only password, documented for rotation).POST/PUT/DELETE /api/v1/admin/productsin a dedicatedAdminProductController, class-level@PreAuthorize("hasRole('ADMIN')"). Authenticated non-admin → 403, anonymous → 401, seeded admin → success.GET /api/v1/products?page=&size=; no params → first 20 by name; oversizedsizeclamped to 100; response is a stablePagedResponseenvelope (content + page metadata), not Spring'sPageImpl.Acceptance criteria (#60)
USER,ADMIN) —V14(issue saidV11; global Flyway timeline was atV13)roles: String[]claim for all users; default["USER"]POST/PUT/DELETE /api/v1/admin/productsguarded byhasRole('ADMIN')AdminProductITGET /products?page=0&size=Nreturns ≤ N with page metadataGET /products?size=200clamped to 100 (documented in ADR-0017)GET /productsreturns the default first page (backward-compatible default; body is now a paged envelope — see ADR-0017)mvn verifygreen (105 tests)POST /api/v1/admin/products/{id}/imageguard — that endpoint isn't built yet (ADR-0017)Closes #60.
🤖 Generated with Claude Code