From 9438bb9994ef8c4dc68bfe100f4f3c28b50990f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Vav=C5=99=C3=ADk?= Date: Fri, 17 Apr 2026 11:14:46 +0200 Subject: [PATCH] chore(testsuite): migrate error package to new test framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Closes: https://github.com/keycloak/keycloak/issues/47815 Signed-off-by: Michal Vavřík --- .../tests/error/EscapeErrorPageTest.java | 76 +++++ .../tests/error/UncaughtErrorPageTest.java | 255 +++++++++++++++++ .../keycloak/tests/suites/Base2TestSuite.java | 1 + .../ErrorTestingRealmResourceProvider.java | 41 +++ ...orTestingRealmResourceProviderFactory.java | 34 +++ ...ices.resource.RealmResourceProviderFactory | 1 + .../rest/TestingResourceProvider.java | 19 -- .../client/resources/TestingResource.java | 19 -- .../testsuite/error/EscapeErrorPageTest.java | 92 ------ .../error/UncaughtErrorPageTest.java | 270 ------------------ .../tests/base/testsuites/base-suite | 1 - 11 files changed, 408 insertions(+), 401 deletions(-) create mode 100644 tests/base/src/test/java/org/keycloak/tests/error/EscapeErrorPageTest.java create mode 100644 tests/base/src/test/java/org/keycloak/tests/error/UncaughtErrorPageTest.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProvider.java create mode 100644 tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProviderFactory.java create mode 100644 tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory delete mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/EscapeErrorPageTest.java delete mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java diff --git a/tests/base/src/test/java/org/keycloak/tests/error/EscapeErrorPageTest.java b/tests/base/src/test/java/org/keycloak/tests/error/EscapeErrorPageTest.java new file mode 100644 index 000000000000..2971d5e41777 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/error/EscapeErrorPageTest.java @@ -0,0 +1,76 @@ +package org.keycloak.tests.error; + +import java.net.URI; + +import org.keycloak.common.util.KeycloakUriBuilder; +import org.keycloak.testframework.annotations.InjectKeycloakUrls; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.server.KeycloakUrls; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.page.ErrorPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.tests.common.CustomProvidersServerConfig; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@KeycloakIntegrationTest(config = CustomProvidersServerConfig.class) +class EscapeErrorPageTest { + + @InjectPage + ErrorPage errorPage; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectKeycloakUrls + KeycloakUrls keycloakUrls; + + @Test + void innerScript() { + checkMessage("\"alert(1)/>\"", "\"alert(1)/>\""); + } + + @Test + void innerURL() { + checkMessage("\"See https://www.keycloak.org/docs\"", "\"See https://www.keycloak.org/docs\""); + } + + @Test + void url() { + checkMessage("See https://www.keycloak.org/docs", "See https://www.keycloak.org/docs"); + } + + @Test + void ampersandEscape() { + checkMessage("<img src="something">", ""); + } + + @Test + void hexEscape() { + checkMessage("<img src=something/>", ""); + } + + @Test + void plainText() { + checkMessage("It doesn't work", "It doesn't work"); + } + + @Test + void textWithPlus() { + checkMessage("Fact: 1+1=2", "Fact: 1+1=2"); + } + + private void checkMessage(String queryParam, String expected) { + URI uri = KeycloakUriBuilder.fromUri(keycloakUrls.getMasterRealm()) + .path("error-testing/display-error-message") + .queryParam("message", queryParam) + .build(); + driver.open(uri.toString()); + errorPage.assertCurrent(); + assertThat("Sanitized error message for input: " + queryParam, errorPage.getError(), is(expected)); + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/error/UncaughtErrorPageTest.java b/tests/base/src/test/java/org/keycloak/tests/error/UncaughtErrorPageTest.java new file mode 100644 index 000000000000..9ac5aec21816 --- /dev/null +++ b/tests/base/src/test/java/org/keycloak/tests/error/UncaughtErrorPageTest.java @@ -0,0 +1,255 @@ +package org.keycloak.tests.error; + +import java.io.IOException; +import java.net.URI; + +import org.keycloak.OAuthErrorException; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.common.util.KeycloakUriBuilder; +import org.keycloak.models.BrowserSecurityHeaders; +import org.keycloak.representations.idm.OAuth2ErrorRepresentation; +import org.keycloak.testframework.annotations.InjectAdminClient; +import org.keycloak.testframework.annotations.InjectHttpClient; +import org.keycloak.testframework.annotations.InjectKeycloakUrls; +import org.keycloak.testframework.annotations.InjectRealm; +import org.keycloak.testframework.annotations.KeycloakIntegrationTest; +import org.keycloak.testframework.injection.LifeCycle; +import org.keycloak.testframework.oauth.OAuthClient; +import org.keycloak.testframework.oauth.annotations.InjectOAuthClient; +import org.keycloak.testframework.realm.ManagedRealm; +import org.keycloak.testframework.server.KeycloakUrls; +import org.keycloak.testframework.ui.annotations.InjectPage; +import org.keycloak.testframework.ui.annotations.InjectWebDriver; +import org.keycloak.testframework.ui.page.ErrorPage; +import org.keycloak.testframework.ui.webdriver.ManagedWebDriver; +import org.keycloak.tests.common.CustomProvidersServerConfig; +import org.keycloak.util.JsonSerialization; +import org.keycloak.utils.MediaType; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; + +import static org.keycloak.utils.MediaType.APPLICATION_JSON; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +@KeycloakIntegrationTest(config = CustomProvidersServerConfig.class) +class UncaughtErrorPageTest { + + @InjectPage + ErrorPage errorPage; + + @InjectWebDriver + ManagedWebDriver driver; + + @InjectKeycloakUrls + KeycloakUrls keycloakUrls; + + @InjectHttpClient + HttpClient httpClient; + + @InjectAdminClient + Keycloak adminClient; + + @InjectOAuthClient(lifecycle = LifeCycle.METHOD) + OAuthClient oauth; + + @InjectRealm(attachTo = "master") + ManagedRealm masterRealm; + + @Test + void invalidResource() { + checkPageNotFound("/nosuch"); + } + + @Test + void invalidRealm() { + checkPageNotFound("/realms/nosuch"); + } + + @Test + void invalidRealmResource() { + checkPageNotFound("/realms/master/nosuch"); + } + + @Test + void uncaughtErrorJson() throws IOException { + HttpResponse response = httpClient.execute(new HttpGet(errorTestingUri("uncaught-error"))); + + assertThat("status code", response.getStatusLine().getStatusCode(), is(500)); + + OAuth2ErrorRepresentation error = readError(response); + assertThat("error code", error.getError(), is("unknown_error")); + assertThat("error description", error.getErrorDescription(), + containsString("For more on this error consult the server log")); + } + + @Test + void uncaughtErrorClientRegistration() throws IOException { + HttpPost post = new HttpPost(masterRealmUri("clients-registrations/openid-connect")); + post.setEntity(new StringEntity("{ invalid : invalid }")); + post.setHeader("Content-Type", APPLICATION_JSON); + + HttpResponse response = httpClient.execute(post); + assertThat("status code", response.getStatusLine().getStatusCode(), is(400)); + + OAuth2ErrorRepresentation error = readError(response); + assertThat("error code", error.getError(), is(OAuthErrorException.INVALID_REQUEST)); + assertThat("error description", error.getErrorDescription(), is(notNullValue())); + } + + @Test + void uncaughtErrorAdmin() throws IOException { + HttpPost post = newAdminPost(adminUri("realms/master/components")); + post.setEntity(new StringEntity("{ invalid : invalid }")); + + HttpResponse response = httpClient.execute(post); + assertThat("status code", response.getStatusLine().getStatusCode(), is(400)); + + OAuth2ErrorRepresentation error = readError(response); + assertThat("error code", error.getError(), is(OAuthErrorException.INVALID_REQUEST)); + assertThat("error description", error.getErrorDescription(), is(notNullValue())); + } + + @Test + void uncaughtErrorAdminPropertyError() throws IOException { + HttpPost post = newAdminPost(adminUri("realms/master/components")); + post.setEntity(new StringEntity("{\"\":1}")); + + HttpResponse response = httpClient.execute(post); + assertThat("status code", response.getStatusLine().getStatusCode(), is(400)); + + assertContentTypeJson(response); + + String body = EntityUtils.toString(response.getEntity()); + assertThat("XSS payload should be escaped in response", body, + containsString("Unrecognized field \\\"\\\"")); + } + + @Test + void uncaughtError() { + driver.open(errorTestingUri("uncaught-error").toString()); + errorPage.assertCurrent(); + assertThat("error page message", errorPage.getError(), is("An internal server error has occurred")); + } + + @Test + void uncaughtErrorHeaders() throws IOException { + HttpGet get = new HttpGet(errorTestingUri("uncaught-error")); + get.setHeader("Accept", MediaType.TEXT_HTML_UTF_8); + + HttpResponse response = httpClient.execute(get); + + for (BrowserSecurityHeaders header : BrowserSecurityHeaders.values()) { + String expectedValue = header.getDefaultValue(); + if (expectedValue == null || expectedValue.isEmpty()) { + assertThat(header.getHeaderName() + " should be absent", + response.getFirstHeader(header.getHeaderName()), is(nullValue())); + } else { + Header actual = response.getFirstHeader(header.getHeaderName()); + assertThat(header.getHeaderName() + " should be present", actual, is(notNullValue())); + assertThat(header.getHeaderName() + " value", actual.getValue(), is(expectedValue)); + } + } + } + + @Test + void errorPageException() { + oauth.realm("master"); + oauth.client("nosuch"); + oauth.openLoginForm(); + + errorPage.assertCurrent(); + assertThat("error page message", errorPage.getError(), is("Client not found.")); + } + + @Test + void internationalisationEnabled() { + masterRealm.updateWithCleanup(r -> r + .internationalizationEnabled(true) + .defaultLocale("en") + .supportedLocales("en")); + + checkPageNotFound("/realms/master/nosuch"); + checkPageNotFound("/nosuch"); + } + + @Test + void switchLocale() { + masterRealm.updateWithCleanup(r -> r + .internationalizationEnabled(true) + .defaultLocale("en") + .supportedLocales("en", "de")); + + checkPageNotFound("/realms/master/nosuch"); + String localeUrl = driver.findElement( + By.xpath("//option[text()[contains(.,'Deutsch')]]")).getDomAttribute("value"); + driver.open(localeUrl); + errorPage.assertCurrent(); + } + + @Test + void jsonProcessingException() throws IOException { + HttpPost post = newAdminPost(adminUri("realms/master/users")); + post.setEntity(new StringEntity("[]")); + + HttpResponse response = httpClient.execute(post); + assertThat("status code", response.getStatusLine().getStatusCode(), is(400)); + + assertContentTypeJson(response); + + OAuth2ErrorRepresentation error = readError(response); + assertThat("error code", error.getError(), is("unknown_error")); + } + + private void checkPageNotFound(String path) { + URI uri = keycloakUrls.getBaseBuilder().path(path).build(); + driver.open(uri.toString()); + errorPage.assertCurrent(); + assertThat("error message for path " + path, errorPage.getError(), is("Page not found")); + } + + private HttpPost newAdminPost(URI uri) { + HttpPost post = new HttpPost(uri); + post.setHeader("Authorization", "Bearer " + adminClient.tokenManager().getAccessTokenString()); + post.setHeader("Content-Type", APPLICATION_JSON); + return post; + } + + private void assertContentTypeJson(HttpResponse response) { + Header contentType = response.getFirstHeader("Content-Type"); + assertThat("Content-Type header", contentType, is(notNullValue())); + assertThat("Content-Type value", contentType.getValue(), is(MediaType.APPLICATION_JSON)); + } + + private OAuth2ErrorRepresentation readError(HttpResponse response) throws IOException { + return JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); + } + + private URI errorTestingUri(String subPath) { + return KeycloakUriBuilder.fromUri(keycloakUrls.getMasterRealm()) + .path("error-testing/" + subPath) + .build(); + } + + private URI masterRealmUri(String subPath) { + return KeycloakUriBuilder.fromUri(keycloakUrls.getMasterRealm()) + .path(subPath) + .build(); + } + + private URI adminUri(String subPath) { + return keycloakUrls.getAdminBuilder().path(subPath).build(); + } +} diff --git a/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java b/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java index dc55f9058d78..a5c51bdb06c0 100644 --- a/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java +++ b/tests/base/src/test/java/org/keycloak/tests/suites/Base2TestSuite.java @@ -12,6 +12,7 @@ "org.keycloak.tests.common", "org.keycloak.tests.cors", "org.keycloak.tests.db", + "org.keycloak.tests.error", "org.keycloak.tests.events", "org.keycloak.tests.forms", "org.keycloak.tests.i18n", diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProvider.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProvider.java new file mode 100644 index 000000000000..35d5757357da --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProvider.java @@ -0,0 +1,41 @@ +package org.keycloak.tests.providers.rest; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Response; + +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.ErrorPage; +import org.keycloak.services.resource.RealmResourceProvider; + +public class ErrorTestingRealmResourceProvider implements RealmResourceProvider { + + private final KeycloakSession session; + + ErrorTestingRealmResourceProvider(KeycloakSession session) { + this.session = session; + } + + @Override + public Object getResource() { + return this; + } + + @GET + @Path("display-error-message") + public Response displayErrorMessage(@QueryParam("message") String message) { + return ErrorPage.error(session, session.getContext().getAuthenticationSession(), + Response.Status.BAD_REQUEST, message == null ? "" : message); + } + + @GET + @Path("uncaught-error") + public Response uncaughtError() { + throw new RuntimeException("Uncaught error"); + } + + @Override + public void close() { + } +} diff --git a/tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProviderFactory.java b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProviderFactory.java new file mode 100644 index 000000000000..543bf54d994a --- /dev/null +++ b/tests/custom-providers/src/main/java/org/keycloak/tests/providers/rest/ErrorTestingRealmResourceProviderFactory.java @@ -0,0 +1,34 @@ +package org.keycloak.tests.providers.rest; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.services.resource.RealmResourceProvider; +import org.keycloak.services.resource.RealmResourceProviderFactory; + +public class ErrorTestingRealmResourceProviderFactory implements RealmResourceProviderFactory { + + static final String ID = "error-testing"; + + @Override + public RealmResourceProvider create(KeycloakSession session) { + return new ErrorTestingRealmResourceProvider(session); + } + + @Override + public String getId() { + return ID; + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } +} diff --git a/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory new file mode 100644 index 000000000000..3eb9ebc604c7 --- /dev/null +++ b/tests/custom-providers/src/main/resources/META-INF/services/org.keycloak.services.resource.RealmResourceProviderFactory @@ -0,0 +1 @@ +org.keycloak.tests.providers.rest.ErrorTestingRealmResourceProviderFactory diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java index 6785ed882e48..d67d5d827c36 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java @@ -104,7 +104,6 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import org.keycloak.representations.idm.EventRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.services.ErrorPage; import org.keycloak.services.ErrorResponse; import org.keycloak.services.managers.AuthenticationSessionManager; import org.keycloak.services.resource.RealmResourceProvider; @@ -740,12 +739,6 @@ public Response suspendPeriodicTasks() { return Response.noContent().build(); } - @GET - @Path("/uncaught-error") - public Response uncaughtError() { - throw new RuntimeException("Uncaught error"); - } - private void suspendTask(String taskName) { TimerProvider.TimerTaskContext taskContext = session.getProvider(TimerProvider.class).cancelTask(taskName); @@ -1023,18 +1016,6 @@ public Response simulatePostRequest(@QueryParam("postRequestUrl") String postReq } - /** - * Display message to Error Page - for testing purposes - * - * @param message message - */ - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/display-error-message") - public Response displayErrorMessage(@QueryParam("message") String message) { - return ErrorPage.error(session, session.getContext().getAuthenticationSession(), Response.Status.BAD_REQUEST, message == null ? "" : message); - } - @GET @Path("/get-provider-implementation-class") @Produces(MediaType.APPLICATION_JSON) diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java index 1839f2e30a28..9729178ebefc 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestingResource.java @@ -286,15 +286,6 @@ public UserRepresentation getUserByUsernameFromFedProviderFactory(@QueryParam("r @Produces(MediaType.APPLICATION_JSON) Response restorePeriodicTasks(); - @GET - @Path("/uncaught-error") - @Produces(MediaType.TEXT_HTML_UTF_8) - Response uncaughtError(); - - @GET - @Path("/uncaught-error") - Response uncaughtErrorJson(); - @POST @Path("/run-on-server") @Consumes(MediaType.TEXT_PLAIN_UTF_8) @@ -378,16 +369,6 @@ void reinitializeProviderFactoryWithSystemPropertiesScope(@QueryParam("provider- Response simulatePostRequest(@QueryParam("postRequestUrl") String postRequestUrl, @QueryParam("encodedFormParameters") String encodedFormParameters); - /** - * Display message to Error Page - for testing purposes - * - * @param message message - */ - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/display-error-message") - Response displayErrorMessage(@QueryParam("message") String message); - /** * @param providerClass Full name of class such as for example "org.keycloak.authentication.Authenticator" * @param providerId providerId referenced in particular provider factory. Can be null (in this case we're returning default provider for particular providerClass) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/EscapeErrorPageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/EscapeErrorPageTest.java deleted file mode 100644 index cb31f7421124..000000000000 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/EscapeErrorPageTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2021 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.testsuite.error; - -import java.net.URI; -import java.util.List; - -import org.keycloak.common.util.KeycloakUriBuilder; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.testsuite.AbstractKeycloakTest; -import org.keycloak.testsuite.pages.ErrorPage; - -import org.hamcrest.CoreMatchers; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Test; - -import static org.hamcrest.MatcherAssert.assertThat; - -public class EscapeErrorPageTest extends AbstractKeycloakTest { - - @Page - public ErrorPage errorPage; - - @Override - public void addTestRealms(List testRealms) { - } - - @Test - public void innerScript() { - checkMessage("\"alert(1)/>\"", "\"alert(1)/>\""); - } - - @Test - public void innerURL() { - checkMessage("\"See https://www.keycloak.org/docs\"", "\"See https://www.keycloak.org/docs\""); - } - - @Test - public void URL() { - checkMessage("See https://www.keycloak.org/docs", "See https://www.keycloak.org/docs"); - } - - @Test - public void ampersandEscape() { - checkMessage("<img src="something">", ""); - } - - @Test - public void hexEscape() { - checkMessage("<img src=something/>", ""); - } - - @Test - public void plainText() { - checkMessage("It doesn't work", "It doesn't work"); - } - - @Test - public void textWithPlus() { - checkMessage("Fact: 1+1=2", "Fact: 1+1=2"); - } - - private void checkMessage(String queryParam, String expected) { - try { - final URI uri = KeycloakUriBuilder.fromUri(suiteContext.getAuthServerInfo().getContextRoot().toURI()) - .path("/auth/realms/master/testing/display-error-message") - .queryParam("message", queryParam) - .build(); - - driver.navigate().to(uri.toURL()); - errorPage.assertCurrent(); - assertThat(errorPage.getError(), CoreMatchers.is(expected)); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java deleted file mode 100644 index c03a54c3abd0..000000000000 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java +++ /dev/null @@ -1,270 +0,0 @@ -package org.keycloak.testsuite.error; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; - -import jakarta.ws.rs.core.Response; - -import org.keycloak.OAuthErrorException; -import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.common.util.StreamUtil; -import org.keycloak.http.simple.SimpleHttpResponse; -import org.keycloak.models.BrowserSecurityHeaders; -import org.keycloak.representations.idm.OAuth2ErrorRepresentation; -import org.keycloak.representations.idm.RealmRepresentation; -import org.keycloak.testsuite.AbstractKeycloakTest; -import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected; -import org.keycloak.testsuite.broker.util.SimpleHttpDefault; -import org.keycloak.testsuite.pages.ErrorPage; -import org.keycloak.util.JsonSerialization; -import org.keycloak.utils.MediaType; - -import org.apache.http.Header; -import org.apache.http.HeaderElement; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.util.EntityUtils; -import org.hamcrest.CoreMatchers; -import org.jboss.arquillian.graphene.page.Page; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; -import org.openqa.selenium.By; - -import static org.keycloak.utils.MediaType.APPLICATION_JSON; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.core.Is.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class UncaughtErrorPageTest extends AbstractKeycloakTest { - - @Page - private ErrorPage errorPage; - - @Override - public void addTestRealms(List testRealms) { - } - - @Test - public void invalidResource() throws MalformedURLException { - checkPageNotFound("/auth/nosuch"); - } - - @Test - public void invalidRealm() throws MalformedURLException { - checkPageNotFound("/auth/realms/nosuch"); - } - - @Test - public void invalidRealmResource() throws MalformedURLException { - checkPageNotFound("/auth/realms/master/nosuch"); - } - - @Test - @UncaughtServerErrorExpected - public void uncaughtErrorJson() throws IOException { - Response response = testingClient.testing().uncaughtError(); - assertEquals(500, response.getStatus()); - - InputStream is = (InputStream) response.getEntity(); - String responseString = StreamUtil.readString(is, StandardCharsets.UTF_8); - - Assertions.assertTrue(responseString.contains("An internal server error has occurred")); - } - - @Test - @UncaughtServerErrorExpected - public void uncaughtErrorClientRegistration() throws IOException { - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - HttpPost post = new HttpPost(suiteContext.getAuthServerInfo().getUriBuilder().path("/auth/realms/master/clients-registrations/openid-connect").build()); - post.setEntity(new StringEntity("{ invalid : invalid }")); - post.setHeader("Content-Type", "application/json"); - - try (CloseableHttpResponse response = client.execute(post)) { - assertEquals(400, response.getStatusLine().getStatusCode()); - - OAuth2ErrorRepresentation error = JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); - assertEquals(OAuthErrorException.INVALID_REQUEST, error.getError()); - assertNotNull(error.getErrorDescription(), "found error with " + error.getError() + "/" + error.getErrorDescription()); - } - } - } - - @Test - @UncaughtServerErrorExpected - public void uncaughtErrorAdmin() throws IOException { - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - String accessToken = adminClient.tokenManager().getAccessTokenString(); - - HttpPost post = new HttpPost(suiteContext.getAuthServerInfo().getUriBuilder().path("/auth/admin/realms/master/components").build()); - post.setEntity(new StringEntity("{ invalid : invalid }")); - post.setHeader("Authorization", "bearer " + accessToken); - post.setHeader("Content-Type", "application/json"); - - try (CloseableHttpResponse response = client.execute(post)) { - assertEquals(400, response.getStatusLine().getStatusCode()); - - OAuth2ErrorRepresentation error = JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); - assertEquals(OAuthErrorException.INVALID_REQUEST, error.getError()); - assertNotNull(error.getErrorDescription(), "found error with " + error.getError() + "/" + error.getErrorDescription()); - } - } - } - - @Test - @UncaughtServerErrorExpected - public void uncaughtErrorAdminPropertyError() throws IOException { - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - String accessToken = adminClient.tokenManager().getAccessTokenString(); - - HttpPost post = new HttpPost(suiteContext.getAuthServerInfo().getUriBuilder().path("/auth/admin/realms/master/components").build()); - post.setEntity(new StringEntity("{\"\":1}")); - post.setHeader("Authorization", "bearer " + accessToken); - post.setHeader("Content-Type", "application/json"); - - try (CloseableHttpResponse response = client.execute(post)) { - assertEquals(400, response.getStatusLine().getStatusCode()); - - Header header = response.getFirstHeader("Content-Type"); - assertThat(header, notNullValue()); - - // Verify the Content-Type is not text/html - assertThat(Arrays.stream(header.getElements()) - .map(HeaderElement::getName) - .filter(Objects::nonNull) - .anyMatch(f -> f.equals(APPLICATION_JSON)), is(true)); - - // The alert is not executed - assertThat(EntityUtils.toString(response.getEntity()), CoreMatchers.containsString("Unrecognized field \\\"\\\"")); - } - } - } - - @Test - @UncaughtServerErrorExpected - public void uncaughtError() throws MalformedURLException { - URI uri = suiteContext.getAuthServerInfo().getUriBuilder().path("/auth/realms/master/testing/uncaught-error").build(); - driver.navigate().to(uri.toURL()); - - assertTrue(errorPage.isCurrent()); - assertEquals("An internal server error has occurred", errorPage.getError()); - } - - @Test - @UncaughtServerErrorExpected - public void uncaughtErrorHeaders() throws IOException { - URI uri = suiteContext.getAuthServerInfo().getUriBuilder().path("/auth/realms/master/testing/uncaught-error").build(); - - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - SimpleHttpResponse response = SimpleHttpDefault.doGet(uri.toString(), client).header("Accept", MediaType.TEXT_HTML_UTF_8).asResponse(); - - for (BrowserSecurityHeaders header : BrowserSecurityHeaders.values()) { - String expectedValue = header.getDefaultValue(); - - if (expectedValue == null || expectedValue.isEmpty()) { - assertNull(response.getFirstHeader(header.getHeaderName())); - } else { - assertEquals(expectedValue, response.getFirstHeader(header.getHeaderName())); - } - } - } - } - - @Test - public void errorPageException() { - oauth.realm("master"); - oauth.client("nosuch"); - oauth.openLoginForm(); - - assertTrue(errorPage.isCurrent()); - assertEquals("Client not found.", errorPage.getError()); - } - - @Test - public void internationalisationEnabled() throws MalformedURLException { - RealmResource testRealm = realmsResouce().realm("master"); - RealmRepresentation rep = testRealm.toRepresentation(); - rep.setInternationalizationEnabled(true); - rep.setDefaultLocale("en"); - rep.setSupportedLocales(Collections.singleton("en")); - testRealm.update(rep); - - try { - checkPageNotFound("/auth/realms/master/nosuch"); - checkPageNotFound("/auth/nosuch"); - } finally { - rep.setInternationalizationEnabled(false); - testRealm.update(rep); - } - } - - @Test - public void switchLocale() throws MalformedURLException { - RealmResource testRealm = realmsResouce().realm("master"); - RealmRepresentation rep = testRealm.toRepresentation(); - rep.setInternationalizationEnabled(true); - rep.setDefaultLocale("en"); - HashSet supported = new HashSet<>(); - supported.add("en"); - supported.add("de"); - rep.setSupportedLocales(supported); - testRealm.update(rep); - - try { - checkPageNotFound("/auth/realms/master/nosuch"); - String url = driver.findElement(By.xpath("//option[text()[contains(.,'Deutsch')]]")).getAttribute("value"); - driver.navigate().to(url); - errorPage.assertCurrent(); - } finally { - rep.setInternationalizationEnabled(false); - testRealm.update(rep); - } - } - - private void checkPageNotFound(String path) throws MalformedURLException { - URI uri = suiteContext.getAuthServerInfo().getUriBuilder().path(path).build(); - driver.navigate().to(uri.toURL()); - - assertTrue(errorPage.isCurrent()); - assertEquals("Page not found", errorPage.getError()); - } - - @Test - public void jsonProcessingException() throws IOException { - try (CloseableHttpClient client = HttpClientBuilder.create().build()) { - String accessToken = adminClient.tokenManager().getAccessTokenString(); - - // send an empty array to the user endpoint which expects a User json object - HttpPost post = new HttpPost(suiteContext.getAuthServerInfo().getUriBuilder().path("/auth/admin/realms/master/users").build()); - post.setEntity(new StringEntity("[]")); - post.setHeader("Authorization", "bearer " + accessToken); - post.setHeader("Content-Type", "application/json"); - - try (CloseableHttpResponse response = client.execute(post)) { - assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatusLine().getStatusCode()); - - Header header = response.getFirstHeader("Content-Type"); - assertThat(header, notNullValue()); - assertEquals(MediaType.APPLICATION_JSON, header.getValue()); - - OAuth2ErrorRepresentation error = JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); - assertEquals("unknown_error", error.getError()); - } - } - } -} diff --git a/testsuite/integration-arquillian/tests/base/testsuites/base-suite b/testsuite/integration-arquillian/tests/base/testsuites/base-suite index 62a43aacda97..6e3381e02a3d 100644 --- a/testsuite/integration-arquillian/tests/base/testsuites/base-suite +++ b/testsuite/integration-arquillian/tests/base/testsuites/base-suite @@ -10,7 +10,6 @@ composites,4 cookies,4 docker,4 domainextension,4 -error,4 exportimport,4 federation,5 forms,5