How to secure your domain objects using Spring Security – Part II

NewYearsPromo-10usd300x250 In last post, we discussed how to secure domain objects using Spring’s PermissionEvaluator. In this post, we’ll take that forward, and see how to code a clean pattern for coding the access rules. Specifically, we will see how to have different PermissionChecker classes, one per domain class.

First, let’s code an abstract PermissionChecker, which’ll be the super class of all the permission checkers.

PermissionChecker

public abstract class PermissionChecker<T> {

    protected String domainClassName;

    public boolean hasPermission(User user, T domainObject, String permission) {

        switch (permission) {
            case "view":
                return hasViewPermission(user, domainObject);
            case "edit":
                return hasEditPermission(user, domainObject);
            ...
            default:
                return hasOtherPermission(user, domainObject, permission);
        }
    }

    protected boolean hasViewPermission(User user, T domainObject) {
        return false;
    }

    protected boolean hasEditPermission(User user, T domainObject) {
        return false;
    }

    ...

    protected boolean hasPermission(User user, T domainObject, String permission) {
        throw new UnsupportedOperationException(
            "Permission " + permission + " not supported for " + domainClassName);
    }

    public String getDomainClassName() {
        return domainClassName;
    }
}

As you see, PermissionChecker above has a hasPermission method, which delegates the checking to other protected methods. Those protected methods can be overridden in our implementations. For example, our EmployeePermissionChecker could look as below:

@Component
public class EmployeePermissionChecker extends PermissionChecker<Employee> {

    public EmployeePermissionChecker() {
        domainClassName = Employee.class.getSimpleName();
    }

    @Override
    public boolean hasViewPermission(User loggedInUser, Employee employee) {

        return loggedInUser != null; // all logged in users can view
    }

    @Override
    public boolean hasEditPermission(User loggedInUser, Employee employee) {

        // only department head can edit
        return employee.getDeparementHead().equals(loggedInUser);
    }
}

So, how to use these permission checkers?

How to use the permission checkers

If you noticed, we have a domainClassName property in PermissionChecker, which we have set in the constructor of EmployeePermissionChecker. This pattern would allow us to inject all the permission checkers in a map in our PermissionEvaluatorImpl, and then use those, as below:

@Component
public class PermissionEvaluatorImpl implements PermissionEvaluator {

    private Map<String, PermissionChecker> permissionCheckers;

    public PermissionEvaluatorImpl(Optional<Set<PermissionChecker>> permissionCheckers) {

        if (permissionCheckers.isPresent())

            this.permissionCheckers = permissionCheckers.get().stream().collect(
                    Collectors.toMap(PermissionChecker::getDomainClassName, Function.identity()));
        else

            this.permissionCheckers = new HashMap<>();
    }

    @Override
    public boolean hasPermission(Authentication auth,
        Object targetDomainObject, Object permission) {
 
        if (targetDomainObject == null) // if no domain object is provided,
            return true;                // let's pass, allowing the service method
                                        // to throw a more sensible error message
 
        // get the respective permission checker
        PermissionChecker permissionChecker =
            permissionCheckers.get(targetDomainObject.getClass().getSimpleName());

        if (permissionChecker == null)
            throw new UnsupportedOperationException(
                "No permission checker found for " +
                targetDomainObject.getClass().getSimpleName());
 
        // Get the User from auth
        User loggedInUser =
            (auth == null || auth.getPrincipal() == null)
                ?  null
                : (User) auth.getPrincipal();
  
        return permissionChecker.hasPermission(
            loggedInUser, targetDomainObject, (String) permission);
    }
 
    @Override
    public boolean hasPermission(Authentication authentication,
             Serializable targetId, String targetType, Object permission) {
 
        throw new UnsupportedOperationException(
                "hasPermission() by ID is not supported");
    }
}

Okay, the above would help you check for permissions on an existing object. But what about permissions for creating a new object? E.g., what to do in this case:

@PreAuthorize("what to write here?")
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public Employee createEmployee(@Valid EmployeeForm employeeForm) {

    ...
}

Well, there could be many ways to handle this. I’d usually write a method as below in the checker class:

@Component
public class EmployeePermissionChecker extends PermissionChecker<Employee> {

    ...

    public boolean hasCreatePermission() {

            // Get the User from auth
            User loggedInUser =
                (auth == null || auth.getPrincipal() == null)
                    ?  null
                    : (User) auth.getPrincipal();

        return loggedInUser != null;
    }
}

Then, I can simply use it in @PreAuthorize, as below

@PreAuthorize("@employeePermissionChecker.hasCreatePermission()")
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public Employee createEmployee(@Valid EmployeeForm employeeForm) {

    ...
}

That’s it!

If you found this helpful, please share it with your friends! Also, you might like to check out our Spring course for learning such essential patterns for developing real-world Spring applications.

Leave a Reply

Your email address will not be published. Required fields are marked *