From b174cf606893e7cea05d8f87acb530d529903f96 Mon Sep 17 00:00:00 2001 From: zichun Date: Thu, 9 Apr 2026 00:16:57 +0800 Subject: [PATCH] feat(security): annotate catalog + partners endpoints with @RequirePermission --- pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/ItemController.kt | 7 +++++++ pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/UomController.kt | 6 ++++++ pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/AddressController.kt | 6 ++++++ pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/ContactController.kt | 17 ++++++++++++----- pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/PartnerController.kt | 5 +++++ 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/ItemController.kt b/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/ItemController.kt index 224116c..d7f5add 100644 --- a/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/ItemController.kt +++ b/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/ItemController.kt @@ -19,6 +19,7 @@ import org.vibeerp.pbc.catalog.application.ItemService import org.vibeerp.pbc.catalog.application.UpdateItemCommand import org.vibeerp.pbc.catalog.domain.Item import org.vibeerp.pbc.catalog.domain.ItemType +import org.vibeerp.platform.security.authz.RequirePermission import java.util.UUID /** @@ -40,16 +41,19 @@ class ItemController( ) { @GetMapping + @RequirePermission("catalog.item.read") fun list(): List = itemService.list().map { it.toResponse() } @GetMapping("/{id}") + @RequirePermission("catalog.item.read") fun get(@PathVariable id: UUID): ResponseEntity { val item = itemService.findById(id) ?: return ResponseEntity.notFound().build() return ResponseEntity.ok(item.toResponse()) } @GetMapping("/by-code/{code}") + @RequirePermission("catalog.item.read") fun getByCode(@PathVariable code: String): ResponseEntity { val item = itemService.findByCode(code) ?: return ResponseEntity.notFound().build() return ResponseEntity.ok(item.toResponse()) @@ -57,6 +61,7 @@ class ItemController( @PostMapping @ResponseStatus(HttpStatus.CREATED) + @RequirePermission("catalog.item.create") fun create(@RequestBody @Valid request: CreateItemRequest): ItemResponse = itemService.create( CreateItemCommand( @@ -70,6 +75,7 @@ class ItemController( ).toResponse() @PatchMapping("/{id}") + @RequirePermission("catalog.item.update") fun update( @PathVariable id: UUID, @RequestBody @Valid request: UpdateItemRequest, @@ -86,6 +92,7 @@ class ItemController( @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) + @RequirePermission("catalog.item.deactivate") fun deactivate(@PathVariable id: UUID) { itemService.deactivate(id) } diff --git a/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/UomController.kt b/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/UomController.kt index 27b10cc..b4e3181 100644 --- a/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/UomController.kt +++ b/pbc/pbc-catalog/src/main/kotlin/org/vibeerp/pbc/catalog/http/UomController.kt @@ -17,6 +17,7 @@ import org.vibeerp.pbc.catalog.application.CreateUomCommand import org.vibeerp.pbc.catalog.application.UomService import org.vibeerp.pbc.catalog.application.UpdateUomCommand import org.vibeerp.pbc.catalog.domain.Uom +import org.vibeerp.platform.security.authz.RequirePermission import java.util.UUID /** @@ -38,16 +39,19 @@ class UomController( ) { @GetMapping + @RequirePermission("catalog.uom.read") fun list(): List = uomService.list().map { it.toResponse() } @GetMapping("/{id}") + @RequirePermission("catalog.uom.read") fun get(@PathVariable id: UUID): ResponseEntity { val uom = uomService.findById(id) ?: return ResponseEntity.notFound().build() return ResponseEntity.ok(uom.toResponse()) } @GetMapping("/by-code/{code}") + @RequirePermission("catalog.uom.read") fun getByCode(@PathVariable code: String): ResponseEntity { val uom = uomService.findByCode(code) ?: return ResponseEntity.notFound().build() return ResponseEntity.ok(uom.toResponse()) @@ -55,6 +59,7 @@ class UomController( @PostMapping @ResponseStatus(HttpStatus.CREATED) + @RequirePermission("catalog.uom.create") fun create(@RequestBody @Valid request: CreateUomRequest): UomResponse = uomService.create( CreateUomCommand( @@ -65,6 +70,7 @@ class UomController( ).toResponse() @PatchMapping("/{id}") + @RequirePermission("catalog.uom.update") fun update( @PathVariable id: UUID, @RequestBody @Valid request: UpdateUomRequest, diff --git a/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/AddressController.kt b/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/AddressController.kt index 2fa45b6..089ef85 100644 --- a/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/AddressController.kt +++ b/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/AddressController.kt @@ -19,6 +19,7 @@ import org.vibeerp.pbc.partners.application.CreateAddressCommand import org.vibeerp.pbc.partners.application.UpdateAddressCommand import org.vibeerp.pbc.partners.domain.Address import org.vibeerp.pbc.partners.domain.AddressType +import org.vibeerp.platform.security.authz.RequirePermission import java.util.UUID /** @@ -38,10 +39,12 @@ class AddressController( ) { @GetMapping + @RequirePermission("partners.address.read") fun list(@PathVariable partnerId: UUID): List = addressService.listFor(partnerId).map { it.toResponse() } @GetMapping("/{id}") + @RequirePermission("partners.address.read") fun get( @PathVariable partnerId: UUID, @PathVariable id: UUID, @@ -53,6 +56,7 @@ class AddressController( @PostMapping @ResponseStatus(HttpStatus.CREATED) + @RequirePermission("partners.address.create") fun create( @PathVariable partnerId: UUID, @RequestBody @Valid request: CreateAddressRequest, @@ -72,6 +76,7 @@ class AddressController( ).toResponse() @PatchMapping("/{id}") + @RequirePermission("partners.address.update") fun update( @PathVariable partnerId: UUID, @PathVariable id: UUID, @@ -93,6 +98,7 @@ class AddressController( @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) + @RequirePermission("partners.address.delete") fun delete( @PathVariable partnerId: UUID, @PathVariable id: UUID, diff --git a/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/ContactController.kt b/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/ContactController.kt index ebaa47b..1acb9f9 100644 --- a/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/ContactController.kt +++ b/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/ContactController.kt @@ -18,6 +18,7 @@ import org.vibeerp.pbc.partners.application.ContactService import org.vibeerp.pbc.partners.application.CreateContactCommand import org.vibeerp.pbc.partners.application.UpdateContactCommand import org.vibeerp.pbc.partners.domain.Contact +import org.vibeerp.platform.security.authz.RequirePermission import java.util.UUID /** @@ -28,11 +29,12 @@ import java.util.UUID * relationship — every operation is implicitly scoped to a specific * partner. * - * **PII boundary.** Contact data is personal information. Once the - * permission system lands (P4.3) this controller will be guarded by - * `partners.contact.read` etc. Until then, plain authentication is the - * only gate, and that is a known short-term posture, NOT the long-term - * one — see the metadata YAML for the planned permission keys. + * **PII boundary.** Contact data is personal information. Every + * endpoint is guarded by a dedicated `partners.contact.*` permission + * so role admin can grant read/write access independently of the + * wider `partners.partner.*` permissions. This is the stronger + * posture that the earlier "TODO: annotate once P4.3 lands" note + * referred to; P4.3 is live and the annotations are now in place. */ @RestController @RequestMapping("/api/v1/partners/partners/{partnerId}/contacts") @@ -41,10 +43,12 @@ class ContactController( ) { @GetMapping + @RequirePermission("partners.contact.read") fun list(@PathVariable partnerId: UUID): List = contactService.listFor(partnerId).map { it.toResponse() } @GetMapping("/{id}") + @RequirePermission("partners.contact.read") fun get( @PathVariable partnerId: UUID, @PathVariable id: UUID, @@ -56,6 +60,7 @@ class ContactController( @PostMapping @ResponseStatus(HttpStatus.CREATED) + @RequirePermission("partners.contact.create") fun create( @PathVariable partnerId: UUID, @RequestBody @Valid request: CreateContactRequest, @@ -72,6 +77,7 @@ class ContactController( ).toResponse() @PatchMapping("/{id}") + @RequirePermission("partners.contact.update") fun update( @PathVariable partnerId: UUID, @PathVariable id: UUID, @@ -90,6 +96,7 @@ class ContactController( @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) + @RequirePermission("partners.contact.deactivate") fun deactivate( @PathVariable partnerId: UUID, @PathVariable id: UUID, diff --git a/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/PartnerController.kt b/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/PartnerController.kt index 28d5a11..0d16fe4 100644 --- a/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/PartnerController.kt +++ b/pbc/pbc-partners/src/main/kotlin/org/vibeerp/pbc/partners/http/PartnerController.kt @@ -40,16 +40,19 @@ class PartnerController( ) { @GetMapping + @RequirePermission("partners.partner.read") fun list(): List = partnerService.list().map { it.toResponse(partnerService) } @GetMapping("/{id}") + @RequirePermission("partners.partner.read") fun get(@PathVariable id: UUID): ResponseEntity { val partner = partnerService.findById(id) ?: return ResponseEntity.notFound().build() return ResponseEntity.ok(partner.toResponse(partnerService)) } @GetMapping("/by-code/{code}") + @RequirePermission("partners.partner.read") fun getByCode(@PathVariable code: String): ResponseEntity { val partner = partnerService.findByCode(code) ?: return ResponseEntity.notFound().build() return ResponseEntity.ok(partner.toResponse(partnerService)) @@ -57,6 +60,7 @@ class PartnerController( @PostMapping @ResponseStatus(HttpStatus.CREATED) + @RequirePermission("partners.partner.create") fun create(@RequestBody @Valid request: CreatePartnerRequest): PartnerResponse = partnerService.create( CreatePartnerCommand( @@ -73,6 +77,7 @@ class PartnerController( ).toResponse(partnerService) @PatchMapping("/{id}") + @RequirePermission("partners.partner.update") fun update( @PathVariable id: UUID, @RequestBody @Valid request: UpdatePartnerRequest, -- libgit2 0.22.2