FileUserProvider.java

682 lines | 27.366 kB Blame History Raw Download
/*
 * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @author tags. All rights reserved.
 *
 * 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.models.file;

import org.keycloak.connections.file.FileConnectionProvider;
import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelException;
import org.keycloak.models.OfflineClientSessionModel;
import org.keycloak.models.OfflineUserSessionModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.entities.FederatedIdentityEntity;
import org.keycloak.models.entities.OfflineClientSessionEntity;
import org.keycloak.models.entities.OfflineUserSessionEntity;
import org.keycloak.models.entities.UserEntity;
import org.keycloak.models.file.adapter.UserAdapter;
import org.keycloak.models.utils.CredentialValidation;
import org.keycloak.models.utils.KeycloakModelUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * UserProvider for JSON persistence.
 *
 * @author Stan Silvert ssilvert@redhat.com (C) 2015 Red Hat Inc.
 */
public class FileUserProvider implements UserProvider {

    private final KeycloakSession session;
    private FileConnectionProvider fcProvider;
    private final InMemoryModel inMemoryModel;

    public FileUserProvider(KeycloakSession session, FileConnectionProvider fcProvider) {
        this.session = session;
        this.fcProvider = fcProvider;
        session.enlistForClose(this);
        this.inMemoryModel = fcProvider.getModel();
    }

    @Override
    public void close() {
        fcProvider.sessionClosed(session);
    }

    @Override
    public UserModel getUserById(String userId, RealmModel realm) {
        return inMemoryModel.getUser(realm.getId(), userId);
    }

    @Override
    public UserModel getUserByUsername(String username, RealmModel realm) {
        for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
            if (user.getUsername() == null) continue;
            if (user.getUsername().equals(username.toLowerCase())) return user;
        }

        return null;
    }

    @Override
    public UserModel getUserByEmail(String email, RealmModel realm) {
        for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
            if (user.getEmail() == null) continue;
            if (user.getEmail().equals(email.toLowerCase())) return user;
        }

        return null;
    }

    @Override
    public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
        for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
            Set<FederatedIdentityModel> identities = this.getFederatedIdentities(user, realm);
            for (FederatedIdentityModel idModel : identities) {
                if (idModel.getUserId().equals(socialLink.getUserId())) return user;
            }
        }

        return null;
    }

    @Override
    public UserModel getUserByServiceAccountClient(ClientModel client) {
        for (UserModel user : inMemoryModel.getUsers(client.getRealm().getId())) {
            if (client.getId().equals(user.getServiceAccountClientLink())) {
                return user;
            }
        }
        return null;
    }

    @Override
    public List<UserModel> getUsers(RealmModel realm, boolean includeServiceAccounts) {
        return getUsers(realm, -1, -1, includeServiceAccounts);
    }

    @Override
    public int getUsersCount(RealmModel realm) {
        return inMemoryModel.getUsers(realm.getId()).size();
    }

    @Override
    public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults, boolean includeServiceAccounts) {
        List<UserModel> users = new ArrayList<>(inMemoryModel.getUsers(realm.getId()));

        if (!includeServiceAccounts) {
            users = filterServiceAccountUsers(users);
        }

        List<UserModel> sortedList = sortedSubList(users, firstResult, maxResults);
        return sortedList;
    }

    private List<UserModel> filterServiceAccountUsers(List<UserModel> users) {
        List<UserModel> result = new ArrayList<>();
        for (UserModel user : users) {
            if (user.getServiceAccountClientLink() == null) {
                result.add(user);
            }
        }
        return result;
    }

    protected List<UserModel> sortedSubList(List list, int firstResult, int maxResults) {
        if (list.isEmpty()) return list;

        Collections.sort(list);
        int first = (firstResult <= 0) ? 0 : firstResult;
        int last = first + maxResults; // could be int overflow
        if ((maxResults > list.size() - first) || (last > list.size())) { // int overflow or regular overflow
            last = list.size();
        }

        if (maxResults <= 0) {
            last = list.size();
        }

        return list.subList(first, last);
    }

    @Override
    public List<UserModel> searchForUser(String search, RealmModel realm) {
        return searchForUser(search, realm, -1, -1);
    }

    @Override
    public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) {
        search = search.trim();
        Pattern caseInsensitivePattern = Pattern.compile("(?i:.*" + search + ".*)", Pattern.CASE_INSENSITIVE);

        int spaceInd = search.lastIndexOf(" ");
        boolean isFirstAndLastSearch = spaceInd != -1;
        Pattern firstNamePattern = null;
        Pattern lastNamePattern = null;
        if (isFirstAndLastSearch) {
            String firstNamePatternString = search.substring(0, spaceInd);
            String lastNamePatternString = search.substring(spaceInd + 1);
            firstNamePattern = Pattern.compile("(?i:.*" + firstNamePatternString + ".*$)", Pattern.CASE_INSENSITIVE);
            lastNamePattern = Pattern.compile("(?i:^.*" + lastNamePatternString + ".*)", Pattern.CASE_INSENSITIVE);
        }

        List<UserModel> found = new ArrayList<UserModel>();

        for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
            String firstName = user.getFirstName();
            String lastName = user.getLastName();
            // Case when we have search string like "ohn Bow". Then firstName must end with "ohn" AND lastName must start with "bow" (everything case-insensitive)
            if (isFirstAndLastSearch) {
                if (isAMatch(firstNamePattern, firstName) &&
                    isAMatch(lastNamePattern, lastName)) {
                    found.add(user);
                    continue;
                }
            }

            if (isAMatch(caseInsensitivePattern, firstName) ||
                isAMatch(caseInsensitivePattern, lastName) ||
                isAMatch(caseInsensitivePattern, user.getUsername()) ||
                isAMatch(caseInsensitivePattern, user.getEmail())) {
                found.add(user);
            }
        }

        // Remove users with service account link
        found = filterServiceAccountUsers(found);

        return sortedSubList(found, firstResult, maxResults);
    }

    @Override
    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm) {
        return searchForUserByAttributes(attributes, realm, -1, -1);
    }

    protected boolean isAMatch(Pattern pattern, String value) {
        return (value != null) && (pattern != null) && pattern.matcher(value).matches();
    }

    @Override
    public List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults) {
        Pattern usernamePattern = null;
        Pattern firstNamePattern = null;
        Pattern lastNamePattern = null;
        Pattern emailPattern = null;
        for (Map.Entry<String, String> entry : attributes.entrySet()) {
            if (entry.getKey().equalsIgnoreCase(UserModel.USERNAME)) {
                usernamePattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE);
            } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
                firstNamePattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE);
            } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
                lastNamePattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE);
            } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
                emailPattern = Pattern.compile(".*" + entry.getValue() + ".*", Pattern.CASE_INSENSITIVE);
            }
        }

        List<UserModel> found = new ArrayList<UserModel>();
        for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
            if (isAMatch(usernamePattern, user.getUsername()) ||
                isAMatch(firstNamePattern, user.getFirstName()) ||
                isAMatch(lastNamePattern, user.getLastName()) ||
                isAMatch(emailPattern, user.getEmail())) {
                found.add(user);
            }
        }

        return sortedSubList(found, firstResult, maxResults);
    }

    @Override
    public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) {
        Collection<UserModel> users = inMemoryModel.getUsers(realm.getId());

        List<UserModel> matchedUsers = new ArrayList<>();
        for (UserModel user : users) {
            List<String> vals = user.getAttribute(attrName);
            if (vals.contains(attrValue)) {
                matchedUsers.add(user);
            }
        }

        return matchedUsers;
    }

    @Override
    public Set<FederatedIdentityModel> getFederatedIdentities(UserModel userModel, RealmModel realm) {
        UserEntity userEntity = ((UserAdapter)getUserById(userModel.getId(), realm)).getUserEntity();
        List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();

        if (linkEntities == null) {
            return Collections.EMPTY_SET;
        }

        Set<FederatedIdentityModel> result = new HashSet<FederatedIdentityModel>();
        for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
            FederatedIdentityModel model = new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(),
                    federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName());
            result.add(model);
        }
        return result;
    }

    private FederatedIdentityEntity findSocialLink(UserModel userModel, String socialProvider, RealmModel realm) {
        UserModel user = getUserById(userModel.getId(), realm);
        UserEntity userEntity = ((UserAdapter)getUserById(userModel.getId(), realm)).getUserEntity();
        List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();
        if (linkEntities == null) {
            return null;
        }

        for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
            if (federatedIdentityEntity.getIdentityProvider().equals(socialProvider)) {
                return federatedIdentityEntity;
            }
        }
        return null;
    }


    @Override
    public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
        FederatedIdentityEntity federatedIdentityEntity = findSocialLink(user, socialProvider, realm);
        return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName()) : null;
    }

    @Override
    public UserAdapter addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions) {
        if (inMemoryModel.hasUserWithUsername(realm.getId(), username.toLowerCase()))
            throw new ModelDuplicateException("User with username " + username + " already exists in realm.");

        UserAdapter userModel = addUserEntity(realm, id, username.toLowerCase());

        if (addDefaultRoles) {
            for (String r : realm.getDefaultRoles()) {
                userModel.grantRole(realm.getRole(r));
            }

            for (ClientModel application : realm.getClients()) {
                for (String r : application.getDefaultRoles()) {
                    userModel.grantRole(application.getRole(r));
                }
            }
        }

        if (addDefaultRequiredActions) {
            for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) {
                if (r.isEnabled() && r.isDefaultAction()) {
                    userModel.addRequiredAction(r.getAlias());
                }
            }
        }


        return userModel;
    }

    protected UserAdapter addUserEntity(RealmModel realm, String userId, String username) {
        if (realm == null) throw new NullPointerException("realm == null");
        if (username == null) throw new NullPointerException("username == null");

        if (userId == null) userId = KeycloakModelUtils.generateId();

        UserEntity userEntity = new UserEntity();
        userEntity.setId(userId);
        userEntity.setCreatedTimestamp(System.currentTimeMillis());
        userEntity.setUsername(username);
        // Compatibility with JPA model, which has user disabled by default
        // userEntity.setEnabled(true);
        userEntity.setRealmId(realm.getId());

        UserAdapter user = new UserAdapter(realm, userEntity, inMemoryModel);
        inMemoryModel.putUser(realm.getId(), userId, user);

        return user;
    }

    @Override
    public boolean removeUser(RealmModel realm, UserModel user) {
        return inMemoryModel.removeUser(realm.getId(), user.getId());
    }


    @Override
    public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
        UserAdapter userAdapter = (UserAdapter)getUserById(user.getId(), realm);
        UserEntity userEntity = userAdapter.getUserEntity();
        FederatedIdentityEntity federatedIdentityEntity = new FederatedIdentityEntity();
        federatedIdentityEntity.setIdentityProvider(socialLink.getIdentityProvider());
        federatedIdentityEntity.setUserId(socialLink.getUserId());
        federatedIdentityEntity.setUserName(socialLink.getUserName().toLowerCase());

        //check if it already exitsts - do I need to do this?
        for (FederatedIdentityEntity fedIdent : userEntity.getFederatedIdentities()) {
            if (fedIdent.equals(federatedIdentityEntity)) return;
        }

        userEntity.getFederatedIdentities().add(federatedIdentityEntity);
    }

    @Override
    public boolean removeFederatedIdentity(RealmModel realm, UserModel userModel, String socialProvider) {
        UserModel user = getUserById(userModel.getId(), realm);
        UserEntity userEntity = ((UserAdapter) user).getUserEntity();
        FederatedIdentityEntity federatedIdentityEntity = findSocialLink(userEntity, socialProvider);
        if (federatedIdentityEntity == null) {
            return false;
        }

        userEntity.getFederatedIdentities().remove(federatedIdentityEntity);
        return true;
    }

    private FederatedIdentityEntity findSocialLink(UserEntity userEntity, String socialProvider) {
        List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();
        if (linkEntities == null) {
            return null;
        }

        for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
            if (federatedIdentityEntity.getIdentityProvider().equals(socialProvider)) {
                return federatedIdentityEntity;
            }
        }
        return null;
    }

    @Override
    public UserModel addUser(RealmModel realm, String username) {
        return this.addUser(realm, KeycloakModelUtils.generateId(), username.toLowerCase(), true, true);
    }

    @Override
    public void preRemove(RealmModel realm) {
        // Nothing to do here?  Federation links are attached to users, which are removed by InMemoryModel
    }

    @Override
    public void preRemove(RealmModel realm, UserFederationProviderModel link) {
        Set<UserModel> toBeRemoved = new HashSet<UserModel>();
        for (UserModel user : inMemoryModel.getUsers(realm.getId())) {
            String fedLink = user.getFederationLink();
            if (fedLink == null) continue;
            if (fedLink.equals(link.getId())) toBeRemoved.add(user);
        }

        for (UserModel user : toBeRemoved) {
            inMemoryModel.removeUser(realm.getId(), user.getId());
        }
    }

    @Override
    public void preRemove(RealmModel realm, RoleModel role) {
        // todo not sure what to do for this
    }

    @Override
    public void preRemove(RealmModel realm, ClientModel client) {
        // TODO
    }

    @Override
    public void preRemove(ClientModel client, ProtocolMapperModel protocolMapper) {
        // TODO
    }

    @Override
    public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
        return CredentialValidation.validCredentials(realm, user, input);
    }

    @Override
    public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
        return CredentialValidation.validCredentials(realm, user, input);
    }

    @Override
    public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
        federatedUser = getUserById(federatedUser.getId(), realm);
        UserEntity userEntity = ((UserAdapter) federatedUser).getUserEntity();
        FederatedIdentityEntity federatedIdentityEntity = findFederatedIdentityLink(userEntity, federatedIdentityModel.getIdentityProvider());

        federatedIdentityEntity.setToken(federatedIdentityModel.getToken());
    }

    private FederatedIdentityEntity findFederatedIdentityLink(UserEntity userEntity, String identityProvider) {
        List<FederatedIdentityEntity> linkEntities = userEntity.getFederatedIdentities();
        if (linkEntities == null) {
            return null;
        }

        for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
            if (federatedIdentityEntity.getIdentityProvider().equals(identityProvider)) {
                return federatedIdentityEntity;
            }
        }
        return null;
    }

    @Override
    public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel... input) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
        return null; // not supported yet
    }

    @Override
    public void addOfflineUserSession(RealmModel realm, UserModel userModel, OfflineUserSessionModel userSession) {
        userModel = getUserById(userModel.getId(), realm);
        UserEntity userEntity = ((UserAdapter) userModel).getUserEntity();

        if (userEntity.getOfflineUserSessions() == null) {
            userEntity.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
        }

        if (getUserSessionEntityById(userEntity, userSession.getUserSessionId()) != null) {
            throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + userEntity.getUsername());
        }

        OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
        entity.setUserSessionId(userSession.getUserSessionId());
        entity.setData(userSession.getData());
        entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
        userEntity.getOfflineUserSessions().add(entity);
    }

    @Override
    public OfflineUserSessionModel getOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
        userModel = getUserById(userModel.getId(), realm);
        UserEntity userEntity = ((UserAdapter) userModel).getUserEntity();

        OfflineUserSessionEntity entity = getUserSessionEntityById(userEntity, userSessionId);
        return entity==null ? null : toModel(entity);
    }

    @Override
    public Collection<OfflineUserSessionModel> getOfflineUserSessions(RealmModel realm, UserModel userModel) {
        userModel = getUserById(userModel.getId(), realm);
        UserEntity user = ((UserAdapter) userModel).getUserEntity();

        if (user.getOfflineUserSessions()==null) {
            return Collections.emptyList();
        } else {
            List<OfflineUserSessionModel> result = new ArrayList<>();
            for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
                result.add(toModel(entity));
            }
            return result;
        }
    }

    private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
        OfflineUserSessionModel model = new OfflineUserSessionModel();
        model.setUserSessionId(entity.getUserSessionId());
        model.setData(entity.getData());
        return model;
    }

    @Override
    public boolean removeOfflineUserSession(RealmModel realm, UserModel userModel, String userSessionId) {
        userModel = getUserById(userModel.getId(), realm);
        UserEntity user = ((UserAdapter) userModel).getUserEntity();

        OfflineUserSessionEntity entity = getUserSessionEntityById(user, userSessionId);
        if (entity != null) {
            user.getOfflineUserSessions().remove(entity);
            return true;
        } else {
            return false;
        }
    }

    private OfflineUserSessionEntity getUserSessionEntityById(UserEntity user, String userSessionId) {
        if (user.getOfflineUserSessions() != null) {
            for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
                if (entity.getUserSessionId().equals(userSessionId)) {
                    return entity;
                }
            }
        }
        return null;
    }

    @Override
    public void addOfflineClientSession(RealmModel realm, OfflineClientSessionModel clientSession) {
        UserModel userModel = getUserById(clientSession.getUserId(), realm);
        UserEntity user = ((UserAdapter) userModel).getUserEntity();

        OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(user, clientSession.getUserSessionId());
        if (userSessionEntity == null) {
            throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + user.getUsername());
        }

        OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
        clEntity.setClientSessionId(clientSession.getClientSessionId());
        clEntity.setClientId(clientSession.getClientId());
        clEntity.setData(clientSession.getData());

        userSessionEntity.getOfflineClientSessions().add(clEntity);
    }

    @Override
    public OfflineClientSessionModel getOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
        userModel = getUserById(userModel.getId(), realm);
        UserEntity user = ((UserAdapter) userModel).getUserEntity();

        if (user.getOfflineUserSessions() != null) {
            for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
                for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
                    if (clSession.getClientSessionId().equals(clientSessionId)) {
                        return toModel(clSession, userSession.getUserSessionId());
                    }
                }
            }
        }

        return null;
    }

    private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userSessionId) {
        OfflineClientSessionModel model = new OfflineClientSessionModel();
        model.setClientSessionId(cls.getClientSessionId());
        model.setClientId(cls.getClientId());
        model.setData(cls.getData());
        model.setUserSessionId(userSessionId);
        return model;
    }

    @Override
    public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel userModel) {
        userModel = getUserById(userModel.getId(), realm);
        UserEntity user = ((UserAdapter) userModel).getUserEntity();

        List<OfflineClientSessionModel> result = new ArrayList<>();

        if (user.getOfflineUserSessions() != null) {
            for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
                for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
                    result.add(toModel(clSession, userSession.getUserSessionId()));
                }
            }
        }

        return result;
    }

    @Override
    public boolean removeOfflineClientSession(RealmModel realm, UserModel userModel, String clientSessionId) {
        userModel = getUserById(userModel.getId(), realm);
        UserEntity user = ((UserAdapter) userModel).getUserEntity();

        if (user.getOfflineUserSessions() != null) {
            for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
                for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
                    if (clSession.getClientSessionId().equals(clientSessionId)) {
                        userSession.getOfflineClientSessions().remove(clSession);
                        return true;
                    }
                }
            }
        }

        return false;
    }

    @Override
    public int getOfflineClientSessionsCount(RealmModel realm, ClientModel client) {
        return getOfflineClientSessions(realm, client, -1, -1).size();
    }

    @Override
    public Collection<OfflineClientSessionModel> getOfflineClientSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
        List<OfflineClientSessionModel> result = new LinkedList<>();

        List<UserModel> users = new ArrayList<>(inMemoryModel.getUsers(realm.getId()));
        users = sortedSubList(users, firstResult, maxResults);

        for (UserModel userModel : users) {
            UserEntity user = ((UserAdapter) userModel).getUserEntity();
            for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
                for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
                    if (clSession.getClientId().equals(client.getId())) {
                        result.add(toModel(clSession, userSession.getUserSessionId()));
                    }
                }
            }
        }
        return result;
    }
}