Fix OfflineServletAdapterTest failures, and improve logging (#25724)
Closes #25714 Closes #14448 Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
parent
6ea9df2cf2
commit
03372d2f41
8 changed files with 101 additions and 50 deletions
|
@ -20,6 +20,7 @@ package org.keycloak.testsuite.adapter.page;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.RefreshToken;
|
import org.keycloak.representations.RefreshToken;
|
||||||
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
|
import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
|
||||||
|
import org.keycloak.testsuite.util.WaitUtils;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.openqa.selenium.NoSuchElementException;
|
import org.openqa.selenium.NoSuchElementException;
|
||||||
import org.openqa.selenium.WebElement;
|
import org.openqa.selenium.WebElement;
|
||||||
|
@ -46,47 +47,43 @@ public abstract class AbstractShowTokensPage extends AbstractPageWithInjectedUrl
|
||||||
|
|
||||||
public AccessToken getAccessToken() {
|
public AccessToken getAccessToken() {
|
||||||
try {
|
try {
|
||||||
|
WaitUtils.waitUntilElement(accessToken).is().visible();
|
||||||
return JsonSerialization.readValue(accessToken.getText(), AccessToken.class);
|
return JsonSerialization.readValue(accessToken.getText(), AccessToken.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
throw new RuntimeException(e);
|
||||||
} catch (NoSuchElementException nsee) {
|
} catch (NoSuchElementException nsee) {
|
||||||
log.warn("No accessToken element found on the page");
|
throw LogScreenContents.fail(driver, "No accessToken element found on the page", nsee);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public RefreshToken getRefreshToken() {
|
public RefreshToken getRefreshToken() {
|
||||||
try {
|
try {
|
||||||
|
WaitUtils.waitUntilElement(refreshToken).is().visible();
|
||||||
return JsonSerialization.readValue(refreshToken.getText(), RefreshToken.class);
|
return JsonSerialization.readValue(refreshToken.getText(), RefreshToken.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
throw new RuntimeException(e);
|
||||||
} catch (NoSuchElementException nsee) {
|
} catch (NoSuchElementException nsee) {
|
||||||
log.warn("No refreshToken element found on the page");
|
throw LogScreenContents.fail(driver, "No refreshToken element found on the page", nsee);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getAccessTokenString() {
|
public String getAccessTokenString() {
|
||||||
try {
|
try {
|
||||||
|
WaitUtils.waitUntilElement(accessTokenString).is().visible();
|
||||||
return accessTokenString.getText();
|
return accessTokenString.getText();
|
||||||
} catch (NoSuchElementException nsee) {
|
} catch (NoSuchElementException nsee) {
|
||||||
log.warn("No accessTokenString element found on the page");
|
throw LogScreenContents.fail(driver, "No accessTokenString element found on the page", nsee);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRefreshTokenString() {
|
public String getRefreshTokenString() {
|
||||||
try {
|
try {
|
||||||
|
WaitUtils.waitUntilElement(refreshTokenString).is().visible();
|
||||||
return refreshTokenString.getText();
|
return refreshTokenString.getText();
|
||||||
} catch (NoSuchElementException nsee) {
|
} catch (NoSuchElementException nsee) {
|
||||||
log.warn("No refreshTokenString element found on the page");
|
throw LogScreenContents.fail(driver, "No refreshTokenString element found on the page", nsee);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 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.adapter.page;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.openqa.selenium.OutputType;
|
||||||
|
import org.openqa.selenium.TakesScreenshot;
|
||||||
|
import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Alexander Schwartz
|
||||||
|
*/
|
||||||
|
public class LogScreenContents {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(LogScreenContents.class);
|
||||||
|
|
||||||
|
public static <T extends Throwable> T fail(WebDriver webDriver, String message, T t) {
|
||||||
|
String screenShotBase64 = null;
|
||||||
|
if (webDriver instanceof TakesScreenshot) {
|
||||||
|
screenShotBase64 = ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.BASE64);
|
||||||
|
}
|
||||||
|
log.error(message + ", url '" + webDriver.getCurrentUrl() + "', title: " + webDriver.getTitle() + ", source: " + webDriver.getPageSource() +
|
||||||
|
(screenShotBase64 != null ? ", screenshot: " + screenShotBase64 : ""));
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,9 @@ import java.util.List;
|
||||||
import org.jboss.shrinkwrap.api.asset.UrlAsset;
|
import org.jboss.shrinkwrap.api.asset.UrlAsset;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
|
||||||
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
||||||
|
|
||||||
|
@ -231,7 +234,7 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
|
||||||
DroneUtils.getCurrentDriver().navigate().to(timeOffsetUri);
|
DroneUtils.getCurrentDriver().navigate().to(timeOffsetUri);
|
||||||
waitForPageToLoad();
|
waitForPageToLoad();
|
||||||
String pageSource = DroneUtils.getCurrentDriver().getPageSource();
|
String pageSource = DroneUtils.getCurrentDriver().getPageSource();
|
||||||
log.info(pageSource);
|
assertThat(pageSource, containsString("Offset set successfully"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,7 +215,7 @@ public class DemoServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
|
|
||||||
@Deployment(name = SecurePortal.DEPLOYMENT_NAME)
|
@Deployment(name = SecurePortal.DEPLOYMENT_NAME)
|
||||||
protected static WebArchive securePortal() {
|
protected static WebArchive securePortal() {
|
||||||
return servletDeployment(SecurePortal.DEPLOYMENT_NAME, CallAuthenticatedServlet.class);
|
return servletDeployment(SecurePortal.DEPLOYMENT_NAME, AdapterActionsFilter.class, CallAuthenticatedServlet.class);
|
||||||
}
|
}
|
||||||
@Deployment(name = SecurePortalRewriteRedirectUri.DEPLOYMENT_NAME)
|
@Deployment(name = SecurePortalRewriteRedirectUri.DEPLOYMENT_NAME)
|
||||||
protected static WebArchive securePortalRewriteRedirectUri() {
|
protected static WebArchive securePortalRewriteRedirectUri() {
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class OIDCPublicKeyRotationAdapterTest extends AbstractServletsAdapterTes
|
||||||
|
|
||||||
@Deployment(name = SecurePortal.DEPLOYMENT_NAME)
|
@Deployment(name = SecurePortal.DEPLOYMENT_NAME)
|
||||||
protected static WebArchive securePortal() {
|
protected static WebArchive securePortal() {
|
||||||
return servletDeployment(SecurePortal.DEPLOYMENT_NAME, CallAuthenticatedServlet.class);
|
return servletDeployment(SecurePortal.DEPLOYMENT_NAME, AdapterActionsFilter.class, CallAuthenticatedServlet.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deployment(name = TokenMinTTLPage.DEPLOYMENT_NAME)
|
@Deployment(name = TokenMinTTLPage.DEPLOYMENT_NAME)
|
||||||
|
@ -136,7 +136,6 @@ public class OIDCPublicKeyRotationAdapterTest extends AbstractServletsAdapterTes
|
||||||
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
|
assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
|
||||||
testRealmLoginPage.form().login("bburke@redhat.com", "password");
|
testRealmLoginPage.form().login("bburke@redhat.com", "password");
|
||||||
URLAssert.assertCurrentUrlStartsWith(tokenMinTTLPage.getInjectedUrl().toString());
|
URLAssert.assertCurrentUrlStartsWith(tokenMinTTLPage.getInjectedUrl().toString());
|
||||||
Assert.assertNull(tokenMinTTLPage.getAccessToken());
|
|
||||||
|
|
||||||
ApiUtil.findUserByUsernameId(adminClient.realm("demo"), "bburke@redhat.com").logout();
|
ApiUtil.findUserByUsernameId(adminClient.realm("demo"), "bburke@redhat.com").logout();
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,9 @@ import org.openqa.selenium.By;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.CoreMatchers.not;
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
|
||||||
import static org.hamcrest.CoreMatchers.nullValue;
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
|
||||||
|
@ -72,6 +72,11 @@ public class OfflineServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
private final String DEFAULT_PASSWORD = "password";
|
private final String DEFAULT_PASSWORD = "password";
|
||||||
private final String OFFLINE_CLIENT_ID = "offline-client";
|
private final String OFFLINE_CLIENT_ID = "offline-client";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL in the deployment that doesn't require authentication and which therefore can be used to trigger activities in the actionfilter.
|
||||||
|
*/
|
||||||
|
private static final String UNSECURED_URL = "unsecured/foo";
|
||||||
|
|
||||||
@Deployment(name = OfflineToken.DEPLOYMENT_NAME)
|
@Deployment(name = OfflineToken.DEPLOYMENT_NAME)
|
||||||
protected static WebArchive offlineClient() {
|
protected static WebArchive offlineClient() {
|
||||||
return servletDeployment(OfflineToken.DEPLOYMENT_NAME, AdapterActionsFilter.class, AbstractShowTokensServlet.class, OfflineTokenServlet.class, ErrorServlet.class, ServletTestUtils.class);
|
return servletDeployment(OfflineToken.DEPLOYMENT_NAME, AdapterActionsFilter.class, AbstractShowTokensServlet.class, OfflineTokenServlet.class, ErrorServlet.class, ServletTestUtils.class);
|
||||||
|
@ -114,7 +119,6 @@ public class OfflineServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
assertCurrentUrlStartsWith(offlineTokenPage);
|
assertCurrentUrlStartsWith(offlineTokenPage);
|
||||||
|
|
||||||
RefreshToken refreshToken = offlineTokenPage.getRefreshToken();
|
RefreshToken refreshToken = offlineTokenPage.getRefreshToken();
|
||||||
assertThat(refreshToken, notNullValue());
|
|
||||||
assertThat(TokenUtil.TOKEN_TYPE_OFFLINE, is(refreshToken.getType()));
|
assertThat(TokenUtil.TOKEN_TYPE_OFFLINE, is(refreshToken.getType()));
|
||||||
assertThat(refreshToken.getExp(), nullValue());
|
assertThat(refreshToken.getExp(), nullValue());
|
||||||
|
|
||||||
|
@ -151,7 +155,7 @@ public class OfflineServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
} finally {
|
} finally {
|
||||||
events.clear();
|
events.clear();
|
||||||
resetTimeOffsetAuthenticated();
|
resetTimeOffset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +174,6 @@ public class OfflineServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
assertCurrentUrlStartsWith(offlineTokenPage);
|
assertCurrentUrlStartsWith(offlineTokenPage);
|
||||||
|
|
||||||
final RefreshToken refreshToken = offlineTokenPage.getRefreshToken();
|
final RefreshToken refreshToken = offlineTokenPage.getRefreshToken();
|
||||||
assertThat(refreshToken, notNullValue());
|
|
||||||
assertThat(refreshToken.getType(), is(TokenUtil.TOKEN_TYPE_OFFLINE));
|
assertThat(refreshToken.getType(), is(TokenUtil.TOKEN_TYPE_OFFLINE));
|
||||||
|
|
||||||
// Assert refresh works with increased time
|
// Assert refresh works with increased time
|
||||||
|
@ -200,7 +203,7 @@ public class OfflineServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
loginPage.assertCurrent();
|
loginPage.assertCurrent();
|
||||||
} finally {
|
} finally {
|
||||||
events.clear();
|
events.clear();
|
||||||
resetTimeOffsetAuthenticated();
|
resetTimeOffset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +238,6 @@ public class OfflineServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
assertCurrentUrlStartsWith(offlineTokenPage);
|
assertCurrentUrlStartsWith(offlineTokenPage);
|
||||||
|
|
||||||
RefreshToken refreshToken = offlineTokenPage.getRefreshToken();
|
RefreshToken refreshToken = offlineTokenPage.getRefreshToken();
|
||||||
assertThat(refreshToken, notNullValue());
|
|
||||||
assertThat(refreshToken.getType(), is(TokenUtil.TOKEN_TYPE_OFFLINE));
|
assertThat(refreshToken.getType(), is(TokenUtil.TOKEN_TYPE_OFFLINE));
|
||||||
|
|
||||||
// Check that the client scopes have been granted by the user
|
// Check that the client scopes have been granted by the user
|
||||||
|
@ -247,39 +249,31 @@ public class OfflineServletsAdapterTest extends AbstractServletsAdapterTest {
|
||||||
AccountHelper.logout(adminClient.realm(TEST), DEFAULT_USERNAME);
|
AccountHelper.logout(adminClient.realm(TEST), DEFAULT_USERNAME);
|
||||||
} finally {
|
} finally {
|
||||||
events.clear();
|
events.clear();
|
||||||
resetTimeOffsetAuthenticated();
|
resetTimeOffset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAdapterAndServerTimeOffset(int timeOffset) {
|
private void setAdapterAndServerTimeOffset(int timeOffset) {
|
||||||
super.setAdapterAndServerTimeOffset(timeOffset, offlineTokenPage.toString());
|
super.setAdapterAndServerTimeOffset(timeOffset, offlineTokenPage.toString() + UNSECURED_URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetTimeOffsetAuthenticated() {
|
@Override
|
||||||
resetTimeOffsetAuthenticated(DEFAULT_USERNAME, DEFAULT_PASSWORD);
|
public void resetTimeOffset() {
|
||||||
|
setAdapterServletTimeOffset(0, offlineTokenPage.toString() + UNSECURED_URL);
|
||||||
|
super.resetTimeOffset();
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Reset time offset for remote environment.
|
|
||||||
* After the token expiration, process of re-authentication is necessary.
|
|
||||||
*
|
|
||||||
* @param username
|
|
||||||
* @param password
|
|
||||||
*/
|
|
||||||
private void resetTimeOffsetAuthenticated(String username, String password) {
|
|
||||||
if (testContext.getAppServerInfo().isUndertow()) {
|
|
||||||
setAdapterAndServerTimeOffset(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.setAdapterServletTimeOffset(0, offlineTokenPage.toString());
|
|
||||||
|
|
||||||
if (loginPage.isCurrent()) {
|
@Override
|
||||||
loginPage.login(username, password);
|
protected void afterAbstractKeycloakTestRealmImport() {
|
||||||
waitForPageToLoad();
|
// after each re-import, ensure that the information stored in JWKPublicKeyLocator is reset
|
||||||
AccountHelper.logout(adminClient.realm(TEST), DEFAULT_USERNAME);
|
String resetDeploymentUri = UriBuilder.fromUri(offlineTokenPage.toString() + UNSECURED_URL)
|
||||||
}
|
.queryParam(AdapterActionsFilter.RESET_DEPLOYMENT_PARAM, "true")
|
||||||
setTimeOffset(0);
|
.build().toString();
|
||||||
// Improve stability of the cleanup and have more time for synchronizing auth and app server living in different JVMs
|
driver.navigate().to(resetDeploymentUri);
|
||||||
pause(400);
|
waitForPageToLoad();
|
||||||
|
|
||||||
|
assertThat(driver.getPageSource(), containsString("Restarted PublicKeyLocator"));
|
||||||
|
super.afterAbstractKeycloakTestRealmImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,13 @@
|
||||||
<url-pattern>/error.html</url-pattern>
|
<url-pattern>/error.html</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<security-constraint>
|
||||||
|
<web-resource-collection>
|
||||||
|
<web-resource-name>Unsecured</web-resource-name>
|
||||||
|
<url-pattern>/unsecured/*</url-pattern>
|
||||||
|
</web-resource-collection>
|
||||||
|
<!-- No auth-constraint = everybody has access -->
|
||||||
|
</security-constraint>
|
||||||
<security-constraint>
|
<security-constraint>
|
||||||
<web-resource-collection>
|
<web-resource-collection>
|
||||||
<web-resource-name>Users</web-resource-name>
|
<web-resource-name>Users</web-resource-name>
|
||||||
|
|
|
@ -23,11 +23,21 @@
|
||||||
|
|
||||||
<module-name>secure-portal</module-name>
|
<module-name>secure-portal</module-name>
|
||||||
|
|
||||||
|
<filter>
|
||||||
|
<filter-name>AdapterActionsFilter</filter-name>
|
||||||
|
<filter-class>org.keycloak.testsuite.adapter.filter.AdapterActionsFilter</filter-class>
|
||||||
|
</filter>
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>Servlet</servlet-name>
|
<servlet-name>Servlet</servlet-name>
|
||||||
<servlet-class>org.keycloak.testsuite.adapter.servlet.CallAuthenticatedServlet</servlet-class>
|
<servlet-class>org.keycloak.testsuite.adapter.servlet.CallAuthenticatedServlet</servlet-class>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>AdapterActionsFilter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
|
||||||
<servlet-mapping>
|
<servlet-mapping>
|
||||||
<servlet-name>Servlet</servlet-name>
|
<servlet-name>Servlet</servlet-name>
|
||||||
<url-pattern>/*</url-pattern>
|
<url-pattern>/*</url-pattern>
|
||||||
|
|
Loading…
Reference in a new issue