mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-08 14:32:05 -03:30
Add logout event to UserSessionLimitsAuthenticator
Closes #44843 Signed-off-by: Robin Meese <39960884+robson90@users.noreply.github.com> Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com> Co-authored-by: Alexander Schwartz <alexander.schwartz@ibm.com>
This commit is contained in:
parent
ac557234a2
commit
35ee49b5d4
@ -39,11 +39,13 @@ only the basic attributes in representations or all of them.
|
||||
The `UserProfile` interface is a private API and should not be implemented by custom code. However, if you have extensions that
|
||||
implement this interface, you will need to update your code to accommodate this new method.
|
||||
|
||||
=== LOGOUT events when logging out sessions via the Account Console
|
||||
=== Additional LOGOUT events
|
||||
|
||||
When logging out sessions via the Account Console {project_name} now creates LOGOUT user events to track this activity.
|
||||
The events are connected to the `account` client.
|
||||
|
||||
When the number of sessions for a user is limited, and you have configured the oldest session to be logged out once the limit is reached, {project_name} now creates LOGOUT user events to track this. The events are connected to the client that triggered the new login.
|
||||
|
||||
=== Identity Provider refactoring
|
||||
|
||||
The private SPI for identity providers has been refactored. This is to allow identity providers to support more use
|
||||
|
||||
@ -14,6 +14,8 @@ import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.AuthenticationFlowException;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
@ -179,7 +181,7 @@ public class UserSessionLimitsAuthenticator implements Authenticator {
|
||||
|
||||
case UserSessionLimitsAuthenticatorFactory.TERMINATE_OLDEST_SESSION:
|
||||
logger.info("Terminating oldest session");
|
||||
var removedSessions = logoutOldestSessions(userSessions, limit);
|
||||
var removedSessions = logoutOldestSessions(userSessions, limit, context.getEvent());
|
||||
context.success();
|
||||
return removedSessions;
|
||||
}
|
||||
@ -190,7 +192,7 @@ public class UserSessionLimitsAuthenticator implements Authenticator {
|
||||
/**
|
||||
* @return A list of logged-out user sessions, if any.
|
||||
*/
|
||||
private List<UserSessionModel> logoutOldestSessions(List<UserSessionModel> userSessions, long limit) {
|
||||
private List<UserSessionModel> logoutOldestSessions(List<UserSessionModel> userSessions, long limit, EventBuilder eventBuilder) {
|
||||
long numberOfSessionsThatNeedToBeLoggedOut = getNumberOfSessionsThatNeedToBeLoggedOut(userSessions.size(), limit);
|
||||
if (numberOfSessionsThatNeedToBeLoggedOut == 1) {
|
||||
logger.info("Logging out oldest session");
|
||||
@ -206,6 +208,11 @@ public class UserSessionLimitsAuthenticator implements Authenticator {
|
||||
|
||||
for (UserSessionModel userSession : userSessionsToBeRemoved) {
|
||||
AuthenticationManager.backchannelLogout(session, userSession, true);
|
||||
eventBuilder.clone()
|
||||
.event(EventType.LOGOUT)
|
||||
.user(userSession.getUser())
|
||||
.session(userSession.getId())
|
||||
.success();
|
||||
}
|
||||
|
||||
return userSessionsToBeRemoved;
|
||||
|
||||
@ -46,6 +46,7 @@ import org.keycloak.testsuite.util.GreenMailRule;
|
||||
import org.keycloak.testsuite.util.MailUtils;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@ -171,14 +172,24 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
||||
// Login and verify login was successful
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
events.expectLogin().assertEvent();
|
||||
EventRepresentation initialLoginEvent = events.expectLogin().assertEvent();
|
||||
String userId = initialLoginEvent.getUserId();
|
||||
String initialLoginSessionID = initialLoginEvent.getSessionId();
|
||||
|
||||
// Delete the cookies, while maintaining the server side session active
|
||||
super.deleteCookies();
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
events.expectLogin().assertEvent();
|
||||
// assert we have a logout session event, as the authenticator should have deleted the first session.
|
||||
events.expect(EventType.LOGOUT)
|
||||
.user(userId)
|
||||
.session(initialLoginSessionID)
|
||||
.assertEvent();
|
||||
// User is first logged out, then logged in with a fresh sessionId
|
||||
events.expectLogin()
|
||||
.session(Matchers.not(initialLoginSessionID))
|
||||
.assertEvent();
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.BROWSER_FLOW, UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
@ -217,14 +228,24 @@ public class UserSessionLimitsTest extends AbstractTestRealmKeycloakTest {
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.BROWSER_FLOW, UserSessionLimitsAuthenticatorFactory.USER_CLIENT_LIMIT, "0");
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
events.expectLogin().assertEvent();
|
||||
EventRepresentation initialLoginEvent = events.expectLogin().assertEvent();
|
||||
String userId = initialLoginEvent.getUserId();
|
||||
String initialLoginSessionID = initialLoginEvent.getSessionId();
|
||||
|
||||
// Delete the cookies, while maintaining the server side session active
|
||||
super.deleteCookies();
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
events.expectLogin().assertEvent();
|
||||
// assert we have a logout session event, as the authenticator should have deleted the first session.
|
||||
events.expect(EventType.LOGOUT)
|
||||
.user(userId)
|
||||
.session(initialLoginSessionID)
|
||||
.assertEvent();
|
||||
// User is first logged out, then logged in with a fresh sessionId
|
||||
events.expectLogin()
|
||||
.session(Matchers.not(initialLoginSessionID))
|
||||
.assertEvent();
|
||||
testingClient.server(realmName).run(assertSessionCount(realmName, username, 1));
|
||||
} finally {
|
||||
setAuthenticatorConfigItem(DefaultAuthenticationFlows.BROWSER_FLOW, UserSessionLimitsAuthenticatorFactory.BEHAVIOR, UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user