2323import com .amazonaws .athena .connector .credentials .DefaultCredentials ;
2424import com .amazonaws .athena .connector .lambda .exceptions .AthenaConnectorException ;
2525import com .amazonaws .athena .connector .lambda .security .CachableSecretsManager ;
26+ import com .fasterxml .jackson .core .JsonProcessingException ;
2627import com .fasterxml .jackson .databind .JsonNode ;
2728import com .fasterxml .jackson .databind .ObjectMapper ;
2829import com .fasterxml .jackson .databind .node .ObjectNode ;
2930import software .amazon .awssdk .services .glue .model .ErrorDetails ;
3031import software .amazon .awssdk .services .glue .model .FederationSourceErrorCode ;
3132import software .amazon .awssdk .services .secretsmanager .SecretsManagerClient ;
3233import software .amazon .awssdk .services .secretsmanager .model .PutSecretValueRequest ;
34+ import software .amazon .awssdk .services .secretsmanager .model .ResourceNotFoundException ;
35+ import software .amazon .awssdk .services .secretsmanager .model .SecretsManagerException ;
3336
37+ import java .io .IOException ;
3438import java .net .URI ;
3539import java .net .http .HttpClient ;
3640import java .net .http .HttpRequest ;
4246
4347public class SynapseCredentialsProvider implements CredentialsProvider
4448{
49+ // Constants for OAuth token response fields
4550 private static final String ACCESS_TOKEN = "access_token" ;
4651 private static final String FETCHED_AT = "fetched_at" ;
4752 private static final String EXPIRES_IN = "expires_in" ;
4853
54+ // Constants for OAuth configuration fields
4955 private static final String CLIENT_ID = "client_id" ;
5056 private static final String CLIENT_SECRET = "client_secret" ;
5157 private static final String TENANT_ID = "tenant_id" ;
5258
59+ // Constants for basic authentication fields
5360 private static final String USER = "user" ;
5461 private static final String PASSWORD = "password" ;
5562 private static final String USERNAME = "username" ;
@@ -96,9 +103,25 @@ public Map<String, String> getCredentialMap()
96103
97104 return credentialMap ;
98105 }
99- catch (Exception e ) {
106+ catch (JsonProcessingException e ) {
100107 throw new AthenaConnectorException (
101- "Failed to retrieve Synapse credentials" ,
108+ "Failed to parse Synapse credentials JSON" ,
109+ ErrorDetails .builder ()
110+ .errorCode (FederationSourceErrorCode .INVALID_RESPONSE_EXCEPTION .toString ())
111+ .build ()
112+ );
113+ }
114+ catch (ResourceNotFoundException e ) {
115+ throw new AthenaConnectorException (
116+ "Secret not found: " + secretName ,
117+ ErrorDetails .builder ()
118+ .errorCode (FederationSourceErrorCode .ENTITY_NOT_FOUND_EXCEPTION .toString ())
119+ .build ()
120+ );
121+ }
122+ catch (SecretsManagerException e ) {
123+ throw new AthenaConnectorException (
124+ "Failed to retrieve Synapse credentials from Secrets Manager" ,
102125 ErrorDetails .builder ()
103126 .errorCode (FederationSourceErrorCode .INTERNAL_SERVICE_EXCEPTION .toString ())
104127 .build ()
@@ -117,14 +140,47 @@ public String getOAuthAccessToken()
117140 }
118141 return null ;
119142 }
120- catch (Exception e ) {
143+ catch (JsonProcessingException e ) {
144+ throw new AthenaConnectorException (
145+ "Failed to parse OAuth configuration JSON" ,
146+ ErrorDetails .builder ()
147+ .errorCode (FederationSourceErrorCode .INVALID_RESPONSE_EXCEPTION .toString ())
148+ .build ()
149+ );
150+ }
151+ catch (ResourceNotFoundException e ) {
121152 throw new AthenaConnectorException (
122- "Failed to get OAuth access token" ,
153+ "Secret not found: " + secretName ,
154+ ErrorDetails .builder ()
155+ .errorCode (FederationSourceErrorCode .ENTITY_NOT_FOUND_EXCEPTION .toString ())
156+ .build ()
157+ );
158+ }
159+ catch (SecretsManagerException e ) {
160+ throw new AthenaConnectorException (
161+ "Failed to retrieve OAuth configuration from Secrets Manager" ,
123162 ErrorDetails .builder ()
124163 .errorCode (FederationSourceErrorCode .INTERNAL_SERVICE_EXCEPTION .toString ())
125164 .build ()
126165 );
127166 }
167+ catch (IOException e ) {
168+ throw new AthenaConnectorException (
169+ "Failed to communicate with OAuth endpoint" ,
170+ ErrorDetails .builder ()
171+ .errorCode (FederationSourceErrorCode .OPERATION_TIMEOUT_EXCEPTION .toString ())
172+ .build ()
173+ );
174+ }
175+ catch (InterruptedException e ) {
176+ Thread .currentThread ().interrupt ();
177+ throw new AthenaConnectorException (
178+ "OAuth token fetch interrupted" ,
179+ ErrorDetails .builder ()
180+ .errorCode (FederationSourceErrorCode .OPERATION_TIMEOUT_EXCEPTION .toString ())
181+ .build ()
182+ );
183+ }
128184 }
129185
130186 private boolean isOAuthConfigured (Map <String , String > oauthConfig )
@@ -137,7 +193,7 @@ private boolean isOAuthConfigured(Map<String, String> oauthConfig)
137193 !oauthConfig .get (TENANT_ID ).isEmpty ();
138194 }
139195
140- private String fetchAccessToken (Map <String , String > oauthConfig ) throws Exception
196+ private String fetchAccessToken (Map <String , String > oauthConfig ) throws IOException , InterruptedException
141197 {
142198 String accessToken = oauthConfig .get (ACCESS_TOKEN );
143199
@@ -156,7 +212,7 @@ private String fetchAccessToken(Map<String, String> oauthConfig) throws Exceptio
156212 return fetchAndStoreNewToken (oauthConfig );
157213 }
158214
159- private String fetchAndStoreNewToken (Map <String , String > oauthConfig ) throws Exception
215+ private String fetchAndStoreNewToken (Map <String , String > oauthConfig ) throws IOException , InterruptedException
160216 {
161217 String clientId = oauthConfig .get (CLIENT_ID );
162218 String clientSecret = oauthConfig .get (CLIENT_SECRET );
@@ -179,33 +235,78 @@ private String fetchAndStoreNewToken(Map<String, String> oauthConfig) throws Exc
179235 HttpResponse <String > response = client .send (request , HttpResponse .BodyHandlers .ofString ());
180236
181237 if (response .statusCode () != 200 ) {
238+ String errorMessage = String .format ("Failed to fetch access token. Status code: %d" , response .statusCode ());
239+ if (response .statusCode () == 401 || response .statusCode () == 403 ) {
240+ throw new AthenaConnectorException (
241+ errorMessage ,
242+ ErrorDetails .builder ()
243+ .errorCode (FederationSourceErrorCode .INVALID_CREDENTIALS_EXCEPTION .toString ())
244+ .build ()
245+ );
246+ }
247+ if (response .statusCode () == 429 ) {
248+ throw new AthenaConnectorException (
249+ errorMessage ,
250+ ErrorDetails .builder ()
251+ .errorCode (FederationSourceErrorCode .THROTTLING_EXCEPTION .toString ())
252+ .build ()
253+ );
254+ }
182255 throw new AthenaConnectorException (
183- "Failed to fetch access token" ,
256+ errorMessage ,
184257 ErrorDetails .builder ()
185258 .errorCode (FederationSourceErrorCode .INTERNAL_SERVICE_EXCEPTION .toString ())
186259 .build ()
187260 );
188261 }
189262
190- JsonNode tokenResponse = objectMapper .readTree (response .body ());
191- String accessToken = tokenResponse .get (ACCESS_TOKEN ).asText ();
192- long expiresIn = tokenResponse .get (EXPIRES_IN ).asLong ();
193- long fetchedAt = Instant .now ().getEpochSecond ();
263+ try {
264+ JsonNode tokenResponse = objectMapper .readTree (response .body ());
265+ String accessToken = tokenResponse .get (ACCESS_TOKEN ).asText ();
266+ long expiresIn = tokenResponse .get (EXPIRES_IN ).asLong ();
267+ long fetchedAt = Instant .now ().getEpochSecond ();
194268
195- oauthConfig .put (ACCESS_TOKEN , accessToken );
196- oauthConfig .put (EXPIRES_IN , String .valueOf (expiresIn ));
197- oauthConfig .put (FETCHED_AT , String .valueOf (fetchedAt ));
269+ oauthConfig .put (ACCESS_TOKEN , accessToken );
270+ oauthConfig .put (EXPIRES_IN , String .valueOf (expiresIn ));
271+ oauthConfig .put (FETCHED_AT , String .valueOf (fetchedAt ));
198272
199- ObjectNode updatedSecretJson = objectMapper .createObjectNode ();
200- for (Map .Entry <String , String > entry : oauthConfig .entrySet ()) {
201- updatedSecretJson .put (entry .getKey (), entry .getValue ());
202- }
273+ ObjectNode updatedSecretJson = objectMapper .createObjectNode ();
274+ for (Map .Entry <String , String > entry : oauthConfig .entrySet ()) {
275+ updatedSecretJson .put (entry .getKey (), entry .getValue ());
276+ }
203277
204- secretsManager .getSecretsManager ().putSecretValue (PutSecretValueRequest .builder ()
205- .secretId (secretName )
206- .secretString (updatedSecretJson .toString ())
207- .build ());
278+ try {
279+ secretsManager .getSecretsManager ().putSecretValue (PutSecretValueRequest .builder ()
280+ .secretId (secretName )
281+ .secretString (updatedSecretJson .toString ())
282+ .build ());
283+ }
284+ catch (ResourceNotFoundException e ) {
285+ throw new AthenaConnectorException (
286+ "Secret not found: " + secretName ,
287+ ErrorDetails .builder ()
288+ .errorCode (FederationSourceErrorCode .ENTITY_NOT_FOUND_EXCEPTION .toString ())
289+ .build ()
290+ );
291+ }
292+ catch (SecretsManagerException e ) {
293+ throw new AthenaConnectorException (
294+ "Failed to update access token in Secrets Manager" ,
295+ ErrorDetails .builder ()
296+ .errorCode (FederationSourceErrorCode .INTERNAL_SERVICE_EXCEPTION .toString ())
297+ .build ()
298+ );
299+ }
208300
209- return accessToken ;
301+ return accessToken ;
302+ }
303+ catch (JsonProcessingException e ) {
304+ throw new AthenaConnectorException (
305+ "Failed to parse OAuth token response" ,
306+ ErrorDetails .builder ()
307+ .errorCode (FederationSourceErrorCode .INVALID_RESPONSE_EXCEPTION .toString ())
308+ .build ()
309+ );
310+ }
210311 }
211312}
0 commit comments