PlayerRegistrationHandlerInterceptor.java

package net.andresbustamante.yafoot.core.web.util;

import net.andresbustamante.yafoot.commons.exceptions.ApplicationException;
import net.andresbustamante.yafoot.commons.exceptions.DatabaseException;
import net.andresbustamante.yafoot.commons.model.UserContext;
import net.andresbustamante.yafoot.core.model.Player;
import net.andresbustamante.yafoot.core.services.PlayerManagementService;
import net.andresbustamante.yafoot.core.services.PlayerSearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.web.servlet.HandlerInterceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Set;

/**
 * Intercepts every http call to check if the player is already registered. Otherwise, it will create the player
 * using the authenticated principal data.
 */
public class PlayerRegistrationHandlerInterceptor implements HandlerInterceptor {

    private static final Collection<HttpMethod> METHODS_TO_INTERCEPT = Set.of(
            HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH);

    private final PlayerManagementService playerManagementService;
    private final PlayerSearchService playerSearchService;

    private final Logger log = LoggerFactory.getLogger(PlayerRegistrationHandlerInterceptor.class);

    /**
     * Public constructor.
     *
     * @param playerManagementService
     * @param playerSearchService
     */
    public PlayerRegistrationHandlerInterceptor(
            final PlayerManagementService playerManagementService, final PlayerSearchService playerSearchService) {
        this.playerManagementService = playerManagementService;
        this.playerSearchService = playerSearchService;
    }

    @Override
    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler)
            throws Exception {
        if (METHODS_TO_INTERCEPT.contains(HttpMethod.valueOf(request.getMethod()))) {
            String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);

            if (authHeader != null) {
                // Check if the authenticated user is already registered as a player
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

                if (authentication instanceof JwtAuthenticationToken) {
                    Jwt credentials = (Jwt) authentication.getCredentials();
                    String username = credentials.getClaimAsString("email");

                    Player player = playerSearchService.findPlayerByEmail(username, null);

                    if (player == null) {
                        log.info("User does not exist as a player: {}", username);
                        createPlayerFromCredentials(credentials);
                    } else {
                        log.debug("User {} is already registered as a player.", username);
                    }
                } else {
                    log.warn("No user to check");
                }
            } else {
                log.warn("No auth header found for request on {}", request.getPathInfo());
            }
        }

        return true;
    }

    private void createPlayerFromCredentials(final Jwt credentials) throws DatabaseException, ApplicationException {
        String username = credentials.getClaimAsString("email");

        Player newPlayer = new Player();
        newPlayer.setFirstName(credentials.getClaimAsString("given_name"));
        newPlayer.setSurname(credentials.getClaimAsString("family_name"));
        newPlayer.setEmail(username);
        newPlayer.setActive(true);

        UserContext userContext = new UserContext();
        userContext.setUsername(username);

        int id = playerManagementService.savePlayer(newPlayer, userContext);
        log.info("Player {} ({}) successfully registered from token information", id, username);
    }
}