Session cross-reference / transaction mismatch
Closes https://github.com/keycloak/keycloak/issues/20855
This commit is contained in:
parent
4b3733de2e
commit
f3fcf1f8c5
2 changed files with 80 additions and 47 deletions
|
@ -76,6 +76,8 @@ import java.util.stream.Stream;
|
|||
import org.keycloak.models.AccountRoles;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
@ -275,14 +277,82 @@ public final class KeycloakModelUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Copy all the objects in the context to the session.
|
||||
* @param session The session
|
||||
* @param context The context
|
||||
* Sets up the context for the specified session with the RealmModel.
|
||||
*
|
||||
* @param origContext The original context to propagate
|
||||
* @param targetSession The new target session to propagate the context to
|
||||
*/
|
||||
public static void propagateContext(KeycloakSession session, KeycloakContext context) {
|
||||
session.getContext().setRealm(context.getRealm());
|
||||
session.getContext().setClient(context.getClient());
|
||||
session.getContext().setAuthenticationSession(context.getAuthenticationSession());
|
||||
public static void cloneContextRealmClientToSession(final KeycloakContext origContext, final KeycloakSession targetSession) {
|
||||
cloneContextToSession(origContext, targetSession, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the context for the specified session with the RealmModel, clientModel and
|
||||
* AuthenticatedSessionModel.
|
||||
*
|
||||
* @param origContext The original context to propagate
|
||||
* @param targetSession The new target session to propagate the context to
|
||||
*/
|
||||
public static void cloneContextRealmClientSessionToSession(final KeycloakContext origContext, final KeycloakSession targetSession) {
|
||||
cloneContextToSession(origContext, targetSession, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the context for the specified session.The original realm's context is used to
|
||||
* determine what models need to be re-loaded using the current session. The models
|
||||
* in the context are re-read from the new session via the IDs.
|
||||
*/
|
||||
private static void cloneContextToSession(final KeycloakContext origContext, final KeycloakSession targetSession,
|
||||
final boolean includeAuthenticatedSessionModel) {
|
||||
if (origContext == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// setup realm model if necessary.
|
||||
RealmModel realmModel = null;
|
||||
if (origContext.getRealm() != null) {
|
||||
realmModel = targetSession.realms().getRealm(origContext.getRealm().getId());
|
||||
if (realmModel != null) {
|
||||
targetSession.getContext().setRealm(realmModel);
|
||||
}
|
||||
}
|
||||
|
||||
// setup client model if necessary.
|
||||
ClientModel clientModel = null;
|
||||
if (origContext.getClient() != null) {
|
||||
if (origContext.getRealm() == null || !Objects.equals(origContext.getRealm().getId(), origContext.getClient().getRealm().getId())) {
|
||||
realmModel = targetSession.realms().getRealm(origContext.getClient().getRealm().getId());
|
||||
}
|
||||
if (realmModel != null) {
|
||||
clientModel = targetSession.clients().getClientById(realmModel, origContext.getClient().getId());
|
||||
if (clientModel != null) {
|
||||
targetSession.getContext().setClient(clientModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setup auth session model if necessary.
|
||||
if (includeAuthenticatedSessionModel && origContext.getAuthenticationSession() != null) {
|
||||
if (origContext.getClient() == null || !Objects.equals(origContext.getClient().getId(), origContext.getAuthenticationSession().getClient().getId())) {
|
||||
realmModel = (origContext.getRealm() == null || !Objects.equals(origContext.getRealm().getId(), origContext.getAuthenticationSession().getRealm().getId()))
|
||||
? targetSession.realms().getRealm(origContext.getAuthenticationSession().getRealm().getId())
|
||||
: targetSession.getContext().getRealm();
|
||||
clientModel = (realmModel != null)
|
||||
? targetSession.clients().getClientById(realmModel, origContext.getAuthenticationSession().getClient().getId())
|
||||
: null;
|
||||
}
|
||||
if (clientModel != null) {
|
||||
RootAuthenticationSessionModel rootAuthSession = targetSession.authenticationSessions().getRootAuthenticationSession(
|
||||
realmModel, origContext.getAuthenticationSession().getParentSession().getId());
|
||||
if (rootAuthSession != null) {
|
||||
AuthenticationSessionModel authSessionModel = rootAuthSession.getAuthenticationSession(clientModel,
|
||||
origContext.getAuthenticationSession().getTabId());
|
||||
if (authSessionModel != null) {
|
||||
targetSession.getContext().setAuthenticationSession(authSessionModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -307,11 +377,9 @@ public final class KeycloakModelUtils {
|
|||
public static <V> V runJobInTransactionWithResult(KeycloakSessionFactory factory, KeycloakContext context, final KeycloakSessionTaskWithResult<V> callable) {
|
||||
V result;
|
||||
try (KeycloakSession session = factory.create()) {
|
||||
if (context != null) {
|
||||
propagateContext(session, context);
|
||||
}
|
||||
session.getTransactionManager().begin();
|
||||
try {
|
||||
cloneContextRealmClientToSession(context, session);
|
||||
result = callable.run(session);
|
||||
} catch (Throwable t) {
|
||||
session.getTransactionManager().setRollbackOnly();
|
||||
|
|
|
@ -19,12 +19,9 @@ package org.keycloak.common.util;
|
|||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionTaskWithResult;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
/**
|
||||
* A {@link KeycloakSessionTaskWithResult} that is aimed to be used by endpoints that want to produce a {@link Response} in
|
||||
|
@ -60,7 +57,7 @@ public abstract class ResponseSessionTask implements KeycloakSessionTaskWithResu
|
|||
KeycloakSession originalContextSession = Resteasy.getContextData(KeycloakSession.class);
|
||||
try {
|
||||
// set up the current session context based on the original session context.
|
||||
this.setupSessionContext(session);
|
||||
KeycloakModelUtils.cloneContextRealmClientSessionToSession(originalSession.getContext(), session);
|
||||
// push the current session into the resteasy context.
|
||||
Resteasy.pushContext(KeycloakSession.class, session);
|
||||
// run the actual task.
|
||||
|
@ -83,38 +80,6 @@ public abstract class ResponseSessionTask implements KeycloakSessionTaskWithResu
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the context for the specified session. The original realm's context is used to determine what models
|
||||
* need to be re-loaded using the current session.
|
||||
*
|
||||
* @param session the session whose context is to be prepared.
|
||||
*/
|
||||
private void setupSessionContext(final KeycloakSession session) {
|
||||
if (this.originalSession == null) return;
|
||||
KeycloakContext context = this.originalSession.getContext();
|
||||
// setup realm model if necessary.
|
||||
RealmModel realmModel = null;
|
||||
if (context.getRealm() != null) {
|
||||
realmModel = session.realms().getRealm(context.getRealm().getId());
|
||||
session.getContext().setRealm(realmModel);
|
||||
}
|
||||
// setup client model if necessary.
|
||||
ClientModel clientModel = null;
|
||||
if (context.getClient() != null) {
|
||||
clientModel = session.clients().getClientById(realmModel, context.getClient().getId());
|
||||
session.getContext().setClient(clientModel);
|
||||
}
|
||||
// setup auth session model if necessary.
|
||||
if (context.getAuthenticationSession() != null) {
|
||||
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().getRootAuthenticationSession(realmModel,
|
||||
context.getAuthenticationSession().getParentSession().getId());
|
||||
if (rootAuthSession != null) {
|
||||
session.getContext().setAuthenticationSession(rootAuthSession.getAuthenticationSession(clientModel,
|
||||
context.getAuthenticationSession().getTabId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the response that is to be returned.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue