Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2016-01-06 16:51:24 -05:00
commit 71b6ed80ae
36 changed files with 1511 additions and 387 deletions

View file

@ -1,10 +1,18 @@
language: java
env:
global:
- MAVEN_SKIP_RC=true
- MAVEN_OPTS="-Xms512m -Xmx2048m"
jdk:
- oraclejdk8
before_script:
- export MAVEN_SKIP_RC=true
install:
- travis_wait mvn install -Pdistribution -DskipTests=true -B -V -q
- mvn install -Pdistribution -DskipTests=true -B -V -q
script:
- mvn test -B

View file

@ -1,4 +1,26 @@
<build xmlns="urn:wildfly:feature-pack-build:1.0">
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2014, Red Hat, Inc., and individual contributors
~ as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<build xmlns="urn:wildfly:feature-pack-build:1.1">
<dependencies>
<artifact name="org.wildfly:wildfly-feature-pack" />
</dependencies>
@ -8,20 +30,31 @@
<property name="jgroups.supplement" value="" />
</standalone>
<domain template="configuration/domain/template.xml" subsystems="configuration/domain/subsystems.xml" output-file="domain/configuration/domain.xml" />
<host template="configuration/host/host.xml" subsystems="configuration/host/subsystems.xml" output-file="domain/configuration/host.xml" />
<host template="configuration/host/host-master.xml" subsystems="configuration/host/subsystems.xml" output-file="domain/configuration/host-master.xml" />
<host template="configuration/host/host-slave.xml" subsystems="configuration/host/subsystems.xml" output-file="domain/configuration/host-slave.xml" />
</config>
<mkdirs>
<dir name="domain/data/content"/>
<dir name="standalone/lib/ext"/>
<dir name="domain/tmp/auth"/>
<dir name="standalone/tmp/auth"/>
<dir name=".installation"/>
</mkdirs>
<file-permissions>
<permission value="755">
<filter pattern="*.sh" include="true"/>
<filter pattern="*" include="false"/>
</permission>
<permission value="700">
<filter pattern="*/tmp/auth" include="true"/>
<filter pattern="*" include="false"/>
</permission>
<permission value="600">
<filter pattern="*-users.properties" include="true" />
<filter pattern="*/.installation" include="true"/>
<filter pattern="*" include="false"/>
</permission>
</file-permissions>
<line-endings>

View file

@ -53,63 +53,4 @@
<subsystem supplement="ha">undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem>
</subsystems>
<subsystems name="full">
<!-- Each subsystem to be included relative to the src/main/resources directory -->
<subsystem>logging.xml</subsystem>
<subsystem>bean-validation.xml</subsystem>
<subsystem>keycloak-datasources.xml</subsystem>
<subsystem supplement="full">ee.xml</subsystem>
<subsystem supplement="full">ejb3.xml</subsystem>
<subsystem>io.xml</subsystem>
<subsystem>keycloak-infinispan.xml</subsystem>
<subsystem>iiop-openjdk.xml</subsystem>
<subsystem>jaxrs.xml</subsystem>
<subsystem>jca.xml</subsystem>
<subsystem>jdr.xml</subsystem>
<subsystem supplement="domain">jmx.xml</subsystem>
<subsystem>jpa.xml</subsystem>
<subsystem>jsf.xml</subsystem>
<subsystem>jsr77.xml</subsystem>
<subsystem>mail.xml</subsystem>
<subsystem>messaging.xml</subsystem>
<subsystem>naming.xml</subsystem>
<subsystem>remoting.xml</subsystem>
<subsystem>request-controller.xml</subsystem>
<subsystem>security.xml</subsystem>
<subsystem>security-manager.xml</subsystem>
<subsystem>transactions.xml</subsystem>
<subsystem>undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem>
</subsystems>
<subsystems name="full-ha">
<!-- Each subsystem to be included relative to the src/main/resources directory -->
<subsystem>logging.xml</subsystem>
<subsystem>bean-validation.xml</subsystem>
<subsystem>keycloak-datasources.xml</subsystem>
<subsystem supplement="full">ee.xml</subsystem>
<subsystem supplement="full-ha">ejb3.xml</subsystem>
<subsystem>io.xml</subsystem>
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
<subsystem>iiop-openjdk.xml</subsystem>
<subsystem>jaxrs.xml</subsystem>
<subsystem>jca.xml</subsystem>
<subsystem>jdr.xml</subsystem>
<subsystem>jgroups.xml</subsystem>
<subsystem supplement="domain">jmx.xml</subsystem>
<subsystem>jpa.xml</subsystem>
<subsystem>jsf.xml</subsystem>
<subsystem>jsr77.xml</subsystem>
<subsystem>mail.xml</subsystem>
<subsystem supplement="ha">messaging.xml</subsystem>
<subsystem>mod_cluster.xml</subsystem>
<subsystem>naming.xml</subsystem>
<subsystem>remoting.xml</subsystem>
<subsystem>resource-adapters.xml</subsystem>
<subsystem>request-controller.xml</subsystem>
<subsystem>security.xml</subsystem>
<subsystem>security-manager.xml</subsystem>
<subsystem>transactions.xml</subsystem>
<subsystem supplement="ha">undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem>
</subsystems>
</config>

View file

@ -24,18 +24,12 @@
</management>
<profiles>
<profile name="default">
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
<profile name="ha">
<?SUBSYSTEMS socket-binding-group="ha-sockets"?>
</profile>
<profile name="full">
<?SUBSYSTEMS socket-binding-group="full-sockets"?>
</profile>
<profile name="full-ha">
<?SUBSYSTEMS socket-binding-group="full-ha-sockets"?>
</profile>
<profile name="default">
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
<profile name="ha">
<?SUBSYSTEMS socket-binding-group="ha-sockets"?>
</profile>
</profiles>
<!--
@ -60,28 +54,20 @@
<!-- Needed for server groups using the 'ha' profile -->
<?SOCKET-BINDINGS?>
</socket-binding-group>
<socket-binding-group name="full-sockets" default-interface="public">
<!-- Needed for server groups using the 'full' profile -->
<?SOCKET-BINDINGS?>
</socket-binding-group>
<socket-binding-group name="full-ha-sockets" default-interface="public">
<!-- Needed for server groups using the 'full-ha' profile -->
<?SOCKET-BINDINGS?>
</socket-binding-group>
</socket-binding-groups>
<server-groups>
<server-group name="main-server-group" profile="full">
<server-group name="main-server-group" profile="default">
<jvm name="default">
<heap size="64m" max-size="512m"/>
</jvm>
<socket-binding-group ref="full-sockets"/>
<socket-binding-group ref="standard-sockets"/>
</server-group>
<server-group name="other-server-group" profile="full-ha">
<server-group name="other-server-group" profile="ha">
<jvm name="default">
<heap size="64m" max-size="512m"/>
</jvm>
<socket-binding-group ref="full-ha-sockets"/>
<socket-binding-group ref="ha-sockets"/>
</server-group>
</server-groups>

View file

@ -0,0 +1,85 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
A simple configuration for a Host Controller that only acts as the master domain controller
and does not itself directly control any servers.
-->
<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
<file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="host-file"/>
</handlers>
</logger>
<server-logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="server-file"/>
</handlers>
</server-logger>
</audit-log>
<management-interfaces>
<native-interface security-realm="ManagementRealm">
<socket interface="management" port="${jboss.management.native.port:9999}"/>
</native-interface>
<http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
<socket interface="management" port="${jboss.management.http.port:9990}"/>
</http-interface>
</management-interfaces>
</management>
<domain-controller>
<local/>
</domain-controller>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
</interfaces>
<jvms>
<jvm name="default">
<heap size="64m" max-size="256m"/>
<jvm-options>
<option value="-server"/>
</jvm-options>
</jvm>
</jvms>
<profile>
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
</host>

View file

@ -0,0 +1,101 @@
<?xml version='1.0' encoding='UTF-8'?>
<host xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<server-identities>
<!-- Replace this with either a base64 password of your own, or use a vault with a vault expression -->
<secret value="c2xhdmVfdXNlcl9wYXNzd29yZA=="/>
</server-identities>
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
<file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="host-file"/>
</handlers>
</logger>
<server-logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="server-file"/>
</handlers>
</server-logger>
</audit-log>
<management-interfaces>
<native-interface security-realm="ManagementRealm">
<socket interface="management" port="${jboss.management.native.port:9999}"/>
</native-interface>
</management-interfaces>
</management>
<domain-controller>
<remote security-realm="ManagementRealm">
<discovery-options>
<static-discovery name="primary" protocol="${jboss.domain.master.protocol:remote}" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}"/>
</discovery-options>
</remote>
</domain-controller>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
<?INTERFACES?>
</interfaces>
<jvms>
<jvm name="default">
<heap size="64m" max-size="256m"/>
<jvm-options>
<option value="-server"/>
</jvm-options>
</jvm>
</jvms>
<servers>
<server name="server-one" group="main-server-group"/>
<server name="server-two" group="other-server-group">
<!-- server-two avoids port conflicts by incrementing the ports in
the default socket-group declared in the server-group -->
<socket-bindings port-offset="150"/>
</server>
</servers>
<profile>
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
</host>

View file

@ -0,0 +1,110 @@
<?xml version='1.0' encoding='UTF-8'?>
<host name="master" xmlns="urn:jboss:domain:4.0">
<extensions>
<?EXTENSIONS?>
</extensions>
<management>
<security-realms>
<security-realm name="ManagementRealm">
<authentication>
<local default-user="$local" skip-group-loading="true"/>
<properties path="mgmt-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization map-groups-to-roles="false">
<properties path="mgmt-groups.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
<properties path="application-users.properties" relative-to="jboss.domain.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relative-to="jboss.domain.config.dir"/>
</authorization>
</security-realm>
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="host-file" formatter="json-formatter" relative-to="jboss.domain.data.dir" path="audit-log.log"/>
<file-handler name="server-file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="host-file"/>
</handlers>
</logger>
<server-logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="server-file"/>
</handlers>
</server-logger>
</audit-log>
<management-interfaces>
<native-interface security-realm="ManagementRealm">
<socket interface="management" port="${jboss.management.native.port:9999}"/>
</native-interface>
<http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
<socket interface="management" port="${jboss.management.http.port:9990}"/>
</http-interface>
</management-interfaces>
</management>
<domain-controller>
<local/>
<!-- Alternative remote domain controller configuration with a host and port -->
<!-- <remote protocol="remote" host="${jboss.domain.master.address}" port="${jboss.domain.master.port:9999}" security-realm="ManagementRealm"/> -->
</domain-controller>
<interfaces>
<interface name="management">
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
</interface>
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
<?INTERFACES?>
</interfaces>
<jvms>
<jvm name="default">
<heap size="64m" max-size="256m"/>
<jvm-options>
<option value="-server"/>
</jvm-options>
</jvm>
</jvms>
<servers>
<server name="server-one" group="main-server-group">
<!-- Remote JPDA debugging for a specific server
<jvm name="default">
<jvm-options>
<option value="-agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=n"/>
</jvm-options>
</jvm>
-->
</server>
<server name="server-two" group="main-server-group" auto-start="true">
<!-- server-two avoids port conflicts by incrementing the ports in
the default socket-group declared in the server-group -->
<socket-bindings port-offset="150"/>
</server>
<server name="server-three" group="other-server-group" auto-start="false">
<!-- server-three avoids port conflicts by incrementing the ports in
the default socket-group declared in the server-group -->
<socket-bindings port-offset="250"/>
</server>
</servers>
<profile>
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
</profile>
</host>

View file

@ -0,0 +1,29 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2015, Red Hat, Inc., and individual contributors
~ as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
<config>
<subsystems>
<subsystem>jmx.xml</subsystem>
</subsystems>
</config>

View file

@ -1,30 +1,30 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
<config>
<subsystems>
<subsystem>logging.xml</subsystem>
<subsystem>bean-validation.xml</subsystem>
<subsystem>keycloak-datasources.xml</subsystem>
<subsystem>ee.xml</subsystem>
<subsystem supplement="ha">ejb3.xml</subsystem>
<subsystem>io.xml</subsystem>
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
<subsystem>jaxrs.xml</subsystem>
<subsystem>jca.xml</subsystem>
<subsystem>jdr.xml</subsystem>
<subsystem>jgroups.xml</subsystem>
<subsystem>jmx.xml</subsystem>
<subsystem>jpa.xml</subsystem>
<subsystem>jsf.xml</subsystem>
<subsystem>mail.xml</subsystem>
<subsystem>mod_cluster.xml</subsystem>
<subsystem>naming.xml</subsystem>
<subsystem>remoting.xml</subsystem>
<subsystem>request-controller.xml</subsystem>
<subsystem>security-manager.xml</subsystem>
<subsystem>security.xml</subsystem>
<subsystem>transactions.xml</subsystem>
<subsystem supplement="ha">undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem>
</subsystems>
<subsystems>
<subsystem>logging.xml</subsystem>
<subsystem>bean-validation.xml</subsystem>
<subsystem>keycloak-datasources.xml</subsystem>
<subsystem>ee.xml</subsystem>
<subsystem supplement="ha">ejb3.xml</subsystem>
<subsystem>io.xml</subsystem>
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
<subsystem>jaxrs.xml</subsystem>
<subsystem>jca.xml</subsystem>
<subsystem>jdr.xml</subsystem>
<subsystem>jgroups.xml</subsystem>
<subsystem>jmx.xml</subsystem>
<subsystem>jpa.xml</subsystem>
<subsystem>jsf.xml</subsystem>
<subsystem>mail.xml</subsystem>
<subsystem>mod_cluster.xml</subsystem>
<subsystem>naming.xml</subsystem>
<subsystem>remoting.xml</subsystem>
<subsystem>request-controller.xml</subsystem>
<subsystem>security-manager.xml</subsystem>
<subsystem>security.xml</subsystem>
<subsystem>transactions.xml</subsystem>
<subsystem supplement="ha">undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem>
</subsystems>
</config>

View file

@ -29,15 +29,15 @@
</security-realms>
<audit-log>
<formatters>
<json-formatter name="json-formatter"/>
<json-formatter name="json-formatter"/>
</formatters>
<handlers>
<file-handler name="file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
<file-handler name="file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
</handlers>
<logger log-boot="true" log-read-only="false" enabled="false">
<handlers>
<handler name="file"/>
</handlers>
<handlers>
<handler name="file"/>
</handlers>
</logger>
</audit-log>
<management-interfaces>
@ -69,8 +69,9 @@
<interface name="public">
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>
<!-- TODO - only show this if the jacorb subsystem is added -->
<?INTERFACES?>
</interfaces>
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">

View file

@ -23,14 +23,12 @@ public interface UserSessionProvider extends Provider {
List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId);
UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId);
List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue);
int getActiveUserSessions(RealmModel realm, ClientModel client);
long getActiveUserSessions(RealmModel realm, ClientModel client);
void removeUserSession(RealmModel realm, UserSessionModel session);
void removeUserSessions(RealmModel realm, UserModel user);
// Implementation should propagate removal of expired userSessions to userSessionPersister too
void removeExpiredUserSessions(RealmModel realm);
void removeExpired(RealmModel realm);
void removeUserSessions(RealmModel realm);
void removeClientSession(RealmModel realm, ClientSessionModel clientSession);
@ -56,7 +54,7 @@ public interface UserSessionProvider extends Provider {
// Don't remove userSession even if it's last userSession
void removeOfflineClientSession(RealmModel realm, String clientSessionId);
int getOfflineSessionsCount(RealmModel realm, ClientModel client);
long getOfflineSessionsCount(RealmModel realm, ClientModel client);
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max);
// Triggered by persister during pre-load

View file

@ -0,0 +1,387 @@
package org.keycloak.models.sessions.infinispan;
import org.infinispan.Cache;
import org.infinispan.distexec.mapreduce.MapReduceTask;
import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.models.*;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.sessions.infinispan.entities.*;
import org.keycloak.models.sessions.infinispan.mapreduce.*;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RealmInfoUtil;
import java.util.*;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class CompatInfinispanUserSessionProvider extends InfinispanUserSessionProvider {
private static final Logger log = Logger.getLogger(CompatInfinispanUserSessionProvider.class);
public CompatInfinispanUserSessionProvider(KeycloakSession session, Cache<String, SessionEntity> sessionCache, Cache<String, SessionEntity> offlineSessionCache,
Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache) {
super(session, sessionCache, offlineSessionCache, loginFailureCache);
}
@Override
public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()))
.reducedWith(new FirstResultReducer())
.execute();
return wrapUserSessions(realm, sessions.values(), false);
}
@Override
public List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).brokerUserId(brokerUserId))
.reducedWith(new FirstResultReducer())
.execute();
return wrapUserSessions(realm, sessions.values(), false);
}
@Override
public UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).brokerSessionId(brokerSessionId))
.reducedWith(new FirstResultReducer())
.execute();
List<UserSessionModel> userSessionModels = wrapUserSessions(realm, sessions.values(), false);
if (userSessionModels.isEmpty()) return null;
return userSessionModels.get(0);
}
@Override
public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
return getUserSessions(realm, client, -1, -1);
}
@Override
public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults) {
return getUserSessions(realm, client, firstResult, maxResults, false);
}
protected List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map<String, Integer> map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()).emitUserSessionAndTimestamp())
.reducedWith(new LargestResultReducer())
.execute();
List<Map.Entry<String, Integer>> sessionTimestamps = new LinkedList<Map.Entry<String, Integer>>(map.entrySet());
Collections.sort(sessionTimestamps, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
return e1.getValue().compareTo(e2.getValue());
}
});
if (firstResult != -1 || maxResults == -1) {
if (firstResult == -1) {
firstResult = 0;
}
if (maxResults == -1) {
maxResults = Integer.MAX_VALUE;
}
if (firstResult > sessionTimestamps.size()) {
return Collections.emptyList();
}
int toIndex = (firstResult + maxResults) < sessionTimestamps.size() ? firstResult + maxResults : sessionTimestamps.size();
sessionTimestamps = sessionTimestamps.subList(firstResult, toIndex);
}
List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
for (Map.Entry<String, Integer> e : sessionTimestamps) {
UserSessionEntity userSessionEntity = (UserSessionEntity) cache.get(e.getKey());
if (userSessionEntity != null) {
userSessions.add(wrap(realm, userSessionEntity, offline));
}
}
return userSessions;
}
@Override
public long getActiveUserSessions(RealmModel realm, ClientModel client) {
return getUserSessionsCount(realm, client, false);
}
protected long getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()).emitUserSessionAndTimestamp())
.reducedWith(new LargestResultReducer()).execute();
return map.size();
}
@Override
public void removeUserSession(RealmModel realm, UserSessionModel session) {
removeUserSession(realm, session.getId());
}
@Override
public void removeUserSessions(RealmModel realm, UserModel user) {
removeUserSessions(realm, user, false);
}
protected void removeUserSessions(RealmModel realm, UserModel user, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map<String, String> sessions = new MapReduceTask(cache)
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : sessions.keySet()) {
removeUserSession(realm, id, offline);
}
}
@Override
public void removeExpired(RealmModel realm) {
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
int expired = Time.currentTime() - realm.getSsoSessionMaxLifespan();
int expiredRefresh = Time.currentTime() - realm.getSsoSessionIdleTimeout();
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
int expiredDettachedClientSession = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
Map<String, String> map = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).expired(expired, expiredRefresh).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : map.keySet()) {
removeUserSession(realm, id);
}
map = new MapReduceTask(sessionCache)
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession(true).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : map.keySet()) {
tx.remove(sessionCache, id);
}
// Remove expired offline user sessions
Map<String, SessionEntity> map2 = new MapReduceTask(offlineSessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).expired(null, expiredOffline))
.reducedWith(new FirstResultReducer())
.execute();
for (Map.Entry<String, SessionEntity> entry : map2.entrySet()) {
String userSessionId = entry.getKey();
tx.remove(offlineSessionCache, userSessionId);
// Propagate to persister
persister.removeUserSession(userSessionId, true);
UserSessionEntity entity = (UserSessionEntity) entry.getValue();
for (String clientSessionId : entity.getClientSessions()) {
tx.remove(offlineSessionCache, clientSessionId);
}
}
// Remove expired offline client sessions
map = new MapReduceTask(offlineSessionCache)
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredOffline).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String clientSessionId : map.keySet()) {
tx.remove(offlineSessionCache, clientSessionId);
persister.removeClientSession(clientSessionId, true);
}
// Remove expired client initial access
map = new MapReduceTask(sessionCache)
.mappedWith(ClientInitialAccessMapper.create(realm.getId()).expired(Time.currentTime()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : map.keySet()) {
tx.remove(sessionCache, id);
}
}
@Override
public void removeUserSessions(RealmModel realm) {
removeUserSessions(realm, false);
}
protected void removeUserSessions(RealmModel realm, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map<String, String> ids = new MapReduceTask(cache)
.mappedWith(SessionMapper.create(realm.getId()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : ids.keySet()) {
cache.remove(id);
}
}
@Override
public void removeUserLoginFailure(RealmModel realm, String username) {
LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
tx.remove(loginFailureCache, key);
}
@Override
public void removeAllUserLoginFailures(RealmModel realm) {
Map<LoginFailureKey, Object> sessions = new MapReduceTask(loginFailureCache)
.mappedWith(UserLoginFailureMapper.create(realm.getId()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (LoginFailureKey id : sessions.keySet()) {
tx.remove(loginFailureCache, id);
}
}
@Override
public void onRealmRemoved(RealmModel realm) {
removeUserSessions(realm, true);
removeUserSessions(realm, false);
removeAllUserLoginFailures(realm);
}
@Override
public void onClientRemoved(RealmModel realm, ClientModel client) {
onClientRemoved(realm, client, true);
onClientRemoved(realm, client, false);
}
private void onClientRemoved(RealmModel realm, ClientModel client, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map<String, ClientSessionEntity> map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()))
.reducedWith(new FirstResultReducer())
.execute();
for (Map.Entry<String, ClientSessionEntity> entry : map.entrySet()) {
// detach from userSession
ClientSessionAdapter adapter = wrap(realm, entry.getValue(), offline);
adapter.setUserSession(null);
tx.remove(cache, entry.getKey());
}
}
@Override
public void onUserRemoved(RealmModel realm, UserModel user) {
removeUserSessions(realm, user, true);
removeUserSessions(realm, user, false);
loginFailureCache.remove(new LoginFailureKey(realm.getId(), user.getUsername()));
loginFailureCache.remove(new LoginFailureKey(realm.getId(), user.getEmail()));
}
@Override
public void removeClientSession(RealmModel realm, ClientSessionModel clientSession) {
removeClientSession(realm, clientSession, false);
}
protected void removeClientSession(RealmModel realm, ClientSessionModel clientSession, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
UserSessionModel userSession = clientSession.getUserSession();
if (userSession != null) {
UserSessionEntity entity = ((UserSessionAdapter) userSession).getEntity();
if (entity.getClientSessions() != null) {
entity.getClientSessions().remove(clientSession.getId());
}
tx.replace(cache, entity.getId(), entity);
}
tx.remove(cache, clientSession.getId());
}
protected void removeUserSession(RealmModel realm, String userSessionId) {
removeUserSession(realm, userSessionId, false);
}
protected void removeUserSession(RealmModel realm, String userSessionId, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
tx.remove(cache, userSessionId);
// TODO: Isn't more effective to retrieve from userSessionEntity directly?
Map<String, String> map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).userSession(userSessionId).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : map.keySet()) {
tx.remove(cache, id);
}
}
@Override
public void removeOfflineUserSession(RealmModel realm, String userSessionId) {
removeUserSession(realm, userSessionId, true);
}
@Override
public List<ClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(offlineSessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()))
.reducedWith(new FirstResultReducer())
.execute();
List<ClientSessionEntity> clientSessions = new LinkedList<>();
for (UserSessionEntity userSession : sessions.values()) {
Set<String> currClientSessions = userSession.getClientSessions();
for (String clientSessionId : currClientSessions) {
ClientSessionEntity cls = (ClientSessionEntity) offlineSessionCache.get(clientSessionId);
if (cls != null) {
clientSessions.add(cls);
}
}
}
return wrapClientSessions(realm, clientSessions, true);
}
@Override
public void removeOfflineClientSession(RealmModel realm, String clientSessionId) {
ClientSessionModel clientSession = getOfflineClientSession(realm, clientSessionId);
removeClientSession(realm, clientSession, true);
}
@Override
public long getOfflineSessionsCount(RealmModel realm, ClientModel client) {
return getUserSessionsCount(realm, client, true);
}
@Override
public List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max) {
return getUserSessions(realm, client, first, max, true);
}
@Override
public List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm) {
Map<String, ClientInitialAccessEntity> entities = new MapReduceTask(sessionCache)
.mappedWith(ClientInitialAccessMapper.create(realm.getId()))
.reducedWith(new FirstResultReducer())
.execute();
return wrapClientInitialAccess(realm, entities.values());
}
}

View file

@ -0,0 +1,66 @@
package org.keycloak.models.sessions.infinispan;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Consumers {
private Consumers() {
}
public static UserSessionModelsConsumer userSessionModels(InfinispanUserSessionProvider provider, RealmModel realm, boolean offline) {
return new UserSessionModelsConsumer(provider, realm, offline);
}
public static class UserSessionIdAndTimestampConsumer implements Consumer<Map.Entry<String, SessionEntity>> {
private Map<String, Integer> sessions = new HashMap<>();
@Override
public void accept(Map.Entry<String, SessionEntity> entry) {
SessionEntity e = entry.getValue();
if (e instanceof ClientSessionEntity) {
ClientSessionEntity ce = (ClientSessionEntity) e;
sessions.put(ce.getUserSession(), ce.getTimestamp());
}
}
}
public static class UserSessionModelsConsumer implements Consumer<Map.Entry<String, SessionEntity>> {
private InfinispanUserSessionProvider provider;
private RealmModel realm;
private boolean offline;
private List<UserSessionModel> sessions = new LinkedList<>();
private UserSessionModelsConsumer(InfinispanUserSessionProvider provider, RealmModel realm, boolean offline) {
this.provider = provider;
this.realm = realm;
this.offline = offline;
}
@Override
public void accept(Map.Entry<String, SessionEntity> entry) {
SessionEntity e = entry.getValue();
sessions.add(provider.wrap(realm, (UserSessionEntity) e, offline));
}
public List<UserSessionModel> getSessions() {
return sessions;
}
}
}

View file

@ -1,25 +1,21 @@
package org.keycloak.models.sessions.infinispan;
import org.infinispan.Cache;
import org.infinispan.distexec.mapreduce.MapReduceTask;
import org.infinispan.CacheStream;
import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.models.*;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.sessions.infinispan.entities.*;
import org.keycloak.models.sessions.infinispan.mapreduce.*;
import org.keycloak.models.sessions.infinispan.stream.*;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RealmInfoUtil;
import org.keycloak.common.util.Time;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -28,11 +24,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
private static final Logger log = Logger.getLogger(InfinispanUserSessionProvider.class);
private final KeycloakSession session;
private final Cache<String, SessionEntity> sessionCache;
private final Cache<String, SessionEntity> offlineSessionCache;
private final Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache;
private final InfinispanKeycloakTransaction tx;
protected final KeycloakSession session;
protected final Cache<String, SessionEntity> sessionCache;
protected final Cache<String, SessionEntity> offlineSessionCache;
protected final Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache;
protected final InfinispanKeycloakTransaction tx;
public InfinispanUserSessionProvider(KeycloakSession session, Cache<String, SessionEntity> sessionCache, Cache<String, SessionEntity> offlineSessionCache,
Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache) {
@ -139,36 +135,31 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return wrap(realm, entity, offline);
}
@Override
public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()))
.reducedWith(new FirstResultReducer())
.execute();
protected List<UserSessionModel> getUserSessions(RealmModel realm, Predicate<Map.Entry<String, SessionEntity>> predicate, boolean offline) {
CacheStream<Map.Entry<String, SessionEntity>> cacheStream = getCache(offline).entrySet().stream();
Iterator<Map.Entry<String, SessionEntity>> itr = cacheStream.filter(predicate).iterator();
List<UserSessionModel> sessions = new LinkedList<>();
while (itr.hasNext()) {
UserSessionEntity e = (UserSessionEntity) itr.next().getValue();
sessions.add(wrap(realm, e, offline));
}
return sessions;
}
return wrapUserSessions(realm, sessions.values(), false);
@Override
public List<UserSessionModel> getUserSessions(final RealmModel realm, UserModel user) {
return getUserSessions(realm, UserSessionPredicate.create(realm.getId()).user(user.getId()), false);
}
@Override
public List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).brokerUserId(brokerUserId))
.reducedWith(new FirstResultReducer())
.execute();
return wrapUserSessions(realm, sessions.values(), false);
return getUserSessions(realm, UserSessionPredicate.create(realm.getId()).brokerUserId(brokerUserId), false);
}
@Override
public UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).brokerSessionId(brokerSessionId))
.reducedWith(new FirstResultReducer())
.execute();
List<UserSessionModel> userSessionModels = wrapUserSessions(realm, sessions.values(), false);
if (userSessionModels.isEmpty()) return null;
return userSessionModels.get(0);
List<UserSessionModel> userSessions = getUserSessions(realm, UserSessionPredicate.create(realm.getId()).brokerSessionId(brokerSessionId), false);
return userSessions.isEmpty() ? null : userSessions.get(0);
}
@Override
@ -181,86 +172,58 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
return getUserSessions(realm, client, firstResult, maxResults, false);
}
protected List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
protected List<UserSessionModel> getUserSessions(final RealmModel realm, ClientModel client, int firstResult, int maxResults, final boolean offline) {
final Cache<String, SessionEntity> cache = getCache(offline);
Map<String, Integer> map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()).emitUserSessionAndTimestamp())
.reducedWith(new LargestResultReducer())
.execute();
Iterator<UserSessionTimestamp> itr = cache.entrySet().stream()
.filter(ClientSessionPredicate.create(realm.getId()).client(client.getId()).requireUserSession())
.map(Mappers.clientSessionToUserSessionTimestamp())
.iterator();
List<Map.Entry<String, Integer>> sessionTimestamps = new LinkedList<Map.Entry<String, Integer>>(map.entrySet());
Map<String, UserSessionTimestamp> m = new HashMap<>();
while(itr.hasNext()) {
UserSessionTimestamp next = itr.next();
if (!m.containsKey(next.getUserSessionId()) || m.get(next.getUserSessionId()).getClientSessionTimestamp() < next.getClientSessionTimestamp()) {
m.put(next.getUserSessionId(), next);
}
}
Collections.sort(sessionTimestamps, new Comparator<Map.Entry<String, Integer>>() {
Stream<UserSessionTimestamp> stream = new LinkedList<>(m.values()).stream().sorted(Comparators.userSessionTimestamp());
if (firstResult > 0) {
stream = stream.skip(firstResult);
}
if (maxResults > 0) {
stream = stream.limit(maxResults);
}
final List<UserSessionModel> sessions = new LinkedList<>();
stream.forEach(new Consumer<UserSessionTimestamp>() {
@Override
public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
return e1.getValue().compareTo(e2.getValue());
public void accept(UserSessionTimestamp userSessionTimestamp) {
SessionEntity entity = cache.get(userSessionTimestamp.getUserSessionId());
if (entity != null) {
sessions.add(wrap(realm, (UserSessionEntity) entity, offline));
}
}
});
if (firstResult != -1 || maxResults == -1) {
if (firstResult == -1) {
firstResult = 0;
}
if (maxResults == -1) {
maxResults = Integer.MAX_VALUE;
}
if (firstResult > sessionTimestamps.size()) {
return Collections.emptyList();
}
int toIndex = (firstResult + maxResults) < sessionTimestamps.size() ? firstResult + maxResults : sessionTimestamps.size();
sessionTimestamps = sessionTimestamps.subList(firstResult, toIndex);
}
List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
for (Map.Entry<String, Integer> e : sessionTimestamps) {
UserSessionEntity userSessionEntity = (UserSessionEntity) cache.get(e.getKey());
if (userSessionEntity != null) {
userSessions.add(wrap(realm, userSessionEntity, offline));
}
}
return userSessions;
return sessions;
}
@Override
public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
HashMap<String, String> notes = new HashMap<>();
notes.put(noteName, noteValue);
return getUserSessionsByNotes(realm, notes);
}
public List<UserSessionModel> getUserSessionsByNotes(RealmModel realm, Map<String, String> notes) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
.mappedWith(UserSessionNoteMapper.create(realm.getId()).notes(notes))
.reducedWith(new FirstResultReducer())
.execute();
return wrapUserSessions(realm, sessions.values(), false);
}
@Override
public int getActiveUserSessions(RealmModel realm, ClientModel client) {
public long getActiveUserSessions(RealmModel realm, ClientModel client) {
return getUserSessionsCount(realm, client, false);
}
protected int getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()).emitUserSessionAndTimestamp())
.reducedWith(new LargestResultReducer()).execute();
return map.size();
protected long getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
return getCache(offline).entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).client(client.getId()).requireUserSession()).map(Mappers.clientSessionToUserSessionId()).distinct().count();
}
@Override
public void removeUserSession(RealmModel realm, UserSessionModel session) {
removeUserSession(realm, session.getId());
removeUserSession(realm, session.getId(), false);
}
@Override
@ -271,80 +234,81 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
protected void removeUserSessions(RealmModel realm, UserModel user, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map<String, String> sessions = new MapReduceTask(cache)
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : sessions.keySet()) {
removeUserSession(realm, id, offline);
Iterator<String> itr = cache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).user(user.getId())).map(Mappers.sessionId()).iterator();
while (itr.hasNext()) {
removeUserSession(realm, itr.next(), offline);
}
}
@Override
public void removeExpiredUserSessions(RealmModel realm) {
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
public void removeExpired(RealmModel realm) {
removeExpiredUserSessions(realm);
removeExpiredClientSessions(realm);
removeExpiredOfflineUserSessions(realm);
removeExpiredOfflineClientSessions(realm);
removeExpiredClientInitialAccess(realm);
}
private void removeExpiredUserSessions(RealmModel realm) {
int expired = Time.currentTime() - realm.getSsoSessionMaxLifespan();
int expiredRefresh = Time.currentTime() - realm.getSsoSessionIdleTimeout();
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).expired(expired, expiredRefresh)).iterator();
while (itr.hasNext()) {
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
tx.remove(sessionCache, entity.getId());
if (entity.getClientSessions() != null) {
for (String clientSessionId : entity.getClientSessions()) {
tx.remove(sessionCache, clientSessionId);
}
}
}
}
private void removeExpiredClientSessions(RealmModel realm) {
int expiredDettachedClientSession = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
Map<String, String> map = new MapReduceTask(sessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).expired(expired, expiredRefresh).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : map.keySet()) {
removeUserSession(realm, id);
Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession()).iterator();
while (itr.hasNext()) {
tx.remove(sessionCache, itr.next().getKey());
}
}
map = new MapReduceTask(sessionCache)
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession(true).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
private void removeExpiredOfflineUserSessions(RealmModel realm) {
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
for (String id : map.keySet()) {
tx.remove(sessionCache, id);
}
Iterator<Map.Entry<String, SessionEntity>> itr = offlineSessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).expired(null, expiredOffline)).iterator();
while (itr.hasNext()) {
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
tx.remove(offlineSessionCache, entity.getId());
// Remove expired offline user sessions
Map<String, SessionEntity> map2 = new MapReduceTask(offlineSessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).expired(null, expiredOffline))
.reducedWith(new FirstResultReducer())
.execute();
persister.removeUserSession(entity.getId(), true);
for (Map.Entry<String, SessionEntity> entry : map2.entrySet()) {
String userSessionId = entry.getKey();
tx.remove(offlineSessionCache, userSessionId);
// Propagate to persister
persister.removeUserSession(userSessionId, true);
UserSessionEntity entity = (UserSessionEntity) entry.getValue();
for (String clientSessionId : entity.getClientSessions()) {
tx.remove(offlineSessionCache, clientSessionId);
}
}
}
// Remove expired offline client sessions
map = new MapReduceTask(offlineSessionCache)
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredOffline).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
private void removeExpiredOfflineClientSessions(RealmModel realm) {
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
for (String clientSessionId : map.keySet()) {
tx.remove(offlineSessionCache, clientSessionId);
persister.removeClientSession(clientSessionId, true);
Iterator<String> itr = offlineSessionCache.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).expiredRefresh(expiredOffline)).map(Mappers.sessionId()).iterator();
while (itr.hasNext()) {
String sessionId = itr.next();
tx.remove(offlineSessionCache, sessionId);
persister.removeClientSession(sessionId, true);
}
}
// Remove expired client initial access
map = new MapReduceTask(sessionCache)
.mappedWith(ClientInitialAccessMapper.create(realm.getId()).expired(Time.currentTime()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : map.keySet()) {
tx.remove(sessionCache, id);
private void removeExpiredClientInitialAccess(RealmModel realm) {
Iterator<String> itr = sessionCache.entrySet().stream().filter(ClientInitialAccessPredicate.create(realm.getId()).expired(Time.currentTime())).map(Mappers.sessionId()).iterator();
while (itr.hasNext()) {
tx.remove(sessionCache, itr.next());
}
}
@ -356,13 +320,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
protected void removeUserSessions(RealmModel realm, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map<String, String> ids = new MapReduceTask(cache)
.mappedWith(SessionMapper.create(realm.getId()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : ids.keySet()) {
cache.remove(id);
Iterator<String> itr = cache.entrySet().stream().filter(SessionPredicate.create(realm.getId())).map(Mappers.sessionId()).iterator();
while (itr.hasNext()) {
cache.remove(itr.next());
}
}
@ -384,24 +344,18 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public void removeUserLoginFailure(RealmModel realm, String username) {
LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
tx.remove(loginFailureCache, key);
tx.remove(loginFailureCache, new LoginFailureKey(realm.getId(), username));
}
@Override
public void removeAllUserLoginFailures(RealmModel realm) {
Map<LoginFailureKey, Object> sessions = new MapReduceTask(loginFailureCache)
.mappedWith(UserLoginFailureMapper.create(realm.getId()).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (LoginFailureKey id : sessions.keySet()) {
tx.remove(loginFailureCache, id);
Iterator<LoginFailureKey> itr = loginFailureCache.entrySet().stream().filter(UserLoginFailurePredicate.create(realm.getId())).map(Mappers.loginFailureId()).iterator();
while (itr.hasNext()) {
LoginFailureKey key = itr.next();
tx.remove(loginFailureCache, key);
}
}
@Override
public void onRealmRemoved(RealmModel realm) {
removeUserSessions(realm, true);
@ -418,18 +372,13 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
private void onClientRemoved(RealmModel realm, ClientModel client, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
Map<String, ClientSessionEntity> map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()))
.reducedWith(new FirstResultReducer())
.execute();
for (Map.Entry<String, ClientSessionEntity> entry : map.entrySet()) {
// detach from userSession
ClientSessionAdapter adapter = wrap(realm, entry.getValue(), offline);
Iterator<Map.Entry<String, SessionEntity>> itr = cache.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).client(client.getId())).iterator();
while (itr.hasNext()) {
ClientSessionEntity entity = (ClientSessionEntity) itr.next().getValue();
ClientSessionAdapter adapter = wrap(realm, entity, offline);
adapter.setUserSession(null);
tx.remove(cache, entry.getKey());
tx.remove(cache, entity.getId());
}
}
@ -491,27 +440,19 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
}
protected void removeUserSession(RealmModel realm, String userSessionId) {
removeUserSession(realm, userSessionId, false);
}
protected void removeUserSession(RealmModel realm, String userSessionId, boolean offline) {
Cache<String, SessionEntity> cache = getCache(offline);
tx.remove(cache, userSessionId);
// TODO: Isn't more effective to retrieve from userSessionEntity directly?
Map<String, String> map = new MapReduceTask(cache)
.mappedWith(ClientSessionMapper.create(realm.getId()).userSession(userSessionId).emitKey())
.reducedWith(new FirstResultReducer())
.execute();
for (String id : map.keySet()) {
tx.remove(cache, id);
UserSessionEntity sessionEntity = (UserSessionEntity) cache.get(userSessionId);
if (sessionEntity.getClientSessions() != null) {
for (String clientSessionId : sessionEntity.getClientSessions()) {
tx.remove(cache, clientSessionId);
}
}
}
InfinispanKeycloakTransaction getTx() {
return tx;
}
@ -522,7 +463,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
List<UserSessionModel> wrapUserSessions(RealmModel realm, Collection<UserSessionEntity> entities, boolean offline) {
List<UserSessionModel> models = new LinkedList<UserSessionModel>();
List<UserSessionModel> models = new LinkedList<>();
for (UserSessionEntity e : entities) {
models.add(wrap(realm, e, offline));
}
@ -553,7 +494,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
List<ClientSessionModel> wrapClientSessions(RealmModel realm, Collection<ClientSessionEntity> entities, boolean offline) {
List<ClientSessionModel> models = new LinkedList<ClientSessionModel>();
List<ClientSessionModel> models = new LinkedList<>();
for (ClientSessionEntity e : entities) {
models.add(wrap(realm, e, offline));
}
@ -600,23 +541,21 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public List<ClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
Map<String, UserSessionEntity> sessions = new MapReduceTask(offlineSessionCache)
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()))
.reducedWith(new FirstResultReducer())
.execute();
Iterator<Map.Entry<String, SessionEntity>> itr = offlineSessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).user(user.getId())).iterator();
List<ClientSessionModel> clientSessions = new LinkedList<>();
List<ClientSessionEntity> clientSessions = new LinkedList<>();
for (UserSessionEntity userSession : sessions.values()) {
Set<String> currClientSessions = userSession.getClientSessions();
while(itr.hasNext()) {
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
Set<String> currClientSessions = entity.getClientSessions();
for (String clientSessionId : currClientSessions) {
ClientSessionEntity cls = (ClientSessionEntity) offlineSessionCache.get(clientSessionId);
if (cls != null) {
clientSessions.add(cls);
clientSessions.add(wrap(realm, cls, true));
}
}
}
return wrapClientSessions(realm, clientSessions, true);
return clientSessions;
}
@Override
@ -626,7 +565,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
@Override
public int getOfflineSessionsCount(RealmModel realm, ClientModel client) {
public long getOfflineSessionsCount(RealmModel realm, ClientModel client) {
return getUserSessionsCount(realm, client, true);
}
@ -721,18 +660,19 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
@Override
public List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm) {
Map<String, ClientInitialAccessEntity> entities = new MapReduceTask(sessionCache)
.mappedWith(ClientInitialAccessMapper.create(realm.getId()))
.reducedWith(new FirstResultReducer())
.execute();
return wrapClientInitialAccess(realm, entities.values());
Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(ClientInitialAccessPredicate.create(realm.getId())).iterator();
List<ClientInitialAccessModel> list = new LinkedList<>();
while (itr.hasNext()) {
list.add(wrap(realm, (ClientInitialAccessEntity) itr.next().getValue()));
}
return list;
}
class InfinispanKeycloakTransaction implements KeycloakTransaction {
private boolean active;
private boolean rollback;
private Map<Object, CacheTask> tasks = new HashMap<Object, CacheTask>();
private Map<Object, CacheTask> tasks = new HashMap<>();
@Override
public void begin() {

View file

@ -47,7 +47,8 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
Cache<String, SessionEntity> cache = connections.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
Cache<String, SessionEntity> offlineSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
Cache<LoginFailureKey, LoginFailureEntity> loginFailures = connections.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
return new InfinispanUserSessionProvider(session, cache, offlineSessionsCache, loginFailures);
return isStreamMode() ? new InfinispanUserSessionProvider(session, cache, offlineSessionsCache, loginFailures) : new CompatInfinispanUserSessionProvider(session, cache, offlineSessionsCache, loginFailures);
} else {
return compatProviderFactory.create(session);
}
@ -147,5 +148,9 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
return false;
}
private boolean isStreamMode() {
return Version.getVersionShort() >= Version.getVersionShort("8.1.0.Final");
}
}

View file

@ -0,0 +1,24 @@
package org.keycloak.models.sessions.infinispan;
import java.io.Serializable;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserSessionTimestamp implements Serializable {
private String userSessionId;
private int clientSessionTimestamp;
public UserSessionTimestamp(String userSessionId, int clientSessionTimestamp) {
this.userSessionId = userSessionId;
this.clientSessionTimestamp = clientSessionTimestamp;
}
public String getUserSessionId() {
return userSessionId;
}
public int getClientSessionTimestamp() {
return clientSessionTimestamp;
}
}

View file

@ -176,17 +176,6 @@ public class MemUserSessionProvider implements UserSessionProvider {
return userSessions;
}
@Override
public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
for (UserSessionEntity s : this.userSessions.values()) {
if (s.getRealm().equals(realm.getId()) && noteValue.equals(s.getNotes().get(noteName))) {
userSessions.add(new UserSessionAdapter(session, this, realm, s));
}
}
return userSessions;
}
@Override
public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
return getUserSessions(realm, client, false);
@ -230,7 +219,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
}
@Override
public int getActiveUserSessions(RealmModel realm, ClientModel client) {
public long getActiveUserSessions(RealmModel realm, ClientModel client) {
return getUserSessions(realm, client, false).size();
}
@ -287,7 +276,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
}
@Override
public void removeExpiredUserSessions(RealmModel realm) {
public void removeExpired(RealmModel realm) {
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
Iterator<UserSessionEntity> itr = userSessions.values().iterator();
@ -565,7 +554,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
}
@Override
public int getOfflineSessionsCount(RealmModel realm, ClientModel client) {
public long getOfflineSessionsCount(RealmModel realm, ClientModel client) {
return getUserSessions(realm, client, true).size();
}

View file

@ -151,13 +151,14 @@ public class InfinispanUserSessionInitializer {
int processors = Runtime.getRuntime().availableProcessors();
ExecutorService localExecutor = Executors.newCachedThreadPool();
DistributedExecutorService distributedExecutorService = new DefaultExecutorService(cache, localExecutor);
Transport transport = cache.getCacheManager().getTransport();
boolean distributed = transport != null;
ExecutorService executorService = distributed ? new DefaultExecutorService(cache, localExecutor) : localExecutor;
int errors = 0;
try {
while (!state.isFinished()) {
Transport transport = cache.getCacheManager().getTransport();
int nodesCount = transport==null ? 1 : transport.getMembers().size();
int distributedWorkersCount = processors * nodesCount;
@ -173,8 +174,11 @@ public class InfinispanUserSessionInitializer {
for (Integer segment : segments) {
SessionInitializerWorker worker = new SessionInitializerWorker();
worker.setWorkerEnvironment(segment, sessionsPerSegment, sessionLoader);
if (!distributed) {
worker.setEnvironment(cache, null);
}
Future<WorkerResult> future = distributedExecutorService.submit(worker);
Future<WorkerResult> future = executorService.submit(worker);
futures.add(future);
}
@ -210,7 +214,9 @@ public class InfinispanUserSessionInitializer {
}
}
} finally {
distributedExecutorService.shutdown();
if (distributed) {
executorService.shutdown();
}
localExecutor.shutdown();
}
}

View file

@ -0,0 +1,59 @@
package org.keycloak.models.sessions.infinispan.stream;
import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Predicate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClientInitialAccessPredicate implements Predicate<Map.Entry<String, SessionEntity>>, Serializable {
public ClientInitialAccessPredicate(String realm) {
this.realm = realm;
}
private String realm;
private Integer expired;
public static ClientInitialAccessPredicate create(String realm) {
return new ClientInitialAccessPredicate(realm);
}
public ClientInitialAccessPredicate expired(int time) {
this.expired = time;
return this;
}
@Override
public boolean test(Map.Entry<String, SessionEntity> entry) {
SessionEntity e = entry.getValue();
if (!realm.equals(e.getRealm())) {
return false;
}
if (!(e instanceof ClientInitialAccessEntity)) {
return false;
}
ClientInitialAccessEntity entity = (ClientInitialAccessEntity) e;
if (expired != null) {
if (entity.getRemainingCount() <= 0) {
return true;
} else if (entity.getExpiration() > 0 && (entity.getTimestamp() + entity.getExpiration()) < expired) {
return true;
} else {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,97 @@
package org.keycloak.models.sessions.infinispan.stream;
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Predicate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ClientSessionPredicate implements Predicate<Map.Entry<String, SessionEntity>>, Serializable {
private String realm;
private String client;
private String userSession;
private Long expiredRefresh;
private Boolean requireUserSession = false;
private Boolean requireNullUserSession = false;
private ClientSessionPredicate(String realm) {
this.realm = realm;
}
public static ClientSessionPredicate create(String realm) {
return new ClientSessionPredicate(realm);
}
public ClientSessionPredicate client(String client) {
this.client = client;
return this;
}
public ClientSessionPredicate userSession(String userSession) {
this.userSession = userSession;
return this;
}
public ClientSessionPredicate expiredRefresh(long expiredRefresh) {
this.expiredRefresh = expiredRefresh;
return this;
}
public ClientSessionPredicate requireUserSession() {
requireUserSession = true;
return this;
}
public ClientSessionPredicate requireNullUserSession() {
requireNullUserSession = true;
return this;
}
@Override
public boolean test(Map.Entry<String, SessionEntity> entry) {
SessionEntity e = entry.getValue();
if (!realm.equals(e.getRealm())) {
return false;
}
if (!(e instanceof ClientSessionEntity)) {
return false;
}
ClientSessionEntity entity = (ClientSessionEntity) e;
if (client != null && !entity.getClient().equals(client)) {
return false;
}
if (userSession != null && !userSession.equals(entity.getUserSession())) {
return false;
}
if (requireUserSession && entity.getUserSession() == null) {
return false;
}
if (requireNullUserSession && entity.getUserSession() != null) {
return false;
}
if (expiredRefresh != null && entity.getTimestamp() > expiredRefresh) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,24 @@
package org.keycloak.models.sessions.infinispan.stream;
import org.keycloak.models.sessions.infinispan.UserSessionTimestamp;
import java.io.Serializable;
import java.util.Comparator;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Comparators {
public static Comparator<UserSessionTimestamp> userSessionTimestamp() {
return new UserSessionTimestampComparator();
}
private static class UserSessionTimestampComparator implements Comparator<UserSessionTimestamp>, Serializable {
@Override
public int compare(UserSessionTimestamp u1, UserSessionTimestamp u2) {
return u1.getClientSessionTimestamp() - u2.getClientSessionTimestamp();
}
}
}

View file

@ -0,0 +1,77 @@
package org.keycloak.models.sessions.infinispan.stream;
import org.keycloak.models.sessions.infinispan.UserSessionTimestamp;
import org.keycloak.models.sessions.infinispan.entities.ClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import java.io.Serializable;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Mappers {
public static Function<Map.Entry<String, SessionEntity>, UserSessionTimestamp> clientSessionToUserSessionTimestamp() {
return new ClientSessionToUserSessionTimestampMapper();
}
public static Function<Map.Entry<String, Optional<UserSessionTimestamp>>, UserSessionTimestamp> userSessionTimestamp() {
return new UserSessionTimestampMapper();
}
public static Function<Map.Entry<String, SessionEntity>, String> sessionId() {
return new SessionIdMapper();
}
public static Function<Map.Entry<LoginFailureKey, LoginFailureEntity>, LoginFailureKey> loginFailureId() {
return new LoginFailureIdMapper();
}
public static Function<Map.Entry<String, SessionEntity>, String> clientSessionToUserSessionId() {
return new ClientSessionToUserSessionIdMapper();
}
private static class ClientSessionToUserSessionTimestampMapper implements Function<Map.Entry<String, SessionEntity>, UserSessionTimestamp>, Serializable {
@Override
public UserSessionTimestamp apply(Map.Entry<String, SessionEntity> entry) {
SessionEntity e = entry.getValue();
ClientSessionEntity entity = (ClientSessionEntity) e;
return new UserSessionTimestamp(entity.getUserSession(), entity.getTimestamp());
}
}
private static class UserSessionTimestampMapper implements Function<Map.Entry<String, Optional<org.keycloak.models.sessions.infinispan.UserSessionTimestamp>>, org.keycloak.models.sessions.infinispan.UserSessionTimestamp>, Serializable {
@Override
public org.keycloak.models.sessions.infinispan.UserSessionTimestamp apply(Map.Entry<String, Optional<org.keycloak.models.sessions.infinispan.UserSessionTimestamp>> e) {
return e.getValue().get();
}
}
private static class SessionIdMapper implements Function<Map.Entry<String, SessionEntity>, String>, Serializable {
@Override
public String apply(Map.Entry<String, SessionEntity> entry) {
return entry.getKey();
}
}
private static class LoginFailureIdMapper implements Function<Map.Entry<LoginFailureKey, LoginFailureEntity>, LoginFailureKey>, Serializable {
@Override
public LoginFailureKey apply(Map.Entry<LoginFailureKey, LoginFailureEntity> entry) {
return entry.getKey();
}
}
private static class ClientSessionToUserSessionIdMapper implements Function<Map.Entry<String, SessionEntity>, String>, Serializable {
@Override
public String apply(Map.Entry<String, SessionEntity> entry) {
SessionEntity e = entry.getValue();
ClientSessionEntity entity = (ClientSessionEntity) e;
return entity.getUserSession();
}
}
}

View file

@ -0,0 +1,29 @@
package org.keycloak.models.sessions.infinispan.stream;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Predicate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class SessionPredicate implements Predicate<Map.Entry<String, SessionEntity>>, Serializable {
private String realm;
private SessionPredicate(String realm) {
this.realm = realm;
}
public static SessionPredicate create(String realm) {
return new SessionPredicate(realm);
}
@Override
public boolean test(Map.Entry<String, SessionEntity> entry) {
return realm.equals(entry.getValue().getRealm());
}
}

View file

@ -0,0 +1,31 @@
package org.keycloak.models.sessions.infinispan.stream;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Predicate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserLoginFailurePredicate implements Predicate<Map.Entry<LoginFailureKey, LoginFailureEntity>>, Serializable {
private String realm;
private UserLoginFailurePredicate(String realm) {
this.realm = realm;
}
public static UserLoginFailurePredicate create(String realm) {
return new UserLoginFailurePredicate(realm);
}
@Override
public boolean test(Map.Entry<LoginFailureKey, LoginFailureEntity> entry) {
LoginFailureEntity e = entry.getValue();
return realm.equals(e.getRealm());
}
}

View file

@ -0,0 +1,91 @@
package org.keycloak.models.sessions.infinispan.stream;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Predicate;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class UserSessionPredicate implements Predicate<Map.Entry<String, SessionEntity>>, Serializable {
private String realm;
private String user;
private Integer expired;
private Integer expiredRefresh;
private String brokerSessionId;
private String brokerUserId;
private UserSessionPredicate(String realm) {
this.realm = realm;
}
public static UserSessionPredicate create(String realm) {
return new UserSessionPredicate(realm);
}
public UserSessionPredicate user(String user) {
this.user = user;
return this;
}
public UserSessionPredicate expired(Integer expired, Integer expiredRefresh) {
this.expired = expired;
this.expiredRefresh = expiredRefresh;
return this;
}
public UserSessionPredicate brokerSessionId(String id) {
this.brokerSessionId = id;
return this;
}
public UserSessionPredicate brokerUserId(String id) {
this.brokerUserId = id;
return this;
}
@Override
public boolean test(Map.Entry<String, SessionEntity> entry) {
SessionEntity e = entry.getValue();
if (!(e instanceof UserSessionEntity)) {
return false;
}
UserSessionEntity entity = (UserSessionEntity) e;
if (!realm.equals(entity.getRealm())) {
return false;
}
if (user != null && !entity.getUser().equals(user)) {
return false;
}
if (brokerSessionId != null && !brokerSessionId.equals(entity.getBrokerSessionId())) {
return false;
}
if (brokerUserId != null && !brokerUserId.equals(entity.getBrokerUserId())) {
return false;
}
if (expired != null && expiredRefresh != null && entity.getStarted() > expired && entity.getLastSessionRefresh() > expiredRefresh) {
return false;
}
if (expired == null && expiredRefresh != null && entity.getLastSessionRefresh() > expiredRefresh) {
return false;
}
return true;
}
}

View file

@ -5,7 +5,7 @@
<parent>
<groupId>org.jboss</groupId>
<artifactId>jboss-parent</artifactId>
<version>16</version>
<version>19</version>
</parent>
<name>Keycloak</name>
@ -49,8 +49,8 @@
<dom4j.version>1.6.1</dom4j.version>
<xml-apis.version>1.4.01</xml-apis.version>
<slf4j.version>1.7.7</slf4j.version>
<wildfly.version>10.0.0.CR4</wildfly.version>
<wildfly.core.version>2.0.1.Final</wildfly.core.version>
<wildfly.version>10.0.0.CR5</wildfly.version>
<wildfly.core.version>2.0.5.Final</wildfly.core.version>
<wildfly.build-tools.version>1.1.0.Final</wildfly.build-tools.version>
<!-- this is EAP 6.4 alpha, publicly available -->
@ -66,7 +66,7 @@
<sun.jaxb.version>2.2.11</sun.jaxb.version>
<sun.xsom.version>20140925</sun.xsom.version>
<javax.mail.version>1.4.5</javax.mail.version>
<infinispan.version>6.0.2.Final</infinispan.version>
<infinispan.version>8.1.0.Final</infinispan.version>
<liquibase.version>3.4.1</liquibase.version>
<jetty9.version>9.1.0.v20131115</jetty9.version>
<osgi.version>4.2.0</osgi.version>

View file

@ -399,9 +399,9 @@ public class ClientResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Integer> getApplicationSessionCount() {
public Map<String, Long> getApplicationSessionCount() {
auth.requireView();
Map<String, Integer> map = new HashMap<String, Integer>();
Map<String, Long> map = new HashMap<>();
map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client));
return map;
}
@ -446,9 +446,9 @@ public class ClientResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Integer> getOfflineSessionCount() {
public Map<String, Long> getOfflineSessionCount() {
auth.requireView();
Map<String, Integer> map = new HashMap<String, Integer>();
Map<String, Long> map = new HashMap<>();
map.put("count", session.sessions().getOfflineSessionsCount(client.getRealm(), client));
return map;
}

View file

@ -369,9 +369,9 @@ public class RealmAdminResource {
auth.requireView();
List<Map<String, String>> data = new LinkedList<Map<String, String>>();
for (ClientModel client : realm.getClients()) {
int size = session.sessions().getActiveUserSessions(client.getRealm(), client);
long size = session.sessions().getActiveUserSessions(client.getRealm(), client);
if (size == 0) continue;
Map<String, String> map = new HashMap<String, String>();
Map<String, String> map = new HashMap<>();
map.put("id", client.getId());
map.put("clientId", client.getClientId());
map.put("active", size + "");

View file

@ -13,7 +13,7 @@ public class ClearExpiredUserSessions implements ScheduledTask {
public void run(KeycloakSession session) {
UserSessionProvider sessions = session.sessions();
for (RealmModel realm : session.realms().getRealms()) {
sessions.removeExpiredUserSessions(realm);
sessions.removeExpired(realm);
}
}

View file

@ -100,6 +100,8 @@ public class AdapterTest {
@Test
public void testLoginSSOAndLogout() throws Exception {
testStrategy.testLoginSSOMax();
testStrategy.testLoginSSOAndLogout();
}

View file

@ -310,7 +310,7 @@ public class AdapterTestStrategy extends ExternalResource {
session = keycloakRule.startSession();
realm = session.realms().getRealmByName("demo");
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
session.getTransaction().commit();
session.close();

View file

@ -286,9 +286,6 @@ public class BruteForceTest {
}
@Test
public void testBrowserInvalidPassword() throws Exception {
loginSuccess();

View file

@ -524,7 +524,7 @@ public class LoginTest {
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
manager.getSession().sessions().removeExpiredUserSessions(appRealm);
manager.getSession().sessions().removeExpired(appRealm);
}
});

View file

@ -347,7 +347,7 @@ public class UserSessionProviderOfflineTest {
resetSession();
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
@ -372,7 +372,7 @@ public class UserSessionProviderOfflineTest {
// Expire everything and assert nothing found
Time.setOffset(3000000);
try {
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();

View file

@ -17,11 +17,7 @@ import org.keycloak.services.managers.UserManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.common.util.Time;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.*;
import static org.junit.Assert.*;
@ -276,7 +272,7 @@ public class UserSessionProviderTest {
resetSession();
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
for (String e : expired) {
@ -309,13 +305,13 @@ public class UserSessionProviderTest {
resetSession();
Time.setOffset(25);
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
assertNotNull(session.sessions().getClientSession(clientSessionId));
Time.setOffset(35);
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
assertNull(session.sessions().getClientSession(clientSessionId));
@ -328,13 +324,13 @@ public class UserSessionProviderTest {
resetSession();
Time.setOffset(35);
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
assertNotNull(session.sessions().getClientSession(clientSessionId));
Time.setOffset(45);
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
assertNull(session.sessions().getClientSession(clientSessionId));
@ -347,13 +343,13 @@ public class UserSessionProviderTest {
resetSession();
Time.setOffset(45);
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
assertNotNull(session.sessions().getClientSession(clientSessionId));
Time.setOffset(55);
session.sessions().removeExpiredUserSessions(realm);
session.sessions().removeExpired(realm);
resetSession();
assertNull(session.sessions().getClientSession(clientSessionId));
@ -463,6 +459,18 @@ public class UserSessionProviderTest {
failure1 = session.sessions().getUserLoginFailure(realm, "user1");
assertEquals(0, failure1.getNumFailures());
session.sessions().removeUserLoginFailure(realm, "user1");
resetSession();
assertNull(session.sessions().getUserLoginFailure(realm, "user1"));
session.sessions().removeAllUserLoginFailures(realm);
resetSession();
assertNull(session.sessions().getUserLoginFailure(realm, "user2"));
}
@Test

View file

@ -263,7 +263,7 @@ public class OfflineTokenTest {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
manager.getSession().sessions().removeExpiredUserSessions(appRealm);
manager.getSession().sessions().removeExpired(appRealm);
}
});