Skip to content

Commit 10fc346

Browse files
authored
Refactor AwsAuthenticator to extract code that obtains credentials from the environment (#879)
JAVA-4501
1 parent ca95c53 commit 10fc346

File tree

3 files changed

+154
-164
lines changed

3 files changed

+154
-164
lines changed

driver-core/src/main/com/mongodb/AwsCredential.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.mongodb;
1818

1919
import com.mongodb.annotations.Beta;
20+
import com.mongodb.lang.Nullable;
2021

2122
import static com.mongodb.assertions.Assertions.notNull;
2223

@@ -38,12 +39,12 @@ public final class AwsCredential {
3839
*
3940
* @param accessKeyId the non-null access key ID that identifies the temporary security credentials.
4041
* @param secretAccessKey the non-null secret access key that can be used to sign requests
41-
* @param sessionToken the non-null session token
42+
* @param sessionToken the session token, which may be null
4243
*/
43-
public AwsCredential(final String accessKeyId, final String secretAccessKey, final String sessionToken) {
44+
public AwsCredential(final String accessKeyId, final String secretAccessKey, @Nullable final String sessionToken) {
4445
this.accessKeyId = notNull("accessKeyId", accessKeyId);
4546
this.secretAccessKey = notNull("secretAccessKey", secretAccessKey);
46-
this.sessionToken = notNull("sessionToken", sessionToken);
47+
this.sessionToken = sessionToken;
4748
}
4849

4950
/**
@@ -69,6 +70,7 @@ public String getSecretAccessKey() {
6970
*
7071
* @return the sessionToken, which may not be null
7172
*/
73+
@Nullable
7274
public String getSessionToken() {
7375
return sessionToken;
7476
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.internal.authentication;
18+
19+
import com.mongodb.AwsCredential;
20+
import com.mongodb.MongoInternalException;
21+
import com.mongodb.lang.NonNull;
22+
import org.bson.BsonDocument;
23+
24+
import java.io.BufferedReader;
25+
import java.io.IOException;
26+
import java.io.InputStreamReader;
27+
import java.net.HttpURLConnection;
28+
import java.net.URL;
29+
import java.nio.charset.StandardCharsets;
30+
import java.util.HashMap;
31+
import java.util.Map;
32+
33+
public final class AwsCredentialHelper {
34+
35+
public static AwsCredential obtainFromEnvironment() {
36+
if (System.getenv("AWS_ACCESS_KEY_ID") != null) {
37+
return obtainFromEnvironmentVariables();
38+
} else {
39+
return obtainFromEc2OrEcsResponse();
40+
}
41+
}
42+
43+
private static AwsCredential obtainFromEnvironmentVariables() {
44+
return new AwsCredential(
45+
System.getenv("AWS_ACCESS_KEY_ID"),
46+
System.getenv("AWS_SECRET_ACCESS_KEY"),
47+
System.getenv("AWS_SESSION_TOKEN"));
48+
}
49+
50+
private static AwsCredential obtainFromEc2OrEcsResponse() {
51+
String path = System.getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI");
52+
BsonDocument ec2OrEcsResponse = path == null ? BsonDocument.parse(getEc2Response()) : BsonDocument.parse(getEcsResponse(path));
53+
54+
return new AwsCredential(
55+
ec2OrEcsResponse.getString("AccessKeyId").getValue(),
56+
ec2OrEcsResponse.getString("SecretAccessKey").getValue(),
57+
ec2OrEcsResponse.getString("Token").getValue());
58+
}
59+
60+
private static String getEcsResponse(final String path) {
61+
return getHttpContents("GET", "http://169.254.170.2" + path, null);
62+
}
63+
64+
private static String getEc2Response() {
65+
final String endpoint = "http://169.254.169.254";
66+
final String path = "/latest/meta-data/iam/security-credentials/";
67+
68+
Map<String, String> header = new HashMap<>();
69+
header.put("X-aws-ec2-metadata-token-ttl-seconds", "30");
70+
String token = getHttpContents("PUT", endpoint + "/latest/api/token", header);
71+
72+
header.clear();
73+
header.put("X-aws-ec2-metadata-token", token);
74+
String role = getHttpContents("GET", endpoint + path, header);
75+
return getHttpContents("GET", endpoint + path + role, header);
76+
}
77+
78+
@NonNull
79+
private static String getHttpContents(final String method, final String endpoint, final Map<String, String> headers) {
80+
StringBuilder content = new StringBuilder();
81+
HttpURLConnection conn = null;
82+
try {
83+
conn = (HttpURLConnection) new URL(endpoint).openConnection();
84+
conn.setRequestMethod(method);
85+
conn.setReadTimeout(10000);
86+
if (headers != null) {
87+
for (Map.Entry<String, String> kvp : headers.entrySet()) {
88+
conn.setRequestProperty(kvp.getKey(), kvp.getValue());
89+
}
90+
}
91+
92+
int status = conn.getResponseCode();
93+
if (status != HttpURLConnection.HTTP_OK) {
94+
throw new IOException(String.format("%d %s", status, conn.getResponseMessage()));
95+
}
96+
97+
try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
98+
String inputLine;
99+
while ((inputLine = in.readLine()) != null) {
100+
content.append(inputLine);
101+
}
102+
}
103+
} catch (IOException e) {
104+
throw new MongoInternalException("Unexpected IOException", e);
105+
} finally {
106+
if (conn != null) {
107+
conn.disconnect();
108+
}
109+
}
110+
return content.toString();
111+
}
112+
113+
private AwsCredentialHelper() {
114+
}
115+
}

0 commit comments

Comments
 (0)