content: package io.aiven.klaw.clusterapi.config;
  
  import io.aiven.klaw.clusterapi.services.JwtTokenUtilService;
  import io.jsonwebtoken.ExpiredJwtException;
  import jakarta.servlet.FilterChain;
  import jakarta.servlet.ServletException;
  import jakarta.servlet.http.HttpServletRequest;
  import jakarta.servlet.http.HttpServletResponse;
  import java.io.IOException;
  import java.util.HashMap;
  import java.util.Map;
  import lombok.extern.slf4j.Slf4j;
  import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  import org.springframework.security.core.context.SecurityContextHolder;
  import org.springframework.security.core.userdetails.UserDetails;
  import org.springframework.security.core.userdetails.UserDetailsService;
  import org.springframework.security.core.userdetails.UsernameNotFoundException;
  import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
  import org.springframework.stereotype.Component;
  import org.springframework.web.filter.OncePerRequestFilter;
  
  @Component
  @Slf4j
  public class JwtRequestFilter extends OncePerRequestFilter {
  
    private static final String BEARER = "Bearer ";
    private static final String USERNAME = "username";
    private static final String TOKEN = "token";
    private final JwtTokenUtilService jwtTokenUtil;
    private final UserDetailsService userDetailsService;
  
    public JwtRequestFilter(
        UserDetailsService userDetailsService, JwtTokenUtilService jwtTokenUtilService) {
      this.userDetailsService = userDetailsService;
      this.jwtTokenUtil = jwtTokenUtilService;
    }
  
    @Override
    protected void doFilterInternal(
        HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws ServletException, IOException {
      final Map<String, String> userTokenMap = new HashMap<>();
      if (isUnAuthorizedRequest(request, response, userTokenMap)) return;
  
      validateToken(request, response, chain, userTokenMap);
    }
  
    private boolean isUnAuthorizedRequest(
        HttpServletRequest request, HttpServletResponse response, Map<String, String> userTokenMap)
        throws IOException {
      final String requestTokenHeader = request.getHeader("Authorization");
      String jwtToken;
  
      // JWT Token is in the form "Bearer token". Remove Bearer word and get
      // only the Token
      if (requestTokenHeader != null && requestTokenHeader.startsWith(BEARER)) {
        jwtToken = requestTokenHeader.substring(BEARER.length());
        userTokenMap.put(TOKEN, jwtToken);
        return extractUsernameFromToken(response, userTokenMap, jwtToken);
      } else {
        if (requestTokenHeader != null) {
          log.warn(
              "Invalid JWT : {}",
              requestTokenHeader.substring(0, Math.min(requestTokenHeader.length(), 10)));
        }
  
        log.warn("JWT Token does not begin with Bearer String");
      }
      return false;
    }
  
    private boolean extractUsernameFromToken(
        HttpServletResponse response, Map<String, String> userTokenMap, String jwtToken)
        throws IOException {
      String username;
      try {
        username = jwtTokenUtil.getUsernameFromToken(jwtToken);
        userTokenMap.put(USERNAME, username);
        return false;
      } catch (IllegalArgumentException e) {
        log.info("Unable to get JWT Token ", e);
      } catch (ExpiredJwtException e) {
        log.info("JWT Token has expired", e);
      } catch (Exception e) {
        log.info("Token validation errors ", e);
      }
      response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
      return true;
    }
  
    // Once we get the token validate it.
    private void validateToken(
        HttpServletRequest request,
        HttpServletResponse response,
        FilterChain chain,
        Map<String, String> userTokenMap)
        throws IOException, ServletException {
      if (userTokenMap.containsKey(USERNAME)
          && SecurityContextHolder.getContext().getAuthentication() == null) {
        UserDetails userDetails;
        try {
          userDetails = this.userDetailsService.loadUserByUsername(userTokenMap.get(USERNAME));
        } catch (UsernameNotFoundException e) {
          throw new RuntimeException("User/subject not found !!", e);
        }
        validateTokenAndUserDetails(request, response, userTokenMap, userDetails);
      }
      chain.doFilter(request, response);
    }
  
    private void validateTokenAndUserDetails(
        HttpServletRequest request,
        HttpServletResponse response,
        Map<String, String> userTokenMap,
        UserDetails userDetails)
        throws IOException {
      // authentication
      if (userDetails != null
          && userTokenMap.containsKey(TOKEN)
          && jwtTokenUtil.validateToken(userTokenMap.get(TOKEN))) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        usernamePasswordAuthenticationToken.setDetails(
            new WebAuthenticationDetailsSource().buildDetails(request));
        // After setting the Authentication in the context, we specify
        // that the current user is authenticated. So it passes the
        // Spring Security Configurations successfully.
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
      } else {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
      }
    }
  }
  