Subject#doAs
vs Subject#doAsPrivileged
The default authorization algorithm employed by the AccessController
is based on Permission
intersection: If all of an AccessControlContext
's ProtectionDomain
s, potentially combined with a Subject
's Principal
s, have, statically and/or as per the Policy
in effect, the Permission
being checked, the evaluation succeeds; otherwise it fails.
Subject#doAs
does not work in your case because your Permission
is grant
ed to the combination of your ProtectionDomain
and your Principal
, but not to the domain itself. Specifically, at the time of the AccessController#checkPermission(customPermission)
invocation, the effective AccessControlContext
included following relevant (as far as Permission
evaluation is concerned) frames:
Frame # | ProtectionDomain | Permissions
--------+---------------------------+---------------------------------------------
2 | "file:./bin/" | { CustomPermission("someMethod"),
| + CustomPrincipal("user") | permissions statically assigned by default
| | by the ClassLoader }
--------+---------------------------+---------------------------------------------
1 | "file:./bin/" | { AuthPermission(
| | "createLoginContext.MyLoginModule"),
| | AuthPermission("doAs"), default as above }
--------+---------------------------+---------------------------------------------
The intersection of those frames' permissions does of course not include the desired CustomPermission
.
Subject#doAsPrivileged
, when given a null AccessControlContext
, on the other hand, does the trick, because it "trims" the effective context's stack to its top-most frame, i.e., the one from which doAsPrivileged
gets invoked. What actually happens is that the null
(blank) context gets treated by the AccessController
as if it were a context whose permission evaluation yields AllPermissions
; in other words:
AllPermissions
⋂ permissionsframe2 = { CustomPermission("someMethod")
, default ones } ,
which is (save for the minimal set of seemingly extraneous statically-assigned Permnission
s) the desired outcome
Of course, in cases where such potentially arbitrary privilege escalation is undesired, a custom context, whose encapsulated domains' permissions express the maximum set of privileges you are willing to grant (to e.g. some Subject
), can be passed to doAsPrivileged
instead of the null
one.
Why the Principal
implementation must override equals(Object)
The following stack trace snippet illustrates why:
at java.lang.Thread.dumpStack(Thread.java:1329)
at com.foo.bar.PrincipalImpl.equals(PrincipalImpl.java:53)
at javax.security.auth.Subject$SecureSet.contains(Subject.java:1201)
at java.util.Collections$SynchronizedCollection.contains(Collections.java:2021)
at java.security.Principal.implies(Principal.java:92)
at sun.security.provider.PolicyFile.addPermissions(PolicyFile.java:1374)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1228)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1191)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1132)
at sun.security.provider.PolicyFile.implies(PolicyFile.java:1086)
at java.security.ProtectionDomain.implies(ProtectionDomain.java:281)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:450)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
...
Further reading: