RequestHeaderAuthenticationProvider.java

package se.jobtechdev.personaldatagateway.api.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;
import se.jobtechdev.personaldatagateway.api.generated.entities.ClientEntity;
import se.jobtechdev.personaldatagateway.api.service.ClientService;
import se.jobtechdev.personaldatagateway.api.util.AuthUtil;
import se.jobtechdev.personaldatagateway.api.util.TimeProvider;

import java.util.List;

@Component
public class RequestHeaderAuthenticationProvider implements AuthenticationProvider {
  private static final Logger logger =
      LoggerFactory.getLogger(RequestHeaderAuthenticationProvider.class);

  private final ClientService clientService;

  @Autowired
  public RequestHeaderAuthenticationProvider(ClientService clientService) {
    this.clientService = clientService;
  }

  public ClientEntity getClientByKey(String authKey) {
    AuthUtil.assertAuthKeyExists(authKey);

    final byte[] decodedAuthKey = AuthUtil.decodeAuthKey(authKey);
    final byte[] authKeyHash = AuthUtil.hashDecodedAuthKey(decodedAuthKey);

    final var optionalClient = clientService.getClientByKeyHash(authKeyHash);
    if (optionalClient.isEmpty())
      throw new BadCredentialsException(
          "Bad Request Header Credentials, provided authKey does not match any known client");

    return optionalClient.get();
  }

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    final var principal = authentication.getPrincipal();
    if (principal == null) {
      throw new BadCredentialsException(
          "Bad Request Header Credentials, provided principal is null");
    }

    final var authKey = String.valueOf(authentication.getPrincipal());
    final var client = getClientByKey(authKey);

    logger.debug(
        "Received authenticated http request by {}[id={}, role={}, revoked={}]",
        client.getName(),
        client.getId(),
        client.getRole(),
        client.getRevoked());

    if (client.getRevoked() != null && TimeProvider.now().isAfter(client.getRevoked())) {
      throw new BadCredentialsException(
          "Bad Request Header Credentials, provided client is revoked");
    }

    final var role = client.getRole();

    final var simpleGrantedAuthority = new SimpleGrantedAuthority(role);

    MDC.put("temp.custom.pdg-api.clientid", client.getId().toString());
    MDC.put("temp.log", "true");

    return new PreAuthenticatedAuthenticationToken(
        client.getId().toString(), null, List.of(simpleGrantedAuthority));
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return authentication.equals(PreAuthenticatedAuthenticationToken.class);
  }
}