Search in sources :

Example 1 with UserSessionManager

use of org.keycloak.services.managers.UserSessionManager in project keycloak by keycloak.

the class TokenRevocationEndpoint method revokeClient.

private void revokeClient() {
    session.users().revokeConsentForClient(realm, user.getId(), client.getId());
    if (TokenUtil.TOKEN_TYPE_OFFLINE.equals(token.getType())) {
        new UserSessionManager(session).revokeOfflineToken(user, client);
    }
    session.sessions().getUserSessionsStream(realm, user).map(userSession -> userSession.getAuthenticatedClientSessionByClient(client.getId())).filter(Objects::nonNull).collect(// collect to avoid concurrent modification as dettachClientSession removes the user sessions.
    Collectors.toList()).forEach(clientSession -> {
        UserSessionModel userSession = clientSession.getUserSession();
        TokenManager.dettachClientSession(clientSession);
        if (userSession != null) {
            // TODO: Might need optimization to prevent loading client sessions from cache in getAuthenticatedClientSessions()
            if (userSession.getAuthenticatedClientSessions().isEmpty()) {
                session.sessions().removeUserSession(realm, userSession);
            }
        }
    });
}
Also used : UserSessionManager(org.keycloak.services.managers.UserSessionManager) ClientModel(org.keycloak.models.ClientModel) SecurityHeadersProvider(org.keycloak.headers.SecurityHeadersProvider) AuthorizeClientUtil(org.keycloak.protocol.oidc.utils.AuthorizeClientUtil) TokenManager(org.keycloak.protocol.oidc.TokenManager) TokenRevokeContext(org.keycloak.services.clientpolicy.context.TokenRevokeContext) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager) TokenRevocationStoreProvider(org.keycloak.models.TokenRevocationStoreProvider) MediaType(javax.ws.rs.core.MediaType) OAuthErrorException(org.keycloak.OAuthErrorException) TokenUtil(org.keycloak.util.TokenUtil) UserModel(org.keycloak.models.UserModel) Consumes(javax.ws.rs.Consumes) AccessToken(org.keycloak.representations.AccessToken) EventBuilder(org.keycloak.events.EventBuilder) ClientConnection(org.keycloak.common.ClientConnection) Cors(org.keycloak.services.resources.Cors) Time(org.keycloak.common.util.Time) POST(javax.ws.rs.POST) Context(javax.ws.rs.core.Context) Errors(org.keycloak.events.Errors) RealmModel(org.keycloak.models.RealmModel) KeycloakSession(org.keycloak.models.KeycloakSession) HttpRequest(org.jboss.resteasy.spi.HttpRequest) EventType(org.keycloak.events.EventType) OPTIONS(javax.ws.rs.OPTIONS) UserSessionModel(org.keycloak.models.UserSessionModel) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) MultivaluedMap(javax.ws.rs.core.MultivaluedMap) HttpHeaders(javax.ws.rs.core.HttpHeaders) Response(javax.ws.rs.core.Response) ClientPolicyException(org.keycloak.services.clientpolicy.ClientPolicyException) Details(org.keycloak.events.Details) UserSessionManager(org.keycloak.services.managers.UserSessionManager) UserSessionModel(org.keycloak.models.UserSessionModel) Objects(java.util.Objects)

Example 2 with UserSessionManager

use of org.keycloak.services.managers.UserSessionManager in project keycloak by keycloak.

the class UserResource method getOfflineSessions.

/**
 * Get offline sessions associated with the user and client
 *
 * @return
 */
@Path("offline-sessions/{clientUuid}")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Stream<UserSessionRepresentation> getOfflineSessions(@PathParam("clientUuid") final String clientUuid) {
    auth.users().requireView(user);
    ClientModel client = realm.getClientById(clientUuid);
    if (client == null) {
        throw new NotFoundException("Client not found");
    }
    return new UserSessionManager(session).findOfflineSessionsStream(realm, user).map(session -> toUserSessionRepresentation(session, clientUuid)).filter(Objects::nonNull);
}
Also used : UserSessionManager(org.keycloak.services.managers.UserSessionManager) EmailTemplateProvider(org.keycloak.email.EmailTemplateProvider) RedirectUtils(org.keycloak.protocol.oidc.utils.RedirectUtils) Produces(javax.ws.rs.Produces) USER_API(org.keycloak.userprofile.UserProfileContext.USER_API) MediaType(javax.ws.rs.core.MediaType) ErrorResponseException(org.keycloak.services.ErrorResponseException) Validation(org.keycloak.services.validation.Validation) Map(java.util.Map) ClientConnection(org.keycloak.common.ClientConnection) UserConsentRepresentation(org.keycloak.representations.idm.UserConsentRepresentation) UriBuilder(javax.ws.rs.core.UriBuilder) Time(org.keycloak.common.util.Time) UserCredentialModel(org.keycloak.models.UserCredentialModel) FederatedIdentityRepresentation(org.keycloak.representations.idm.FederatedIdentityRepresentation) Set(java.util.Set) IdentityProviderModel(org.keycloak.models.IdentityProviderModel) ModelToRepresentation(org.keycloak.models.utils.ModelToRepresentation) Stream(java.util.stream.Stream) LoginActionsService(org.keycloak.services.resources.LoginActionsService) BruteForceProtector(org.keycloak.services.managers.BruteForceProtector) WebApplicationException(javax.ws.rs.WebApplicationException) GET(javax.ws.rs.GET) Constants(org.keycloak.models.Constants) ArrayList(java.util.ArrayList) ResteasyProviderFactory(org.jboss.resteasy.spi.ResteasyProviderFactory) UserModel(org.keycloak.models.UserModel) UserProfileProvider(org.keycloak.userprofile.UserProfileProvider) UserConsentManager(org.keycloak.services.managers.UserConsentManager) ProviderFactory(org.keycloak.provider.ProviderFactory) UserManager(org.keycloak.models.UserManager) Properties(java.util.Properties) CredentialModel(org.keycloak.credential.CredentialModel) ExecuteActionsActionToken(org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken) AdminPermissionEvaluator(org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator) KeycloakSession(org.keycloak.models.KeycloakSession) EventType(org.keycloak.events.EventType) RequiredActionProvider(org.keycloak.authentication.RequiredActionProvider) IMPERSONATOR_USERNAME(org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME) ModelDuplicateException(org.keycloak.models.ModelDuplicateException) ValidationException(org.keycloak.userprofile.ValidationException) ResourceType(org.keycloak.events.admin.ResourceType) Path(javax.ws.rs.Path) GroupRepresentation(org.keycloak.representations.idm.GroupRepresentation) RepresentationToModel(org.keycloak.models.utils.RepresentationToModel) QueryParam(javax.ws.rs.QueryParam) AuthenticationManager(org.keycloak.services.managers.AuthenticationManager) Consumes(javax.ws.rs.Consumes) ReadOnlyException(org.keycloak.storage.ReadOnlyException) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) DefaultValue(javax.ws.rs.DefaultValue) CredentialRepresentation(org.keycloak.representations.idm.CredentialRepresentation) BadRequestException(javax.ws.rs.BadRequestException) URI(java.net.URI) AccountFormService(org.keycloak.services.resources.account.AccountFormService) DELETE(javax.ws.rs.DELETE) RealmModel(org.keycloak.models.RealmModel) Context(javax.ws.rs.core.Context) Collectors(java.util.stream.Collectors) NotFoundException(javax.ws.rs.NotFoundException) IMPERSONATOR_ID(org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID) Objects(java.util.Objects) List(java.util.List) HttpHeaders(javax.ws.rs.core.HttpHeaders) Response(javax.ws.rs.core.Response) Details(org.keycloak.events.Details) OIDCLoginProtocol(org.keycloak.protocol.oidc.OIDCLoginProtocol) ForbiddenException(org.keycloak.services.ForbiddenException) ClientModel(org.keycloak.models.ClientModel) OperationType(org.keycloak.events.admin.OperationType) UserProfile(org.keycloak.userprofile.UserProfile) PathParam(javax.ws.rs.PathParam) UserSessionRepresentation(org.keycloak.representations.idm.UserSessionRepresentation) Profile(org.keycloak.common.Profile) Logger(org.jboss.logging.Logger) HashMap(java.util.HashMap) ServicesLogger(org.keycloak.services.ServicesLogger) ErrorRepresentation(org.keycloak.representations.idm.ErrorRepresentation) MessageFormat(java.text.MessageFormat) HashSet(java.util.HashSet) EventBuilder(org.keycloak.events.EventBuilder) UserConsentModel(org.keycloak.models.UserConsentModel) EmailException(org.keycloak.email.EmailException) GroupModel(org.keycloak.models.GroupModel) LinkedList(java.util.LinkedList) ProfileHelper(org.keycloak.utils.ProfileHelper) Status(javax.ws.rs.core.Response.Status) FederatedIdentityModel(org.keycloak.models.FederatedIdentityModel) UserRepresentation(org.keycloak.representations.idm.UserRepresentation) POST(javax.ws.rs.POST) UserLoginFailureModel(org.keycloak.models.UserLoginFailureModel) UserSessionModel(org.keycloak.models.UserSessionModel) TimeUnit(java.util.concurrent.TimeUnit) NoCache(org.jboss.resteasy.annotations.cache.NoCache) UserSessionManager(org.keycloak.services.managers.UserSessionManager) ModelException(org.keycloak.models.ModelException) PUT(javax.ws.rs.PUT) Collections(java.util.Collections) ErrorResponse(org.keycloak.services.ErrorResponse) ClientModel(org.keycloak.models.ClientModel) Objects(java.util.Objects) NotFoundException(javax.ws.rs.NotFoundException) Path(javax.ws.rs.Path) Produces(javax.ws.rs.Produces) GET(javax.ws.rs.GET) NoCache(org.jboss.resteasy.annotations.cache.NoCache)

Example 3 with UserSessionManager

use of org.keycloak.services.managers.UserSessionManager in project keycloak by keycloak.

the class TokenManager method validateTokenReuseForIntrospection.

private boolean validateTokenReuseForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token) {
    UserSessionModel userSession = null;
    if (token.getType().equals(TokenUtil.TOKEN_TYPE_REFRESH)) {
        userSession = session.sessions().getUserSession(realm, token.getSessionState());
    } else {
        UserSessionManager sessionManager = new UserSessionManager(session);
        userSession = sessionManager.findOfflineUserSession(realm, token.getSessionState());
    }
    ClientModel client = realm.getClientByClientId(token.getIssuedFor());
    AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
    try {
        validateTokenReuse(session, realm, token, clientSession, false);
        return true;
    } catch (OAuthErrorException e) {
        return false;
    }
}
Also used : UserSessionManager(org.keycloak.services.managers.UserSessionManager) ClientModel(org.keycloak.models.ClientModel) UserSessionModel(org.keycloak.models.UserSessionModel) OAuthErrorException(org.keycloak.OAuthErrorException) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel)

Example 4 with UserSessionManager

use of org.keycloak.services.managers.UserSessionManager in project keycloak by keycloak.

the class TokenManager method validateToken.

public TokenValidation validateToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, RefreshToken oldToken, HttpHeaders headers) throws OAuthErrorException {
    UserSessionModel userSession = null;
    boolean offline = TokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType());
    if (offline) {
        UserSessionManager sessionManager = new UserSessionManager(session);
        userSession = sessionManager.findOfflineUserSession(realm, oldToken.getSessionState());
        if (userSession != null) {
            // Revoke timeouted offline userSession
            if (!AuthenticationManager.isOfflineSessionValid(realm, userSession)) {
                sessionManager.revokeOfflineUserSession(userSession);
                throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline session not active", "Offline session not active");
            }
        } else {
            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline user session not found", "Offline user session not found");
        }
    } else {
        // Find userSession regularly for online tokens
        userSession = session.sessions().getUserSession(realm, oldToken.getSessionState());
        if (!AuthenticationManager.isSessionValid(realm, userSession)) {
            AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
        }
    }
    UserModel user = userSession.getUser();
    if (user == null) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown user");
    }
    if (!user.isEnabled()) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled");
    }
    if (oldToken.isIssuedBeforeSessionStart(userSession.getStarted())) {
        logger.debug("Refresh toked issued before the user session started");
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh toked issued before the user session started");
    }
    ClientModel client = session.getContext().getClient();
    AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
    // Can theoretically happen in cross-dc environment. Try to see if userSession with our client is available in remoteCache
    if (clientSession == null) {
        userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSession.getId(), offline, client.getId());
        if (userSession != null) {
            clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
        } else {
            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session doesn't have required client", "Session doesn't have required client");
        }
    }
    if (oldToken.isIssuedBeforeSessionStart(clientSession.getStarted())) {
        logger.debug("Refresh toked issued before the client session started");
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh toked issued before the client session started");
    }
    if (!client.getClientId().equals(oldToken.getIssuedFor())) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Unmatching clients", "Unmatching clients");
    }
    try {
        TokenVerifier.createWithoutSignature(oldToken).withChecks(NotBeforeCheck.forModel(client), NotBeforeCheck.forModel(session, realm, user)).verify();
    } catch (VerificationException e) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
    }
    // Setup clientScopes from refresh token to the context
    String oldTokenScope = oldToken.getScope();
    // Case when offline token is migrated from previous version
    if (oldTokenScope == null && userSession.isOffline()) {
        logger.debugf("Migrating offline token of user '%s' for client '%s' of realm '%s'", user.getUsername(), client.getClientId(), realm.getName());
        MigrationUtils.migrateOldOfflineToken(session, realm, client, user);
        oldTokenScope = OAuth2Constants.OFFLINE_ACCESS;
    }
    ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, oldTokenScope, session);
    // Check user didn't revoke granted consent
    if (!verifyConsentStillAvailable(session, user, client, clientSessionCtx.getClientScopesStream())) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user");
    }
    clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, oldToken.getNonce());
    // recreate token.
    AccessToken newToken = createClientAccessToken(session, realm, client, user, userSession, clientSessionCtx);
    return new TokenValidation(user, userSession, clientSessionCtx, newToken);
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) OAuthErrorException(org.keycloak.OAuthErrorException) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager) UserSessionManager(org.keycloak.services.managers.UserSessionManager) UserModel(org.keycloak.models.UserModel) ClientModel(org.keycloak.models.ClientModel) DefaultClientSessionContext(org.keycloak.services.util.DefaultClientSessionContext) ClientSessionContext(org.keycloak.models.ClientSessionContext) AccessToken(org.keycloak.representations.AccessToken) VerificationException(org.keycloak.common.VerificationException)

Example 5 with UserSessionManager

use of org.keycloak.services.managers.UserSessionManager in project keycloak by keycloak.

the class UserSessionProviderOfflineTest method testOfflineSessionsCrud.

@Test
@ModelTest
public void testOfflineSessionsCrud(KeycloakSession session) {
    Map<String, Set<String>> offlineSessions = new HashMap<>();
    KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud) -> {
        // Create some online sessions in infinispan
        reloadState(sessionCrud);
        createSessions(sessionCrud);
    });
    KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud2) -> {
        currentSession = sessionCrud2;
        realm = currentSession.realms().getRealm("test");
        sessionManager = new UserSessionManager(currentSession);
        // Key is userSession ID, values are client UUIDS
        // Persist 3 created userSessions and clientSessions as offline
        ClientModel testApp = realm.getClientByClientId("test-app");
        currentSession.sessions().getUserSessionsStream(realm, testApp).collect(Collectors.toList()).forEach(userSession -> offlineSessions.put(userSession.getId(), createOfflineSessionIncludeClientSessions(currentSession, userSession)));
    });
    KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud3) -> {
        currentSession = sessionCrud3;
        realm = currentSession.realms().getRealm("test");
        sessionManager = new UserSessionManager(currentSession);
        // Assert all previously saved offline sessions found
        for (Map.Entry<String, Set<String>> entry : offlineSessions.entrySet()) {
            UserSessionModel offlineSession = sessionManager.findOfflineUserSession(realm, entry.getKey());
            Assert.assertNotNull(offlineSession);
            Assert.assertEquals(offlineSession.getAuthenticatedClientSessions().keySet(), entry.getValue());
        }
        // Find clients with offline token
        UserModel user1 = currentSession.users().getUserByUsername(realm, "user1");
        Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
        Assert.assertEquals(clients.size(), 2);
        for (ClientModel client : clients) {
            Assert.assertTrue(client.getClientId().equals("test-app") || client.getClientId().equals("third-party"));
        }
        UserModel user2 = currentSession.users().getUserByUsername(realm, "user2");
        clients = sessionManager.findClientsWithOfflineToken(realm, user2);
        Assert.assertEquals(clients.size(), 1);
        Assert.assertEquals("test-app", clients.iterator().next().getClientId());
        // Test count
        ClientModel testApp = realm.getClientByClientId("test-app");
        ClientModel thirdparty = realm.getClientByClientId("third-party");
        Assert.assertEquals(3, currentSession.sessions().getOfflineSessionsCount(realm, testApp));
        Assert.assertEquals(1, currentSession.sessions().getOfflineSessionsCount(realm, thirdparty));
        // Revoke "test-app" for user1
        sessionManager.revokeOfflineToken(user1, testApp);
    });
    KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud4) -> {
        currentSession = sessionCrud4;
        realm = currentSession.realms().getRealm("test");
        sessionManager = new UserSessionManager(currentSession);
        // Assert userSession revoked
        ClientModel thirdparty = realm.getClientByClientId("third-party");
        List<UserSessionModel> thirdpartySessions = currentSession.sessions().getOfflineUserSessionsStream(realm, thirdparty, 0, 10).collect(Collectors.toList());
        Assert.assertEquals(1, thirdpartySessions.size());
        Assert.assertEquals("127.0.0.1", thirdpartySessions.get(0).getIpAddress());
        Assert.assertEquals("user1", thirdpartySessions.get(0).getUser().getUsername());
        UserModel user1 = currentSession.users().getUserByUsername(realm, "user1");
        UserModel user2 = currentSession.users().getUserByUsername(realm, "user2");
        Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
        Assert.assertEquals(1, clients.size());
        Assert.assertEquals("third-party", clients.iterator().next().getClientId());
        clients = sessionManager.findClientsWithOfflineToken(realm, user2);
        Assert.assertEquals(1, clients.size());
        Assert.assertEquals("test-app", clients.iterator().next().getClientId());
        // Revoke the second currentSession for user1 too.
        sessionManager.revokeOfflineToken(user1, thirdparty);
    });
    KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCrud5) -> {
        currentSession = sessionCrud5;
        realm = currentSession.realms().getRealm("test");
        sessionManager = new UserSessionManager(currentSession);
        ClientModel testApp = realm.getClientByClientId("test-app");
        ClientModel thirdparty = realm.getClientByClientId("third-party");
        // Accurate count now. All sessions of user1 cleared
        Assert.assertEquals(1, currentSession.sessions().getOfflineSessionsCount(realm, testApp));
        Assert.assertEquals(0, currentSession.sessions().getOfflineSessionsCount(realm, thirdparty));
        List<UserSessionModel> testAppSessions = currentSession.sessions().getOfflineUserSessionsStream(realm, testApp, 0, 10).collect(Collectors.toList());
        Assert.assertEquals(1, testAppSessions.size());
        Assert.assertEquals("127.0.0.3", testAppSessions.get(0).getIpAddress());
        Assert.assertEquals("user2", testAppSessions.get(0).getUser().getUsername());
        UserModel user1 = currentSession.users().getUserByUsername(realm, "user1");
        Set<ClientModel> clients = sessionManager.findClientsWithOfflineToken(realm, user1);
        Assert.assertEquals(0, clients.size());
    });
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) HashSet(java.util.HashSet) Set(java.util.Set) HashMap(java.util.HashMap) UserSessionManager(org.keycloak.services.managers.UserSessionManager) UserModel(org.keycloak.models.UserModel) ClientModel(org.keycloak.models.ClientModel) KeycloakSession(org.keycloak.models.KeycloakSession) HashMap(java.util.HashMap) Map(java.util.Map) ModelTest(org.keycloak.testsuite.arquillian.annotation.ModelTest) ModelTest(org.keycloak.testsuite.arquillian.annotation.ModelTest) Test(org.junit.Test) AbstractTestRealmKeycloakTest(org.keycloak.testsuite.AbstractTestRealmKeycloakTest)

Aggregations

UserSessionManager (org.keycloak.services.managers.UserSessionManager)15 UserSessionModel (org.keycloak.models.UserSessionModel)11 ClientModel (org.keycloak.models.ClientModel)9 AuthenticatedClientSessionModel (org.keycloak.models.AuthenticatedClientSessionModel)8 HashSet (java.util.HashSet)6 KeycloakSession (org.keycloak.models.KeycloakSession)6 RealmModel (org.keycloak.models.RealmModel)6 UserModel (org.keycloak.models.UserModel)6 Test (org.junit.Test)5 HashMap (java.util.HashMap)4 Map (java.util.Map)4 Set (java.util.Set)4 Collectors (java.util.stream.Collectors)4 Consumes (javax.ws.rs.Consumes)4 POST (javax.ws.rs.POST)4 LinkedList (java.util.LinkedList)3 List (java.util.List)3 Objects (java.util.Objects)3 AtomicReference (java.util.concurrent.atomic.AtomicReference)3 Context (javax.ws.rs.core.Context)3