0

TL;DR: I've got an @Around-Interceptor using AspectJ that works fine on any @RestController but does not get called within any other @Service

AspectJ is activated using

@Configurable
@EnableAspectJAutoProxy
class AspectJConfig

This is my Aspect:

@Aspect
@Component
class EntitlementAspect(
    private val userService: UserService,
) {

    @Around("@annotation(RequiresEntitlementsAnyOf)")
    @Throws(EPTException::class)
    fun checkEntitlementsAnyOf(joinPoint: ProceedingJoinPoint): Any? {
        val signature = joinPoint.signature as MethodSignature
        val method = signature.method
        val annotation = method.getAnnotation(RequiresEntitlementsAnyOf::class.java)
        val entitlements = annotation.entitlements

        val user = userService.getCurrentUser()
        if (user.hasAnyEntitlementOf(*entitlements))
            return joinPoint.proceed()
        else throw ErrorCodeException(HttpStatus.FORBIDDEN, ErrorCodes.MISSING_PERMISSION)
    }
}

This does perfectly work on my Rest controller:

@RestController
class EcuVersionController(
    private val myTestService: TestService
) : BaseController() {

    @PostMapping
    @RequiresEntitlementsAnyOf([Entitlement.DEVELOPER])
    fun supply(@RequestParam id: UUID) {
        myTestService.doTest()
    }
}

However, when using the exact same annotation within the TestService that is called anyways, the aspect does not get called (checked using Debugger)

@RestController
class EcuVersionController(
    private val myTestService: TestService
) : BaseController() {

    @PostMapping
    fun supply(@RequestParam id: UUID) {
        myTestService.doTest()
    }
}


@Service
class TestService{
    fun doTest() {
        thisMethodShouldNotBeCalled()
    }

    @RequiresEntitlementsAnyOf([Entitlement.DEVELOPER])
    fun thisMethodShouldNotBeCalled() {
        // but it is called...
    }
}

I could not find any information about restrictions based on the containing class. Afaik, the aspect @annotation(RequiresEntitlementsAnyOf) should work for ANY method that has the annotation RequiresEntitlementAnyOf, no matter what kind of bean the method is contained in. Any ideas?

FeXseven
  • 77
  • 6
  • 1
    Is there a reason you are reinventing Spring Security, I would suggest to use that instead. That being said you are internally calling a method, as spring uses proxies this method call doesn't pass through the proxy and your annotation doesn't apply. If you directly call `thisMethodShouldNotBeCalled` from the controller it would work. – M. Deinum Mar 07 '23 at 10:18
  • We actually don't reinvent spring security. My example was broken down to the relevant things but in fact, this endpoint can be called by any user without entitlements but it checks for a lot of things and only if one of them fail, an admin or a developer can "force" the execution. Is there any way to hint the proxy to also check internal calls or something? Otherwise I must move logic out of services and into controllers or just don't use the aspect at all – FeXseven Mar 07 '23 at 10:25
  • You can inject the object into it self and do a self invocation which makes it an external call again. Or use loadtime or compile time weaving with full blown aspectj. What you are experiencing is a limitation of proxy use (it has always been this way and isn't limited to Spring but goes for everything that uses a proxy, even back in the EJB days you had this issue and needed self invocation). – M. Deinum Mar 07 '23 at 10:37
  • Thanks for the explanation! This makes totally sense. But I think, for the little thing I am trying to achieve, your suggestions might be too heavy. I think, I will stick to the manual entitlement-check. – FeXseven Mar 07 '23 at 11:00
  • How is self injection too heavy? Inject the object into itself, call the method on it. – M. Deinum Mar 07 '23 at 12:13

0 Answers0