View Javadoc
1   package de.dlr.shepard.common.filters;
2   
3   import de.dlr.shepard.auth.security.JWTPrincipal;
4   import de.dlr.shepard.auth.security.UserLastSeenCache;
5   import de.dlr.shepard.auth.security.Userinfo;
6   import de.dlr.shepard.auth.security.UserinfoService;
7   import de.dlr.shepard.auth.users.entities.User;
8   import de.dlr.shepard.auth.users.services.UserService;
9   import de.dlr.shepard.common.exceptions.ApiError;
10  import de.dlr.shepard.common.exceptions.ShepardProcessingException;
11  import io.quarkus.logging.Log;
12  import jakarta.annotation.Priority;
13  import jakarta.enterprise.context.ApplicationScoped;
14  import jakarta.inject.Inject;
15  import jakarta.ws.rs.Priorities;
16  import jakarta.ws.rs.container.ContainerRequestContext;
17  import jakarta.ws.rs.container.ContainerRequestFilter;
18  import jakarta.ws.rs.core.HttpHeaders;
19  import jakarta.ws.rs.core.Response;
20  import jakarta.ws.rs.core.Response.Status;
21  import jakarta.ws.rs.ext.Provider;
22  import java.io.IOException;
23  
24  @Provider
25  @Priority(Priorities.AUTHENTICATION + 1)
26  @ApplicationScoped
27  public class UserFilter implements ContainerRequestFilter {
28  
29    @Inject
30    UserLastSeenCache userLastSeenCache;
31  
32    @Inject
33    UserService userService;
34  
35    @Inject
36    UserinfoService userInfoService;
37  
38    @Override
39    public void filter(ContainerRequestContext requestContext) throws IOException {
40      if (PublicEndpointRegistry.isRequestPathPublic(requestContext)) return;
41      var principal = requestContext.getSecurityContext().getUserPrincipal();
42      if (!(principal instanceof JWTPrincipal)) {
43        Log.warnf("Unknown principal %s", principal);
44        abort(requestContext, "User could not be read from the request context");
45        return;
46      }
47      var jwtPrincipal = (JWTPrincipal) principal;
48      var header = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
49      if (header != null && header.startsWith("Bearer ") && !userLastSeenCache.isKeyCached(jwtPrincipal.getUsername())) {
50        User user;
51        Userinfo userinfo;
52        try {
53          userinfo = userInfoService.fetchUserinfo(header);
54        } catch (ShepardProcessingException e) {
55          abort(requestContext, "User info could not be retrieved");
56          return;
57        }
58        user = parseUserFromUserinfo(userinfo);
59        if (!jwtPrincipal.getUsername().equals(user.getUsername())) {
60          Log.warn("The usernames from the access token and the userinfo response do not match");
61          abort(requestContext, "The usernames from the access token and the userinfo response do not match");
62          return;
63        }
64        var created = userService.createOrUpdateUser(user);
65        if (created == null) {
66          Log.warn("The user could not be updated or created");
67          abort(requestContext, "The user could not be updated or created");
68          return;
69        }
70        userLastSeenCache.cacheKey(jwtPrincipal.getUsername());
71      }
72    }
73  
74    private void abort(ContainerRequestContext requestContext, String reason) {
75      requestContext.abortWith(
76        Response.status(Status.UNAUTHORIZED)
77          .entity(new ApiError(Status.UNAUTHORIZED.getStatusCode(), "AuthenticationException", reason))
78          .build()
79      );
80    }
81  
82    private User parseUserFromUserinfo(Userinfo userinfo) {
83      // We only want the last part of the subject, since this is usually a human
84      // readable user name
85      var splitted = userinfo.getSub().split(":");
86      String username = splitted[splitted.length - 1];
87  
88      User user = new User(username, userinfo.getGivenName(), userinfo.getFamilyName(), userinfo.getEmail());
89      return user;
90    }
91  }