Describe the bug
Referencing parameter names in a @PreAuthorize("#x...") fails when using kotlin coroutines.
@PreAuthorize("#action.getUserId() == 1")
suspend fun save(action: Action)
Fails with:
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method getUserId() cannot be found on type java.lang.Object[]
Because in org.springframework.expression.spel.ast.MethodReference the value/targetObject is not of the expected Type Action but of type Object[] holding
[0] = "Action(userId=1)"
[1] = "Continuation at com.example.demoActionServiceTest$save...."
To Reproduce See sample README.
Expected behavior I expect to be able to reference parameters by their name.
Sample
https://github.com/RobertHeim/spring-security-bug-preauth-coroutines
Note that the sample uses SNAPSHOT, but the RC1 has the same bug.
Workaround
First, recognize that the problem only occurs when referencing the last parameter (because this is the one "transformed" to an object in order to hold the argument ([0]) as well as the coroutine continuation ([1])).
Adding [0] works:
@PreAuthorize("#action[0].getUserId() == 1")
Also removing the suspend and returning Mono works as well:
@PreAuthorize("#action.getUserId() == 1")
fun save(action: Action) : Mono<Unit>
Comment From: eleftherias
Thanks for trying out the coroutines support @RobertHeim!
This issue is broader than Spring Security and occurs whenever a SpEL expression references a suspending function parameter. I've created https://github.com/spring-projects/spring-framework/issues/26867 to capture that scenario. We will wait until the Framework issue is resolved.
Comment From: RobertHeim
@eleftherias Thank you!
FYI regarding the workarounds: the indexing is only required for the last parameter (because this is the one "transformed" to an object in order to hold the argument ([0]) as well as the coroutine continuation ([1])).
Comment From: mayank-upswing
I have figured out a temporary workaround while this issue is still up: just add a Param("x") annotation to the required variable. It can then be access as "#x"
Comment From: sdeleuze
Looks like fixed on Spring Framework side with Kotlin 1.7+ as far as I can tell, see https://github.com/spring-projects/spring-framework/issues/26867, if not please comment on the Framework issue.