Map Store Removal: Remove LockObjectsForModification
(#25323)
Signed-off-by: vramik <vramik@redhat.com> Closes #24793
This commit is contained in:
parent
0e535d2bbe
commit
df465456b8
23 changed files with 36 additions and 182 deletions
|
@ -1,104 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2022 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.utils;
|
|
||||||
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.UserSessionModel;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This flags the session that all information loaded from the stores should be locked as the service layer
|
|
||||||
* plans to modify it.
|
|
||||||
*
|
|
||||||
* This is just a hint to the underlying storage, and a store might choose to ignore it.
|
|
||||||
* The lock for any object retrieved from the session will be kept until the end of the transaction.
|
|
||||||
*
|
|
||||||
* If the store supports it, this could prevent exceptions due to optimistic locking
|
|
||||||
* problems later in the processing. If the caller retrieved objects without this wrapper, they would still be
|
|
||||||
* able to modify those objects, and those changes would be written to the store at the end of the transaction at the lastet,
|
|
||||||
* but they won't be locked.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author Alexander Schwartz
|
|
||||||
*/
|
|
||||||
public class LockObjectsForModification {
|
|
||||||
|
|
||||||
private static final String ATTRIBUTE = LockObjectsForModification.class.getCanonicalName();
|
|
||||||
|
|
||||||
public static boolean isEnabled(KeycloakSession session, Class<?> model) {
|
|
||||||
Set<Class<?>> lockedModels = getAttribute(session);
|
|
||||||
return lockedModels != null && lockedModels.contains(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Set<Class<?>> getAttribute(KeycloakSession session) {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (Set<Class<?>>) session.getAttribute(ATTRIBUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Set<Class<?>> getOrCreateAttribute(KeycloakSession session) {
|
|
||||||
Set<Class<?>> attribute = getAttribute(session);
|
|
||||||
if (attribute == null) {
|
|
||||||
attribute = new HashSet<>();
|
|
||||||
session.setAttribute(ATTRIBUTE, attribute);
|
|
||||||
}
|
|
||||||
return attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> V lockUserSessionsForModification(KeycloakSession session, CallableWithoutThrowingAnException<V> callable) {
|
|
||||||
return lockObjectsForModification(session, UserSessionModel.class, callable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> V lockRealmsForModification(KeycloakSession session, CallableWithoutThrowingAnException<V> callable) {
|
|
||||||
return lockObjectsForModification(session, RealmModel.class, callable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <V> V lockObjectsForModification(KeycloakSession session, Class<?> model, CallableWithoutThrowingAnException<V> callable) {
|
|
||||||
// Only map storage supported locking objects for modification, this logic will be remove in a follow up PR
|
|
||||||
return callable.call();
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface CallableWithoutThrowingAnException<V> {
|
|
||||||
/**
|
|
||||||
* Computes a result.
|
|
||||||
*
|
|
||||||
* @return computed result
|
|
||||||
*/
|
|
||||||
V call();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Enabled implements AutoCloseable {
|
|
||||||
|
|
||||||
private final KeycloakSession session;
|
|
||||||
private final Class<?> model;
|
|
||||||
|
|
||||||
public Enabled(KeycloakSession session, Class<?> model) {
|
|
||||||
this.session = session;
|
|
||||||
this.model = model;
|
|
||||||
getOrCreateAttribute(session).add(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
getOrCreateAttribute(session).remove(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -71,7 +71,6 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.keycloak.models.light.LightweightUserAdapter.isLightweightUser;
|
import static org.keycloak.models.light.LightweightUserAdapter.isLightweightUser;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -1064,7 +1063,7 @@ public class AuthenticationProcessor {
|
||||||
|
|
||||||
if (userSession == null) { // if no authenticator attached a usersession
|
if (userSession == null) { // if no authenticator attached a usersession
|
||||||
|
|
||||||
userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, authSession.getParentSession().getId()));
|
userSession = session.sessions().getUserSession(realm, authSession.getParentSession().getId());
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.fromString(authSession.getClientNote(AuthenticationManager.USER_SESSION_PERSISTENT_STATE));
|
UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.fromString(authSession.getClientNote(AuthenticationManager.USER_SESSION_PERSISTENT_STATE));
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,6 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.utils.StringUtil;
|
import org.keycloak.utils.StringUtil;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
public class UserSessionLimitsAuthenticator implements Authenticator {
|
public class UserSessionLimitsAuthenticator implements Authenticator {
|
||||||
|
|
||||||
private static Logger logger = Logger.getLogger(UserSessionLimitsAuthenticator.class);
|
private static Logger logger = Logger.getLogger(UserSessionLimitsAuthenticator.class);
|
||||||
|
@ -58,7 +56,7 @@ public class UserSessionLimitsAuthenticator implements Authenticator {
|
||||||
if (context.getRealm() != null && context.getUser() != null) {
|
if (context.getRealm() != null && context.getUser() != null) {
|
||||||
|
|
||||||
// Get the session count in this realm for this specific user
|
// Get the session count in this realm for this specific user
|
||||||
List<UserSessionModel> userSessionsForRealm = lockUserSessionsForModification(session, () -> session.sessions().getUserSessionsStream(context.getRealm(), context.getUser()).collect(Collectors.toList()));
|
List<UserSessionModel> userSessionsForRealm = session.sessions().getUserSessionsStream(context.getRealm(), context.getUser()).collect(Collectors.toList());
|
||||||
int userSessionCountForRealm = userSessionsForRealm.size();
|
int userSessionCountForRealm = userSessionsForRealm.size();
|
||||||
|
|
||||||
// Get the session count related to the current client for this user
|
// Get the session count related to the current client for this user
|
||||||
|
|
|
@ -100,8 +100,6 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.services.util.DefaultClientSessionContext;
|
import org.keycloak.services.util.DefaultClientSessionContext;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
|
@ -318,7 +316,7 @@ public class AuthorizationTokenService {
|
||||||
userSessionModel = new UserSessionManager(keycloakSession).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), request.getClientConnection().getRemoteAddr(),
|
userSessionModel = new UserSessionManager(keycloakSession).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), request.getClientConnection().getRemoteAddr(),
|
||||||
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
|
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
|
||||||
} else {
|
} else {
|
||||||
userSessionModel = lockUserSessionsForModification(keycloakSession, () -> sessions.getUserSession(realm, accessToken.getSessionState()));
|
userSessionModel = sessions.getUserSession(realm, accessToken.getSessionState());
|
||||||
|
|
||||||
if (userSessionModel == null) {
|
if (userSessionModel == null) {
|
||||||
userSessionModel = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
userSessionModel = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
||||||
|
|
|
@ -46,8 +46,6 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
|
@ -128,7 +126,7 @@ public class KeycloakIdentity implements Identity {
|
||||||
this.accessToken = AccessToken.class.cast(token);
|
this.accessToken = AccessToken.class.cast(token);
|
||||||
} else {
|
} else {
|
||||||
UserSessionProvider sessions = keycloakSession.sessions();
|
UserSessionProvider sessions = keycloakSession.sessions();
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(keycloakSession, () -> sessions.getUserSession(realm, token.getSessionState()));
|
UserSessionModel userSession = sessions.getUserSession(realm, token.getSessionState());
|
||||||
|
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
userSession = sessions.getOfflineUserSession(realm, token.getSessionState());
|
||||||
|
@ -297,7 +295,7 @@ public class KeycloakIdentity implements Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSessionProvider sessions = keycloakSession.sessions();
|
UserSessionProvider sessions = keycloakSession.sessions();
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(keycloakSession, () -> sessions.getUserSession(realm, accessToken.getSessionState()));
|
UserSessionModel userSession = sessions.getUserSession(realm, accessToken.getSessionState());
|
||||||
|
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
userSession = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
userSession = sessions.getOfflineUserSession(realm, accessToken.getSessionState());
|
||||||
|
|
|
@ -43,8 +43,6 @@ import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
|
@ -97,7 +95,7 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
|
||||||
if (action.getKeycloakSessionIds() != null) {
|
if (action.getKeycloakSessionIds() != null) {
|
||||||
for (String sessionId : action.getKeycloakSessionIds()) {
|
for (String sessionId : action.getKeycloakSessionIds()) {
|
||||||
String brokerSessionId = provider.getConfig().getAlias() + "." + sessionId;
|
String brokerSessionId = provider.getConfig().getAlias() + "." + sessionId;
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId));
|
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
|
||||||
if (userSession != null
|
if (userSession != null
|
||||||
&& userSession.getState() != UserSessionModel.State.LOGGING_OUT
|
&& userSession.getState() != UserSessionModel.State.LOGGING_OUT
|
||||||
&& userSession.getState() != UserSessionModel.State.LOGGED_OUT
|
&& userSession.getState() != UserSessionModel.State.LOGGED_OUT
|
||||||
|
|
|
@ -82,8 +82,6 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Pedro Igor
|
* @author Pedro Igor
|
||||||
*/
|
*/
|
||||||
|
@ -333,7 +331,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||||
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
|
|
||||||
}
|
}
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, state));
|
UserSessionModel userSession = session.sessions().getUserSession(realm, state);
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
logger.error("no valid user session");
|
logger.error("no valid user session");
|
||||||
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||||
|
|
|
@ -129,8 +129,6 @@ import java.util.Collections;
|
||||||
import jakarta.ws.rs.core.MultivaluedMap;
|
import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
import javax.xml.crypto.dsig.XMLSignature;
|
import javax.xml.crypto.dsig.XMLSignature;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
|
@ -349,7 +347,7 @@ public class SAMLEndpoint {
|
||||||
} else {
|
} else {
|
||||||
for (String sessionIndex : request.getSessionIndex()) {
|
for (String sessionIndex : request.getSessionIndex()) {
|
||||||
String brokerSessionId = config.getAlias() + "." + sessionIndex;
|
String brokerSessionId = config.getAlias() + "." + sessionIndex;
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId));
|
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
|
||||||
if (userSession != null) {
|
if (userSession != null) {
|
||||||
if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {
|
if (userSession.getState() == UserSessionModel.State.LOGGING_OUT || userSession.getState() == UserSessionModel.State.LOGGED_OUT) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -724,7 +722,7 @@ public class SAMLEndpoint {
|
||||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||||
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
}
|
}
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, relayState));
|
UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
logger.error("no valid user session");
|
logger.error("no valid user session");
|
||||||
event.event(EventType.LOGOUT);
|
event.event(EventType.LOGOUT);
|
||||||
|
|
|
@ -116,7 +116,6 @@ import jakarta.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import static org.keycloak.models.light.LightweightUserAdapter.isLightweightUser;
|
import static org.keycloak.models.light.LightweightUserAdapter.isLightweightUser;
|
||||||
import static org.keycloak.representations.IDToken.NONCE;
|
import static org.keycloak.representations.IDToken.NONCE;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stateless object that creates tokens and manages oauth access codes
|
* Stateless object that creates tokens and manages oauth access codes
|
||||||
|
@ -164,7 +163,7 @@ public class TokenManager {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Find userSession regularly for online tokens
|
// Find userSession regularly for online tokens
|
||||||
userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, oldToken.getSessionState()));
|
userSession = session.sessions().getUserSession(realm, oldToken.getSessionState());
|
||||||
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
|
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
|
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
|
||||||
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
|
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
|
||||||
|
@ -325,10 +324,10 @@ public class TokenManager {
|
||||||
private boolean validateTokenReuseForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token) {
|
private boolean validateTokenReuseForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token) {
|
||||||
UserSessionModel userSession = null;
|
UserSessionModel userSession = null;
|
||||||
if (token.getType().equals(TokenUtil.TOKEN_TYPE_REFRESH)) {
|
if (token.getType().equals(TokenUtil.TOKEN_TYPE_REFRESH)) {
|
||||||
userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, token.getSessionState()));
|
userSession = session.sessions().getUserSession(realm, token.getSessionState());
|
||||||
} else {
|
} else {
|
||||||
UserSessionManager sessionManager = new UserSessionManager(session);
|
UserSessionManager sessionManager = new UserSessionManager(session);
|
||||||
userSession = lockUserSessionsForModification(session, () -> sessionManager.findOfflineUserSession(realm, token.getSessionState()));
|
userSession = sessionManager.findOfflineUserSession(realm, token.getSessionState());
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.keycloak.protocol.oidc.endpoints;
|
||||||
import static org.keycloak.models.UserSessionModel.State.LOGGED_OUT;
|
import static org.keycloak.models.UserSessionModel.State.LOGGED_OUT;
|
||||||
import static org.keycloak.models.UserSessionModel.State.LOGGING_OUT;
|
import static org.keycloak.models.UserSessionModel.State.LOGGING_OUT;
|
||||||
import static org.keycloak.services.resources.LoginActionsService.SESSION_CODE;
|
import static org.keycloak.services.resources.LoginActionsService.SESSION_CODE;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
|
@ -435,7 +434,7 @@ public class LogoutEndpoint {
|
||||||
String idTokenIssuedAtStr = logoutSession.getAuthNote(OIDCLoginProtocol.LOGOUT_VALIDATED_ID_TOKEN_ISSUED_AT);
|
String idTokenIssuedAtStr = logoutSession.getAuthNote(OIDCLoginProtocol.LOGOUT_VALIDATED_ID_TOKEN_ISSUED_AT);
|
||||||
if (userSessionIdFromIdToken != null && idTokenIssuedAtStr != null) {
|
if (userSessionIdFromIdToken != null && idTokenIssuedAtStr != null) {
|
||||||
try {
|
try {
|
||||||
userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, userSessionIdFromIdToken));
|
userSession = session.sessions().getUserSession(realm, userSessionIdFromIdToken);
|
||||||
|
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
event.event(EventType.LOGOUT);
|
event.event(EventType.LOGOUT);
|
||||||
|
@ -452,8 +451,7 @@ public class LogoutEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
|
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
|
||||||
AuthenticationManager.AuthResult authResult = lockUserSessionsForModification(session,
|
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
|
||||||
() -> AuthenticationManager.authenticateIdentityCookie(session, realm, false));
|
|
||||||
if (authResult != null) {
|
if (authResult != null) {
|
||||||
userSession = userSession != null ? userSession : authResult.getSession();
|
userSession = userSession != null ? userSession : authResult.getSession();
|
||||||
return initiateBrowserLogout(userSession);
|
return initiateBrowserLogout(userSession);
|
||||||
|
@ -533,7 +531,7 @@ public class LogoutEndpoint {
|
||||||
userSessionModel = sessionManager.findOfflineUserSession(realm, token.getSessionState());
|
userSessionModel = sessionManager.findOfflineUserSession(realm, token.getSessionState());
|
||||||
} else {
|
} else {
|
||||||
String sessionState = token.getSessionState();
|
String sessionState = token.getSessionState();
|
||||||
userSessionModel = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, sessionState));
|
userSessionModel = session.sessions().getUserSession(realm, sessionState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userSessionModel != null) {
|
if (userSessionModel != null) {
|
||||||
|
@ -633,8 +631,7 @@ public class LogoutEndpoint {
|
||||||
AtomicReference<BackchannelLogoutResponse> backchannelLogoutResponse = new AtomicReference<>(new BackchannelLogoutResponse());
|
AtomicReference<BackchannelLogoutResponse> backchannelLogoutResponse = new AtomicReference<>(new BackchannelLogoutResponse());
|
||||||
backchannelLogoutResponse.get().setLocalLogoutSucceeded(true);
|
backchannelLogoutResponse.get().setLocalLogoutSucceeded(true);
|
||||||
identityProviderAliases.forEach(identityProviderAlias -> {
|
identityProviderAliases.forEach(identityProviderAlias -> {
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSessionByBrokerSessionId(realm,
|
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, identityProviderAlias + "." + sessionId);
|
||||||
identityProviderAlias + "." + sessionId));
|
|
||||||
|
|
||||||
if (logoutOfflineSessions) {
|
if (logoutOfflineSessions) {
|
||||||
if (offlineSessionsLazyLoadingEnabled) {
|
if (offlineSessionsLazyLoadingEnabled) {
|
||||||
|
|
|
@ -124,8 +124,6 @@ import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
|
@ -574,7 +572,7 @@ public class TokenEndpoint {
|
||||||
res = responseBuilder.build();
|
res = responseBuilder.build();
|
||||||
|
|
||||||
if (!responseBuilder.isOfflineToken()) {
|
if (!responseBuilder.isOfflineToken()) {
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, res.getSessionState()));
|
UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
|
||||||
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
|
||||||
updateClientSession(clientSession);
|
updateClientSession(clientSession);
|
||||||
updateUserSessionFromClientAuth(userSession);
|
updateUserSessionFromClientAuth(userSession);
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package org.keycloak.protocol.oidc.grants.device;
|
package org.keycloak.protocol.oidc.grants.device;
|
||||||
|
|
||||||
import static org.keycloak.protocol.oidc.OIDCLoginProtocolService.tokenServiceBaseUrl;
|
import static org.keycloak.protocol.oidc.OIDCLoginProtocolService.tokenServiceBaseUrl;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
|
@ -291,7 +290,7 @@ public class DeviceGrantType {
|
||||||
client.getId());
|
client.getId());
|
||||||
|
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, userSessionId));
|
userSession = session.sessions().getUserSession(realm, userSessionId);
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING,
|
throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING,
|
||||||
"The authorization request is verified but can not lookup the user session yet",
|
"The authorization request is verified but can not lookup the user session yet",
|
||||||
|
|
|
@ -31,8 +31,6 @@ import org.keycloak.models.SingleUseObjectProvider;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.services.managers.UserSessionCrossDCManager;
|
import org.keycloak.services.managers.UserSessionCrossDCManager;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
|
@ -101,10 +99,10 @@ public class OAuth2CodeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve UserSession
|
// Retrieve UserSession
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, clientUUID));
|
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, clientUUID);
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
// Needed to track if code is invalid or was already used.
|
// Needed to track if code is invalid or was already used.
|
||||||
userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, userSessionId));
|
userSession = session.sessions().getUserSession(realm, userSessionId);
|
||||||
if (userSession == null) {
|
if (userSession == null) {
|
||||||
return result.illegalCode();
|
return result.illegalCode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,8 +144,6 @@ import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resource class for the saml connect token service
|
* Resource class for the saml connect token service
|
||||||
|
@ -1164,7 +1162,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
return emptyArtifactResponseMessage(artifactResolveMessage, null);
|
return emptyArtifactResponseMessage(artifactResolveMessage, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSessionModel userSessionModel = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, sessionMapping.get(SamlProtocol.USER_SESSION_ID)));
|
UserSessionModel userSessionModel = session.sessions().getUserSession(realm, sessionMapping.get(SamlProtocol.USER_SESSION_ID));
|
||||||
if (userSessionModel == null) {
|
if (userSessionModel == null) {
|
||||||
logger.errorf("UserSession with id: %s, that corresponds to artifact: %s does not exist.", sessionMapping.get(SamlProtocol.USER_SESSION_ID), artifact);
|
logger.errorf("UserSession with id: %s, that corresponds to artifact: %s does not exist.", sessionMapping.get(SamlProtocol.USER_SESSION_ID), artifact);
|
||||||
return emptyArtifactResponseMessage(artifactResolveMessage, null);
|
return emptyArtifactResponseMessage(artifactResolveMessage, null);
|
||||||
|
|
|
@ -115,7 +115,6 @@ import static org.keycloak.models.light.LightweightUserAdapter.isLightweightUser
|
||||||
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
|
import static org.keycloak.models.UserSessionModel.CORRESPONDING_SESSION_ID;
|
||||||
import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.isOAuth2DeviceVerificationFlow;
|
import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.isOAuth2DeviceVerificationFlow;
|
||||||
import static org.keycloak.services.util.CookieHelper.getCookie;
|
import static org.keycloak.services.util.CookieHelper.getCookie;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stateless object that manages authentication
|
* Stateless object that manages authentication
|
||||||
|
@ -229,7 +228,7 @@ public class AuthenticationManager {
|
||||||
verifier.verifierContext(signatureVerifier);
|
verifier.verifierContext(signatureVerifier);
|
||||||
|
|
||||||
AccessToken token = verifier.verify().getToken();
|
AccessToken token = verifier.verify().getToken();
|
||||||
UserSessionModel cookieSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, token.getSessionState()));
|
UserSessionModel cookieSession = session.sessions().getUserSession(realm, token.getSessionState());
|
||||||
if (cookieSession == null || !cookieSession.getId().equals(userSession.getId())) return true;
|
if (cookieSession == null || !cookieSession.getId().equals(userSession.getId())) return true;
|
||||||
expireIdentityCookie(realm, uriInfo, session);
|
expireIdentityCookie(realm, uriInfo, session);
|
||||||
return true;
|
return true;
|
||||||
|
@ -319,9 +318,9 @@ public class AuthenticationManager {
|
||||||
|
|
||||||
// Check if "online" session still exists and remove it too
|
// Check if "online" session still exists and remove it too
|
||||||
String onlineUserSessionId = userSession.getNote(CORRESPONDING_SESSION_ID);
|
String onlineUserSessionId = userSession.getNote(CORRESPONDING_SESSION_ID);
|
||||||
UserSessionModel onlineUserSession = lockUserSessionsForModification(session, () -> (onlineUserSessionId != null) ?
|
UserSessionModel onlineUserSession = onlineUserSessionId != null ?
|
||||||
session.sessions().getUserSession(realm, onlineUserSessionId) :
|
session.sessions().getUserSession(realm, onlineUserSessionId) :
|
||||||
session.sessions().getUserSession(realm, userSession.getId()));
|
session.sessions().getUserSession(realm, userSession.getId());
|
||||||
|
|
||||||
if (onlineUserSession != null) {
|
if (onlineUserSession != null) {
|
||||||
session.sessions().removeUserSession(realm, onlineUserSession);
|
session.sessions().removeUserSession(realm, onlineUserSession);
|
||||||
|
@ -950,7 +949,7 @@ public class AuthenticationManager {
|
||||||
if (split.length >= 3) {
|
if (split.length >= 3) {
|
||||||
String oldSessionId = split[2];
|
String oldSessionId = split[2];
|
||||||
if (!oldSessionId.equals(userSession.getId())) {
|
if (!oldSessionId.equals(userSession.getId())) {
|
||||||
UserSessionModel oldSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, oldSessionId));
|
UserSessionModel oldSession = session.sessions().getUserSession(realm, oldSessionId);
|
||||||
if (oldSession != null) {
|
if (oldSession != null) {
|
||||||
logger.debugv("Removing old user session: session: {0}", oldSessionId);
|
logger.debugv("Removing old user session: session: {0}", oldSessionId);
|
||||||
session.sessions().removeUserSession(realm, oldSession);
|
session.sessions().removeUserSession(realm, oldSession);
|
||||||
|
|
|
@ -28,23 +28,18 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.SessionExpiration;
|
import org.keycloak.models.utils.SessionExpiration;
|
||||||
import org.keycloak.protocol.RestartLoginCookie;
|
import org.keycloak.protocol.RestartLoginCookie;
|
||||||
import org.keycloak.services.resources.LoginActionsService;
|
|
||||||
import org.keycloak.services.util.CookieHelper;
|
import org.keycloak.services.util.CookieHelper;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
import org.keycloak.sessions.CommonClientSessionModel;
|
|
||||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
import org.keycloak.sessions.StickySessionEncoderProvider;
|
import org.keycloak.sessions.StickySessionEncoderProvider;
|
||||||
|
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.keycloak.authentication.AuthenticationProcessor.CURRENT_FLOW_PATH;
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -108,7 +103,7 @@ public class AuthenticationSessionManager {
|
||||||
AuthSessionId authSessionId = decodeAuthSessionId(oldEncodedId);
|
AuthSessionId authSessionId = decodeAuthSessionId(oldEncodedId);
|
||||||
String sessionId = authSessionId.getDecodedId();
|
String sessionId = authSessionId.getDecodedId();
|
||||||
|
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, sessionId));
|
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||||
|
|
||||||
if (userSession != null) {
|
if (userSession != null) {
|
||||||
reencodeAuthSessionCookie(oldEncodedId, authSessionId, realm);
|
reencodeAuthSessionCookie(oldEncodedId, authSessionId, realm);
|
||||||
|
@ -284,7 +279,7 @@ public class AuthenticationSessionManager {
|
||||||
|
|
||||||
// Check to see if we already have authenticationSession with same ID
|
// Check to see if we already have authenticationSession with same ID
|
||||||
public UserSessionModel getUserSession(AuthenticationSessionModel authSession) {
|
public UserSessionModel getUserSession(AuthenticationSessionModel authSession) {
|
||||||
return lockUserSessionsForModification(session, () -> session.sessions().getUserSession(authSession.getRealm(), authSession.getParentSession().getId()));
|
return session.sessions().getUserSession(authSession.getRealm(), authSession.getParentSession().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
|
||||||
import static org.keycloak.services.managers.AuthenticationManager.authenticateIdentityCookie;
|
import static org.keycloak.services.managers.AuthenticationManager.authenticateIdentityCookie;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -72,7 +71,7 @@ public class UserSessionCrossDCManager {
|
||||||
// in case the auth session was removed, we fall back to the identity cookie
|
// in case the auth session was removed, we fall back to the identity cookie
|
||||||
// we are here doing the user session lookup twice, however the second lookup is going to make sure the
|
// we are here doing the user session lookup twice, however the second lookup is going to make sure the
|
||||||
// session exists in remote caches
|
// session exists in remote caches
|
||||||
AuthenticationManager.AuthResult authResult = lockUserSessionsForModification(kcSession, () -> authenticateIdentityCookie(kcSession, realm, true));
|
AuthenticationManager.AuthResult authResult = authenticateIdentityCookie(kcSession, realm, true);
|
||||||
|
|
||||||
if (authResult != null && authResult.getSession() != null) {
|
if (authResult != null && authResult.getSession() != null) {
|
||||||
sessionCookies = Collections.singletonList(authResult.getSession().getId());
|
sessionCookies = Collections.singletonList(authResult.getSession().getId());
|
||||||
|
@ -84,9 +83,9 @@ public class UserSessionCrossDCManager {
|
||||||
String sessionId = authSessionId.getDecodedId();
|
String sessionId = authSessionId.getDecodedId();
|
||||||
|
|
||||||
// This will remove userSession "locally" if it doesn't exist on remoteCache
|
// This will remove userSession "locally" if it doesn't exist on remoteCache
|
||||||
lockUserSessionsForModification(kcSession, () -> kcSession.sessions().getUserSessionWithPredicate(realm, sessionId, false, (UserSessionModel userSession2) -> userSession2 == null));
|
kcSession.sessions().getUserSessionWithPredicate(realm, sessionId, false, (UserSessionModel userSession2) -> userSession2 == null);
|
||||||
|
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(kcSession, () -> kcSession.sessions().getUserSession(realm, sessionId));
|
UserSessionModel userSession = kcSession.sessions().getUserSession(realm, sessionId);
|
||||||
|
|
||||||
if (userSession != null) {
|
if (userSession != null) {
|
||||||
asm.reencodeAuthSessionCookie(oldEncodedId, authSessionId, realm);
|
asm.reencodeAuthSessionCookie(oldEncodedId, authSessionId, realm);
|
||||||
|
|
|
@ -43,8 +43,6 @@ import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author hmlnarik
|
* @author hmlnarik
|
||||||
|
@ -123,7 +121,7 @@ public class LoginActionsServiceChecks {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(context.getSession(), () -> context.getSession().sessions().getUserSession(context.getRealm(), authSessionId));
|
UserSessionModel userSession = context.getSession().sessions().getUserSession(context.getRealm(), authSessionId);
|
||||||
boolean hasNoRequiredActions =
|
boolean hasNoRequiredActions =
|
||||||
(userSession == null || userSession.getUser().getRequiredActionsStream().count() == 0)
|
(userSession == null || userSession.getUser().getRequiredActionsStream().count() == 0)
|
||||||
&&
|
&&
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package org.keycloak.services.resources;
|
package org.keycloak.services.resources;
|
||||||
|
|
||||||
import static org.keycloak.services.managers.AuthenticationManager.authenticateIdentityCookie;
|
import static org.keycloak.services.managers.AuthenticationManager.authenticateIdentityCookie;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
@ -40,7 +39,6 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
|
||||||
import org.keycloak.protocol.AuthorizationEndpointBase;
|
import org.keycloak.protocol.AuthorizationEndpointBase;
|
||||||
import org.keycloak.protocol.RestartLoginCookie;
|
import org.keycloak.protocol.RestartLoginCookie;
|
||||||
import org.keycloak.services.ErrorPage;
|
import org.keycloak.services.ErrorPage;
|
||||||
|
@ -185,7 +183,7 @@ public class SessionCodeChecks {
|
||||||
|
|
||||||
// if restart from cookie was not found check if the user is already authenticated
|
// if restart from cookie was not found check if the user is already authenticated
|
||||||
if (response.getStatus() != Response.Status.FOUND.getStatusCode()) {
|
if (response.getStatus() != Response.Status.FOUND.getStatusCode()) {
|
||||||
AuthenticationManager.AuthResult authResult = lockUserSessionsForModification(session, () -> authenticateIdentityCookie(session, realm, false));
|
AuthenticationManager.AuthResult authResult = authenticateIdentityCookie(session, realm, false);
|
||||||
|
|
||||||
if (authResult != null && authResult.getSession() != null) {
|
if (authResult != null && authResult.getSession() != null) {
|
||||||
LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class).setAuthenticationSession(authSession)
|
LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class).setAuthenticationSession(authSession)
|
||||||
|
|
|
@ -32,7 +32,6 @@ import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.keycloak.common.util.Time;
|
|
||||||
import org.keycloak.device.DeviceActivityManager;
|
import org.keycloak.device.DeviceActivityManager;
|
||||||
import org.keycloak.models.AccountRoles;
|
import org.keycloak.models.AccountRoles;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -46,8 +45,6 @@ import org.keycloak.representations.account.SessionRepresentation;
|
||||||
import org.keycloak.services.managers.Auth;
|
import org.keycloak.services.managers.Auth;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
|
@ -147,7 +144,7 @@ public class SessionResource {
|
||||||
@NoCache
|
@NoCache
|
||||||
public Response logout(@PathParam("id") String id) {
|
public Response logout(@PathParam("id") String id) {
|
||||||
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
auth.require(AccountRoles.MANAGE_ACCOUNT);
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, id));
|
UserSessionModel userSession = session.sessions().getUserSession(realm, id);
|
||||||
if (userSession != null && userSession.getUser().equals(user)) {
|
if (userSession != null && userSession.getUser().equals(user)) {
|
||||||
AuthenticationManager.backchannelLogout(session, userSession, true);
|
AuthenticationManager.backchannelLogout(session, userSession, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.services.resources.admin;
|
package org.keycloak.services.resources.admin;
|
||||||
|
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
import static org.keycloak.util.JsonSerialization.readValue;
|
import static org.keycloak.util.JsonSerialization.readValue;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -633,7 +632,7 @@ public class RealmAdminResource {
|
||||||
public void deleteSession(@PathParam("session") String sessionId) {
|
public void deleteSession(@PathParam("session") String sessionId) {
|
||||||
auth.users().requireManage();
|
auth.users().requireManage();
|
||||||
|
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, sessionId));
|
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
|
||||||
if (userSession == null) throw new NotFoundException("Sesssion not found");
|
if (userSession == null) throw new NotFoundException("Sesssion not found");
|
||||||
AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), connection, headers, true);
|
AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), connection, headers, true);
|
||||||
adminEvent.operation(OperationType.DELETE).resource(ResourceType.USER_SESSION).resourcePath(session.getContext().getUri()).success();
|
adminEvent.operation(OperationType.DELETE).resource(ResourceType.USER_SESSION).resourcePath(session.getContext().getUri()).success();
|
||||||
|
|
|
@ -125,7 +125,6 @@ import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
|
||||||
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
|
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
|
||||||
import static org.keycloak.services.resources.admin.UserProfileResource.createUserProfileMetadata;
|
import static org.keycloak.services.resources.admin.UserProfileResource.createUserProfileMetadata;
|
||||||
import static org.keycloak.userprofile.UserProfileContext.USER_API;
|
import static org.keycloak.userprofile.UserProfileContext.USER_API;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base resource for managing users
|
* Base resource for managing users
|
||||||
|
@ -359,7 +358,7 @@ public class UserResource {
|
||||||
String sessionState = auth.adminAuth().getToken().getSessionState();
|
String sessionState = auth.adminAuth().getToken().getSessionState();
|
||||||
if (authenticatedRealm.getId().equals(realm.getId()) && sessionState != null) {
|
if (authenticatedRealm.getId().equals(realm.getId()) && sessionState != null) {
|
||||||
sameRealm = true;
|
sameRealm = true;
|
||||||
UserSessionModel userSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(authenticatedRealm, sessionState));
|
UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, sessionState);
|
||||||
AuthenticationManager.expireIdentityCookie(realm, session.getContext().getUri(), session);
|
AuthenticationManager.expireIdentityCookie(realm, session.getContext().getUri(), session);
|
||||||
AuthenticationManager.expireRememberMeCookie(realm, session.getContext().getUri(), session);
|
AuthenticationManager.expireRememberMeCookie(realm, session.getContext().getUri(), session);
|
||||||
AuthenticationManager.expireAuthSessionCookie(realm, session.getContext().getUri(), session);
|
AuthenticationManager.expireAuthSessionCookie(realm, session.getContext().getUri(), session);
|
||||||
|
|
|
@ -37,8 +37,6 @@ import java.util.stream.IntStream;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.aMapWithSize;
|
import static org.hamcrest.Matchers.aMapWithSize;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification;
|
|
||||||
|
|
||||||
|
|
||||||
@RequireProvider(UserSessionProvider.class)
|
@RequireProvider(UserSessionProvider.class)
|
||||||
public class UserSessionConcurrencyTest extends KeycloakModelTest {
|
public class UserSessionConcurrencyTest extends KeycloakModelTest {
|
||||||
|
@ -87,7 +85,7 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest {
|
||||||
RealmModel realm = session.realms().getRealm(realmId);
|
RealmModel realm = session.realms().getRealm(realmId);
|
||||||
ClientModel client = realm.getClientByClientId("client" + (n % CLIENTS_COUNT));
|
ClientModel client = realm.getClientByClientId("client" + (n % CLIENTS_COUNT));
|
||||||
|
|
||||||
UserSessionModel uSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, uId));
|
UserSessionModel uSession = session.sessions().getUserSession(realm, uId);
|
||||||
AuthenticatedClientSessionModel cSession = uSession.getAuthenticatedClientSessionByClient(client.getId());
|
AuthenticatedClientSessionModel cSession = uSession.getAuthenticatedClientSessionByClient(client.getId());
|
||||||
if (cSession == null) {
|
if (cSession == null) {
|
||||||
wasWriting.set(true);
|
wasWriting.set(true);
|
||||||
|
|
Loading…
Reference in a new issue