Skip to content

Commit a3eca13

Browse files
Abstract common OAuth handling and add OAuth support to Athena DataLake Gen2 Connector (#2932)
Co-authored-by: burhan94 <[email protected]>
1 parent e3c9482 commit a3eca13

File tree

28 files changed

+1423
-122
lines changed

28 files changed

+1423
-122
lines changed

athena-cloudera-hive/src/main/java/com/amazonaws/athena/connectors/cloudera/HiveJdbcConnectionFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package com.amazonaws.athena.connectors.cloudera;
2222

23+
import com.amazonaws.athena.connector.credentials.CredentialsConstants;
2324
import com.amazonaws.athena.connector.credentials.CredentialsProvider;
2425
import com.amazonaws.athena.connectors.jdbc.connection.DatabaseConnectionConfig;
2526
import com.amazonaws.athena.connectors.jdbc.connection.DatabaseConnectionInfo;
@@ -62,7 +63,7 @@ public Connection getConnection(final CredentialsProvider credentialsProvider)
6263
if (null != credentialsProvider) {
6364
Matcher secretMatcher = SECRET_NAME_PATTERN.matcher(databaseConnectionConfig.getJdbcConnectionString());
6465
final String secretReplacement = String.format("UID=%s;PWD=%s",
65-
credentialsProvider.getCredential().getUser(), credentialsProvider.getCredential().getPassword());
66+
credentialsProvider.getCredentialMap().get(CredentialsConstants.USER), credentialsProvider.getCredentialMap().get(CredentialsConstants.PASSWORD));
6667
derivedJdbcString = secretMatcher.replaceAll(Matcher.quoteReplacement(secretReplacement));
6768
}
6869
else {

athena-cloudera-impala/src/main/java/com/amazonaws/athena/connectors/cloudera/ImpalaJdbcConnectionFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package com.amazonaws.athena.connectors.cloudera;
2222

23+
import com.amazonaws.athena.connector.credentials.CredentialsConstants;
2324
import com.amazonaws.athena.connector.credentials.CredentialsProvider;
2425
import com.amazonaws.athena.connectors.jdbc.connection.DatabaseConnectionConfig;
2526
import com.amazonaws.athena.connectors.jdbc.connection.DatabaseConnectionInfo;
@@ -63,7 +64,7 @@ public Connection getConnection(final CredentialsProvider credentialsProvider)
6364
if (null != credentialsProvider) {
6465
Matcher secretMatcher = SECRET_NAME_PATTERN.matcher(databaseConnectionConfig.getJdbcConnectionString());
6566
final String secretReplacement = String.format("UID=%s;PWD=%s",
66-
credentialsProvider.getCredential().getUser(), credentialsProvider.getCredential().getPassword());
67+
credentialsProvider.getCredentialMap().get(CredentialsConstants.USER), credentialsProvider.getCredentialMap().get(CredentialsConstants.PASSWORD));
6768
derivedJdbcString = secretMatcher.replaceAll(Matcher.quoteReplacement(secretReplacement));
6869
}
6970
else {

athena-datalakegen2/athena-datalakegen2-connection.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ Resources:
104104
Statement:
105105
- Action:
106106
- secretsmanager:GetSecretValue
107+
- secretsmanager:PutSecretValue
107108
Effect: Allow
108109
Resource: !Sub 'arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretName}*'
109110
- Action:

athena-datalakegen2/athena-datalakegen2-package.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Resources:
8282
- Statement:
8383
- Action:
8484
- secretsmanager:GetSecretValue
85+
- secretsmanager:PutSecretValue
8586
Effect: Allow
8687
Resource: !Sub 'arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretNamePrefix}*'
8788
Version: '2012-10-17'

athena-datalakegen2/athena-datalakegen2.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Resources:
8787
- Statement:
8888
- Action:
8989
- secretsmanager:GetSecretValue
90+
- secretsmanager:PutSecretValue
9091
Effect: Allow
9192
Resource: !Sub 'arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${SecretNamePrefix}*'
9293
Version: '2012-10-17'

athena-datalakegen2/src/main/java/com/amazonaws/athena/connectors/datalakegen2/DataLakeGen2JdbcConnectionFactory.java

Lines changed: 0 additions & 81 deletions
This file was deleted.

athena-datalakegen2/src/main/java/com/amazonaws/athena/connectors/datalakegen2/DataLakeGen2MetadataHandler.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*/
2020
package com.amazonaws.athena.connectors.datalakegen2;
2121

22+
import com.amazonaws.athena.connector.credentials.CredentialsProvider;
23+
import com.amazonaws.athena.connector.credentials.CredentialsProviderFactory;
2224
import com.amazonaws.athena.connector.lambda.QueryStatusChecker;
2325
import com.amazonaws.athena.connector.lambda.data.Block;
2426
import com.amazonaws.athena.connector.lambda.data.BlockAllocator;
@@ -42,6 +44,7 @@
4244
import com.amazonaws.athena.connectors.datalakegen2.resolver.DataLakeGen2CaseResolver;
4345
import com.amazonaws.athena.connectors.jdbc.connection.DatabaseConnectionConfig;
4446
import com.amazonaws.athena.connectors.jdbc.connection.DatabaseConnectionInfo;
47+
import com.amazonaws.athena.connectors.jdbc.connection.GenericJdbcConnectionFactory;
4548
import com.amazonaws.athena.connectors.jdbc.connection.JdbcConnectionFactory;
4649
import com.amazonaws.athena.connectors.jdbc.manager.JDBCUtil;
4750
import com.amazonaws.athena.connectors.jdbc.manager.JdbcArrowTypeConverter;
@@ -96,7 +99,7 @@ public DataLakeGen2MetadataHandler(java.util.Map<String, String> configOptions)
9699
public DataLakeGen2MetadataHandler(DatabaseConnectionConfig databaseConnectionConfig, java.util.Map<String, String> configOptions)
97100
{
98101
this(databaseConnectionConfig,
99-
new DataLakeGen2JdbcConnectionFactory(databaseConnectionConfig, JDBC_PROPERTIES,
102+
new GenericJdbcConnectionFactory(databaseConnectionConfig, JDBC_PROPERTIES,
100103
new DatabaseConnectionInfo(DataLakeGen2Constants.DRIVER_CLASS, DataLakeGen2Constants.DEFAULT_PORT)),
101104
configOptions);
102105
}
@@ -283,4 +286,14 @@ protected Schema getSchema(Connection jdbcConnection, TableName tableName, Schem
283286
return schemaBuilder.build();
284287
}
285288
}
289+
290+
@Override
291+
protected CredentialsProvider getCredentialProvider()
292+
{
293+
return CredentialsProviderFactory.createCredentialProvider(
294+
getDatabaseConnectionConfig().getSecret(),
295+
getCachableSecretsManager(),
296+
new DataLakeGen2OAuthCredentialsProvider()
297+
);
298+
}
286299
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*-
2+
* #%L
3+
* athena-datalakegen2
4+
* %%
5+
* Copyright (C) 2019 - 2025 Amazon Web Services
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package com.amazonaws.athena.connectors.datalakegen2;
21+
22+
import com.amazonaws.athena.connector.credentials.CredentialsConstants;
23+
import com.amazonaws.athena.connector.credentials.OAuthCredentialsProvider;
24+
import com.google.common.annotations.VisibleForTesting;
25+
26+
import java.net.URI;
27+
import java.net.http.HttpClient;
28+
import java.net.http.HttpRequest;
29+
import java.util.Map;
30+
31+
/**
32+
* OAuth credentials provider for Azure Data Lake Gen2.
33+
*/
34+
public class DataLakeGen2OAuthCredentialsProvider extends OAuthCredentialsProvider
35+
{
36+
private static final String TOKEN_ENDPOINT_FORMAT = "https://login.microsoftonline.com/%s/oauth2/v2.0/token";
37+
private static final String SCOPE = "https://sql.azuresynapse.net/.default";
38+
private static final String TENANT_ID = "tenant_id";
39+
40+
public DataLakeGen2OAuthCredentialsProvider()
41+
{
42+
super();
43+
}
44+
45+
@VisibleForTesting
46+
protected DataLakeGen2OAuthCredentialsProvider(HttpClient httpClient)
47+
{
48+
super(httpClient);
49+
}
50+
51+
@Override
52+
protected boolean isOAuthConfigured(Map<String, String> secretMap)
53+
{
54+
return secretMap.containsKey(CredentialsConstants.CLIENT_ID) &&
55+
!secretMap.get(CredentialsConstants.CLIENT_ID).isEmpty() &&
56+
secretMap.containsKey(CredentialsConstants.CLIENT_SECRET) &&
57+
!secretMap.get(CredentialsConstants.CLIENT_SECRET).isEmpty() &&
58+
secretMap.containsKey(TENANT_ID) &&
59+
!secretMap.get(TENANT_ID).isEmpty();
60+
}
61+
62+
@Override
63+
protected HttpRequest buildTokenRequest(Map<String, String> secretMap)
64+
{
65+
String clientId = secretMap.get(CredentialsConstants.CLIENT_ID);
66+
String clientSecret = secretMap.get(CredentialsConstants.CLIENT_SECRET);
67+
String tenantId = secretMap.get(TENANT_ID);
68+
String tokenEndpoint = String.format(TOKEN_ENDPOINT_FORMAT, tenantId);
69+
70+
String formData = String.format(
71+
"grant_type=client_credentials&scope=%s&client_id=%s&client_secret=%s",
72+
SCOPE, clientId, clientSecret);
73+
74+
return HttpRequest.newBuilder()
75+
.uri(URI.create(tokenEndpoint))
76+
.header("Content-Type", "application/x-www-form-urlencoded")
77+
.POST(HttpRequest.BodyPublishers.ofString(formData))
78+
.build();
79+
}
80+
}

athena-datalakegen2/src/main/java/com/amazonaws/athena/connectors/datalakegen2/DataLakeGen2RecordHandler.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
* #L%
1919
*/
2020
package com.amazonaws.athena.connectors.datalakegen2;
21+
import com.amazonaws.athena.connector.credentials.CredentialsProvider;
22+
import com.amazonaws.athena.connector.credentials.CredentialsProviderFactory;
2123
import com.amazonaws.athena.connector.lambda.domain.Split;
2224
import com.amazonaws.athena.connector.lambda.domain.TableName;
2325
import com.amazonaws.athena.connector.lambda.domain.predicate.Constraints;
@@ -76,4 +78,14 @@ public PreparedStatement buildSplitSql(Connection jdbcConnection, String catalog
7678
preparedStatement.setFetchSize(FETCH_SIZE);
7779
return preparedStatement;
7880
}
81+
82+
@Override
83+
protected CredentialsProvider getCredentialProvider()
84+
{
85+
return CredentialsProviderFactory.createCredentialProvider(
86+
getDatabaseConnectionConfig().getSecret(),
87+
getCachableSecretsManager(),
88+
new DataLakeGen2OAuthCredentialsProvider()
89+
);
90+
}
7991
}

0 commit comments

Comments
 (0)