keycloak-aplcache
Changes
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js 14(+10 -4)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html 4(+2 -2)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html 12(+6 -6)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html 2(+0 -2)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java 13(+12 -1)
Details
diff --git a/distribution/server-overlay/eap6/eap6-server-overlay/assembly.xml b/distribution/server-overlay/eap6/eap6-server-overlay/assembly.xml
index 1d978dd..e95f6aa 100755
--- a/distribution/server-overlay/eap6/eap6-server-overlay/assembly.xml
+++ b/distribution/server-overlay/eap6/eap6-server-overlay/assembly.xml
@@ -29,6 +29,11 @@
<destName>standalone-keycloak.xml</destName>
</file>
<file>
+ <source>${project.build.directory}/standalone-ha.xml</source>
+ <outputDirectory>standalone/configuration</outputDirectory>
+ <destName>standalone-keycloak-ha.xml</destName>
+ </file>
+ <file>
<source>src/main/keycloak-server.json</source>
<outputDirectory>standalone/configuration</outputDirectory>
</file>
diff --git a/distribution/server-overlay/eap6/eap6-server-overlay/pom.xml b/distribution/server-overlay/eap6/eap6-server-overlay/pom.xml
index 4b5acc7..9f9fb74 100755
--- a/distribution/server-overlay/eap6/eap6-server-overlay/pom.xml
+++ b/distribution/server-overlay/eap6/eap6-server-overlay/pom.xml
@@ -72,6 +72,25 @@
</transformationSets>
</configuration>
</execution>
+ <execution>
+ <id>generate-resources-2</id>
+ <phase>package</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>src/main</dir>
+ <stylesheet>src/main/xslt/standalone-ha.xsl</stylesheet>
+ <includes>
+ <include>standalone-ha.xml</include>
+ </includes>
+ <outputDir>${project.build.directory}</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
</executions>
</plugin>
<plugin>
diff --git a/distribution/server-overlay/eap6/eap6-server-overlay/src/main/standalone-ha.xml b/distribution/server-overlay/eap6/eap6-server-overlay/src/main/standalone-ha.xml
new file mode 100644
index 0000000..e208a7d
--- /dev/null
+++ b/distribution/server-overlay/eap6/eap6-server-overlay/src/main/standalone-ha.xml
@@ -0,0 +1,421 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<server xmlns="urn:jboss:domain:1.7">
+ <extensions>
+ <extension module="org.jboss.as.clustering.infinispan"/>
+ <extension module="org.jboss.as.clustering.jgroups"/>
+ <extension module="org.jboss.as.connector"/>
+ <extension module="org.jboss.as.deployment-scanner"/>
+ <extension module="org.jboss.as.ee"/>
+ <extension module="org.jboss.as.ejb3"/>
+ <extension module="org.jboss.as.jaxrs"/>
+ <extension module="org.jboss.as.jdr"/>
+ <extension module="org.jboss.as.jmx"/>
+ <extension module="org.jboss.as.jpa"/>
+ <extension module="org.jboss.as.jsf"/>
+ <extension module="org.jboss.as.logging"/>
+ <extension module="org.jboss.as.mail"/>
+ <extension module="org.jboss.as.modcluster"/>
+ <extension module="org.jboss.as.naming"/>
+ <extension module="org.jboss.as.pojo"/>
+ <extension module="org.jboss.as.remoting"/>
+ <extension module="org.jboss.as.sar"/>
+ <extension module="org.jboss.as.security"/>
+ <extension module="org.jboss.as.threads"/>
+ <extension module="org.jboss.as.transactions"/>
+ <extension module="org.jboss.as.web"/>
+ <extension module="org.jboss.as.webservices"/>
+ <extension module="org.jboss.as.weld"/>
+ </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.server.config.dir"/>
+ </authentication>
+ <authorization map-groups-to-roles="false">
+ <properties path="mgmt-groups.properties" relative-to="jboss.server.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.server.config.dir"/>
+ </authentication>
+ <authorization>
+ <properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
+ </authorization>
+ </security-realm>
+ </security-realms>
+ <audit-log>
+ <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"/>
+ </handlers>
+ <logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="file"/>
+ </handlers>
+ </logger>
+ </audit-log>
+ <management-interfaces>
+ <native-interface security-realm="ManagementRealm">
+ <socket-binding native="management-native"/>
+ </native-interface>
+ <http-interface security-realm="ManagementRealm">
+ <socket-binding http="management-http"/>
+ </http-interface>
+ </management-interfaces>
+ <access-control provider="simple">
+ <role-mapping>
+ <role name="SuperUser">
+ <include>
+ <user name="$local"/>
+ </include>
+ </role>
+ </role-mapping>
+ </access-control>
+ </management>
+ <profile>
+ <subsystem xmlns="urn:jboss:domain:logging:1.5">
+ <console-handler name="CONSOLE">
+ <level name="INFO"/>
+ <formatter>
+ <named-formatter name="COLOR-PATTERN"/>
+ </formatter>
+ </console-handler>
+ <periodic-rotating-file-handler name="FILE" autoflush="true">
+ <formatter>
+ <named-formatter name="PATTERN"/>
+ </formatter>
+ <file relative-to="jboss.server.log.dir" path="server.log"/>
+ <suffix value=".yyyy-MM-dd"/>
+ <append value="true"/>
+ </periodic-rotating-file-handler>
+ <logger category="com.arjuna">
+ <level name="WARN"/>
+ </logger>
+ <logger category="org.apache.tomcat.util.modeler">
+ <level name="WARN"/>
+ </logger>
+ <logger category="org.jboss.as.config">
+ <level name="DEBUG"/>
+ </logger>
+ <logger category="sun.rmi">
+ <level name="WARN"/>
+ </logger>
+ <logger category="jacorb">
+ <level name="WARN"/>
+ </logger>
+ <logger category="jacorb.config">
+ <level name="ERROR"/>
+ </logger>
+ <root-logger>
+ <level name="INFO"/>
+ <handlers>
+ <handler name="CONSOLE"/>
+ <handler name="FILE"/>
+ </handlers>
+ </root-logger>
+ <formatter name="PATTERN">
+ <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
+ </formatter>
+ <formatter name="COLOR-PATTERN">
+ <pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
+ </formatter>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:datasources:1.2">
+ <datasources>
+ <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
+ <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
+ <driver>h2</driver>
+ <security>
+ <user-name>sa</user-name>
+ <password>sa</password>
+ </security>
+ </datasource>
+ <drivers>
+ <driver name="h2" module="com.h2database.h2">
+ <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
+ </driver>
+ </drivers>
+ </datasources>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:deployment-scanner:1.1">
+ <deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:ee:1.2">
+ <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
+ <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>
+ <annotation-property-replacement>false</annotation-property-replacement>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:ejb3:1.5">
+ <session-bean>
+ <stateless>
+ <bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
+ </stateless>
+ <stateful default-access-timeout="5000" cache-ref="simple" clustered-cache-ref="clustered"/>
+ <singleton default-access-timeout="5000"/>
+ </session-bean>
+ <pools>
+ <bean-instance-pools>
+ <strict-max-pool name="slsb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
+ <strict-max-pool name="mdb-strict-max-pool" max-pool-size="20" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
+ </bean-instance-pools>
+ </pools>
+ <caches>
+ <cache name="simple" aliases="NoPassivationCache"/>
+ <cache name="passivating" passivation-store-ref="file" aliases="SimpleStatefulCache"/>
+ <cache name="clustered" passivation-store-ref="infinispan" aliases="StatefulTreeCache"/>
+ </caches>
+ <passivation-stores>
+ <file-passivation-store name="file"/>
+ <cluster-passivation-store name="infinispan" cache-container="ejb"/>
+ </passivation-stores>
+ <async thread-pool-name="default"/>
+ <timer-service thread-pool-name="default" default-data-store="default-file-store">
+ <data-stores>
+ <file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
+ </data-stores>
+ </timer-service>
+ <remote connector-ref="remoting-connector" thread-pool-name="default"/>
+ <thread-pools>
+ <thread-pool name="default">
+ <max-threads count="10"/>
+ <keepalive-time time="100" unit="milliseconds"/>
+ </thread-pool>
+ </thread-pools>
+ <default-security-domain value="other"/>
+ <default-missing-method-permissions-deny-access value="true"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:infinispan:1.5">
+ <cache-container name="singleton" aliases="cluster ha-partition" default-cache="default">
+ <transport lock-timeout="60000"/>
+ <replicated-cache name="default" mode="SYNC" batching="true">
+ <locking isolation="REPEATABLE_READ"/>
+ </replicated-cache>
+ </cache-container>
+ <cache-container name="web" aliases="standard-session-cache" default-cache="repl" module="org.jboss.as.clustering.web.infinispan">
+ <transport lock-timeout="60000"/>
+ <replicated-cache name="repl" mode="ASYNC" batching="true">
+ <file-store/>
+ </replicated-cache>
+ <replicated-cache name="sso" mode="SYNC" batching="true"/>
+ <distributed-cache name="dist" mode="ASYNC" batching="true" l1-lifespan="0">
+ <file-store/>
+ </distributed-cache>
+ </cache-container>
+ <cache-container name="ejb" aliases="sfsb sfsb-cache" default-cache="repl" module="org.jboss.as.clustering.ejb3.infinispan">
+ <transport lock-timeout="60000"/>
+ <replicated-cache name="repl" mode="ASYNC" batching="true">
+ <eviction strategy="LRU" max-entries="10000"/>
+ <file-store/>
+ </replicated-cache>
+ <!--
+ ~ Clustered cache used internally by EJB subsytem for managing the client-mapping(s) of
+ ~ the socketbinding referenced by the EJB remoting connector
+ -->
+ <replicated-cache name="remote-connector-client-mappings" mode="SYNC" batching="true"/>
+ <distributed-cache name="dist" mode="ASYNC" batching="true" l1-lifespan="0">
+ <eviction strategy="LRU" max-entries="10000"/>
+ <file-store/>
+ </distributed-cache>
+ </cache-container>
+ <cache-container name="hibernate" default-cache="local-query" module="org.jboss.as.jpa.hibernate:4">
+ <transport lock-timeout="60000"/>
+ <local-cache name="local-query">
+ <transaction mode="NONE"/>
+ <eviction strategy="LRU" max-entries="10000"/>
+ <expiration max-idle="100000"/>
+ </local-cache>
+ <invalidation-cache name="entity" mode="SYNC">
+ <transaction mode="NON_XA"/>
+ <eviction strategy="LRU" max-entries="10000"/>
+ <expiration max-idle="100000"/>
+ </invalidation-cache>
+ <replicated-cache name="timestamps" mode="ASYNC">
+ <transaction mode="NONE"/>
+ <eviction strategy="NONE"/>
+ </replicated-cache>
+ </cache-container>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jaxrs:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:jca:1.1">
+ <archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
+ <bean-validation enabled="true"/>
+ <default-workmanager>
+ <short-running-threads>
+ <core-threads count="50"/>
+ <queue-length count="50"/>
+ <max-threads count="50"/>
+ <keepalive-time time="10" unit="seconds"/>
+ </short-running-threads>
+ <long-running-threads>
+ <core-threads count="50"/>
+ <queue-length count="50"/>
+ <max-threads count="50"/>
+ <keepalive-time time="10" unit="seconds"/>
+ </long-running-threads>
+ </default-workmanager>
+ <cached-connection-manager/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jdr:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:jgroups:1.1" default-stack="udp">
+ <stack name="udp">
+ <transport type="UDP" socket-binding="jgroups-udp"/>
+ <protocol type="PING"/>
+ <protocol type="MERGE3"/>
+ <protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
+ <protocol type="FD"/>
+ <protocol type="VERIFY_SUSPECT"/>
+ <protocol type="pbcast.NAKACK"/>
+ <protocol type="UNICAST2"/>
+ <protocol type="pbcast.STABLE"/>
+ <protocol type="pbcast.GMS"/>
+ <protocol type="UFC"/>
+ <protocol type="MFC"/>
+ <protocol type="FRAG2"/>
+ <protocol type="RSVP"/>
+ </stack>
+ <stack name="tcp">
+ <transport type="TCP" socket-binding="jgroups-tcp"/>
+ <protocol type="MPING" socket-binding="jgroups-mping"/>
+ <protocol type="MERGE2"/>
+ <protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
+ <protocol type="FD"/>
+ <protocol type="VERIFY_SUSPECT"/>
+ <protocol type="pbcast.NAKACK"/>
+ <protocol type="UNICAST2"/>
+ <protocol type="pbcast.STABLE"/>
+ <protocol type="pbcast.GMS"/>
+ <protocol type="UFC"/>
+ <protocol type="MFC"/>
+ <protocol type="FRAG2"/>
+ <protocol type="RSVP"/>
+ </stack>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jmx:1.3">
+ <expose-resolved-model/>
+ <expose-expression-model/>
+ <remoting-connector/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jpa:1.1">
+ <jpa default-datasource="" default-extended-persistence-inheritance="DEEP"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jsf:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:mail:1.2">
+ <mail-session name="default" jndi-name="java:jboss/mail/Default">
+ <smtp-server outbound-socket-binding-ref="mail-smtp"/>
+ </mail-session>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:modcluster:1.2">
+ <mod-cluster-config advertise-socket="modcluster" connector="ajp">
+ <dynamic-load-provider>
+ <load-metric type="busyness"/>
+ </dynamic-load-provider>
+ </mod-cluster-config>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:naming:1.4">
+ <remote-naming/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:pojo:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:remoting:1.2">
+ <connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:resource-adapters:1.1"/>
+ <subsystem xmlns="urn:jboss:domain:sar:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:security:1.2">
+ <security-domains>
+ <security-domain name="other" cache-type="default">
+ <authentication>
+ <login-module code="Remoting" flag="optional">
+ <module-option name="password-stacking" value="useFirstPass"/>
+ </login-module>
+ <login-module code="RealmDirect" flag="required">
+ <module-option name="password-stacking" value="useFirstPass"/>
+ </login-module>
+ </authentication>
+ </security-domain>
+ <security-domain name="jboss-web-policy" cache-type="default">
+ <authorization>
+ <policy-module code="Delegating" flag="required"/>
+ </authorization>
+ </security-domain>
+ <security-domain name="jboss-ejb-policy" cache-type="default">
+ <authorization>
+ <policy-module code="Delegating" flag="required"/>
+ </authorization>
+ </security-domain>
+ </security-domains>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:threads:1.1"/>
+ <subsystem xmlns="urn:jboss:domain:transactions:1.5">
+ <core-environment>
+ <process-id>
+ <uuid/>
+ </process-id>
+ </core-environment>
+ <recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
+ <coordinator-environment default-timeout="300"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:web:2.2" default-virtual-server="default-host" native="false">
+ <connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/>
+ <connector name="ajp" protocol="AJP/1.3" scheme="http" socket-binding="ajp"/>
+ <virtual-server name="default-host" enable-welcome-root="true">
+ <alias name="localhost"/>
+ <alias name="example.com"/>
+ </virtual-server>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:webservices:1.2">
+ <modify-wsdl-address>true</modify-wsdl-address>
+ <wsdl-host>${jboss.bind.address:127.0.0.1}</wsdl-host>
+ <endpoint-config name="Standard-Endpoint-Config"/>
+ <endpoint-config name="Recording-Endpoint-Config">
+ <pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
+ <handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/>
+ </pre-handler-chain>
+ </endpoint-config>
+ <client-config name="Standard-Client-Config"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:weld:1.0"/>
+ </profile>
+ <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>
+ <!-- TODO - only show this if the jacorb subsystem is added -->
+ <interface name="unsecure">
+ <!--
+ ~ Used for IIOP sockets in the standard configuration.
+ ~ To secure JacORB you need to setup SSL
+ -->
+ <inet-address value="${jboss.bind.address.unsecure:127.0.0.1}"/>
+ </interface>
+ </interfaces>
+ <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
+ <socket-binding name="management-native" interface="management" port="${jboss.management.native.port:9999}"/>
+ <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
+ <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9443}"/>
+ <socket-binding name="ajp" port="8009"/>
+ <socket-binding name="http" port="8080"/>
+ <socket-binding name="https" port="8443"/>
+ <socket-binding name="jgroups-mping" port="0" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
+ <socket-binding name="jgroups-tcp" port="7600"/>
+ <socket-binding name="jgroups-tcp-fd" port="57600"/>
+ <socket-binding name="jgroups-udp" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
+ <socket-binding name="jgroups-udp-fd" port="54200"/>
+ <socket-binding name="modcluster" port="0" multicast-address="224.0.1.105" multicast-port="23364"/>
+ <socket-binding name="remoting" port="4447"/>
+ <socket-binding name="txn-recovery-environment" port="4712"/>
+ <socket-binding name="txn-status-manager" port="4713"/>
+ <outbound-socket-binding name="mail-smtp">
+ <remote-destination host="localhost" port="25"/>
+ </outbound-socket-binding>
+ </socket-binding-group>
+</server>
\ No newline at end of file
diff --git a/distribution/server-overlay/eap6/eap6-server-overlay/src/main/xslt/standalone-ha.xsl b/distribution/server-overlay/eap6/eap6-server-overlay/src/main/xslt/standalone-ha.xsl
new file mode 100755
index 0000000..31f681f
--- /dev/null
+++ b/distribution/server-overlay/eap6/eap6-server-overlay/src/main/xslt/standalone-ha.xsl
@@ -0,0 +1,76 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:1.7"
+ xmlns:ds="urn:jboss:domain:datasources:1.2"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+ <xsl:variable name="log" select="'urn:jboss:domain:logging:'"/>
+ <xsl:variable name="inf" select="'urn:jboss:domain:infinispan:'"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-server-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//ds:datasources">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='datasource']"/>
+ <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
+ <connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
+ <driver>h2</driver>
+ <security>
+ <user-name>sa</user-name>
+ <password>sa</password>
+ </security>
+ </datasource>
+ <xsl:apply-templates select="node()[name(.)='drivers']"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $inf)]">
+ <xsl:copy>
+ <cache-container name="keycloak" jndi-name="infinispan/Keycloak" start="EAGER">
+ <transport lock-timeout="60000"/>
+ <invalidation-cache name="realms" mode="SYNC"/>
+ <invalidation-cache name="users" mode="SYNC"/>
+ <distributed-cache name="sessions" mode="SYNC" owners="1"/>
+ <distributed-cache name="loginFailures" mode="SYNC" owners="1"/>
+ </cache-container>
+ <xsl:apply-templates select="node()|@*"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
+ <web-context>auth</web-context>
+ </subsystem>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $log)]">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <logger category="org.jboss.resteasy.resteasy_jaxrs.i18n">
+ <level name="ERROR"/>
+ </logger>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/distribution/server-overlay/wf9-server-overlay/assembly.xml b/distribution/server-overlay/wf9-server-overlay/assembly.xml
index e7fcb1b..04b9ca8 100755
--- a/distribution/server-overlay/wf9-server-overlay/assembly.xml
+++ b/distribution/server-overlay/wf9-server-overlay/assembly.xml
@@ -59,6 +59,11 @@
<destName>standalone-keycloak.xml</destName>
</file>
<file>
+ <source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/standalone-ha.xml</source>
+ <outputDirectory>standalone/configuration</outputDirectory>
+ <destName>standalone-keycloak-ha.xml</destName>
+ </file>
+ <file>
<source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/keycloak-server.json</source>
<outputDirectory>standalone/configuration</outputDirectory>
</file>
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index d669e6b..556708e 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -167,8 +167,7 @@
"clientId": "admin-client",
"enabled": true,
"publicClient": true,
- "directGrantsOnly": true,
- "consentRequired": true
+ "directGrantsOnly": true
},
{
"clientId": "product-sa-client",
diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html
index 796aebd..153053b 100644
--- a/examples/js-console/src/main/webapp/index.html
+++ b/examples/js-console/src/main/webapp/index.html
@@ -10,6 +10,7 @@
<button onclick="refreshToken(9999)">Refresh Token</button>
<button onclick="refreshToken(30)">Refresh Token (if <30s validity)</button>
<button onclick="loadProfile()">Get Profile</button>
+ <button onclick="loadUserInfo()">Get User Info</button>
<button onclick="output(keycloak.tokenParsed)">Show Token</button>
<button onclick="output(keycloak.refreshTokenParsed)">Show Refresh Token</button>
<button onclick="output(keycloak.idTokenParsed)">Show ID Token</button>
@@ -35,6 +36,14 @@
});
}
+ function loadUserInfo() {
+ keycloak.loadUserInfo().success(function(userInfo) {
+ output(userInfo);
+ }).error(function() {
+ output('Failed to load user info');
+ });
+ }
+
function refreshToken(minValidity) {
keycloak.updateToken(minValidity).success(function(refreshed) {
if (refreshed) {
diff --git a/examples/themes/src/main/resources/theme/logo-example/admin/resources/css/logo.css b/examples/themes/src/main/resources/theme/logo-example/admin/resources/css/logo.css
index c00e814..81ed18e 100755
--- a/examples/themes/src/main/resources/theme/logo-example/admin/resources/css/logo.css
+++ b/examples/themes/src/main/resources/theme/logo-example/admin/resources/css/logo.css
@@ -1,7 +1,4 @@
.navbar-pf .navbar-brand {
- background: url('../img/red-hat-logo.png') no-repeat 0px 0px;
- display: block;
- height: 25px;
- width: 200px;
+ background: url('../img/red-hat-logo.png') no-repeat 0px 5px;
}
\ No newline at end of file
diff --git a/examples/themes/src/main/resources/theme/logo-example/admin/resources/img/red-hat-logo.png b/examples/themes/src/main/resources/theme/logo-example/admin/resources/img/red-hat-logo.png
index 7dcf731..0b01b1a 100755
Binary files a/examples/themes/src/main/resources/theme/logo-example/admin/resources/img/red-hat-logo.png and b/examples/themes/src/main/resources/theme/logo-example/admin/resources/img/red-hat-logo.png differ
diff --git a/examples/themes/src/main/resources/theme/logo-example/admin/theme.properties b/examples/themes/src/main/resources/theme/logo-example/admin/theme.properties
index 8cf8245..4bbbf0e 100755
--- a/examples/themes/src/main/resources/theme/logo-example/admin/theme.properties
+++ b/examples/themes/src/main/resources/theme/logo-example/admin/theme.properties
@@ -1,3 +1,3 @@
parent=keycloak
import=common/keycloak
-styles=css/styles.css lib/patternfly/css/patternfly.css lib/select2-3.4.1/select2.css css/styles.css css/logo.css
\ No newline at end of file
+styles=lib/patternfly/css/patternfly.css lib/select2-3.4.1/select2.css css/styles.css css/logo.css
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 493d9af..61eefd9 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -310,17 +310,23 @@ module.controller('ClientCertificateExportCtrl', function($scope, $location, $ht
data: $scope.jks,
headers: {
'Content-Type': 'application/json',
- 'Accept': 'client/octet-stream'
+ 'Accept': 'application/octet-stream'
}
}).success(function(data){
var blob = new Blob([data], {
- type: 'client/octet-stream'
+ type: 'application/octet-stream'
});
var ext = ".jks";
if ($scope.jks.format == 'PKCS12') ext = ".p12";
saveAs(blob, 'keystore' + ext);
- }).error(function(){
- Notifications.error("Error downloading.");
+ }).error(function(data) {
+ var errorMsg = 'Error downloading';
+ try {
+ var error = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(data)));
+ errorMsg = error['error_description'] ? error['error_description'] : errorMsg;
+ } catch (err) {
+ }
+ Notifications.error(errorMsg);
});
}
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
index d0fc0d1..50f01b9 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
@@ -9,7 +9,7 @@
<th colspan="6" class="kc-table-actions">
<div class="dropdown pull-left">
<select class="form-control" ng-model="flow"
- ng-options="flow.alias for flow in flows"
+ ng-options="(flow.alias|capitalize) for flow in flows"
data-ng-change="setupForm()">
</select>
</div>
@@ -24,7 +24,7 @@
<tbody>
<tr ng-repeat="execution in executions" data-ng-show="executions.length > 0">
<td ng-show="execution.subFlow"></td>
- <td><h2>{{execution.referenceType}}</h2></td>
+ <td>{{execution.referenceType|capitalize}}</td>
<td ng-hide="execution.subFlow"></td>
<td ng-repeat="choice in execution.requirementChoices">
<!--
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
index eff7050..540fa9d 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
@@ -30,9 +30,9 @@
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button class="btn btn-primary" type="submit" data-ng-click="generateSigningKey()">Generate new keys</button>
- <button class="btn btn-primary" type="submit" data-ng-click="importSigningKey()">Import</button>
- <button class="btn btn-primary" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">Export</button>
+ <button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">Generate new keys</button>
+ <button class="btn btn-default" type="submit" data-ng-click="importSigningKey()">Import</button>
+ <button class="btn btn-default" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">Export</button>
</div>
</div>
</fieldset>
@@ -56,9 +56,9 @@
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button class="btn btn-primary" type="submit" data-ng-click="generateEncryptionKey()">Generate new keys</button>
- <button class="btn btn-primary" type="submit" data-ng-click="importEncryptionKey()">Import</button>
- <button class="btn btn-primary" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">Export</button>
+ <button class="btn btn-default" type="submit" data-ng-click="generateEncryptionKey()">Generate new keys</button>
+ <button class="btn btn-default" type="submit" data-ng-click="importEncryptionKey()">Import</button>
+ <button class="btn btn-default" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">Export</button>
</div>
</div>
</fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
index 03a38d9..29bb4ab 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
@@ -5,8 +5,6 @@
<li>{{client.clientId}}</li>
</ol>
- <h1>{{client.clientId|capitalize}}</h1>
-
<kc-tabs-client></kc-tabs-client>
<h2><span>{{client.clientId}}</span> Service Accounts </h2>
diff --git a/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css b/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
index 30fb25a..e550668 100644
--- a/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
+++ b/forms/common-themes/src/main/resources/theme/keycloak/admin/resources/css/styles.css
@@ -2,15 +2,6 @@ html,body {
height: 100%;
}
-/**
-.navbar-pf .navbar-brand {
- background: url('../img/brand.svg') no-repeat 0px 5px;
- display: block;
- height: 25px;
- width: 200px;
-}
-**/
-
form {
margin-top: 20px;
}
@@ -269,15 +260,15 @@ table {
border-top: none!important;
}
-.navbar-brand{
- padding: 0!important;
- height: 56px!important;
+.navbar-pf .navbar-brand {
+ padding: 0;
+ height: 56px;
line-height: 56px;
- background-position: center center!important;
- background-image: url('../img/keyclok-logo.svg')!important;
- background-size: 148px 36px !important;
-background-repeat: no-repeat;
- width: 148px!important;
+ background-position: center center;
+ background-image: url('../img/keyclok-logo.svg');
+ background-size: 148px 36px;
+ background-repeat: no-repeat;
+ width: 148px;
}
.navbar-pf .navbar-utility > li > a{
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 2202009..83776df 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -2,6 +2,7 @@ package org.keycloak.models.sessions.infinispan;
import org.infinispan.Cache;
import org.infinispan.distexec.mapreduce.MapReduceTask;
+import org.jboss.logging.Logger;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
@@ -41,6 +42,8 @@ import java.util.Map;
*/
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<LoginFailureKey, LoginFailureEntity> loginFailureCache;
@@ -473,6 +476,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
public void put(Cache cache, Object key, Object value) {
+ log.tracev("Adding cache operation: {0} on {1}", CacheOperation.ADD, key);
+
if (tasks.containsKey(key)) {
throw new IllegalStateException("Can't add session: task in progress for session");
} else {
@@ -481,6 +486,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
public void replace(Cache cache, Object key, Object value) {
+ log.tracev("Adding cache operation: {0} on {1}", CacheOperation.REPLACE, key);
+
CacheTask current = tasks.get(key);
if (current != null) {
switch (current.operation) {
@@ -489,7 +496,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
current.value = value;
return;
case REMOVE:
- throw new IllegalStateException("Can't remove session: task in progress for session");
+ return;
}
} else {
tasks.put(key, new CacheTask(cache, CacheOperation.REPLACE, key, value));
@@ -497,6 +504,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
public void remove(Cache cache, Object key) {
+ log.tracev("Adding cache operation: {0} on {1}", CacheOperation.REMOVE, key);
+
tasks.put(key, new CacheTask(cache, CacheOperation.REMOVE, key, null));
}
@@ -514,6 +523,8 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
}
public void execute() {
+ log.tracev("Executing cache operation: {0} on {1}", operation, key);
+
switch (operation) {
case ADD:
cache.put(key, value);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
index f17cc27..f410bec 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
@@ -36,6 +36,7 @@ import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.Urls;
@@ -117,13 +118,17 @@ public class UserInfoEndpoint {
AccessToken token = null;
try {
- token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
+ token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()), true);
} catch (Exception e) {
throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Token invalid", Status.FORBIDDEN);
}
UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
ClientSessionModel clientSession = session.sessions().getClientSession(token.getClientSession());
+ if (userSession == null || clientSession == null || !AuthenticationManager.isSessionValid(realm, userSession)) {
+ throw new ErrorResponseException(OAuthErrorException.INVALID_GRANT, "Token invalid", Status.FORBIDDEN);
+ }
+
ClientModel clientModel = realm.getClientByClientId(token.getIssuedFor());
UserModel userModel = userSession.getUser();
AccessToken userInfo = new AccessToken();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index 03ddcfd..d988aaa 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -11,6 +11,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.services.ErrorResponseException;
import org.keycloak.util.CertificateUtils;
import org.keycloak.util.PemUtils;
@@ -21,6 +22,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.ByteArrayOutputStream;
@@ -281,10 +283,10 @@ public class ClientAttributeCertificateResource {
throw new NotFoundException("keypair not generated for client");
}
if (privatePem != null && config.getKeyPassword() == null) {
- throw new BadRequestException("Need to specify a key password for jks download");
+ throw new ErrorResponseException("password-missing", "Need to specify a key password for jks download", Response.Status.BAD_REQUEST);
}
if (config.getStorePassword() == null) {
- throw new BadRequestException("Need to specify a store password for jks download");
+ throw new ErrorResponseException("password-missing", "Need to specify a store password for jks download", Response.Status.BAD_REQUEST);
}
final KeyStore keyStore;
try {
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index a6888a9..4476557 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -255,6 +255,9 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
ClientSessionModel clientSession = clientCode.getClientSession();
+
+ session.getContext().setClient(clientSession.getClient());
+
context.getIdp().preprocessFederatedIdentity(session, realmModel, context);
Set<IdentityProviderMapperModel> mappers = realmModel.getIdentityProviderMappersByAlias(context.getIdpConfig().getAlias());
if (mappers != null) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java
index 6b739ba..11ced77 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oidc/UserInfoTest.java
@@ -25,6 +25,7 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.AccessTokenResponse;
@@ -54,8 +55,6 @@ import static org.junit.Assert.assertNotNull;
*/
public class UserInfoTest {
- private static RealmModel realm;
-
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule();
@@ -89,6 +88,27 @@ public class UserInfoTest {
}
@Test
+ public void testSessionExpired() throws Exception {
+ Client client = ClientBuilder.newClient();
+ UriBuilder builder = UriBuilder.fromUri(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT);
+ URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
+ WebTarget grantTarget = client.target(grantUri);
+ AccessTokenResponse accessTokenResponse = executeGrantAccessTokenRequest(grantTarget);
+
+ KeycloakSession session = keycloakRule.startSession();
+ keycloakRule.startSession().sessions().removeUserSessions(session.realms().getRealm("test"));
+ keycloakRule.stopSession(session, true);
+
+ Response response = executeUserInfoRequest(accessTokenResponse.getToken());
+
+ assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
+
+ response.close();
+
+ client.close();
+ }
+
+ @Test
public void testUnsuccessfulUserInfoRequest() throws Exception {
Response response = executeUserInfoRequest("bad");
diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index 277a708..c760152 100755
--- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
@@ -23,15 +23,15 @@
},
"userSessions": {
- "provider" : "${keycloak.userSessions.provider:mem}"
+ "provider" : "${keycloak.userSessions.provider:infinispan}"
},
"realmCache": {
- "provider": "${keycloak.realm.cache.provider:mem}"
+ "provider": "${keycloak.realm.cache.provider:infinispan}"
},
"userCache": {
- "provider": "${keycloak.user.cache.provider:mem}",
+ "provider": "${keycloak.user.cache.provider:infinispan}",
"mem": {
"maxSize": 20000
}