use of org.keycloak.models.UserSessionModel in project keycloak by keycloak.
the class JpaUserSessionPersisterProvider method loadUserSessionsWithClientSessions.
private Stream<UserSessionModel> loadUserSessionsWithClientSessions(TypedQuery<PersistentUserSessionEntity> query, String offlineStr) {
List<PersistentUserSessionAdapter> userSessionAdapters = closing(query.getResultStream().map(this::toAdapter).filter(Objects::nonNull)).collect(Collectors.toList());
Map<String, PersistentUserSessionAdapter> sessionsById = userSessionAdapters.stream().collect(Collectors.toMap(UserSessionModel::getId, Function.identity()));
Set<String> removedClientUUIDs = new HashSet<>();
if (!sessionsById.isEmpty()) {
String fromUserSessionId = userSessionAdapters.get(0).getId();
String toUserSessionId = userSessionAdapters.get(userSessionAdapters.size() - 1).getId();
TypedQuery<PersistentClientSessionEntity> queryClientSessions = em.createNamedQuery("findClientSessionsOrderedById", PersistentClientSessionEntity.class);
queryClientSessions.setParameter("offline", offlineStr);
queryClientSessions.setParameter("fromSessionId", fromUserSessionId);
queryClientSessions.setParameter("toSessionId", toUserSessionId);
closing(queryClientSessions.getResultStream()).forEach(clientSession -> {
PersistentUserSessionAdapter userSession = sessionsById.get(clientSession.getUserSessionId());
// check if we have a user session for the client session
if (userSession != null) {
boolean added = addClientSessionToAuthenticatedClientSessionsIfPresent(userSession, clientSession);
if (!added) {
// client was removed in the meantime
removedClientUUIDs.add(clientSession.getClientId());
}
}
});
}
for (String clientUUID : removedClientUUIDs) {
onClientRemoved(clientUUID);
}
return userSessionAdapters.stream().map(UserSessionModel.class::cast);
}
use of org.keycloak.models.UserSessionModel in project keycloak by keycloak.
the class UserSessionLimitsAuthenticator method handleLimitExceeded.
private void handleLimitExceeded(AuthenticationFlowContext context, List<UserSessionModel> userSessions, String eventDetails) {
switch(behavior) {
case UserSessionLimitsAuthenticatorFactory.DENY_NEW_SESSION:
logger.info("Denying new session");
String errorMessage = Optional.ofNullable(context.getAuthenticatorConfig()).map(AuthenticatorConfigModel::getConfig).map(f -> f.get(UserSessionLimitsAuthenticatorFactory.ERROR_MESSAGE)).orElse(SESSION_LIMIT_EXCEEDED);
context.getEvent().error(Errors.GENERIC_AUTHENTICATION_ERROR);
Response challenge = context.form().setError(errorMessage).createErrorPage(Response.Status.FORBIDDEN);
context.failure(AuthenticationFlowError.GENERIC_AUTHENTICATION_ERROR, challenge, eventDetails, errorMessage);
break;
case UserSessionLimitsAuthenticatorFactory.TERMINATE_OLDEST_SESSION:
logger.info("Terminating oldest session");
logoutOldestSession(userSessions);
context.success();
break;
}
}
use of org.keycloak.models.UserSessionModel in project keycloak by keycloak.
the class UserSessionLimitsAuthenticator method authenticate.
@Override
public void authenticate(AuthenticationFlowContext context) {
AuthenticatorConfigModel authenticatorConfig = context.getAuthenticatorConfig();
Map<String, String> config = authenticatorConfig.getConfig();
// Get the configuration for this authenticator
behavior = config.get(UserSessionLimitsAuthenticatorFactory.BEHAVIOR);
int userRealmLimit = getIntConfigProperty(UserSessionLimitsAuthenticatorFactory.USER_REALM_LIMIT, config);
int userClientLimit = getIntConfigProperty(UserSessionLimitsAuthenticatorFactory.USER_CLIENT_LIMIT, config);
if (context.getRealm() != null && context.getUser() != null) {
// Get the session count in this realm for this specific user
List<UserSessionModel> userSessionsForRealm = session.sessions().getUserSessionsStream(context.getRealm(), context.getUser()).collect(Collectors.toList());
int userSessionCountForRealm = userSessionsForRealm.size();
// Get the session count related to the current client for this user
ClientModel currentClient = context.getAuthenticationSession().getClient();
logger.debugf("session-limiter's current keycloak clientId: %s", currentClient.getClientId());
List<UserSessionModel> userSessionsForClient = getUserSessionsForClientIfEnabled(userSessionsForRealm, currentClient, userClientLimit);
int userSessionCountForClient = userSessionsForClient.size();
logger.debugf("session-limiter's configured realm session limit: %s", userRealmLimit);
logger.debugf("session-limiter's configured client session limit: %s", userClientLimit);
logger.debugf("session-limiter's count of total user sessions for the entire realm (could be apps other than web apps): %s", userSessionCountForRealm);
logger.debugf("session-limiter's count of total user sessions for this keycloak client: %s", userSessionCountForClient);
// First check if the user has too many sessions in this realm
if (exceedsLimit(userSessionCountForRealm, userRealmLimit)) {
logger.infof("Too many session in this realm for the current user. Session count: %s", userSessionCountForRealm);
String eventDetails = String.format(realmEventDetailsTemplate, context.getRealm().getName(), userRealmLimit, userSessionCountForRealm, context.getUser().getId());
handleLimitExceeded(context, userSessionsForRealm, eventDetails);
} else // otherwise if the user is still allowed to create a new session in the realm, check if this applies for this specific client as well.
if (exceedsLimit(userSessionCountForClient, userClientLimit)) {
logger.infof("Too many sessions related to the current client for this user. Session count: %s", userSessionCountForRealm);
String eventDetails = String.format(clientEventDetailsTemplate, context.getRealm().getName(), userClientLimit, userSessionCountForClient, context.getUser().getId());
handleLimitExceeded(context, userSessionsForClient, eventDetails);
} else {
context.success();
}
} else {
context.success();
}
}
use of org.keycloak.models.UserSessionModel in project keycloak by keycloak.
the class SamlService method artifactResolve.
/**
* Takes an artifact resolve message and returns the artifact response, if the artifact is found belonging to a session
* of the issuer.
* @param artifactResolveMessage The artifact resolve message sent by the client
* @param artifactResolveHolder the document containing the artifact resolve message sent by the client
* @return a Response containing the SOAP message with the ArifactResponse
* @throws ParsingException
* @throws ConfigurationException
* @throws ProcessingException
*/
public Response artifactResolve(ArtifactResolveType artifactResolveMessage, SAMLDocumentHolder artifactResolveHolder) throws ParsingException, ConfigurationException, ProcessingException {
logger.debug("Received artifactResolve message for artifact " + artifactResolveMessage.getArtifact() + "\n" + "Message: \n" + DocumentUtil.getDocumentAsString(artifactResolveHolder.getSamlDocument()));
// Artifact from resolve request
String artifact = artifactResolveMessage.getArtifact();
if (artifact == null) {
logger.errorf("Artifact to resolve was null");
return emptyArtifactResponseMessage(artifactResolveMessage, null, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.getUri());
}
ArtifactResolver artifactResolver = getArtifactResolver(artifact);
if (artifactResolver == null) {
logger.errorf("Cannot find ArtifactResolver for artifact %s", artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, null, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.getUri());
}
// Obtain details of session that issued artifact and check if it corresponds to issuer of Resolve message
SamlArtifactSessionMappingModel sessionMapping = getArtifactSessionMappingStore().get(artifact);
if (sessionMapping == null) {
logger.errorf("No data stored for artifact %s", artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, null);
}
UserSessionModel userSessionModel = session.sessions().getUserSession(realm, sessionMapping.getUserSessionId());
if (userSessionModel == null) {
logger.errorf("UserSession with id: %s, that corresponds to artifact: %s does not exist.", sessionMapping.getUserSessionId(), artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, null);
}
AuthenticatedClientSessionModel clientSessionModel = userSessionModel.getAuthenticatedClientSessions().get(sessionMapping.getClientSessionId());
if (clientSessionModel == null) {
logger.errorf("ClientSession with id: %s, that corresponds to artifact: %s and UserSession: %s does not exist.", sessionMapping.getClientSessionId(), artifact, sessionMapping.getUserSessionId());
return emptyArtifactResponseMessage(artifactResolveMessage, null);
}
ClientModel clientModel = getAndCheckClientModel(sessionMapping.getClientSessionId(), artifactResolveMessage.getIssuer().getValue());
SamlClient samlClient = new SamlClient(clientModel);
// Check signature within ArtifactResolve request if client requires it
if (samlClient.requiresClientSignature()) {
try {
SamlProtocolUtils.verifyDocumentSignature(clientModel, artifactResolveHolder.getSamlDocument());
} catch (VerificationException e) {
SamlService.logger.error("request validation failed", e);
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel);
}
}
// Obtain artifactResponse from clientSessionModel
String artifactResponseString;
try {
artifactResponseString = artifactResolver.resolveArtifact(clientSessionModel, artifact);
} catch (ArtifactResolverProcessingException e) {
logger.errorf(e, "Failed to resolve artifact: %s.", artifact);
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel);
}
// Artifact is successfully resolved, we can remove session mapping from storage
getArtifactSessionMappingStore().remove(artifact);
Document artifactResponseDocument = null;
ArtifactResponseType artifactResponseType = null;
try {
SAMLDataMarshaller marshaller = new SAMLDataMarshaller();
artifactResponseType = marshaller.deserialize(artifactResponseString, ArtifactResponseType.class);
artifactResponseDocument = SamlProtocolUtils.convert(artifactResponseType);
} catch (ParsingException | ConfigurationException | ProcessingException e) {
logger.errorf(e, "Failed to obtain document from ArtifactResponseString: %s.", artifactResponseString);
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel);
}
// If clientSession is in LOGGING_OUT action, now we can move it to LOGGED_OUT
if (CommonClientSessionModel.Action.LOGGING_OUT.name().equals(clientSessionModel.getAction())) {
clientSessionModel.setAction(CommonClientSessionModel.Action.LOGGED_OUT.name());
// If Keycloak sent LogoutResponse we need to also remove UserSession
if (artifactResponseType.getAny() instanceof StatusResponseType && artifactResponseString.contains(JBossSAMLConstants.LOGOUT_RESPONSE.get())) {
if (!UserSessionModel.State.LOGGED_OUT_UNCONFIRMED.equals(userSessionModel.getState())) {
logger.warnf("Keycloak issued LogoutResponse for clientSession %s, however user session %s was not in LOGGED_OUT_UNCONFIRMED state.", clientSessionModel.getId(), userSessionModel.getId());
}
AuthenticationManager.finishUnconfirmedUserSession(session, realm, userSessionModel);
}
}
return artifactResponseMessage(artifactResolveMessage, artifactResponseDocument, clientModel);
}
use of org.keycloak.models.UserSessionModel in project keycloak by keycloak.
the class OAuth2CodeParser method parseCode.
/**
* Will parse the code and retrieve the corresponding OAuth2Code and AuthenticatedClientSessionModel. Will also check if code wasn't already
* used and if it wasn't expired. If it was already used (or other error happened during parsing), then returned parser will have "isIllegalHash"
* set to true. If it was expired, the parser will have "isExpired" set to true
*
* @param session
* @param code
* @param realm
* @param event
* @return
*/
public static ParseResult parseCode(KeycloakSession session, String code, RealmModel realm, EventBuilder event) {
ParseResult result = new ParseResult(code);
String[] parsed = DOT.split(code, 3);
if (parsed.length < 3) {
logger.warn("Invalid format of the code");
return result.illegalCode();
}
String userSessionId = parsed[1];
String clientUUID = parsed[2];
event.detail(Details.CODE_ID, userSessionId);
event.session(userSessionId);
// Parse UUID
UUID codeUUID;
try {
codeUUID = UUID.fromString(parsed[0]);
} catch (IllegalArgumentException re) {
logger.warn("Invalid format of the UUID in the code");
return result.illegalCode();
}
// Retrieve UserSession
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, clientUUID);
if (userSession == null) {
// Needed to track if code is invalid or was already used.
userSession = session.sessions().getUserSession(realm, userSessionId);
if (userSession == null) {
return result.illegalCode();
}
}
result.clientSession = userSession.getAuthenticatedClientSessionByClient(clientUUID);
CodeToTokenStoreProvider codeStore = session.getProvider(CodeToTokenStoreProvider.class);
Map<String, String> codeData = codeStore.remove(codeUUID);
// Either code not available or was already used
if (codeData == null) {
logger.warnf("Code '%s' already used for userSession '%s' and client '%s'.", codeUUID, userSessionId, clientUUID);
return result.illegalCode();
}
logger.tracef("Successfully verified code '%s'. User session: '%s', client: '%s'", codeUUID, userSessionId, clientUUID);
result.codeData = OAuth2Code.deserializeCode(codeData);
// Finally doublecheck if code is not expired
int currentTime = Time.currentTime();
if (currentTime > result.codeData.getExpiration()) {
return result.expiredCode();
}
return result;
}
Aggregations