keycloak-memoizeit
Changes
.travis.yml 10(+9 -1)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/subsystems.xml 59(+0 -59)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml 34(+10 -24)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml 110(+110 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml 85(+85 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml 101(+101 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/subsystems.xml 29(+29 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml 52(+26 -26)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml 17(+9 -8)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/MemUserSessionProvider.java 17(+3 -14)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CompatInfinispanUserSessionProvider.java 387(+387 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/Consumers.java 66(+66 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java 356(+148 -208)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java 7(+6 -1)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/initializer/InfinispanUserSessionInitializer.java 14(+10 -4)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientInitialAccessPredicate.java 59(+59 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientSessionPredicate.java 97(+97 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Comparators.java 24(+24 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java 77(+77 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/SessionPredicate.java 29(+29 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java 31(+31 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java 91(+91 -0)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionTimestamp.java 24(+24 -0)
pom.xml 8(+4 -4)
testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java 4(+2 -2)
Details
.travis.yml 10(+9 -1)
diff --git a/.travis.yml b/.travis.yml
index b2b3627..3da0679 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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
diff --git a/distribution/feature-packs/server-feature-pack/feature-pack-build.xml b/distribution/feature-packs/server-feature-pack/feature-pack-build.xml
index f920885..6ef7e7b 100644
--- a/distribution/feature-packs/server-feature-pack/feature-pack-build.xml
+++ b/distribution/feature-packs/server-feature-pack/feature-pack-build.xml
@@ -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>
@@ -33,4 +66,4 @@
<filter pattern="*.conf" include="true"/>
</unix>
</line-endings>
-</build>
\ No newline at end of file
+</build>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/subsystems.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/subsystems.xml
index 29c6e63..bea2e10 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/subsystems.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/subsystems.xml
@@ -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>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml
index 8c4464c..770d261 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/domain/template.xml
@@ -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>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml
new file mode 100644
index 0000000..501fae8
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host.xml
@@ -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>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml
new file mode 100644
index 0000000..5d959bd
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-master.xml
@@ -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>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml
new file mode 100644
index 0000000..4b79b0a
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/host-slave.xml
@@ -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>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/subsystems.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/subsystems.xml
new file mode 100644
index 0000000..3df2a95
--- /dev/null
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/host/subsystems.xml
@@ -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>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml
index 151ab71..bf84e79 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml
@@ -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>
\ No newline at end of file
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml
index 76fbe9e..4df055c 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/template.xml
@@ -29,15 +29,15 @@
</security-realms>
<audit-log>
<formatters>
- <json-formatter name="json-formatter"/>
- </formatters>
+ <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}">
@@ -80,4 +81,4 @@
<?SOCKET-BINDINGS?>
</socket-binding-group>
-</server>
\ No newline at end of file
+</server>
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java b/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
index 0c1df9c..f2acf49 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -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
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/MemUserSessionProvider.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/MemUserSessionProvider.java
index db20ef8..693db67 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/MemUserSessionProvider.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/MemUserSessionProvider.java
@@ -177,17 +177,6 @@ public class MemUserSessionProvider implements UserSessionProvider {
}
@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();
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CompatInfinispanUserSessionProvider.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CompatInfinispanUserSessionProvider.java
new file mode 100644
index 0000000..bcd72e7
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/CompatInfinispanUserSessionProvider.java
@@ -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());
+ }
+
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/Consumers.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/Consumers.java
new file mode 100644
index 0000000..0eed330
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/Consumers.java
@@ -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;
+ }
+
+ }
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index d89f47b..7e66b12 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -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());
-
- 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;
+ 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);
}
+ }
- if (maxResults == -1) {
- maxResults = Integer.MAX_VALUE;
- }
+ Stream<UserSessionTimestamp> stream = new LinkedList<>(m.values()).stream().sorted(Comparators.userSessionTimestamp());
- if (firstResult > sessionTimestamps.size()) {
- return Collections.emptyList();
- }
-
- int toIndex = (firstResult + maxResults) < sessionTimestamps.size() ? firstResult + maxResults : sessionTimestamps.size();
- sessionTimestamps = sessionTimestamps.subList(firstResult, toIndex);
+ if (firstResult > 0) {
+ stream = stream.skip(firstResult);
}
- 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));
- }
+ if (maxResults > 0) {
+ stream = stream.limit(maxResults);
}
- return userSessions;
- }
-
- @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);
+ final List<UserSessionModel> sessions = new LinkedList<>();
+ stream.forEach(new Consumer<UserSessionTimestamp>() {
+ @Override
+ public void accept(UserSessionTimestamp userSessionTimestamp) {
+ SessionEntity entity = cache.get(userSessionTimestamp.getUserSessionId());
+ if (entity != null) {
+ sessions.add(wrap(realm, (UserSessionEntity) entity, offline));
+ }
+ }
+ });
+ return sessions;
}
@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();
- 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();
+ 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());
- for (String id : map.keySet()) {
- removeUserSession(realm, id);
+ if (entity.getClientSessions() != null) {
+ for (String clientSessionId : entity.getClientSessions()) {
+ tx.remove(sessionCache, clientSessionId);
+ }
+ }
}
+ }
- map = new MapReduceTask(sessionCache)
- .mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession(true).emitKey())
- .reducedWith(new FirstResultReducer())
- .execute();
+ private void removeExpiredClientSessions(RealmModel realm) {
+ int expiredDettachedClientSession = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
- for (String id : map.keySet()) {
- tx.remove(sessionCache, 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());
}
+ }
+
+ private void removeExpiredOfflineUserSessions(RealmModel realm) {
+ UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
+ int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
- // Remove expired offline user sessions
- Map<String, SessionEntity> map2 = new MapReduceTask(offlineSessionCache)
- .mappedWith(UserSessionMapper.create(realm.getId()).expired(null, expiredOffline))
- .reducedWith(new FirstResultReducer())
- .execute();
+ 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());
- for (Map.Entry<String, SessionEntity> entry : map2.entrySet()) {
- String userSessionId = entry.getKey();
- tx.remove(offlineSessionCache, userSessionId);
- // Propagate to persister
- persister.removeUserSession(userSessionId, true);
+ persister.removeUserSession(entity.getId(), 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();
-
- List<ClientSessionEntity> clientSessions = new LinkedList<>();
- for (UserSessionEntity userSession : sessions.values()) {
- Set<String> currClientSessions = userSession.getClientSessions();
+ Iterator<Map.Entry<String, SessionEntity>> itr = offlineSessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).user(user.getId())).iterator();
+ List<ClientSessionModel> clientSessions = new LinkedList<>();
+
+ 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() {
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
index 382d01f..e18aa84 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java
@@ -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");
+ }
+
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/initializer/InfinispanUserSessionInitializer.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/initializer/InfinispanUserSessionInitializer.java
index b368fd3..2a9058d 100644
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/initializer/InfinispanUserSessionInitializer.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/initializer/InfinispanUserSessionInitializer.java
@@ -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();
}
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientInitialAccessPredicate.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientInitialAccessPredicate.java
new file mode 100644
index 0000000..22518ef
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientInitialAccessPredicate.java
@@ -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;
+ }
+
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientSessionPredicate.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientSessionPredicate.java
new file mode 100644
index 0000000..490b8f4
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientSessionPredicate.java
@@ -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;
+ }
+
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Comparators.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Comparators.java
new file mode 100644
index 0000000..ba7fd02
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Comparators.java
@@ -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();
+ }
+ }
+
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java
new file mode 100644
index 0000000..3164f6d
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/Mappers.java
@@ -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();
+ }
+ }
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/SessionPredicate.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/SessionPredicate.java
new file mode 100755
index 0000000..6dd5540
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/SessionPredicate.java
@@ -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());
+ }
+
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java
new file mode 100755
index 0000000..fb0fb7e
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserLoginFailurePredicate.java
@@ -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());
+ }
+
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java
new file mode 100644
index 0000000..2add049
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/UserSessionPredicate.java
@@ -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;
+ }
+}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionTimestamp.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionTimestamp.java
new file mode 100644
index 0000000..3284e43
--- /dev/null
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionTimestamp.java
@@ -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;
+ }
+}
pom.xml 8(+4 -4)
diff --git a/pom.xml b/pom.xml
index b2aa2f4..03e1416 100755
--- a/pom.xml
+++ b/pom.xml
@@ -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>
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index 03b0636..73ebe44 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -383,9 +383,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;
}
@@ -430,9 +430,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;
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 9d144e4..cb97855 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -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 + "");
diff --git a/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
index 96c8bad..8f6b91c 100755
--- a/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
+++ b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredUserSessions.java
@@ -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);
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 5d02107..08529ca 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -100,6 +100,8 @@ public class AdapterTest {
@Test
public void testLoginSSOAndLogout() throws Exception {
+ testStrategy.testLoginSSOMax();
+
testStrategy.testLoginSSOAndLogout();
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index 1e60096..f93c4eb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -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();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java
index 4d43897..19beaf6 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/BruteForceTest.java
@@ -286,9 +286,6 @@ public class BruteForceTest {
}
-
-
-
@Test
public void testBrowserInvalidPassword() throws Exception {
loginSuccess();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
index 30d94fa..c12e6fe 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
@@ -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);
}
});
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
index ab93b1c..147ee86 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
@@ -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();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 88e7597..8106458 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -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
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
index caa61ed..1af7515 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
@@ -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);
}
});