|
25 | 25 | import static com.github.tomakehurst.wiremock.client.WireMock.verify; |
26 | 26 | import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; |
27 | 27 | import static org.assertj.core.api.Assertions.assertThat; |
| 28 | +import static org.assertj.core.api.Assertions.assertThatThrownBy; |
28 | 29 |
|
29 | 30 | import com.github.tomakehurst.wiremock.junit5.WireMockExtension; |
30 | 31 | import com.github.tomakehurst.wiremock.junit5.WireMockTest; |
|
33 | 34 | import org.junit.jupiter.api.Test; |
34 | 35 | import org.junit.jupiter.api.extension.RegisterExtension; |
35 | 36 | import software.amazon.awssdk.core.SdkSystemSetting; |
| 37 | +import software.amazon.awssdk.core.exception.SdkClientException; |
36 | 38 | import software.amazon.awssdk.testutils.EnvironmentVariableHelper; |
37 | 39 | import software.amazon.awssdk.utils.DateUtils; |
38 | 40 |
|
@@ -117,6 +119,134 @@ void resolveCredentials_fallsBackToLegacy_noAccountId() { |
117 | 119 | verifyImdsCallWithToken(false); |
118 | 120 | } |
119 | 121 |
|
| 122 | + @Test |
| 123 | + void resolveCredentials_withImdsDisabled_returnsNoCredentials() { |
| 124 | + environmentVariableHelper.set(SdkSystemSetting.AWS_EC2_METADATA_DISABLED.environmentVariable(), "true"); |
| 125 | + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); |
| 126 | + |
| 127 | + assertThatThrownBy(() -> provider.resolveCredentials()) |
| 128 | + .isInstanceOf(SdkClientException.class) |
| 129 | + .hasMessageContaining("IMDS credentials have been disabled"); |
| 130 | + |
| 131 | + verify(0, putRequestedFor(urlPathEqualTo(TOKEN_RESOURCE_PATH))); |
| 132 | + verify(0, getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))); |
| 133 | + verify(0, getRequestedFor(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH))); |
| 134 | + } |
| 135 | + |
| 136 | + |
| 137 | + @Test |
| 138 | + void resolveCredentials_withInvalidProfile_throwsException() { |
| 139 | + String invalidProfile = "my-profile-0004"; |
| 140 | + |
| 141 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH)) |
| 142 | + .willReturn(aResponse().withBody(invalidProfile))); |
| 143 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)) |
| 144 | + .willReturn(aResponse().withBody(invalidProfile))); |
| 145 | + |
| 146 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH + invalidProfile)) |
| 147 | + .willReturn(aResponse().withStatus(404))); |
| 148 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + invalidProfile)) |
| 149 | + .willReturn(aResponse().withStatus(404))); |
| 150 | + |
| 151 | + wireMockServer.stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)) |
| 152 | + .willReturn(aResponse().withBody(TOKEN_STUB))); |
| 153 | + |
| 154 | + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); |
| 155 | + |
| 156 | + assertThatThrownBy(() -> provider.resolveCredentials()) |
| 157 | + .isInstanceOf(SdkClientException.class) |
| 158 | + .hasMessageContaining("Failed to load credentials from IMDS."); |
| 159 | + } |
| 160 | + |
| 161 | + @Test |
| 162 | + void resolveCredentials_withUnstableProfile_noAccountId_refreshesCredentials() { |
| 163 | + String firstCredentials = String.format( |
| 164 | + "{\"AccessKeyId\":\"ASIAIOSFODNN7EXAMPLE\"," + |
| 165 | + "\"SecretAccessKey\":\"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"," + |
| 166 | + "\"Token\":\"AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKw\"," + |
| 167 | + "\"Expiration\":\"%s\"," + |
| 168 | + "\"Code\":\"Success\"," + |
| 169 | + "\"Type\":\"AWS-HMAC\"," + |
| 170 | + "\"LastUpdated\":\"2025-03-18T20:53:17.832308Z\"," + |
| 171 | + "\"UnexpectedElement7\":{\"Name\":\"ignore-me-7\"}}", |
| 172 | + DateUtils.formatIso8601Date(Instant.now().plus(Duration.ofDays(1))) |
| 173 | + ); |
| 174 | + |
| 175 | + String secondCredentials = String.format( |
| 176 | + "{\"AccessKeyId\":\"ASIAIOSFODNN7EXAMPLE\"," + |
| 177 | + "\"SecretAccessKey\":\"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"," + |
| 178 | + "\"Token\":\"AQoEXAMPLEH4aoAH0gNCAPyJxz4BlCFFxWNE1OPTgk5TthT+FvwqnKw\"," + |
| 179 | + "\"Expiration\":\"%s\"," + |
| 180 | + "\"Code\":\"Success\"," + |
| 181 | + "\"Type\":\"AWS-HMAC\"," + |
| 182 | + "\"LastUpdated\":\"2025-03-18T20:53:17.832308Z\"," + |
| 183 | + "\"UnexpectedElement7\":{\"Name\":\"ignore-me-7\"}}", |
| 184 | + DateUtils.formatIso8601Date(Instant.now().plus(Duration.ofDays(1))) |
| 185 | + ); |
| 186 | + |
| 187 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH)) |
| 188 | + .inScenario("Profile Change No AccountId") |
| 189 | + .whenScenarioStateIs("Started") |
| 190 | + .willReturn(aResponse().withBody("my-profile-0007")) |
| 191 | + .willSetStateTo("First Profile")); |
| 192 | + |
| 193 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH + "my-profile-0007")) |
| 194 | + .inScenario("Profile Change No AccountId") |
| 195 | + .whenScenarioStateIs("First Profile") |
| 196 | + .willReturn(aResponse().withBody(firstCredentials)) |
| 197 | + .willSetStateTo("First Profile Done")); |
| 198 | + |
| 199 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH + "my-profile-0007")) |
| 200 | + .inScenario("Profile Change No AccountId") |
| 201 | + .whenScenarioStateIs("First Profile Done") |
| 202 | + .willReturn(aResponse().withStatus(404)) |
| 203 | + .willSetStateTo("Profile Changed")); |
| 204 | + |
| 205 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH)) |
| 206 | + .inScenario("Profile Change No AccountId") |
| 207 | + .whenScenarioStateIs("Profile Changed") |
| 208 | + .willReturn(aResponse().withBody("my-profile-0007-b"))); |
| 209 | + |
| 210 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH + "my-profile-0007-b")) |
| 211 | + .willReturn(aResponse().withBody(secondCredentials))); |
| 212 | + |
| 213 | + wireMockServer.stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)) |
| 214 | + .willReturn(aResponse().withBody(TOKEN_STUB))); |
| 215 | + |
| 216 | + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); |
| 217 | + |
| 218 | + AwsCredentials creds1 = provider.resolveCredentials(); |
| 219 | + assertThat(creds1.accountId()).isEmpty(); |
| 220 | + assertThat(creds1.accessKeyId()).isEqualTo("ASIAIOSFODNN7EXAMPLE"); |
| 221 | + |
| 222 | + AwsCredentials creds2 = provider.resolveCredentials(); |
| 223 | + assertThat(creds2.accountId()).isEmpty(); |
| 224 | + assertThat(creds2.accessKeyId()).isEqualTo("ASIAIOSFODNN7EXAMPLE"); |
| 225 | + } |
| 226 | + |
| 227 | + @Test |
| 228 | + void resolveCredentials_withDiscoveredInvalidProfile_noAccountId_throwsException() { |
| 229 | + String invalidProfile = "my-profile-0008"; |
| 230 | + |
| 231 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH)) |
| 232 | + .willReturn(aResponse().withBody(invalidProfile))); |
| 233 | + |
| 234 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_EXTENDED_RESOURCE_PATH + invalidProfile)) |
| 235 | + .willReturn(aResponse().withStatus(404))); |
| 236 | + |
| 237 | + wireMockServer.stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + invalidProfile)) |
| 238 | + .willReturn(aResponse().withStatus(404))); |
| 239 | + |
| 240 | + wireMockServer.stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)) |
| 241 | + .willReturn(aResponse().withBody(TOKEN_STUB))); |
| 242 | + |
| 243 | + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); |
| 244 | + |
| 245 | + assertThatThrownBy(() -> provider.resolveCredentials()) |
| 246 | + .isInstanceOf(SdkClientException.class) |
| 247 | + .hasMessageContaining("Failed to load credentials from IMDS"); |
| 248 | + } |
| 249 | + |
120 | 250 | @Test |
121 | 251 | void resolveCredentials_cachesProfile_maintainsAccountId() { |
122 | 252 | String credentialsWithAccountId = String.format( |
|
0 commit comments