Skip to content

Commit 697b152

Browse files
committed
update junit test cases and handle case where we can get security exception other than one expected
1 parent 5049b73 commit 697b152

3 files changed

Lines changed: 85 additions & 12 deletions

File tree

http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/Apache5HttpClient.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ public interface Builder extends SdkHttpClient.Builder<Apache5HttpClient.Builder
544544
}
545545

546546
private static final class DefaultBuilder implements Builder {
547-
private static final String[] REQUIRED_TCP_KEEPALIVE_PERMISSIONS = {
547+
private static final String[] REQUIRED_TCP_SOCKET_OPTION_PERMISSIONS = {
548548
"setOption.TCP_KEEPIDLE",
549549
"setOption.TCP_KEEPINTERVAL",
550550
"setOption.TCP_KEEPCOUNT"
@@ -751,7 +751,7 @@ public void setAuthSchemeProviderRegistry(Registry<AuthSchemeFactory> authScheme
751751
public SdkHttpClient buildWithDefaults(AttributeMap serviceDefaults) {
752752
AttributeMap resolvedOptions = standardOptions.build().merge(serviceDefaults).merge(
753753
SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS);
754-
checkTcpKeepAlivePermissions();
754+
checkTcpSocketOptionPermissions();
755755
return new Apache5HttpClient(this, resolvedOptions);
756756
}
757757

@@ -760,28 +760,46 @@ public SdkHttpClient buildWithDefaults(AttributeMap serviceDefaults) {
760760
* that Apache HC5 requires for its default TCP keepalive socket options.
761761
* No-op when no SecurityManager is installed (including Java 24+).
762762
*/
763-
private static void checkTcpKeepAlivePermissions() {
763+
private static void checkTcpSocketOptionPermissions() {
764764
SecurityManager sm = System.getSecurityManager();
765765
if (sm == null) {
766766
return;
767767
}
768768

769769
try {
770770
Class<?> permClass = ClassLoaderHelper.loadClass("jdk.net.NetworkPermission", Apache5HttpClient.class);
771-
for (String permName : REQUIRED_TCP_KEEPALIVE_PERMISSIONS) {
771+
for (String permName : REQUIRED_TCP_SOCKET_OPTION_PERMISSIONS) {
772772
java.security.Permission perm =
773773
(java.security.Permission) permClass.getConstructor(String.class).newInstance(permName);
774774
sm.checkPermission(perm);
775775
}
776776
} catch (SecurityException e) {
777-
throw new IllegalStateException(
778-
"Apache5HttpClient requires jdk.net.NetworkPermission for \""
779-
+ String.join("\", \"", REQUIRED_TCP_KEEPALIVE_PERMISSIONS)
780-
+ "\" when a SecurityManager is active.", e);
777+
if (isTcpSocketOptionPermissionDenied(e)) {
778+
throw new IllegalStateException(
779+
"Apache5HttpClient requires jdk.net.NetworkPermission for \""
780+
+ String.join("\", \"", REQUIRED_TCP_SOCKET_OPTION_PERMISSIONS)
781+
+ "\" when a SecurityManager is active.", e);
782+
}
783+
log.debug(() -> "SecurityManager denied a non-TCP socket option permission during "
784+
+ "verification: " + e.getMessage(), e);
781785
} catch (Exception e) {
782-
log.warn(() -> "Unable to verify TCP keepalive permissions: " + e.getMessage(), e);
786+
log.debug(() -> "Could not verify jdk.net.NetworkPermission for TCP socket options: " + e.getMessage(), e);
783787
}
784788
}
789+
790+
private static boolean isTcpSocketOptionPermissionDenied(SecurityException e) {
791+
String message = e.getMessage();
792+
if (message == null) {
793+
return false;
794+
}
795+
for (String perm : REQUIRED_TCP_SOCKET_OPTION_PERMISSIONS) {
796+
if (message.contains(perm)) {
797+
return true;
798+
}
799+
}
800+
return false;
801+
}
802+
785803
}
786804

787805
private static class ApacheConnectionManagerFactory {

http-clients/apache5-client/src/test/java/software/amazon/awssdk/http/apache5/Apache5HttpClientSecurityManagerTest.java

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package software.amazon.awssdk.http.apache5;
1717

18+
import static org.assertj.core.api.Assertions.assertThat;
1819
import static org.assertj.core.api.Assertions.assertThatNoException;
1920
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2021

@@ -23,13 +24,15 @@
2324
import java.util.HashSet;
2425
import java.util.Set;
2526
import java.util.stream.Stream;
27+
import org.apache.logging.log4j.Level;
2628
import org.junit.jupiter.api.AfterEach;
2729
import org.junit.jupiter.api.Test;
2830
import org.junit.jupiter.api.condition.EnabledForJreRange;
2931
import org.junit.jupiter.api.condition.JRE;
3032
import org.junit.jupiter.params.ParameterizedTest;
3133
import org.junit.jupiter.params.provider.Arguments;
3234
import org.junit.jupiter.params.provider.MethodSource;
35+
import software.amazon.awssdk.testutils.LogCaptor;
3336

3437
/**
3538
* Tests that Apache5HttpClient fails fast at construction time when a SecurityManager
@@ -47,7 +50,7 @@ void tearDown() {
4750

4851
@Test
4952
void buildWithDefaults_whenStandardPermissionsGrantedButNetworkPermissionMissing_shouldThrowIllegalStateException() {
50-
System.setProperty("java.security.policy", "=" + getPolicyUrl());
53+
System.setProperty("java.security.policy", "=" + getPolicyUrl("security-manager-test.policy"));
5154
java.security.Policy.getPolicy().refresh();
5255
System.setSecurityManager(new SecurityManager());
5356

@@ -56,8 +59,42 @@ void buildWithDefaults_whenStandardPermissionsGrantedButNetworkPermissionMissing
5659
.hasMessageContaining("jdk.net.NetworkPermission");
5760
}
5861

59-
private String getPolicyUrl() {
60-
return getClass().getResource("security-manager-test.policy").toExternalForm();
62+
@Test
63+
void buildWithDefaults_whenPolicyGrantsNetworkPermissions_shouldSucceed() {
64+
System.setProperty("java.security.policy", "=" + getPolicyUrl("security-manager-test-with-network-permissions.policy"));
65+
java.security.Policy.getPolicy().refresh();
66+
System.setSecurityManager(new SecurityManager());
67+
68+
assertThatNoException().isThrownBy(() -> {
69+
Apache5HttpClient.builder().build().close();
70+
});
71+
}
72+
73+
private String getPolicyUrl(String policyFileName) {
74+
return getClass().getResource(policyFileName).toExternalForm();
75+
}
76+
77+
@Test
78+
void buildWithDefaults_whenUnrelatedSecurityExceptionThrown_shouldNotThrow() {
79+
System.setSecurityManager(new SecurityManager() {
80+
@Override
81+
public void checkPermission(Permission perm) {
82+
if ("jdk.net.NetworkPermission".equals(perm.getClass().getName())) {
83+
throw new SecurityException("access denied: some.unrelated.permission");
84+
}
85+
}
86+
});
87+
88+
try (LogCaptor logCaptor = LogCaptor.create(Level.DEBUG)) {
89+
assertThatNoException().isThrownBy(() -> {
90+
Apache5HttpClient.builder().build().close();
91+
});
92+
assertThat(logCaptor.loggedEvents()).anySatisfy(logEvent -> {
93+
assertThat(logEvent.getLevel()).isEqualTo(Level.DEBUG);
94+
assertThat(logEvent.getMessage().getFormattedMessage())
95+
.contains("SecurityManager denied a non-TCP socket option permission");
96+
});
97+
}
6198
}
6299

63100
@ParameterizedTest
@@ -120,4 +157,5 @@ public void checkPermission(Permission perm) {
120157
}
121158
}
122159
}
160+
123161
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
grant {
2+
permission java.util.PropertyPermission "*", "read,write";
3+
permission java.io.FilePermission "<<ALL FILES>>", "read,write";
4+
permission java.lang.RuntimePermission "getenv.*";
5+
permission "java.lang.RuntimePermission" "accessDeclaredMembers";
6+
permission "java.lang.RuntimePermission" "modifyThread";
7+
permission "javax.net.ssl.SSLPermission" "setDefaultSSLContext";
8+
permission "java.net.SocketPermission" "*", "connect,resolve";
9+
10+
// Needed for test to remove the security manager
11+
permission java.lang.RuntimePermission "setSecurityManager";
12+
13+
// TCP socket option permissions required by Apache HC5
14+
permission "jdk.net.NetworkPermission" "setOption.TCP_KEEPIDLE";
15+
permission "jdk.net.NetworkPermission" "setOption.TCP_KEEPINTERVAL";
16+
permission "jdk.net.NetworkPermission" "setOption.TCP_KEEPCOUNT";
17+
};

0 commit comments

Comments
 (0)