package it.inaf.ia2.gms.rap; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import it.inaf.ia2.gms.authn.SessionData; import it.inaf.ia2.gms.model.RapUser; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; @Component public class RapClient { @Value("${rap.ws-url}") private String rapBaseUrl; @Value("${security.oauth2.client.access-token-uri}") private String accessTokenUri; @Value("${security.oauth2.client.client-id}") private String clientId; @Value("${security.oauth2.client.client-secret}") private String clientSecret; @Value("${security.oauth2.client.scope}") private String scope; @Autowired private HttpServletRequest request; @Autowired(required = false) private SessionData sessionData; private final RestTemplate rapRestTemplate; private final RestTemplate refreshTokenRestTemplate; private final ObjectMapper objectMapper = new ObjectMapper(); @Autowired public RapClient(RestTemplate rapRestTemplate) { this.rapRestTemplate = rapRestTemplate; this.refreshTokenRestTemplate = new RestTemplate(); } public RapUser getUser(String userId) { String url = rapBaseUrl + "/user/" + userId; return httpCall(entity -> { return rapRestTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference() { }).getBody(); }); } public List getUsers(Set identifiers) { if (identifiers.isEmpty()) { return new ArrayList<>(); } String url = rapBaseUrl + "/user?identifiers=" + String.join(",", identifiers); return httpCall(entity -> { return rapRestTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference>() { }).getBody(); }); } public List searchUsers(String searchText) { if (searchText == null || searchText.trim().isEmpty()) { return new ArrayList<>(); } String url = rapBaseUrl + "/user?search=" + searchText; return httpCall(entity -> { return rapRestTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference>() { }).getBody(); }); } private R httpCall(Function, R> function) { return httpCall(function, null); } private R httpCall(Function, R> function, T body) { try { try { return function.apply(getEntity(body)); } catch (HttpClientErrorException.Unauthorized ex) { if (request.getSession(false) == null || sessionData.getExpiresIn() > 0) { // we can't refresh the token without a session throw ex; } refreshToken(); return function.apply(getEntity(body)); } } catch (HttpStatusCodeException ex) { try { Map map = objectMapper.readValue(ex.getResponseBodyAsString(), Map.class); if (map.containsKey("error")) { String error = map.get("error"); if (ex instanceof HttpClientErrorException) { throw new HttpClientErrorException(ex.getStatusCode(), error); } else if (ex instanceof HttpServerErrorException) { throw new HttpServerErrorException(ex.getStatusCode(), error); } } } catch (JsonProcessingException ignore) { } throw ex; } } private HttpEntity getEntity(T body) { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); if (request.getSession(false) != null) { headers.add("Authorization", "Bearer " + sessionData.getAccessToken()); } else { // from JWT web service headers.add("Authorization", request.getHeader("Authorization")); } return new HttpEntity<>(body, headers); } public void refreshToken() { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.setBasicAuth(clientId, clientSecret); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); map.add("grant_type", "refresh_token"); map.add("refresh_token", sessionData.getRefreshToken()); map.add("scope", scope.replace(",", " ")); HttpEntity> request = new HttpEntity<>(map, headers); ResponseEntity response = refreshTokenRestTemplate.postForEntity(accessTokenUri, request, Map.class); Map values = response.getBody(); sessionData.setAccessToken((String) values.get("access_token")); sessionData.setRefreshToken((String) values.get("refresh_token")); sessionData.setExpiresIn((int) values.get("expires_in")); } }