KEYCLOAK-15849 : auth-remote-server exclude -> removed duplicated annotation, fixed @Test(timeout) bug -> replaced by lambda expression.

This commit is contained in:
Lukas Hanusovsky 2021-01-27 15:29:36 +01:00 committed by Pavel Drozd
parent 63b19389c1
commit 4a2830bc2e
5 changed files with 167 additions and 133 deletions

View file

@ -27,6 +27,7 @@ import org.jboss.logging.Logger;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.model.TestTimedOutException;
import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.AuthenticationManagementResource; import org.keycloak.admin.client.resource.AuthenticationManagementResource;
import org.keycloak.admin.client.resource.RealmsResource; import org.keycloak.admin.client.resource.RealmsResource;
@ -78,6 +79,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.concurrent.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -403,6 +405,34 @@ public abstract class AbstractKeycloakTest {
.replace("8180", "8543"); .replace("8180", "8543");
} }
protected interface ExecutableTestMethod {
void execute() throws Exception;
}
protected void runTestWithTimeout(long timeout, ExecutableTestMethod executableTestMethod) throws Exception {
ExecutorService service = Executors.newSingleThreadExecutor();
Callable<Object> callable = new Callable<Object>() {
public Object call() throws Exception {
executableTestMethod.execute();
return null;
}
};
Future<Object> result = service.submit(callable);
service.shutdown();
try {
boolean terminated = service.awaitTermination(timeout,
TimeUnit.MILLISECONDS);
if (!terminated) {
service.shutdownNow();
}
result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation
} catch (TimeoutException e) {
throw new TestTimedOutException(timeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new Exception(e);
}
}
/** /**
* @return Return <code>true</code> if you wish to automatically post-process realm and replace * @return Return <code>true</code> if you wish to automatically post-process realm and replace
* all http values with https (and correct ports). * all http values with https (and correct ports).

View file

@ -132,41 +132,42 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest {
oauth.clientId("hardcoded-client"); oauth.clientId("hardcoded-client");
} }
@Test(timeout = 4000) @Test
@AuthServerContainerExclude(AuthServer.REMOTE) // testingClient doesn't work with remote public void testSearchTimeout() throws Exception{
public void testSearchTimeout() { runTestWithTimeout(4000, () -> {
String hardcodedClient = HardcodedClientStorageProviderFactory.PROVIDER_ID; String hardcodedClient = HardcodedClientStorageProviderFactory.PROVIDER_ID;
String delayedSearch = HardcodedClientStorageProviderFactory.DELAYED_SEARCH; String delayedSearch = HardcodedClientStorageProviderFactory.DELAYED_SEARCH;
String providerId = this.providerId; String providerId = this.providerId;
testingClient.server().run(session -> { testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
assertThat(session.clientStorageManager()
.searchClientsByClientIdStream(realm, "client", null, null)
.map(ClientModel::getClientId)
.collect(Collectors.toList()),
allOf(
hasItem(hardcodedClient),
hasItem("root-url-client"))
);
//update the provider to simulate delay during the search
ComponentModel memoryProvider = realm.getComponent(providerId);
memoryProvider.getConfig().putSingle(delayedSearch, Boolean.toString(true));
realm.updateComponent(memoryProvider);
}); assertThat(session.clientStorageManager()
.searchClientsByClientIdStream(realm, "client", null, null)
testingClient.server().run(session -> { .map(ClientModel::getClientId)
// search for clients and check hardcoded-client is not present .collect(Collectors.toList()),
assertThat(session.clientStorageManager() allOf(
.searchClientsByClientIdStream(session.realms().getRealmByName(AuthRealm.TEST), "client", null, null) hasItem(hardcodedClient),
.map(ClientModel::getClientId) hasItem("root-url-client"))
.collect(Collectors.toList()), );
allOf(
not(hasItem(hardcodedClient)), //update the provider to simulate delay during the search
hasItem("root-url-client") ComponentModel memoryProvider = realm.getComponent(providerId);
)); memoryProvider.getConfig().putSingle(delayedSearch, Boolean.toString(true));
realm.updateComponent(memoryProvider);
});
testingClient.server().run(session -> {
// search for clients and check hardcoded-client is not present
assertThat(session.clientStorageManager()
.searchClientsByClientIdStream(session.realms().getRealmByName(AuthRealm.TEST), "client", null, null)
.map(ClientModel::getClientId)
.collect(Collectors.toList()),
allOf(
not(hasItem(hardcodedClient)),
hasItem("root-url-client")
));
});
}); });
} }

View file

@ -86,41 +86,42 @@ public class GroupStorageTest extends AbstractTestRealmKeycloakTest {
}); });
} }
@Test(timeout = 4000) @Test
@AuthServerContainerExclude(AuthServer.REMOTE) // testingClient doesn't work with remote public void testSearchTimeout() throws Exception{
public void testSearchTimeout() { runTestWithTimeout(4000, () -> {
String hardcodedGroup = HardcodedGroupStorageProviderFactory.PROVIDER_ID; String hardcodedGroup = HardcodedGroupStorageProviderFactory.PROVIDER_ID;
String delayedSearch = HardcodedGroupStorageProviderFactory.DELAYED_SEARCH; String delayedSearch = HardcodedGroupStorageProviderFactory.DELAYED_SEARCH;
String providerId = this.providerId; String providerId = this.providerId;
testingClient.server().run(session -> { testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
assertThat(session.groupStorageManager() assertThat(session.groupStorageManager()
.searchForGroupByName(realm, "group", null, null).stream() .searchForGroupByName(realm, "group", null, null).stream()
.map(GroupModel::getName) .map(GroupModel::getName)
.collect(Collectors.toList()), .collect(Collectors.toList()),
allOf( allOf(
hasItem(hardcodedGroup), hasItem(hardcodedGroup),
hasItem("sample-realm-group")) hasItem("sample-realm-group"))
); );
//update the provider to simulate delay during the search //update the provider to simulate delay during the search
ComponentModel memoryProvider = realm.getComponent(providerId); ComponentModel memoryProvider = realm.getComponent(providerId);
memoryProvider.getConfig().putSingle(delayedSearch, Boolean.toString(true)); memoryProvider.getConfig().putSingle(delayedSearch, Boolean.toString(true));
realm.updateComponent(memoryProvider); realm.updateComponent(memoryProvider);
}); });
testingClient.server().run(session -> { testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
// search for groups and check hardcoded-group is not present // search for groups and check hardcoded-group is not present
assertThat(session.groupStorageManager() assertThat(session.groupStorageManager()
.searchForGroupByName(realm, "group", null, null).stream() .searchForGroupByName(realm, "group", null, null).stream()
.map(GroupModel::getName) .map(GroupModel::getName)
.collect(Collectors.toList()), .collect(Collectors.toList()),
allOf( allOf(
not(hasItem(hardcodedGroup)), not(hasItem(hardcodedGroup)),
hasItem("sample-realm-group") hasItem("sample-realm-group")
)); ));
});
}); });
} }

View file

@ -93,41 +93,42 @@ public class RoleStorageTest extends AbstractTestRealmKeycloakTest {
}); });
} }
@Test(timeout = 4000) @Test
@AuthServerContainerExclude(AuthServer.REMOTE) // testingClient doesn't work with remote public void testSearchTimeout() throws Exception{
public void testSearchTimeout() { runTestWithTimeout(4000, () -> {
String hardcodedRole = HardcodedRoleStorageProviderFactory.PROVIDER_ID; String hardcodedRole = HardcodedRoleStorageProviderFactory.PROVIDER_ID;
String delayedSearch = HardcodedRoleStorageProviderFactory.DELAYED_SEARCH; String delayedSearch = HardcodedRoleStorageProviderFactory.DELAYED_SEARCH;
String providerId = this.providerId; String providerId = this.providerId;
testingClient.server().run(session -> { testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
assertThat(session.roleStorageManager() assertThat(session.roleStorageManager()
.searchForRolesStream(realm, "role", null, null) .searchForRolesStream(realm, "role", null, null)
.map(RoleModel::getName) .map(RoleModel::getName)
.collect(Collectors.toList()), .collect(Collectors.toList()),
allOf( allOf(
hasItem(hardcodedRole), hasItem(hardcodedRole),
hasItem("sample-realm-role")) hasItem("sample-realm-role"))
); );
//update the provider to simulate delay during the search //update the provider to simulate delay during the search
ComponentModel memoryProvider = realm.getComponent(providerId); ComponentModel memoryProvider = realm.getComponent(providerId);
memoryProvider.getConfig().putSingle(delayedSearch, Boolean.toString(true)); memoryProvider.getConfig().putSingle(delayedSearch, Boolean.toString(true));
realm.updateComponent(memoryProvider); realm.updateComponent(memoryProvider);
}); });
testingClient.server().run(session -> { testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST); RealmModel realm = session.realms().getRealmByName(AuthRealm.TEST);
// search for roles and check hardcoded-role is not present // search for roles and check hardcoded-role is not present
assertThat(session.roleStorageManager() assertThat(session.roleStorageManager()
.searchForRolesStream(realm, "role", null, null) .searchForRolesStream(realm, "role", null, null)
.map(RoleModel::getName) .map(RoleModel::getName)
.collect(Collectors.toList()), .collect(Collectors.toList()),
allOf( allOf(
not(hasItem(hardcodedRole)), not(hasItem(hardcodedRole)),
hasItem("sample-realm-role") hasItem("sample-realm-role")
)); ));
});
}); });
} }

View file

@ -43,47 +43,48 @@ import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
@AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT9) @AppServerContainer(ContainerConstants.APP_SERVER_TOMCAT9)
public class SamlXMLAttacksTest extends AbstractSamlTest { public class SamlXMLAttacksTest extends AbstractSamlTest {
@Test(timeout = 4000) @Test
public void testXMLBombAttackResistance() throws Exception { public void testXMLBombAttackResistance() throws Exception {
runTestWithTimeout(4000, () -> {
String bombDoctype = "<!DOCTYPE AuthnRequest [" +
" <!ENTITY lol \"lol\">" +
"<!ELEMENT AuthnRequest (#PCDATA)>" +
"<!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">" +
"<!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">" +
"<!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">" +
"<!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">" +
"<!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">" +
"<!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">" +
"<!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">" +
"<!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">" +
"<!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" +
"]>";
String bombDoctype = "<!DOCTYPE AuthnRequest [" + String samlAuthnRequest = "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"a123\" Version=\"2.0\" IssueInstant=\"2014-07-16T23:52:45Z\" >" +
" <!ENTITY lol \"lol\">" + "<saml:Issuer>" + SAML_CLIENT_ID_SALES_POST + "&lol9;</saml:Issuer>" +
"<!ELEMENT AuthnRequest (#PCDATA)>" + "</samlp:AuthnRequest>";
"<!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">" +
"<!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">" +
"<!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">" +
"<!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">" +
"<!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">" +
"<!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">" +
"<!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">" +
"<!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">" +
"<!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">" +
"]>";
String samlAuthnRequest = "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"a123\" Version=\"2.0\" IssueInstant=\"2014-07-16T23:52:45Z\" >" +
"<saml:Issuer>" + SAML_CLIENT_ID_SALES_POST + "&lol9;</saml:Issuer>" +
"</samlp:AuthnRequest>";
try (CloseableHttpClient client = HttpClientBuilder.create().build()) { try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
HttpPost post = new HttpPost(getAuthServerSamlEndpoint(REALM_NAME)); HttpPost post = new HttpPost(getAuthServerSamlEndpoint(REALM_NAME));
List<NameValuePair> parameters = new LinkedList<>(); List<NameValuePair> parameters = new LinkedList<>();
String encoded = PostBindingUtil.base64Encode(bombDoctype + samlAuthnRequest); String encoded = PostBindingUtil.base64Encode(bombDoctype + samlAuthnRequest);
parameters.add(new BasicNameValuePair(GeneralConstants.SAML_REQUEST_KEY, encoded)); parameters.add(new BasicNameValuePair(GeneralConstants.SAML_REQUEST_KEY, encoded));
UrlEncodedFormEntity formEntity; UrlEncodedFormEntity formEntity;
try { try {
formEntity = new UrlEncodedFormEntity(parameters, "UTF-8"); formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
}
post.setEntity(formEntity);
try (CloseableHttpResponse response = client.execute(post)) {
assertThat(response, bodyHC(containsString("Invalid Request")));
}
} }
});
post.setEntity(formEntity);
try (CloseableHttpResponse response = client.execute(post)) {
assertThat(response, bodyHC(containsString("Invalid Request")));
}
}
} }
@Deployment(name = "DTD") @Deployment(name = "DTD")