Merge pull request #1981 from stianst/master

KEYCLOAK-2247 Upgrade to WildFly 10.0.0.CR5
This commit is contained in:
Stian Thorgersen 2016-01-06 19:19:35 +01:00
commit 084873f7fb
36 changed files with 1511 additions and 387 deletions

View file

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

View file

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

View file

@ -53,63 +53,4 @@
<subsystem supplement="ha">undertow.xml</subsystem> <subsystem supplement="ha">undertow.xml</subsystem>
<subsystem>keycloak-server.xml</subsystem> <subsystem>keycloak-server.xml</subsystem>
</subsystems> </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> </config>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -47,7 +47,8 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
Cache<String, SessionEntity> cache = connections.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME); Cache<String, SessionEntity> cache = connections.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
Cache<String, SessionEntity> offlineSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_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); 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 { } else {
return compatProviderFactory.create(session); return compatProviderFactory.create(session);
} }
@ -147,5 +148,9 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
return false; return false;
} }
private boolean isStreamMode() {
return Version.getVersionShort() >= Version.getVersionShort("8.1.0.Final");
}
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -383,9 +383,9 @@ public class ClientResource {
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Map<String, Integer> getApplicationSessionCount() { public Map<String, Long> getApplicationSessionCount() {
auth.requireView(); 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)); map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client));
return map; return map;
} }
@ -430,9 +430,9 @@ public class ClientResource {
@GET @GET
@NoCache @NoCache
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public Map<String, Integer> getOfflineSessionCount() { public Map<String, Long> getOfflineSessionCount() {
auth.requireView(); 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)); map.put("count", session.sessions().getOfflineSessionsCount(client.getRealm(), client));
return map; return map;
} }

View file

@ -369,9 +369,9 @@ public class RealmAdminResource {
auth.requireView(); auth.requireView();
List<Map<String, String>> data = new LinkedList<Map<String, String>>(); List<Map<String, String>> data = new LinkedList<Map<String, String>>();
for (ClientModel client : realm.getClients()) { 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; 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("id", client.getId());
map.put("clientId", client.getClientId()); map.put("clientId", client.getClientId());
map.put("active", size + ""); map.put("active", size + "");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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