|
8 | 8 | import com.google.gson.JsonObject; |
9 | 9 | import com.google.gson.JsonParser; |
10 | 10 | import com.sap.cds.sdm.caching.CacheConfig; |
| 11 | +import com.sap.cds.sdm.caching.CacheKey; |
11 | 12 | import com.sap.cds.sdm.caching.TokenCacheKey; |
12 | 13 | import com.sap.cds.sdm.constants.SDMConstants; |
13 | 14 | import com.sap.cds.sdm.model.SDMCredentials; |
|
19 | 20 | import com.sap.cloud.sdk.cloudplatform.connectivity.OAuth2DestinationBuilder; |
20 | 21 | import com.sap.cloud.sdk.cloudplatform.connectivity.OnBehalfOf; |
21 | 22 | import com.sap.cloud.security.config.ClientCredentials; |
| 23 | +import com.sap.cloud.security.xsuaa.client.OAuth2ServiceException; |
| 24 | +import com.sap.cloud.security.xsuaa.http.HttpHeaders; |
| 25 | +import com.sap.cloud.security.xsuaa.http.MediaType; |
22 | 26 | import java.io.*; |
23 | 27 | import java.net.HttpURLConnection; |
24 | 28 | import java.net.URL; |
25 | 29 | import java.net.URLEncoder; |
26 | 30 | import java.nio.charset.StandardCharsets; |
27 | 31 | import java.time.Duration; |
| 32 | +import java.util.Arrays; |
| 33 | +import java.util.HashMap; |
28 | 34 | import java.util.List; |
29 | 35 | import java.util.Map; |
30 | 36 | import java.util.stream.Collectors; |
31 | 37 | import org.apache.commons.codec.binary.Base64; |
| 38 | +import org.apache.http.HttpResponse; |
| 39 | +import org.apache.http.HttpStatus; |
| 40 | +import org.apache.http.client.ClientProtocolException; |
32 | 41 | import org.apache.http.client.HttpClient; |
| 42 | +import org.apache.http.client.entity.UrlEncodedFormEntity; |
| 43 | +import org.apache.http.client.methods.HttpPost; |
| 44 | +import org.apache.http.impl.client.CloseableHttpClient; |
| 45 | +import org.apache.http.impl.client.HttpClients; |
| 46 | +import org.apache.http.message.BasicNameValuePair; |
| 47 | +import org.json.JSONObject; |
| 48 | +import org.slf4j.Logger; |
| 49 | +import org.slf4j.LoggerFactory; |
33 | 50 |
|
34 | 51 | public class TokenHandler { |
| 52 | + private static final Logger logger = LoggerFactory.getLogger(TokenHandler.class); |
35 | 53 |
|
36 | 54 | private static final ObjectMapper mapper = new ObjectMapper(); |
37 | 55 |
|
@@ -140,6 +158,109 @@ public static String getDITokenUsingAuthorities( |
140 | 158 | return cachedToken; |
141 | 159 | } |
142 | 160 |
|
| 161 | + public static String getDIToken(String token, SDMCredentials sdmCredentials) throws IOException { |
| 162 | + JsonObject payloadObj = getTokenFields(token); |
| 163 | + String email = payloadObj.get("email").getAsString(); |
| 164 | + JsonObject tenantDetails = payloadObj.get("ext_attr").getAsJsonObject(); |
| 165 | + String subdomain = tenantDetails.get("zdn").getAsString(); |
| 166 | + String tokenexpiry = payloadObj.get("exp").getAsString(); |
| 167 | + CacheKey cacheKey = new CacheKey(); |
| 168 | + cacheKey.setKey(email + "_" + subdomain); |
| 169 | + cacheKey.setExpiration(tokenexpiry); |
| 170 | + String cachedToken = CacheConfig.getUserTokenCache().get(cacheKey); |
| 171 | + if (cachedToken == null) { |
| 172 | + cachedToken = generateDITokenFromTokenExchange(token, sdmCredentials, payloadObj); |
| 173 | + } |
| 174 | + return cachedToken; |
| 175 | + } |
| 176 | + |
| 177 | + public static Map<String, String> fillTokenExchangeBody(String token, SDMCredentials sdmEnv) { |
| 178 | + Map<String, String> parameters = new HashMap<>(); |
| 179 | + parameters.put("assertion", token); |
| 180 | + return parameters; |
| 181 | + } |
| 182 | + |
| 183 | + public static String generateDITokenFromTokenExchange( |
| 184 | + String token, SDMCredentials sdmCredentials, JsonObject payloadObj) |
| 185 | + throws OAuth2ServiceException { |
| 186 | + String cachedToken = null; |
| 187 | + CloseableHttpClient httpClient = null; |
| 188 | + try { |
| 189 | + httpClient = HttpClients.createDefault(); |
| 190 | + if (sdmCredentials.getClientId() == null) { |
| 191 | + throw new IOException(SDMConstants.NO_SDM_BINDING); |
| 192 | + } |
| 193 | + Map<String, String> parameters = fillTokenExchangeBody(token, sdmCredentials); |
| 194 | + HttpPost httpPost = |
| 195 | + new HttpPost(sdmCredentials.getBaseTokenUrl() + SDMConstants.DI_TOKEN_EXCHANGE_PARAMS); |
| 196 | + httpPost.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.value()); |
| 197 | + httpPost.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED.value()); |
| 198 | + httpPost.setHeader("X-zid", getTokenFields(token).get("zid").getAsString()); |
| 199 | + |
| 200 | + String encoded = |
| 201 | + java.util.Base64.getEncoder() |
| 202 | + .encodeToString( |
| 203 | + (sdmCredentials.getClientId() + ":" + sdmCredentials.getClientSecret()) |
| 204 | + .getBytes()); |
| 205 | + httpPost.setHeader("Authorization", "Basic " + encoded); |
| 206 | + |
| 207 | + List<BasicNameValuePair> basicNameValuePairs = |
| 208 | + parameters.entrySet().stream() |
| 209 | + .map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue())) |
| 210 | + .collect(Collectors.toList()); |
| 211 | + httpPost.setEntity(new UrlEncodedFormEntity(basicNameValuePairs)); |
| 212 | + |
| 213 | + HttpResponse response = httpClient.execute(httpPost); |
| 214 | + String responseBody = extractResponseBodyAsString(response); |
| 215 | + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { |
| 216 | + logger.error("Error fetching token with JWT bearer : " + responseBody); |
| 217 | + throw new OAuth2ServiceException( |
| 218 | + String.format(SDMConstants.DI_TOKEN_EXCHANGE_ERROR, responseBody)); |
| 219 | + } |
| 220 | + Map<String, Object> accessTokenMap = new JSONObject(responseBody).toMap(); |
| 221 | + cachedToken = String.valueOf(accessTokenMap.get("access_token")); |
| 222 | + String expiryTime = payloadObj.get("exp").getAsString(); |
| 223 | + CacheKey cacheKey = new CacheKey(); |
| 224 | + JsonObject tenantDetails = payloadObj.get("ext_attr").getAsJsonObject(); |
| 225 | + String subdomain = tenantDetails.get("zdn").getAsString(); |
| 226 | + cacheKey.setKey(payloadObj.get("email").getAsString() + "_" + subdomain); |
| 227 | + cacheKey.setExpiration(expiryTime); |
| 228 | + CacheConfig.getUserTokenCache().put(cacheKey, cachedToken); |
| 229 | + } catch (UnsupportedEncodingException e) { |
| 230 | + throw new OAuth2ServiceException("Unexpected error parsing URI: " + e.getMessage()); |
| 231 | + } catch (ClientProtocolException e) { |
| 232 | + throw new OAuth2ServiceException( |
| 233 | + "Unexpected error while fetching client protocol: " + e.getMessage()); |
| 234 | + } catch (IOException e) { |
| 235 | + logger.error( |
| 236 | + "Error in POST request while fetching token with JWT bearer \n" |
| 237 | + + Arrays.toString(e.getStackTrace())); |
| 238 | + throw new OAuth2ServiceException( |
| 239 | + "Error in POST request while fetching token with JWT bearer: " + e.getMessage()); |
| 240 | + } finally { |
| 241 | + safeClose(httpClient); |
| 242 | + } |
| 243 | + return cachedToken; |
| 244 | + } |
| 245 | + |
| 246 | + private static void safeClose(CloseableHttpClient httpClient) { |
| 247 | + if (httpClient != null) { |
| 248 | + try { |
| 249 | + httpClient.close(); |
| 250 | + } catch (IOException ex) { |
| 251 | + logger.error("Failed to close httpclient \n" + Arrays.toString(ex.getStackTrace())); |
| 252 | + } |
| 253 | + } |
| 254 | + } |
| 255 | + |
| 256 | + public static String extractResponseBodyAsString(HttpResponse response) throws IOException { |
| 257 | + // Ensure that InputStream and BufferedReader are automatically closed |
| 258 | + try (InputStream inputStream = response.getEntity().getContent(); |
| 259 | + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) { |
| 260 | + return bufferedReader.lines().collect(Collectors.joining(System.lineSeparator())); |
| 261 | + } |
| 262 | + } |
| 263 | + |
143 | 264 | public static JsonObject getTokenFields(String token) { |
144 | 265 | String[] chunks = token.split("\\."); |
145 | 266 | java.util.Base64.Decoder decoder = java.util.Base64.getUrlDecoder(); |
@@ -189,7 +310,7 @@ public static HttpClient getHttpClient( |
189 | 310 | DefaultHttpClientFactory.DefaultHttpClientFactoryBuilder builder = |
190 | 311 | DefaultHttpClientFactory.builder(); |
191 | 312 | if (connectionPoolConfig == null) { |
192 | | - Duration timeout = Duration.ofSeconds(SDMConstants.CONNECTION_TIMEOUT); |
| 313 | + Duration timeout = Duration.ofSeconds((long) SDMConstants.CONNECTION_TIMEOUT); |
193 | 314 | builder.timeoutMilliseconds((int) timeout.toMillis()); |
194 | 315 | builder.maxConnectionsPerRoute(SDMConstants.MAX_CONNECTIONS); |
195 | 316 | builder.maxConnectionsTotal(SDMConstants.MAX_CONNECTIONS); |
|
0 commit comments