0

From a REST API, we write to the database. We want to store who was the person that triggered this change. We have this information at the controller layer. The saving happens at the repository layer (We use spring boot, and have a service layer in between for business logic).

How can I stop leaking the auditing information to the business layer? If I pass the user from the controller to the repository, every method will have to take the user, either directly or wrapped in another object.

Is there a way to avoid this? Maybe specifically with Spring?

  • 7
    If auditing is required, then it's a business process, and therefore your users are part of the business layer. No more leak. No more problem. – Tibo Feb 22 '18 at 13:57
  • @Tibo beat me to it. Your question makes no sense to me. Auditing should be a business concern, therefore the business layer absolutely must know about it. – David Arno Feb 22 '18 at 14:03
  • But the business layer is not doing anything with it. It's just passing it forward because it needs to reach the repository layer. – Can't Tell Feb 22 '18 at 14:20
  • 1
    In which case, it sounds like you have business code in the repository layer... – David Arno Feb 22 '18 at 14:24
  • @DavidAmo ok. I see your point :D – Can't Tell Feb 22 '18 at 14:30
1

While I agree that auditing can be part of the business layer, its easy to tweak your question to avoid this ambiguity.

Say for example you are logging response times by the server so you can detect if some are overloaded. This would potentially also hit a database, but a different one than your business logic.

The solution is simply to have a different repository and to add a service layer which encapsulates your business logic, separating it from the hosting layer.

This is also useful, when you have other things that you need to do, but which arent really part of the business logic.

public class MyController
{
    MyController(IServiceLayer service, ILogger logger)
    {
       ...
    }

    public myThing GetThing(string id)
    {
        //the rest service needs to log how many times its called
        logger.log(user, metric);

        //this service could be hosted anywhere, it doesn't care
        var thing = service.GetThing(id);

        //the rest service needs to return json and http error codes, 
        //not objects and exceptions
        var httpReturn = new HttpReturnObject(200, Json.Serialise(thing));
        return httpReturn;
    }
}
1

When building a Spring rest api, you can incorporate Spring Security. It is possible to configure it so that a representation of the user (for example taken from a bearer token in the requests Authorization header) is stored in the Spring security context, which can then be retrieved in the data layer by calling something like:

MyUserDetails getUserFromSecurityContext(){
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null || !authentication.isAuthenticated()) {
      return null;
    }
    return ((MyUserDetails) authentication.getPrincipal()).getUser();
}

In fact your business layer could/should do this as well(*), but not necessarily so. The 'trick' here is using the static SecurityContextHolder, thereby circumventing the service layer so that it is not necessary to pass the user as an argument through all intermediate layers, since it is implicitly available in the static SecurityContextHolder.

(*) You can use the authenticated user in deciding if the user has access to a service method or not, for example by configuring methodsecurity

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.