I have the following situation: my application’s authorization mechanism is implemented using Spring security. The central class implements AccessDecisionManager and uses voters (each of which implements AccessDecisionVoter) to decide whether to grant access to some method or not. The algorithm that tallies the votes is custom:
public class PermissionManagerImpl extends AbstractAccessDecisionManager { public void decide( Authentication authentication, Object object, ConfigAttributeDefinition config) throws AccessDeniedException { Iterator<?> iter = getDecisionVoters().iterator(); boolean wasDenied = false; while (iter.hasNext()) { AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); int result = voter.vote(authentication, object, config); switch (result) { // Some tallying calculations } } if (wasDenied) { throw new AccessDeniedException('Access is denied'); } } }
Upon denying an access to some method, the client of the application is interested in obtaining an informative exception that specifies exactly why the access is denied. This implies passing some information from voters to the decision manager. Unfortunately, the only information the standard AccessDecisionVoter passes back to the decision manager is one of the possible return values (ACCESS_GRANTED, ACCESS_ABSTAIN or ACCESS_DENIED).
What is the best way to do it?
Thanks.
Well, the
AccesssDecisionVoterinterface actually returns anintin this situation. Granted, the built-in voter implementations always only return one of the three constants you mentioned (and these are what the standard access decision managers check for), but then they don’t really have anything extra to return – theRoleVoterfor instance will deny access if and only if the principal doesn’t have the required role.Since you’re using your own implementations both of the voters and the access decision manager, you have several options available as I see it:
ACCESS_GRANTED,ACCESS_ABSTAINandACCESS_DENIEDas their typical values, but treat any other integer as ‘access denied’ with an error code. Ideally have a lookup table of error codes available – essentially a poor man’s enum.ACCESS_DENIEDas usual, and set some publically accessible property (either on the voter object itself or perhaps some statically-accessible field) with the error reason. In your manager, if you get access denied from your custom voter, check the property to get the details.Authenticationbeing passed in is one of your own custom subclasses that provides a good location to set/retrieve this information.AccessDeniedException(or suitable subclass) from within your voter itself. This is not ideal as it presupposes the logic in the access decision manager; but you could either let this bubble straight up, or if needed catch it within the manager (a custom subclass would definitely be good for this) and rethrow if access really is denied (something similar to what theProviderManagerclass does with itslastExceptionvariable).None of these sticks out as the obviously correct and elegant answer, but you should be able to get something workable from whichever one is most appropriate. Since there is no explicit support within the voter framework for communicating reasons (it’s a straight boolean response fundamentally) I don’t think you can do much better.