Automated test for session-limits authenticator with identity brokering (post-broker login flow) (#11723)
Closes #11004
This commit is contained in:
parent
37134ec8b0
commit
fdcbc9b27b
5 changed files with 176 additions and 43 deletions
|
@ -0,0 +1,85 @@
|
|||
package org.keycloak.testsuite.sessionlimits;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.authentication.authenticators.sessionlimits.UserSessionLimitsAuthenticatorFactory;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.testsuite.broker.AbstractInitializedBaseBrokerTest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import static org.keycloak.testsuite.sessionlimits.UserSessionLimitsUtil.assertSessionCount;
|
||||
import static org.keycloak.testsuite.sessionlimits.UserSessionLimitsUtil.configureSessionLimits;
|
||||
import static org.keycloak.testsuite.sessionlimits.UserSessionLimitsUtil.ERROR_TO_DISPLAY;
|
||||
|
||||
public abstract class AbstractUserSessionLimitsBrokerTest extends AbstractInitializedBaseBrokerTest {
|
||||
@Test
|
||||
public void testSessionCountExceededAndNewSessionDeniedFirstBrokerLoginFlow() throws Exception {
|
||||
configureFlow(UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION, "0", "1");
|
||||
loginTwiceAndVerifyBehavior(UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionCountExceededAndOldestSessionRemovedFirstBrokerLoginFlow() throws Exception {
|
||||
configureFlow(UserSessionLimitsAuthenticatorFactory.TERMINATE_OLDEST_SESSION, "0", "1");
|
||||
loginTwiceAndVerifyBehavior(UserSessionLimitsAuthenticatorFactory.TERMINATE_OLDEST_SESSION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRealmSessionCountExceededAndNewSessionDeniedFirstBrokerLoginFlow() throws Exception {
|
||||
configureFlow(UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION, "1", "0");
|
||||
loginTwiceAndVerifyBehavior(UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRealmSessionCountExceededAndOldestFirstBrokerLoginFlow() throws Exception {
|
||||
configureFlow(UserSessionLimitsAuthenticatorFactory.TERMINATE_OLDEST_SESSION, "1", "0");
|
||||
loginTwiceAndVerifyBehavior(UserSessionLimitsAuthenticatorFactory.TERMINATE_OLDEST_SESSION);
|
||||
}
|
||||
|
||||
private void configureFlow(String behavior, String realmLimit, String clientLimit)
|
||||
{
|
||||
String realmName = bc.consumerRealmName();
|
||||
String idpAlias = bc.getIDPAlias();
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName(realmName);
|
||||
AuthenticationFlowModel postBrokerFlow = new AuthenticationFlowModel();
|
||||
postBrokerFlow.setAlias("post-broker");
|
||||
postBrokerFlow.setDescription("post-broker flow with session limits");
|
||||
postBrokerFlow.setProviderId("basic-flow");
|
||||
postBrokerFlow.setTopLevel(true);
|
||||
postBrokerFlow.setBuiltIn(false);
|
||||
postBrokerFlow = realm.addAuthenticationFlow(postBrokerFlow);
|
||||
|
||||
configureSessionLimits(realm, postBrokerFlow, behavior, realmLimit, clientLimit);
|
||||
|
||||
IdentityProviderModel idp = realm.getIdentityProviderByAlias(idpAlias);
|
||||
idp.setPostBrokerLoginFlowId(postBrokerFlow.getId());
|
||||
realm.updateIdentityProvider(idp);
|
||||
});
|
||||
}
|
||||
|
||||
private void loginTwiceAndVerifyBehavior(String behavior) {
|
||||
logInAsUserInIDPForFirstTime();
|
||||
assertLoggedInAccountManagement();
|
||||
|
||||
deleteAllCookiesForRealm(bc.consumerRealmName());
|
||||
deleteAllCookiesForRealm(bc.providerRealmName());
|
||||
|
||||
logInAsUserInIDP();
|
||||
|
||||
if (UserSessionLimitsAuthenticatorFactory.TERMINATE_OLDEST_SESSION.equals(behavior)) {
|
||||
assertLoggedInAccountManagement();
|
||||
testingClient.server(bc.consumerRealmName()).run(assertSessionCount(bc.consumerRealmName(), bc.getUserLogin(), 1));
|
||||
}
|
||||
else if (UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION.equals(behavior)) {
|
||||
errorPage.assertCurrent();
|
||||
assertEquals(ERROR_TO_DISPLAY, errorPage.getError());
|
||||
}
|
||||
else {
|
||||
fail("Invalid behavior " + behavior);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.keycloak.testsuite.sessionlimits;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.broker.BrokerConfiguration;
|
||||
import org.keycloak.testsuite.broker.KcOidcBrokerConfiguration;
|
||||
|
||||
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
||||
|
||||
@AuthServerContainerExclude(REMOTE)
|
||||
public class KcOidcUserSessionLimitsBrokerTest extends AbstractUserSessionLimitsBrokerTest {
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return KcOidcBrokerConfiguration.INSTANCE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package org.keycloak.testsuite.sessionlimits;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.broker.BrokerConfiguration;
|
||||
import org.keycloak.testsuite.broker.KcSamlBrokerConfiguration;
|
||||
|
||||
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
||||
|
||||
@AuthServerContainerExclude(REMOTE)
|
||||
public class KcSamlUserSessionLimitsBrokerTest extends AbstractUserSessionLimitsBrokerTest {
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return KcSamlBrokerConfiguration.INSTANCE;
|
||||
}
|
||||
}
|
|
@ -30,7 +30,6 @@ import org.keycloak.models.AuthenticationFlowModel;
|
|||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.representations.idm.EventRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
@ -44,8 +43,6 @@ import org.keycloak.testsuite.util.GreenMailRule;
|
|||
import org.keycloak.testsuite.util.MailUtils;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
|
||||
|
@ -53,11 +50,14 @@ import javax.mail.internet.MimeMessage;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
|
||||
import static org.keycloak.testsuite.sessionlimits.UserSessionLimitsUtil.assertSessionCount;
|
||||
import static org.keycloak.testsuite.sessionlimits.UserSessionLimitsUtil.configureSessionLimits;
|
||||
import static org.keycloak.testsuite.sessionlimits.UserSessionLimitsUtil.ERROR_TO_DISPLAY;
|
||||
|
||||
@AuthServerContainerExclude(REMOTE)
|
||||
public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
private static final String ERROR_TO_DISPLAY = "This account has too many sessions";
|
||||
private String realmName = "test";
|
||||
private String username = "test-user@localhost";
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
|
@ -75,13 +75,13 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
AuthenticationFlowModel browser = realm.getBrowserFlow();
|
||||
configureUsernamePassword(realm, browser);
|
||||
configureSessionLimits(realm, browser);
|
||||
configureSessionLimits(realm, browser, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION, "0", "1");
|
||||
|
||||
AuthenticationFlowModel directGrant = realm.getDirectGrantFlow();
|
||||
configureSessionLimits(realm, directGrant);
|
||||
configureSessionLimits(realm, directGrant, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION, "0", "1");
|
||||
|
||||
AuthenticationFlowModel resetPasswordFlow = realm.getResetCredentialsFlow();
|
||||
configureSessionLimits(realm, resetPasswordFlow);
|
||||
configureSessionLimits(realm, resetPasswordFlow, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION, "0", "1");
|
||||
});
|
||||
testContext.setInitialized(true);
|
||||
}
|
||||
|
@ -96,27 +96,6 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
realm.addAuthenticatorExecution(execution);
|
||||
}
|
||||
|
||||
private static void configureSessionLimits(RealmModel realm, AuthenticationFlowModel flow) {
|
||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(flow.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
execution.setAuthenticator(UserSessionLimitsAuthenticatorFactory.USER_SESSION_LIMITS);
|
||||
execution.setPriority(30);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
|
||||
AuthenticatorConfigModel configModel = new AuthenticatorConfigModel();
|
||||
Map<String, String> sessionAuthenticatorConfig = new HashMap<>();
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.USER_REALM_LIMIT, "0");
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.USER_CLIENT_LIMIT, "1");
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.ERROR_MESSAGE, ERROR_TO_DISPLAY);
|
||||
configModel.setConfig(sessionAuthenticatorConfig);
|
||||
configModel.setAlias("user-session-limits-" + flow.getId());
|
||||
configModel = realm.addAuthenticatorConfig(configModel);
|
||||
execution.setAuthenticatorConfig(configModel.getId());
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
}
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
|
@ -169,7 +148,7 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
events.expectLogin().assertEvent();
|
||||
assertSessionCount(1);
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.BROWSER_FLOW, UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
}
|
||||
|
@ -215,7 +194,7 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
events.expectLogin().assertEvent();
|
||||
assertSessionCount(1);
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.BROWSER_FLOW, UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.BROWSER_FLOW, UserSessionLimitsAuthenticatorFactory.USER_REALM_LIMIT, "0");
|
||||
|
@ -243,7 +222,7 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
assertEquals(200, response.getStatusCode());
|
||||
assertSessionCount(1);
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW, UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
}
|
||||
|
@ -278,7 +257,7 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
assertEquals(200, response.getStatusCode());
|
||||
assertSessionCount(1);
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW, UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW, UserSessionLimitsAuthenticatorFactory.USER_REALM_LIMIT, "0");
|
||||
|
@ -365,7 +344,7 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
updatePasswordPage.assertCurrent();
|
||||
updatePasswordPage.changePassword("resetPassword", "resetPassword");
|
||||
|
||||
assertSessionCount(1);
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
ApiUtil.resetUserPassword(testRealm().users().get(findUser("test-user@localhost").getId()), "password", false);
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW, UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
|
@ -452,7 +431,7 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
updatePasswordPage.assertCurrent();
|
||||
updatePasswordPage.changePassword("resetPassword", "resetPassword");
|
||||
|
||||
assertSessionCount(1);
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
ApiUtil.resetUserPassword(testRealm().users().get(findUser("test-user@localhost").getId()), "password", false);
|
||||
|
||||
|
@ -471,12 +450,4 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
|||
realm.updateAuthenticatorConfig(configModel);
|
||||
});
|
||||
}
|
||||
|
||||
private void assertSessionCount(int count) {
|
||||
testingClient.server().run(session -> {
|
||||
RealmModel realm = session.realms().getRealmByName("test");
|
||||
UserModel user = session.users().getUserByUsername(realm, "test-user@localhost");
|
||||
assertEquals(count, session.sessions().getUserSessionsStream(realm, user).count());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.keycloak.testsuite.sessionlimits;
|
||||
|
||||
import org.keycloak.authentication.authenticators.sessionlimits.UserSessionLimitsAuthenticatorFactory;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.testsuite.runonserver.RunOnServer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class UserSessionLimitsUtil {
|
||||
protected static final String ERROR_TO_DISPLAY = "This account has too many sessions";
|
||||
|
||||
protected static void configureSessionLimits(RealmModel realm, AuthenticationFlowModel flow, String behavior, String realmLimit, String clientLimit) {
|
||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(flow.getId());
|
||||
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
|
||||
execution.setAuthenticator(UserSessionLimitsAuthenticatorFactory.USER_SESSION_LIMITS);
|
||||
execution.setPriority(30);
|
||||
execution.setAuthenticatorFlow(false);
|
||||
|
||||
AuthenticatorConfigModel configModel = new AuthenticatorConfigModel();
|
||||
Map<String, String> sessionAuthenticatorConfig = new HashMap<>();
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.BEHAVIOR, behavior);
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.USER_REALM_LIMIT, realmLimit);
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.USER_CLIENT_LIMIT, clientLimit);
|
||||
sessionAuthenticatorConfig.put(UserSessionLimitsAuthenticatorFactory.ERROR_MESSAGE, ERROR_TO_DISPLAY);
|
||||
configModel.setConfig(sessionAuthenticatorConfig);
|
||||
configModel.setAlias("user-session-limits-" + flow.getId());
|
||||
configModel = realm.addAuthenticatorConfig(configModel);
|
||||
execution.setAuthenticatorConfig(configModel.getId());
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
}
|
||||
|
||||
static RunOnServer assertSessionCount(String realmName, String username, int count) {
|
||||
return (session) -> {
|
||||
RealmModel realm = session.realms().getRealmByName(realmName);
|
||||
UserModel user = session.users().getUserByUsername(realm, username);
|
||||
assertEquals(count, session.sessions().getUserSessionsStream(realm, user).count());
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue