@@ -28,7 +28,7 @@ class RoutableResourceMetadataCollectionFactory implements ResourceMetadataColle
2828{
2929 private ResourceMetadataCollectionFactoryInterface $ decorated ;
3030
31- public function __construct (ResourceMetadataCollectionFactoryInterface $ decorated )
31+ public function __construct (ResourceMetadataCollectionFactoryInterface $ decorated, private readonly ? string $ securityStr = null )
3232 {
3333 $ this ->decorated = $ decorated ;
3434 }
@@ -50,12 +50,23 @@ public function create(string $resourceClass): ResourceMetadataCollection
5050 if ($ operations ) {
5151 /** @var Operation $operation */
5252 foreach ($ operations as $ i => $ operation ) {
53- if (
54- HttpOperation::METHOD_POST !== $ operation ->getMethod ()
55- && !$ operation instanceof CollectionOperationInterface
56- && !$ operation ->getSecurity ()) {
53+ if ($ operation ->getSecurity ()) {
54+ $ newOperations [$ i ] = $ operation ;
55+ continue ;
56+ }
57+
58+ if (HttpOperation::METHOD_POST === $ operation ->getMethod ()) {
59+ // POST (creation) — apply securityStr directly since the voter cannot
60+ // check the subject pre-denormalize. No restriction if securityStr is null.
61+ if ($ this ->securityStr ) {
62+ $ operation = $ operation ->withSecurity ($ this ->securityStr );
63+ }
64+ } elseif (!$ operation instanceof CollectionOperationInterface) {
65+ // Item operations (GET, PATCH, DELETE, PUT) — delegate to the routable voter
66+ // which checks the route or falls back to securityStr.
5767 $ operation = $ operation ->withSecurity (\sprintf ("is_granted('%s', object) " , AbstractRoutableVoter::READ_ROUTABLE ));
5868 }
69+
5970 $ newOperations [$ i ] = $ operation ;
6071 }
6172 }
0 commit comments