Search in sources :

Example 16 with UserSessionModel

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);
}
Also used : PersistentUserSessionAdapter(org.keycloak.models.session.PersistentUserSessionAdapter) PersistentUserSessionModel(org.keycloak.models.session.PersistentUserSessionModel) UserSessionModel(org.keycloak.models.UserSessionModel) Objects(java.util.Objects) HashSet(java.util.HashSet)

Example 17 with UserSessionModel

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;
    }
}
Also used : ClientModel(org.keycloak.models.ClientModel) AuthenticationFlowError(org.keycloak.authentication.AuthenticationFlowError) Errors(org.keycloak.events.Errors) RealmModel(org.keycloak.models.RealmModel) Authenticator(org.keycloak.authentication.Authenticator) StringUtil(org.keycloak.utils.StringUtil) Logger(org.jboss.logging.Logger) KeycloakSession(org.keycloak.models.KeycloakSession) UserSessionModel(org.keycloak.models.UserSessionModel) Collectors(java.util.stream.Collectors) List(java.util.List) UserModel(org.keycloak.models.UserModel) AuthenticationManager(org.keycloak.services.managers.AuthenticationManager) Response(javax.ws.rs.core.Response) Map(java.util.Map) AuthenticatorConfigModel(org.keycloak.models.AuthenticatorConfigModel) Optional(java.util.Optional) AuthenticationFlowContext(org.keycloak.authentication.AuthenticationFlowContext) Comparator(java.util.Comparator) Collections(java.util.Collections) Response(javax.ws.rs.core.Response) AuthenticatorConfigModel(org.keycloak.models.AuthenticatorConfigModel)

Example 18 with UserSessionModel

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();
    }
}
Also used : ClientModel(org.keycloak.models.ClientModel) UserSessionModel(org.keycloak.models.UserSessionModel) AuthenticatorConfigModel(org.keycloak.models.AuthenticatorConfigModel)

Example 19 with UserSessionModel

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);
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) Document(org.w3c.dom.Document) SAMLDataMarshaller(org.keycloak.broker.saml.SAMLDataMarshaller) StatusResponseType(org.keycloak.dom.saml.v2.protocol.StatusResponseType) SamlArtifactSessionMappingModel(org.keycloak.models.SamlArtifactSessionMappingModel) ClientModel(org.keycloak.models.ClientModel) ConfigurationException(org.keycloak.saml.common.exceptions.ConfigurationException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException) ParsingException(org.keycloak.saml.common.exceptions.ParsingException) VerificationException(org.keycloak.common.VerificationException) ArtifactResponseType(org.keycloak.dom.saml.v2.protocol.ArtifactResponseType) ProcessingException(org.keycloak.saml.common.exceptions.ProcessingException)

Example 20 with UserSessionModel

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;
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) CodeToTokenStoreProvider(org.keycloak.models.CodeToTokenStoreProvider) UUID(java.util.UUID) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager)

Aggregations

UserSessionModel (org.keycloak.models.UserSessionModel)133 RealmModel (org.keycloak.models.RealmModel)68 Test (org.junit.Test)53 ClientModel (org.keycloak.models.ClientModel)44 UserModel (org.keycloak.models.UserModel)43 AuthenticatedClientSessionModel (org.keycloak.models.AuthenticatedClientSessionModel)38 AbstractTestRealmKeycloakTest (org.keycloak.testsuite.AbstractTestRealmKeycloakTest)29 KeycloakSession (org.keycloak.models.KeycloakSession)26 ModelTest (org.keycloak.testsuite.arquillian.annotation.ModelTest)26 AuthenticationSessionModel (org.keycloak.sessions.AuthenticationSessionModel)21 ClientSessionContext (org.keycloak.models.ClientSessionContext)20 AtomicReference (java.util.concurrent.atomic.AtomicReference)18 RootAuthenticationSessionModel (org.keycloak.sessions.RootAuthenticationSessionModel)17 KeycloakModelTest (org.keycloak.testsuite.model.KeycloakModelTest)17 Response (javax.ws.rs.core.Response)15 ClientPolicyException (org.keycloak.services.clientpolicy.ClientPolicyException)14 List (java.util.List)13 CorsErrorResponseException (org.keycloak.services.CorsErrorResponseException)13 Map (java.util.Map)12 UserSessionPersisterProvider (org.keycloak.models.session.UserSessionPersisterProvider)12