I did not know whether to log this as a bug or an enhancement, but it feels more like a bug to me. Either in the migration documentation or in code. :-)
Describe the bug
Given 2 AuthorizationManagers/DecisionVoters:
- AffirmativeBased way of working denies access when the first DecisionVoterabstains and the second denies.
- AuthorizationManagers.anyOf(...) allows access when the first AuthorizationManagerabstains.
To Reproduce
I believe there is a small difference in the implementation between AffirmativeBasedand AuthorizationManagers.anyOf().
More precisely in handling AccessDecisionVoter.ABSTAIN vs a nullinstance of AuthorizationDecision.
AffirmativeBased.java
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException {
int deny = 0;
for (AccessDecisionVoter voter : getDecisionVoters()) {
int result = voter.vote(authentication, object, configAttributes);
switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;
case AccessDecisionVoter.ACCESS_DENIED:
deny++;
break;
default:
break; // --> Continues the loop and checks all DecisionVoters.
}
}
if (deny > 0) {
throw new AccessDeniedException(
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
}
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}
AuthorizationManagers.java
public static <T> AuthorizationManager<T> anyOf(AuthorizationManager<T>... managers) {
return (authentication, object) -> {
List<AuthorizationDecision> decisions = new ArrayList<>();
for (AuthorizationManager<T> manager : managers) {
AuthorizationDecision decision = manager.check(authentication, object);
if (decision == null || decision.isGranted()) {
return decision; // --> Shortcuts the whole decision making process. Basically says ABSTAIN == GRANTED
}
decisions.add(decision);
}
return new CompositeAuthorizationDecision(false, decisions);
};
}
Expected behavior The behavior should be the same, or the migration docs should state that the implementations are not equivalent.
Comment From: jzheaux
Hi, @KaVeKal. I agree that this is a bug in AuthorizationManagers#anyOf, but I also see a bug in the docs related to allOf. anyOf should return true only if any of the non-abstaining delegates return true. allOf should return true only if all of the non-abstaining delegates return true.
I'll update the code as well as the docs accordingly.
Also, I'm thinking that it would be good to introduce a configuration option to clarify what should be done when all delegates abstain. I've created #13085 to address that.