Moves the existing virtual-thread/context-propagation project into context-propagation/ and adds method-security/ for the Spring Security 7 method-security article: nine runnable demos, fourteen assertions, and every transcript the article quotes, regenerated by scripts/run-all.sh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSrsDSRKVsY588yFiMJMo9
59 lines
4.3 KiB
Plaintext
59 lines
4.3 KiB
Plaintext
==============================================================================
|
|
Demo 5 -- filterObject: mutability, container types, and the silent no-op
|
|
==============================================================================
|
|
SLF4J(W): No SLF4J providers were found.
|
|
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
|
|
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
|
|
|
|
@PreFilter needs a MUTABLE argument -- and does not tell you when it is not
|
|
---------------------------------------------------------------------------
|
|
method body saw: [Account[1,alice,100], Account[3,alice,300]]
|
|
new ArrayList<>(..) ALLOWED -> (void)
|
|
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
|
|
List.of(..) (immutable) ALLOWED -> (void)
|
|
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
|
|
List.copyOf(..) (immutable) ALLOWED -> (void)
|
|
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
|
|
Arrays.asList(..) ALLOWED -> (void)
|
|
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
|
|
Collections.unmodifiableList(..) ALLOWED -> (void)
|
|
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
|
|
stream().toList() (unmodifiable since 16) ALLOWED -> (void)
|
|
method body saw: [Account[1,alice,100], Account[3,alice,300]]
|
|
stream().collect(toList()) (ArrayList) ALLOWED -> (void)
|
|
Account[] (arrays rejected outright) DENIED -> IllegalStateException: Pre-filtering on array types is not supported. Using a Collection will solve this problem.
|
|
|
|
Read the second and third lines again: bob's account reached the method
|
|
body. @PreFilter filters by CLEARING the caller's collection and adding
|
|
the survivors back. On an immutable list that throws, and
|
|
DefaultMethodSecurityExpressionHandler.filterCollection catches the
|
|
UnsupportedOperationException and returns a fresh list instead -- which
|
|
PreFilterAuthorizationMethodInterceptor.invoke then discards, because it
|
|
ignores filter()'s return value entirely. No exception, no WARN, no 403.
|
|
|
|
The only trace it leaves (same call, logger at TRACE)
|
|
-----------------------------------------------------
|
|
method body saw: [Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
|
|
List.of(..) with TRACE on ALLOWED -> (void)
|
|
|
|
What @PostFilter accepts as a return type
|
|
-----------------------------------------
|
|
List<Account> ALLOWED -> [Account[1,alice,100], Account[3,alice,300]]
|
|
Account[] ALLOWED -> [Account[1,alice,100], Account[3,alice,300]]
|
|
Stream<Account> (collected here) ALLOWED -> [alice, alice]
|
|
Map<String, Account> ALLOWED -> {acct-1=Account[1,alice,100], acct-3=Account[3,alice,300]}
|
|
Optional<Account> (alice's) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Optional[Account[1,alice,100]]
|
|
Optional<Account> (bob's) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Optional[Account[2,bob,200]]
|
|
List.of(..) (immutable return) ALLOWED -> [Account[1,alice,100], Account[3,alice,300]]
|
|
Ledger (a type Spring Security does not know) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Ledger[Account[1,alice,100], Account[2,bob,200], Account[3,alice,300]]
|
|
Page<Account> (real Spring Data PageImpl) DENIED -> IllegalArgumentException: Filter target must be a collection, array, map or stream type, but was Page 1 of 1 containing com.ankurm.methodsec.Account instances
|
|
|
|
Identity: does @PostFilter hand back the same object?
|
|
-----------------------------------------------------
|
|
returned == the list the method returned : true
|
|
the method's own list, after filtering : [Account[1,alice,100], Account[3,alice,300]]
|
|
|
|
@PostFilter mutates the returned collection in place and hands the same
|
|
reference back. If that collection is a cached or shared instance, you
|
|
have just deleted rows from it for every future caller.
|