Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
71b6ed80ae
36 changed files with 1511 additions and 387 deletions
10
.travis.yml
10
.travis.yml
|
@ -1,10 +1,18 @@
|
|||
language: java
|
||||
|
||||
env:
|
||||
global:
|
||||
- MAVEN_SKIP_RC=true
|
||||
- MAVEN_OPTS="-Xms512m -Xmx2048m"
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
before_script:
|
||||
- export MAVEN_SKIP_RC=true
|
||||
|
||||
install:
|
||||
- travis_wait mvn install -Pdistribution -DskipTests=true -B -V -q
|
||||
- mvn install -Pdistribution -DskipTests=true -B -V -q
|
||||
|
||||
script:
|
||||
- mvn test -B
|
||||
|
|
|
@ -1,4 +1,26 @@
|
|||
<build xmlns="urn:wildfly:feature-pack-build:1.0">
|
||||
<!--
|
||||
~ JBoss, Home of Professional Open Source.
|
||||
~ Copyright 2014, Red Hat, Inc., and individual contributors
|
||||
~ as indicated by the @author tags. See the copyright.txt file in the
|
||||
~ distribution for a full listing of individual contributors.
|
||||
~
|
||||
~ This is free software; you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU Lesser General Public License as
|
||||
~ published by the Free Software Foundation; either version 2.1 of
|
||||
~ the License, or (at your option) any later version.
|
||||
~
|
||||
~ This software is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
~ Lesser General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Lesser General Public
|
||||
~ License along with this software; if not, write to the Free
|
||||
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
-->
|
||||
|
||||
<build xmlns="urn:wildfly:feature-pack-build:1.1">
|
||||
<dependencies>
|
||||
<artifact name="org.wildfly:wildfly-feature-pack" />
|
||||
</dependencies>
|
||||
|
@ -8,20 +30,31 @@
|
|||
<property name="jgroups.supplement" value="" />
|
||||
</standalone>
|
||||
<domain template="configuration/domain/template.xml" subsystems="configuration/domain/subsystems.xml" output-file="domain/configuration/domain.xml" />
|
||||
<host template="configuration/host/host.xml" subsystems="configuration/host/subsystems.xml" output-file="domain/configuration/host.xml" />
|
||||
<host template="configuration/host/host-master.xml" subsystems="configuration/host/subsystems.xml" output-file="domain/configuration/host-master.xml" />
|
||||
<host template="configuration/host/host-slave.xml" subsystems="configuration/host/subsystems.xml" output-file="domain/configuration/host-slave.xml" />
|
||||
</config>
|
||||
|
||||
<mkdirs>
|
||||
<dir name="domain/data/content"/>
|
||||
<dir name="standalone/lib/ext"/>
|
||||
<dir name="domain/tmp/auth"/>
|
||||
<dir name="standalone/tmp/auth"/>
|
||||
<dir name=".installation"/>
|
||||
</mkdirs>
|
||||
<file-permissions>
|
||||
<permission value="755">
|
||||
<filter pattern="*.sh" include="true"/>
|
||||
<filter pattern="*" include="false"/>
|
||||
</permission>
|
||||
<permission value="700">
|
||||
<filter pattern="*/tmp/auth" include="true"/>
|
||||
<filter pattern="*" include="false"/>
|
||||
</permission>
|
||||
<permission value="600">
|
||||
<filter pattern="*-users.properties" include="true" />
|
||||
<filter pattern="*/.installation" include="true"/>
|
||||
<filter pattern="*" include="false"/>
|
||||
</permission>
|
||||
</file-permissions>
|
||||
<line-endings>
|
||||
|
@ -33,4 +66,4 @@
|
|||
<filter pattern="*.conf" include="true"/>
|
||||
</unix>
|
||||
</line-endings>
|
||||
</build>
|
||||
</build>
|
||||
|
|
|
@ -53,63 +53,4 @@
|
|||
<subsystem supplement="ha">undertow.xml</subsystem>
|
||||
<subsystem>keycloak-server.xml</subsystem>
|
||||
</subsystems>
|
||||
<subsystems name="full">
|
||||
<!-- Each subsystem to be included relative to the src/main/resources directory -->
|
||||
<subsystem>logging.xml</subsystem>
|
||||
<subsystem>bean-validation.xml</subsystem>
|
||||
<subsystem>keycloak-datasources.xml</subsystem>
|
||||
<subsystem supplement="full">ee.xml</subsystem>
|
||||
<subsystem supplement="full">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem>keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>iiop-openjdk.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
<subsystem supplement="domain">jmx.xml</subsystem>
|
||||
<subsystem>jpa.xml</subsystem>
|
||||
<subsystem>jsf.xml</subsystem>
|
||||
<subsystem>jsr77.xml</subsystem>
|
||||
<subsystem>mail.xml</subsystem>
|
||||
<subsystem>messaging.xml</subsystem>
|
||||
<subsystem>naming.xml</subsystem>
|
||||
<subsystem>remoting.xml</subsystem>
|
||||
<subsystem>request-controller.xml</subsystem>
|
||||
<subsystem>security.xml</subsystem>
|
||||
<subsystem>security-manager.xml</subsystem>
|
||||
<subsystem>transactions.xml</subsystem>
|
||||
<subsystem>undertow.xml</subsystem>
|
||||
<subsystem>keycloak-server.xml</subsystem>
|
||||
</subsystems>
|
||||
<subsystems name="full-ha">
|
||||
<!-- Each subsystem to be included relative to the src/main/resources directory -->
|
||||
<subsystem>logging.xml</subsystem>
|
||||
<subsystem>bean-validation.xml</subsystem>
|
||||
<subsystem>keycloak-datasources.xml</subsystem>
|
||||
<subsystem supplement="full">ee.xml</subsystem>
|
||||
<subsystem supplement="full-ha">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>iiop-openjdk.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
<subsystem>jgroups.xml</subsystem>
|
||||
<subsystem supplement="domain">jmx.xml</subsystem>
|
||||
<subsystem>jpa.xml</subsystem>
|
||||
<subsystem>jsf.xml</subsystem>
|
||||
<subsystem>jsr77.xml</subsystem>
|
||||
<subsystem>mail.xml</subsystem>
|
||||
<subsystem supplement="ha">messaging.xml</subsystem>
|
||||
<subsystem>mod_cluster.xml</subsystem>
|
||||
<subsystem>naming.xml</subsystem>
|
||||
<subsystem>remoting.xml</subsystem>
|
||||
<subsystem>resource-adapters.xml</subsystem>
|
||||
<subsystem>request-controller.xml</subsystem>
|
||||
<subsystem>security.xml</subsystem>
|
||||
<subsystem>security-manager.xml</subsystem>
|
||||
<subsystem>transactions.xml</subsystem>
|
||||
<subsystem supplement="ha">undertow.xml</subsystem>
|
||||
<subsystem>keycloak-server.xml</subsystem>
|
||||
</subsystems>
|
||||
</config>
|
||||
|
|
|
@ -24,18 +24,12 @@
|
|||
</management>
|
||||
|
||||
<profiles>
|
||||
<profile name="default">
|
||||
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
|
||||
</profile>
|
||||
<profile name="ha">
|
||||
<?SUBSYSTEMS socket-binding-group="ha-sockets"?>
|
||||
</profile>
|
||||
<profile name="full">
|
||||
<?SUBSYSTEMS socket-binding-group="full-sockets"?>
|
||||
</profile>
|
||||
<profile name="full-ha">
|
||||
<?SUBSYSTEMS socket-binding-group="full-ha-sockets"?>
|
||||
</profile>
|
||||
<profile name="default">
|
||||
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
|
||||
</profile>
|
||||
<profile name="ha">
|
||||
<?SUBSYSTEMS socket-binding-group="ha-sockets"?>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<!--
|
||||
|
@ -60,28 +54,20 @@
|
|||
<!-- Needed for server groups using the 'ha' profile -->
|
||||
<?SOCKET-BINDINGS?>
|
||||
</socket-binding-group>
|
||||
<socket-binding-group name="full-sockets" default-interface="public">
|
||||
<!-- Needed for server groups using the 'full' profile -->
|
||||
<?SOCKET-BINDINGS?>
|
||||
</socket-binding-group>
|
||||
<socket-binding-group name="full-ha-sockets" default-interface="public">
|
||||
<!-- Needed for server groups using the 'full-ha' profile -->
|
||||
<?SOCKET-BINDINGS?>
|
||||
</socket-binding-group>
|
||||
</socket-binding-groups>
|
||||
|
||||
<server-groups>
|
||||
<server-group name="main-server-group" profile="full">
|
||||
<server-group name="main-server-group" profile="default">
|
||||
<jvm name="default">
|
||||
<heap size="64m" max-size="512m"/>
|
||||
</jvm>
|
||||
<socket-binding-group ref="full-sockets"/>
|
||||
<socket-binding-group ref="standard-sockets"/>
|
||||
</server-group>
|
||||
<server-group name="other-server-group" profile="full-ha">
|
||||
<server-group name="other-server-group" profile="ha">
|
||||
<jvm name="default">
|
||||
<heap size="64m" max-size="512m"/>
|
||||
</jvm>
|
||||
<socket-binding-group ref="full-ha-sockets"/>
|
||||
<socket-binding-group ref="ha-sockets"/>
|
||||
</server-group>
|
||||
</server-groups>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,30 +1,30 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
|
||||
<config>
|
||||
<subsystems>
|
||||
<subsystem>logging.xml</subsystem>
|
||||
<subsystem>bean-validation.xml</subsystem>
|
||||
<subsystem>keycloak-datasources.xml</subsystem>
|
||||
<subsystem>ee.xml</subsystem>
|
||||
<subsystem supplement="ha">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
<subsystem>jgroups.xml</subsystem>
|
||||
<subsystem>jmx.xml</subsystem>
|
||||
<subsystem>jpa.xml</subsystem>
|
||||
<subsystem>jsf.xml</subsystem>
|
||||
<subsystem>mail.xml</subsystem>
|
||||
<subsystem>mod_cluster.xml</subsystem>
|
||||
<subsystem>naming.xml</subsystem>
|
||||
<subsystem>remoting.xml</subsystem>
|
||||
<subsystem>request-controller.xml</subsystem>
|
||||
<subsystem>security-manager.xml</subsystem>
|
||||
<subsystem>security.xml</subsystem>
|
||||
<subsystem>transactions.xml</subsystem>
|
||||
<subsystem supplement="ha">undertow.xml</subsystem>
|
||||
<subsystem>keycloak-server.xml</subsystem>
|
||||
</subsystems>
|
||||
<subsystems>
|
||||
<subsystem>logging.xml</subsystem>
|
||||
<subsystem>bean-validation.xml</subsystem>
|
||||
<subsystem>keycloak-datasources.xml</subsystem>
|
||||
<subsystem>ee.xml</subsystem>
|
||||
<subsystem supplement="ha">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
<subsystem>jgroups.xml</subsystem>
|
||||
<subsystem>jmx.xml</subsystem>
|
||||
<subsystem>jpa.xml</subsystem>
|
||||
<subsystem>jsf.xml</subsystem>
|
||||
<subsystem>mail.xml</subsystem>
|
||||
<subsystem>mod_cluster.xml</subsystem>
|
||||
<subsystem>naming.xml</subsystem>
|
||||
<subsystem>remoting.xml</subsystem>
|
||||
<subsystem>request-controller.xml</subsystem>
|
||||
<subsystem>security-manager.xml</subsystem>
|
||||
<subsystem>security.xml</subsystem>
|
||||
<subsystem>transactions.xml</subsystem>
|
||||
<subsystem supplement="ha">undertow.xml</subsystem>
|
||||
<subsystem>keycloak-server.xml</subsystem>
|
||||
</subsystems>
|
||||
</config>
|
|
@ -29,15 +29,15 @@
|
|||
</security-realms>
|
||||
<audit-log>
|
||||
<formatters>
|
||||
<json-formatter name="json-formatter"/>
|
||||
</formatters>
|
||||
<json-formatter name="json-formatter"/>
|
||||
</formatters>
|
||||
<handlers>
|
||||
<file-handler name="file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
|
||||
<file-handler name="file" formatter="json-formatter" relative-to="jboss.server.data.dir" path="audit-log.log"/>
|
||||
</handlers>
|
||||
<logger log-boot="true" log-read-only="false" enabled="false">
|
||||
<handlers>
|
||||
<handler name="file"/>
|
||||
</handlers>
|
||||
<handlers>
|
||||
<handler name="file"/>
|
||||
</handlers>
|
||||
</logger>
|
||||
</audit-log>
|
||||
<management-interfaces>
|
||||
|
@ -69,8 +69,9 @@
|
|||
<interface name="public">
|
||||
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
|
||||
</interface>
|
||||
<!-- TODO - only show this if the jacorb subsystem is added -->
|
||||
|
||||
<?INTERFACES?>
|
||||
|
||||
</interfaces>
|
||||
|
||||
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
|
||||
|
@ -80,4 +81,4 @@
|
|||
<?SOCKET-BINDINGS?>
|
||||
|
||||
</socket-binding-group>
|
||||
</server>
|
||||
</server>
|
||||
|
|
|
@ -23,14 +23,12 @@ public interface UserSessionProvider extends Provider {
|
|||
List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId);
|
||||
UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId);
|
||||
|
||||
List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue);
|
||||
|
||||
int getActiveUserSessions(RealmModel realm, ClientModel client);
|
||||
long getActiveUserSessions(RealmModel realm, ClientModel client);
|
||||
void removeUserSession(RealmModel realm, UserSessionModel session);
|
||||
void removeUserSessions(RealmModel realm, UserModel user);
|
||||
|
||||
// Implementation should propagate removal of expired userSessions to userSessionPersister too
|
||||
void removeExpiredUserSessions(RealmModel realm);
|
||||
void removeExpired(RealmModel realm);
|
||||
void removeUserSessions(RealmModel realm);
|
||||
void removeClientSession(RealmModel realm, ClientSessionModel clientSession);
|
||||
|
||||
|
@ -56,7 +54,7 @@ public interface UserSessionProvider extends Provider {
|
|||
// Don't remove userSession even if it's last userSession
|
||||
void removeOfflineClientSession(RealmModel realm, String clientSessionId);
|
||||
|
||||
int getOfflineSessionsCount(RealmModel realm, ClientModel client);
|
||||
long getOfflineSessionsCount(RealmModel realm, ClientModel client);
|
||||
List<UserSessionModel> getOfflineUserSessions(RealmModel realm, ClientModel client, int first, int max);
|
||||
|
||||
// Triggered by persister during pre-load
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,25 +1,21 @@
|
|||
package org.keycloak.models.sessions.infinispan;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.distexec.mapreduce.MapReduceTask;
|
||||
import org.infinispan.CacheStream;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
import org.keycloak.models.sessions.infinispan.entities.*;
|
||||
import org.keycloak.models.sessions.infinispan.mapreduce.*;
|
||||
import org.keycloak.models.sessions.infinispan.stream.*;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RealmInfoUtil;
|
||||
import org.keycloak.common.util.Time;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -28,11 +24,11 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
private static final Logger log = Logger.getLogger(InfinispanUserSessionProvider.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final Cache<String, SessionEntity> sessionCache;
|
||||
private final Cache<String, SessionEntity> offlineSessionCache;
|
||||
private final Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache;
|
||||
private final InfinispanKeycloakTransaction tx;
|
||||
protected final KeycloakSession session;
|
||||
protected final Cache<String, SessionEntity> sessionCache;
|
||||
protected final Cache<String, SessionEntity> offlineSessionCache;
|
||||
protected final Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache;
|
||||
protected final InfinispanKeycloakTransaction tx;
|
||||
|
||||
public InfinispanUserSessionProvider(KeycloakSession session, Cache<String, SessionEntity> sessionCache, Cache<String, SessionEntity> offlineSessionCache,
|
||||
Cache<LoginFailureKey, LoginFailureEntity> loginFailureCache) {
|
||||
|
@ -139,36 +135,31 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return wrap(realm, entity, offline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessions(RealmModel realm, UserModel user) {
|
||||
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
protected List<UserSessionModel> getUserSessions(RealmModel realm, Predicate<Map.Entry<String, SessionEntity>> predicate, boolean offline) {
|
||||
CacheStream<Map.Entry<String, SessionEntity>> cacheStream = getCache(offline).entrySet().stream();
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = cacheStream.filter(predicate).iterator();
|
||||
List<UserSessionModel> sessions = new LinkedList<>();
|
||||
while (itr.hasNext()) {
|
||||
UserSessionEntity e = (UserSessionEntity) itr.next().getValue();
|
||||
sessions.add(wrap(realm, e, offline));
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
return wrapUserSessions(realm, sessions.values(), false);
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessions(final RealmModel realm, UserModel user) {
|
||||
return getUserSessions(realm, UserSessionPredicate.create(realm.getId()).user(user.getId()), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessionByBrokerUserId(RealmModel realm, String brokerUserId) {
|
||||
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).brokerUserId(brokerUserId))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
return wrapUserSessions(realm, sessions.values(), false);
|
||||
return getUserSessions(realm, UserSessionPredicate.create(realm.getId()).brokerUserId(brokerUserId), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSessionModel getUserSessionByBrokerSessionId(RealmModel realm, String brokerSessionId) {
|
||||
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).brokerSessionId(brokerSessionId))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
List<UserSessionModel> userSessionModels = wrapUserSessions(realm, sessions.values(), false);
|
||||
if (userSessionModels.isEmpty()) return null;
|
||||
return userSessionModels.get(0);
|
||||
List<UserSessionModel> userSessions = getUserSessions(realm, UserSessionPredicate.create(realm.getId()).brokerSessionId(brokerSessionId), false);
|
||||
return userSessions.isEmpty() ? null : userSessions.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -181,86 +172,58 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
return getUserSessions(realm, client, firstResult, maxResults, false);
|
||||
}
|
||||
|
||||
protected List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client, int firstResult, int maxResults, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
protected List<UserSessionModel> getUserSessions(final RealmModel realm, ClientModel client, int firstResult, int maxResults, final boolean offline) {
|
||||
final Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
Map<String, Integer> map = new MapReduceTask(cache)
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()).emitUserSessionAndTimestamp())
|
||||
.reducedWith(new LargestResultReducer())
|
||||
.execute();
|
||||
Iterator<UserSessionTimestamp> itr = cache.entrySet().stream()
|
||||
.filter(ClientSessionPredicate.create(realm.getId()).client(client.getId()).requireUserSession())
|
||||
.map(Mappers.clientSessionToUserSessionTimestamp())
|
||||
.iterator();
|
||||
|
||||
List<Map.Entry<String, Integer>> sessionTimestamps = new LinkedList<Map.Entry<String, Integer>>(map.entrySet());
|
||||
Map<String, UserSessionTimestamp> m = new HashMap<>();
|
||||
while(itr.hasNext()) {
|
||||
UserSessionTimestamp next = itr.next();
|
||||
if (!m.containsKey(next.getUserSessionId()) || m.get(next.getUserSessionId()).getClientSessionTimestamp() < next.getClientSessionTimestamp()) {
|
||||
m.put(next.getUserSessionId(), next);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(sessionTimestamps, new Comparator<Map.Entry<String, Integer>>() {
|
||||
Stream<UserSessionTimestamp> stream = new LinkedList<>(m.values()).stream().sorted(Comparators.userSessionTimestamp());
|
||||
|
||||
if (firstResult > 0) {
|
||||
stream = stream.skip(firstResult);
|
||||
}
|
||||
|
||||
if (maxResults > 0) {
|
||||
stream = stream.limit(maxResults);
|
||||
}
|
||||
|
||||
final List<UserSessionModel> sessions = new LinkedList<>();
|
||||
stream.forEach(new Consumer<UserSessionTimestamp>() {
|
||||
@Override
|
||||
public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
|
||||
return e1.getValue().compareTo(e2.getValue());
|
||||
public void accept(UserSessionTimestamp userSessionTimestamp) {
|
||||
SessionEntity entity = cache.get(userSessionTimestamp.getUserSessionId());
|
||||
if (entity != null) {
|
||||
sessions.add(wrap(realm, (UserSessionEntity) entity, offline));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (firstResult != -1 || maxResults == -1) {
|
||||
if (firstResult == -1) {
|
||||
firstResult = 0;
|
||||
}
|
||||
|
||||
if (maxResults == -1) {
|
||||
maxResults = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
if (firstResult > sessionTimestamps.size()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
int toIndex = (firstResult + maxResults) < sessionTimestamps.size() ? firstResult + maxResults : sessionTimestamps.size();
|
||||
sessionTimestamps = sessionTimestamps.subList(firstResult, toIndex);
|
||||
}
|
||||
|
||||
List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
|
||||
for (Map.Entry<String, Integer> e : sessionTimestamps) {
|
||||
UserSessionEntity userSessionEntity = (UserSessionEntity) cache.get(e.getKey());
|
||||
if (userSessionEntity != null) {
|
||||
userSessions.add(wrap(realm, userSessionEntity, offline));
|
||||
}
|
||||
}
|
||||
|
||||
return userSessions;
|
||||
return sessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
|
||||
HashMap<String, String> notes = new HashMap<>();
|
||||
notes.put(noteName, noteValue);
|
||||
return getUserSessionsByNotes(realm, notes);
|
||||
}
|
||||
|
||||
public List<UserSessionModel> getUserSessionsByNotes(RealmModel realm, Map<String, String> notes) {
|
||||
Map<String, UserSessionEntity> sessions = new MapReduceTask(sessionCache)
|
||||
.mappedWith(UserSessionNoteMapper.create(realm.getId()).notes(notes))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
return wrapUserSessions(realm, sessions.values(), false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveUserSessions(RealmModel realm, ClientModel client) {
|
||||
public long getActiveUserSessions(RealmModel realm, ClientModel client) {
|
||||
return getUserSessionsCount(realm, client, false);
|
||||
}
|
||||
|
||||
protected int getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
Map map = new MapReduceTask(cache)
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()).emitUserSessionAndTimestamp())
|
||||
.reducedWith(new LargestResultReducer()).execute();
|
||||
|
||||
return map.size();
|
||||
protected long getUserSessionsCount(RealmModel realm, ClientModel client, boolean offline) {
|
||||
return getCache(offline).entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).client(client.getId()).requireUserSession()).map(Mappers.clientSessionToUserSessionId()).distinct().count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserSession(RealmModel realm, UserSessionModel session) {
|
||||
removeUserSession(realm, session.getId());
|
||||
removeUserSession(realm, session.getId(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -271,80 +234,81 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
protected void removeUserSessions(RealmModel realm, UserModel user, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
Map<String, String> sessions = new MapReduceTask(cache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
for (String id : sessions.keySet()) {
|
||||
removeUserSession(realm, id, offline);
|
||||
Iterator<String> itr = cache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).user(user.getId())).map(Mappers.sessionId()).iterator();
|
||||
while (itr.hasNext()) {
|
||||
removeUserSession(realm, itr.next(), offline);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeExpiredUserSessions(RealmModel realm) {
|
||||
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
public void removeExpired(RealmModel realm) {
|
||||
removeExpiredUserSessions(realm);
|
||||
removeExpiredClientSessions(realm);
|
||||
removeExpiredOfflineUserSessions(realm);
|
||||
removeExpiredOfflineClientSessions(realm);
|
||||
removeExpiredClientInitialAccess(realm);
|
||||
}
|
||||
|
||||
private void removeExpiredUserSessions(RealmModel realm) {
|
||||
int expired = Time.currentTime() - realm.getSsoSessionMaxLifespan();
|
||||
int expiredRefresh = Time.currentTime() - realm.getSsoSessionIdleTimeout();
|
||||
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
|
||||
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).expired(expired, expiredRefresh)).iterator();
|
||||
|
||||
while (itr.hasNext()) {
|
||||
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
|
||||
tx.remove(sessionCache, entity.getId());
|
||||
|
||||
if (entity.getClientSessions() != null) {
|
||||
for (String clientSessionId : entity.getClientSessions()) {
|
||||
tx.remove(sessionCache, clientSessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeExpiredClientSessions(RealmModel realm) {
|
||||
int expiredDettachedClientSession = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
|
||||
|
||||
Map<String, String> map = new MapReduceTask(sessionCache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).expired(expired, expiredRefresh).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
for (String id : map.keySet()) {
|
||||
removeUserSession(realm, id);
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession()).iterator();
|
||||
while (itr.hasNext()) {
|
||||
tx.remove(sessionCache, itr.next().getKey());
|
||||
}
|
||||
}
|
||||
|
||||
map = new MapReduceTask(sessionCache)
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredDettachedClientSession).requireNullUserSession(true).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
private void removeExpiredOfflineUserSessions(RealmModel realm) {
|
||||
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
|
||||
|
||||
for (String id : map.keySet()) {
|
||||
tx.remove(sessionCache, id);
|
||||
}
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = offlineSessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).expired(null, expiredOffline)).iterator();
|
||||
while (itr.hasNext()) {
|
||||
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
|
||||
tx.remove(offlineSessionCache, entity.getId());
|
||||
|
||||
// Remove expired offline user sessions
|
||||
Map<String, SessionEntity> map2 = new MapReduceTask(offlineSessionCache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).expired(null, expiredOffline))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
persister.removeUserSession(entity.getId(), true);
|
||||
|
||||
for (Map.Entry<String, SessionEntity> entry : map2.entrySet()) {
|
||||
String userSessionId = entry.getKey();
|
||||
tx.remove(offlineSessionCache, userSessionId);
|
||||
// Propagate to persister
|
||||
persister.removeUserSession(userSessionId, true);
|
||||
|
||||
UserSessionEntity entity = (UserSessionEntity) entry.getValue();
|
||||
for (String clientSessionId : entity.getClientSessions()) {
|
||||
tx.remove(offlineSessionCache, clientSessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove expired offline client sessions
|
||||
map = new MapReduceTask(offlineSessionCache)
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).expiredRefresh(expiredOffline).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
private void removeExpiredOfflineClientSessions(RealmModel realm) {
|
||||
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
int expiredOffline = Time.currentTime() - realm.getOfflineSessionIdleTimeout();
|
||||
|
||||
for (String clientSessionId : map.keySet()) {
|
||||
tx.remove(offlineSessionCache, clientSessionId);
|
||||
persister.removeClientSession(clientSessionId, true);
|
||||
Iterator<String> itr = offlineSessionCache.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).expiredRefresh(expiredOffline)).map(Mappers.sessionId()).iterator();
|
||||
while (itr.hasNext()) {
|
||||
String sessionId = itr.next();
|
||||
tx.remove(offlineSessionCache, sessionId);
|
||||
persister.removeClientSession(sessionId, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove expired client initial access
|
||||
map = new MapReduceTask(sessionCache)
|
||||
.mappedWith(ClientInitialAccessMapper.create(realm.getId()).expired(Time.currentTime()).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
for (String id : map.keySet()) {
|
||||
tx.remove(sessionCache, id);
|
||||
private void removeExpiredClientInitialAccess(RealmModel realm) {
|
||||
Iterator<String> itr = sessionCache.entrySet().stream().filter(ClientInitialAccessPredicate.create(realm.getId()).expired(Time.currentTime())).map(Mappers.sessionId()).iterator();
|
||||
while (itr.hasNext()) {
|
||||
tx.remove(sessionCache, itr.next());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,13 +320,9 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
protected void removeUserSessions(RealmModel realm, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
Map<String, String> ids = new MapReduceTask(cache)
|
||||
.mappedWith(SessionMapper.create(realm.getId()).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
for (String id : ids.keySet()) {
|
||||
cache.remove(id);
|
||||
Iterator<String> itr = cache.entrySet().stream().filter(SessionPredicate.create(realm.getId())).map(Mappers.sessionId()).iterator();
|
||||
while (itr.hasNext()) {
|
||||
cache.remove(itr.next());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,24 +344,18 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
@Override
|
||||
public void removeUserLoginFailure(RealmModel realm, String username) {
|
||||
LoginFailureKey key = new LoginFailureKey(realm.getId(), username);
|
||||
tx.remove(loginFailureCache, key);
|
||||
tx.remove(loginFailureCache, new LoginFailureKey(realm.getId(), username));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllUserLoginFailures(RealmModel realm) {
|
||||
Map<LoginFailureKey, Object> sessions = new MapReduceTask(loginFailureCache)
|
||||
.mappedWith(UserLoginFailureMapper.create(realm.getId()).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
for (LoginFailureKey id : sessions.keySet()) {
|
||||
tx.remove(loginFailureCache, id);
|
||||
Iterator<LoginFailureKey> itr = loginFailureCache.entrySet().stream().filter(UserLoginFailurePredicate.create(realm.getId())).map(Mappers.loginFailureId()).iterator();
|
||||
while (itr.hasNext()) {
|
||||
LoginFailureKey key = itr.next();
|
||||
tx.remove(loginFailureCache, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onRealmRemoved(RealmModel realm) {
|
||||
removeUserSessions(realm, true);
|
||||
|
@ -418,18 +372,13 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
private void onClientRemoved(RealmModel realm, ClientModel client, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
Map<String, ClientSessionEntity> map = new MapReduceTask(cache)
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).client(client.getId()))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
for (Map.Entry<String, ClientSessionEntity> entry : map.entrySet()) {
|
||||
|
||||
// detach from userSession
|
||||
ClientSessionAdapter adapter = wrap(realm, entry.getValue(), offline);
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = cache.entrySet().stream().filter(ClientSessionPredicate.create(realm.getId()).client(client.getId())).iterator();
|
||||
while (itr.hasNext()) {
|
||||
ClientSessionEntity entity = (ClientSessionEntity) itr.next().getValue();
|
||||
ClientSessionAdapter adapter = wrap(realm, entity, offline);
|
||||
adapter.setUserSession(null);
|
||||
|
||||
tx.remove(cache, entry.getKey());
|
||||
tx.remove(cache, entity.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,27 +440,19 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
}
|
||||
|
||||
protected void removeUserSession(RealmModel realm, String userSessionId) {
|
||||
removeUserSession(realm, userSessionId, false);
|
||||
}
|
||||
|
||||
protected void removeUserSession(RealmModel realm, String userSessionId, boolean offline) {
|
||||
Cache<String, SessionEntity> cache = getCache(offline);
|
||||
|
||||
tx.remove(cache, userSessionId);
|
||||
|
||||
// TODO: Isn't more effective to retrieve from userSessionEntity directly?
|
||||
Map<String, String> map = new MapReduceTask(cache)
|
||||
.mappedWith(ClientSessionMapper.create(realm.getId()).userSession(userSessionId).emitKey())
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
|
||||
for (String id : map.keySet()) {
|
||||
tx.remove(cache, id);
|
||||
UserSessionEntity sessionEntity = (UserSessionEntity) cache.get(userSessionId);
|
||||
if (sessionEntity.getClientSessions() != null) {
|
||||
for (String clientSessionId : sessionEntity.getClientSessions()) {
|
||||
tx.remove(cache, clientSessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
InfinispanKeycloakTransaction getTx() {
|
||||
return tx;
|
||||
}
|
||||
|
@ -522,7 +463,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
List<UserSessionModel> wrapUserSessions(RealmModel realm, Collection<UserSessionEntity> entities, boolean offline) {
|
||||
List<UserSessionModel> models = new LinkedList<UserSessionModel>();
|
||||
List<UserSessionModel> models = new LinkedList<>();
|
||||
for (UserSessionEntity e : entities) {
|
||||
models.add(wrap(realm, e, offline));
|
||||
}
|
||||
|
@ -553,7 +494,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
List<ClientSessionModel> wrapClientSessions(RealmModel realm, Collection<ClientSessionEntity> entities, boolean offline) {
|
||||
List<ClientSessionModel> models = new LinkedList<ClientSessionModel>();
|
||||
List<ClientSessionModel> models = new LinkedList<>();
|
||||
for (ClientSessionEntity e : entities) {
|
||||
models.add(wrap(realm, e, offline));
|
||||
}
|
||||
|
@ -600,23 +541,21 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
@Override
|
||||
public List<ClientSessionModel> getOfflineClientSessions(RealmModel realm, UserModel user) {
|
||||
Map<String, UserSessionEntity> sessions = new MapReduceTask(offlineSessionCache)
|
||||
.mappedWith(UserSessionMapper.create(realm.getId()).user(user.getId()))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = offlineSessionCache.entrySet().stream().filter(UserSessionPredicate.create(realm.getId()).user(user.getId())).iterator();
|
||||
List<ClientSessionModel> clientSessions = new LinkedList<>();
|
||||
|
||||
List<ClientSessionEntity> clientSessions = new LinkedList<>();
|
||||
for (UserSessionEntity userSession : sessions.values()) {
|
||||
Set<String> currClientSessions = userSession.getClientSessions();
|
||||
while(itr.hasNext()) {
|
||||
UserSessionEntity entity = (UserSessionEntity) itr.next().getValue();
|
||||
Set<String> currClientSessions = entity.getClientSessions();
|
||||
for (String clientSessionId : currClientSessions) {
|
||||
ClientSessionEntity cls = (ClientSessionEntity) offlineSessionCache.get(clientSessionId);
|
||||
if (cls != null) {
|
||||
clientSessions.add(cls);
|
||||
clientSessions.add(wrap(realm, cls, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wrapClientSessions(realm, clientSessions, true);
|
||||
return clientSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -626,7 +565,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getOfflineSessionsCount(RealmModel realm, ClientModel client) {
|
||||
public long getOfflineSessionsCount(RealmModel realm, ClientModel client) {
|
||||
return getUserSessionsCount(realm, client, true);
|
||||
}
|
||||
|
||||
|
@ -721,18 +660,19 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
|
|||
|
||||
@Override
|
||||
public List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm) {
|
||||
Map<String, ClientInitialAccessEntity> entities = new MapReduceTask(sessionCache)
|
||||
.mappedWith(ClientInitialAccessMapper.create(realm.getId()))
|
||||
.reducedWith(new FirstResultReducer())
|
||||
.execute();
|
||||
return wrapClientInitialAccess(realm, entities.values());
|
||||
Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(ClientInitialAccessPredicate.create(realm.getId())).iterator();
|
||||
List<ClientInitialAccessModel> list = new LinkedList<>();
|
||||
while (itr.hasNext()) {
|
||||
list.add(wrap(realm, (ClientInitialAccessEntity) itr.next().getValue()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
class InfinispanKeycloakTransaction implements KeycloakTransaction {
|
||||
|
||||
private boolean active;
|
||||
private boolean rollback;
|
||||
private Map<Object, CacheTask> tasks = new HashMap<Object, CacheTask>();
|
||||
private Map<Object, CacheTask> tasks = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
|
|
|
@ -47,7 +47,8 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
|
|||
Cache<String, SessionEntity> cache = connections.getCache(InfinispanConnectionProvider.SESSION_CACHE_NAME);
|
||||
Cache<String, SessionEntity> offlineSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME);
|
||||
Cache<LoginFailureKey, LoginFailureEntity> loginFailures = connections.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME);
|
||||
return new InfinispanUserSessionProvider(session, cache, offlineSessionsCache, loginFailures);
|
||||
|
||||
return isStreamMode() ? new InfinispanUserSessionProvider(session, cache, offlineSessionsCache, loginFailures) : new CompatInfinispanUserSessionProvider(session, cache, offlineSessionsCache, loginFailures);
|
||||
} else {
|
||||
return compatProviderFactory.create(session);
|
||||
}
|
||||
|
@ -147,5 +148,9 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean isStreamMode() {
|
||||
return Version.getVersionShort() >= Version.getVersionShort("8.1.0.Final");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -176,17 +176,6 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
return userSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessionsByNote(RealmModel realm, String noteName, String noteValue) {
|
||||
List<UserSessionModel> userSessions = new LinkedList<UserSessionModel>();
|
||||
for (UserSessionEntity s : this.userSessions.values()) {
|
||||
if (s.getRealm().equals(realm.getId()) && noteValue.equals(s.getNotes().get(noteName))) {
|
||||
userSessions.add(new UserSessionAdapter(session, this, realm, s));
|
||||
}
|
||||
}
|
||||
return userSessions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserSessionModel> getUserSessions(RealmModel realm, ClientModel client) {
|
||||
return getUserSessions(realm, client, false);
|
||||
|
@ -230,7 +219,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getActiveUserSessions(RealmModel realm, ClientModel client) {
|
||||
public long getActiveUserSessions(RealmModel realm, ClientModel client) {
|
||||
return getUserSessions(realm, client, false).size();
|
||||
}
|
||||
|
||||
|
@ -287,7 +276,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void removeExpiredUserSessions(RealmModel realm) {
|
||||
public void removeExpired(RealmModel realm) {
|
||||
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
|
||||
|
||||
Iterator<UserSessionEntity> itr = userSessions.values().iterator();
|
||||
|
@ -565,7 +554,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getOfflineSessionsCount(RealmModel realm, ClientModel client) {
|
||||
public long getOfflineSessionsCount(RealmModel realm, ClientModel client) {
|
||||
return getUserSessions(realm, client, true).size();
|
||||
}
|
||||
|
||||
|
|
|
@ -151,13 +151,14 @@ public class InfinispanUserSessionInitializer {
|
|||
int processors = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
ExecutorService localExecutor = Executors.newCachedThreadPool();
|
||||
DistributedExecutorService distributedExecutorService = new DefaultExecutorService(cache, localExecutor);
|
||||
Transport transport = cache.getCacheManager().getTransport();
|
||||
boolean distributed = transport != null;
|
||||
ExecutorService executorService = distributed ? new DefaultExecutorService(cache, localExecutor) : localExecutor;
|
||||
|
||||
int errors = 0;
|
||||
|
||||
try {
|
||||
while (!state.isFinished()) {
|
||||
Transport transport = cache.getCacheManager().getTransport();
|
||||
int nodesCount = transport==null ? 1 : transport.getMembers().size();
|
||||
int distributedWorkersCount = processors * nodesCount;
|
||||
|
||||
|
@ -173,8 +174,11 @@ public class InfinispanUserSessionInitializer {
|
|||
for (Integer segment : segments) {
|
||||
SessionInitializerWorker worker = new SessionInitializerWorker();
|
||||
worker.setWorkerEnvironment(segment, sessionsPerSegment, sessionLoader);
|
||||
if (!distributed) {
|
||||
worker.setEnvironment(cache, null);
|
||||
}
|
||||
|
||||
Future<WorkerResult> future = distributedExecutorService.submit(worker);
|
||||
Future<WorkerResult> future = executorService.submit(worker);
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
|
@ -210,7 +214,9 @@ public class InfinispanUserSessionInitializer {
|
|||
}
|
||||
}
|
||||
} finally {
|
||||
distributedExecutorService.shutdown();
|
||||
if (distributed) {
|
||||
executorService.shutdown();
|
||||
}
|
||||
localExecutor.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
8
pom.xml
8
pom.xml
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>org.jboss</groupId>
|
||||
<artifactId>jboss-parent</artifactId>
|
||||
<version>16</version>
|
||||
<version>19</version>
|
||||
</parent>
|
||||
|
||||
<name>Keycloak</name>
|
||||
|
@ -49,8 +49,8 @@
|
|||
<dom4j.version>1.6.1</dom4j.version>
|
||||
<xml-apis.version>1.4.01</xml-apis.version>
|
||||
<slf4j.version>1.7.7</slf4j.version>
|
||||
<wildfly.version>10.0.0.CR4</wildfly.version>
|
||||
<wildfly.core.version>2.0.1.Final</wildfly.core.version>
|
||||
<wildfly.version>10.0.0.CR5</wildfly.version>
|
||||
<wildfly.core.version>2.0.5.Final</wildfly.core.version>
|
||||
<wildfly.build-tools.version>1.1.0.Final</wildfly.build-tools.version>
|
||||
|
||||
<!-- this is EAP 6.4 alpha, publicly available -->
|
||||
|
@ -66,7 +66,7 @@
|
|||
<sun.jaxb.version>2.2.11</sun.jaxb.version>
|
||||
<sun.xsom.version>20140925</sun.xsom.version>
|
||||
<javax.mail.version>1.4.5</javax.mail.version>
|
||||
<infinispan.version>6.0.2.Final</infinispan.version>
|
||||
<infinispan.version>8.1.0.Final</infinispan.version>
|
||||
<liquibase.version>3.4.1</liquibase.version>
|
||||
<jetty9.version>9.1.0.v20131115</jetty9.version>
|
||||
<osgi.version>4.2.0</osgi.version>
|
||||
|
|
|
@ -399,9 +399,9 @@ public class ClientResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Map<String, Integer> getApplicationSessionCount() {
|
||||
public Map<String, Long> getApplicationSessionCount() {
|
||||
auth.requireView();
|
||||
Map<String, Integer> map = new HashMap<String, Integer>();
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client));
|
||||
return map;
|
||||
}
|
||||
|
@ -446,9 +446,9 @@ public class ClientResource {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Map<String, Integer> getOfflineSessionCount() {
|
||||
public Map<String, Long> getOfflineSessionCount() {
|
||||
auth.requireView();
|
||||
Map<String, Integer> map = new HashMap<String, Integer>();
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
map.put("count", session.sessions().getOfflineSessionsCount(client.getRealm(), client));
|
||||
return map;
|
||||
}
|
||||
|
|
|
@ -369,9 +369,9 @@ public class RealmAdminResource {
|
|||
auth.requireView();
|
||||
List<Map<String, String>> data = new LinkedList<Map<String, String>>();
|
||||
for (ClientModel client : realm.getClients()) {
|
||||
int size = session.sessions().getActiveUserSessions(client.getRealm(), client);
|
||||
long size = session.sessions().getActiveUserSessions(client.getRealm(), client);
|
||||
if (size == 0) continue;
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("id", client.getId());
|
||||
map.put("clientId", client.getClientId());
|
||||
map.put("active", size + "");
|
||||
|
|
|
@ -13,7 +13,7 @@ public class ClearExpiredUserSessions implements ScheduledTask {
|
|||
public void run(KeycloakSession session) {
|
||||
UserSessionProvider sessions = session.sessions();
|
||||
for (RealmModel realm : session.realms().getRealms()) {
|
||||
sessions.removeExpiredUserSessions(realm);
|
||||
sessions.removeExpired(realm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,8 @@ public class AdapterTest {
|
|||
|
||||
@Test
|
||||
public void testLoginSSOAndLogout() throws Exception {
|
||||
testStrategy.testLoginSSOMax();
|
||||
|
||||
testStrategy.testLoginSSOAndLogout();
|
||||
}
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ public class AdapterTestStrategy extends ExternalResource {
|
|||
|
||||
session = keycloakRule.startSession();
|
||||
realm = session.realms().getRealmByName("demo");
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
|
|
|
@ -286,9 +286,6 @@ public class BruteForceTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testBrowserInvalidPassword() throws Exception {
|
||||
loginSuccess();
|
||||
|
|
|
@ -524,7 +524,7 @@ public class LoginTest {
|
|||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
manager.getSession().sessions().removeExpiredUserSessions(appRealm);
|
||||
manager.getSession().sessions().removeExpired(appRealm);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -347,7 +347,7 @@ public class UserSessionProviderOfflineTest {
|
|||
|
||||
resetSession();
|
||||
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
|
||||
resetSession();
|
||||
|
||||
|
@ -372,7 +372,7 @@ public class UserSessionProviderOfflineTest {
|
|||
// Expire everything and assert nothing found
|
||||
Time.setOffset(3000000);
|
||||
try {
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
|
||||
resetSession();
|
||||
|
||||
|
|
|
@ -17,11 +17,7 @@ import org.keycloak.services.managers.UserManager;
|
|||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.common.util.Time;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
@ -276,7 +272,7 @@ public class UserSessionProviderTest {
|
|||
|
||||
resetSession();
|
||||
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
for (String e : expired) {
|
||||
|
@ -309,13 +305,13 @@ public class UserSessionProviderTest {
|
|||
resetSession();
|
||||
|
||||
Time.setOffset(25);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(35);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
@ -328,13 +324,13 @@ public class UserSessionProviderTest {
|
|||
resetSession();
|
||||
|
||||
Time.setOffset(35);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(45);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
@ -347,13 +343,13 @@ public class UserSessionProviderTest {
|
|||
resetSession();
|
||||
|
||||
Time.setOffset(45);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNotNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
||||
Time.setOffset(55);
|
||||
session.sessions().removeExpiredUserSessions(realm);
|
||||
session.sessions().removeExpired(realm);
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getClientSession(clientSessionId));
|
||||
|
@ -463,6 +459,18 @@ public class UserSessionProviderTest {
|
|||
|
||||
failure1 = session.sessions().getUserLoginFailure(realm, "user1");
|
||||
assertEquals(0, failure1.getNumFailures());
|
||||
|
||||
session.sessions().removeUserLoginFailure(realm, "user1");
|
||||
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getUserLoginFailure(realm, "user1"));
|
||||
|
||||
session.sessions().removeAllUserLoginFailures(realm);
|
||||
|
||||
resetSession();
|
||||
|
||||
assertNull(session.sessions().getUserLoginFailure(realm, "user2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -263,7 +263,7 @@ public class OfflineTokenTest {
|
|||
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
manager.getSession().sessions().removeExpiredUserSessions(appRealm);
|
||||
manager.getSession().sessions().removeExpired(appRealm);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue