I feel a bit awkward answering my own question, but I figured out the right solution, and think it only right to add it here, so it is documented for the future in case someone stumbles over this question.
TL;DR:
My custom SecurityManager did not have the right permissions. Since it was on the callstack between the class invoking the doPrivileged(...)
-block, and the AccessController, the intersection of the privileges was no privileges at all.
Long version
The Java security model works as follows. When the AccessController verifies if a class is allowed to invoke a method or not, it looks at the permissions from the top of the callstack towards the bottom. If each entry in the callstack has the permission, then the action is allowed.
Here is an arbitrary example where everything works out fine:
+-----------+-------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------+-------------------+-----------------------+
| Some | {Read,Write} | {Read} |
| Other | {Read} | {Read} |
+-----------+-------------------+-----------------------+
Now, in the case of my question, the lower layers in the callstack have no permissions at all.
Hence we end up with a picture like this, where the query
at the top, in effect has no permissions.
+-----------+-------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------+-------------------+-----------------------+
| Query | {Read} | {} |
| Other | {} | {} |
+-----------+-------------------+-----------------------+
You get around this problem by using a doPrivileged(...)
-block. This allows the permission search through the callstack to end at the entry invoking the privileged action:
+-----------+-------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------+-------------------+-----------------------+
| Query | {Read} | {Read} |
| Other | {} | {} |
+-----------+-------------------+-----------------------+
This is why everything worked fine when I called the AccessController.checkPermission(...)
from within the query. It did have the correct permissions after all. (Un)fortunately the java API's (for backwards compatibility), always call the SecurityManager. In my case the SecurityManager had no privileges at all. Since it, in effect, was on the callstack between the query making the privileged call, and the AccessController, the net resulting permissions were none:
+-----------------+-------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------------+-------------------+-----------------------+
| SecurityManager | {} | {} |
| Query | {Read} | {Read} |
| Other | {} | {} |
+-----------------+-------------------+-----------------------+
Solution
The solution was to give the SecurityManager a base set of permissions. As a result, the permissions granted to the Query were indeed the ones needed:
+-----------------+---------------------+-----------------------+
| Callstack | Class permissions | Permissions in effect |
+-----------------+---------------------+-----------------------+
| SecurityManager | {Read,Write,Delete} | {Read} |
| Query | {Read} | {Read} |
| Other | {} | {} |
+-----------------+---------------------+-----------------------+
Phew! That was quite a mouthful! Hope this was useful to someone out there :)