0

I need some kind of @preFilter (or, if it isnot possible than @postFilter) to filter the results of my REST API. I can not use the preFilteranotation, because I need to consider the user role. I have three different roles:

  • user the normal user, who shold only access data which he owns

  • teamleader this role should access all data of his team

  • admin who can access all data.

Because our database structure is really complex, it will be necessary, to access some other data, before I can decide if the user can access the requested data or parts of the requested data.

The snippet works only for the roles user and admin. For teamleader it will be more complex, then there will be a bunch of masterDataId which have to be connect with or.

Here is some pseudocode, hopefully its not to confusing:

public class RoleFilter {

    DimensionAttributeValueRepository dimensionAttributeValueRepository;


    public void doFilter(Collection<AllDatas> data) {
        if (user.getRole() != "admin") {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();

            DimensionAttributeValue tmpValue = dimensionAttributeValueRepository.findByChrValue(auth.getUsername());

            MasterData masterData = tmpValue.getMasterData();

            data.filter(data.masterDataId == masterData.getMasterDataID());
        }
    }
}

Update: Example

Lets say I have two users, user A is a normal user with the role "user". User B is an admin with the role "admin".

There is a Database table, in which the userData are stored. The table looks like the following.

| ID | username | name | email |

Both of them are sending a simple authenticated GET request to /userData.

Now my backend detects based on the authentication header the users and add the roles.

Nwo depending on the role, the user A should only get an answere which contains his personal data, user B should get all data which are accessible though /userData.

Response for user A:

{
   "res":[
      {
         "id":1,
         "username":"userA",
         "name":"A",
         "email":"userA@mail.com"
      }
   ]
}

Response for user B:

{
   "res":[
      {
         "id":1,
         "username":"userA",
         "name":"A",
         "email":"userA@mail.com"
      },
      {
         "id":2,
         "username":"userB",
         "name":"B",
         "email":"userB@mail.com"
      },
      {
         "id":3,
         "username":"userC",
         "name":"C",
         "email":"userC@mail.com"
      }
   ]
}
Max
  • 1,368
  • 4
  • 18
  • 43

1 Answers1

0

For your usecase I would recommend to use a custom filter and integrate it into the spring-security filter chain. Here is a tutorial, explaining it in general. You could configure your custom filter so that it checks your complex roles against the database and then overwrite the current users authentication object with a new one.

Example implementation:

public class CustomFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws IOException, ServletException {
            // HERE GOES YOUR CODE

            // Depending on the extracted authentication details of the current user, you can now overwrite the users GrantedAuthorities

            Collection<SimpleGrantedAuthority> oldAuthorities = (Collection<SimpleGrantedAuthority>)SecurityContextHolder.getContext().getAuthentication().getAuthorities();
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_TEAMLEADER");
            List<SimpleGrantedAuthority> updatedAuthorities = new ArrayList<SimpleGrantedAuthority>();
            updatedAuthorities.add(authority);
            updatedAuthorities.addAll(oldAuthorities);

            SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(     
                SecurityContextHolder.getContext().getAuthentication().getPrincipal(),      
                SecurityContextHolder.getContext().getAuthentication().getCredentials(),
                updatedAuthorities));

            chain.doFilter(request, response);
    }
}

Afterwards you can check for your roles with this statement: @PreAuthorize("hasRole('ROLE_TEAMLEADER')")

Then you can just access the users roles with the help of the spring-security-context object: SecurityContextHolder.getContext().getAuthentication().getAuthorities()

Based on its results you can now customize your answer upon the roles that are stored in this object. You could for example implement a RestCall on /userData like this:

@GetMapping("/userData")
public List<Object> getUserData() {
  List<SimpleGrantedAuthority> roles = (List<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
  SimpleGrantedAuthority authorityTeamLeader = new SimpleGrantedAuthority("ROLE_TEAMLEADER");

  List<Object> result = new ArrayList<>();

  if (roles.contains(authorityTeamLeader)) {
    result = getAllUsers();
  } else {
    result = getPersonalUser(roles);
  }

  return result;
}
git-flo
  • 1,044
  • 13
  • 23
  • Hi, thanks for your answer. But if I understand you correctly, this will not lead to a solution of my problem. I know the user roles, the problem is, I like to return a different set of data, depending on the role. – Max Oct 12 '18 at 06:34
  • Updated my answer :) – git-flo Oct 12 '18 at 07:50
  • Again thanks, but still won’t lead to a solution. let me clarify the problem a little bit more. I have that user roles and I have also implemented something similar like your code do check which kind of user sends the request. I will update my original post with an example. Thanks for trying to help me! – Max Oct 12 '18 at 08:47
  • I have added the example – Max Oct 12 '18 at 08:58
  • I have added another example implementation of a custom Get-Controller – git-flo Oct 14 '18 at 08:01