keycloak-uncached
Changes
distribution/demo-dist/pom.xml 25(+25 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json 89(+0 -89)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml 2(+2 -0)
distribution/server-overlay/pom.xml 19(+19 -0)
services/pom.xml 5(+5 -0)
testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl 65(+65 -0)
wildfly/server-subsystem/pom.xml 25(+24 -1)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ModulesListAttributeBuilder.java 32(+32 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ProvidersListAttributeBuilder.java 37(+37 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/JsonConfigConverter.java 262(+262 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java 131(+130 -1)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java 11(+10 -1)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java 47(+42 -5)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java 7(+5 -2)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java 30(+26 -4)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java 212(+210 -2)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java 5(+2 -3)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/MigrateJsonOperation.java 97(+97 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceAddHandler.java 48(+48 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceDefinition.java 70(+70 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceRemoveHandler.java 32(+32 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceAddHandler.java 47(+47 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceDefinition.java 59(+59 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceRemoveHandler.java 32(+32 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceAddHandler.java 60(+60 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceDefinition.java 135(+135 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceRemoveHandler.java 32(+32 -0)
wildfly/server-subsystem/src/main/resources/default-config/keycloak-server-default-config.xml 1(+1 -0)
wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties 31(+31 -0)
wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/JsonConfigConverterTestCase.java 446(+446 -0)
Details
distribution/demo-dist/pom.xml 25(+25 -0)
diff --git a/distribution/demo-dist/pom.xml b/distribution/demo-dist/pom.xml
index 187d62d..71a5a1a 100755
--- a/distribution/demo-dist/pom.xml
+++ b/distribution/demo-dist/pom.xml
@@ -202,6 +202,31 @@
</execution>
</executions>
</plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wildfly-server-subsystem</artifactId>
+ <version>${project.version}</version>
+ <type>jar</type>
+ <includes>default-config/*.xml</includes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl
index 4ef3f0e..855efc0 100755
--- a/distribution/demo-dist/src/main/xslt/standalone.xsl
+++ b/distribution/demo-dist/src/main/xslt/standalone.xsl
@@ -1,19 +1,19 @@
<!--
- ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
- ~ and other contributors as indicated by the @author tags.
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
+~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+~ and other contributors as indicated by the @author tags.
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
@@ -42,7 +42,7 @@
<xsl:template match="//ds:datasources">
<xsl:copy>
<xsl:apply-templates select="node()[name(.)='datasource']"/>
- <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" use-java-context="true">
+ <datasource jndi-name="java:jboss/datasources/KeycloakDS" jta="false" pool-name="KeycloakDS" use-java-context="true">
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
<driver>h2</driver>
<security>
@@ -57,9 +57,7 @@
<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-of select="document('../../../target/dependency/default-config/keycloak-server-default-config.xml')"/>
<subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
</xsl:copy>
diff --git a/distribution/feature-packs/server-feature-pack/assembly.xml b/distribution/feature-packs/server-feature-pack/assembly.xml
index 41ef173..861c698 100644
--- a/distribution/feature-packs/server-feature-pack/assembly.xml
+++ b/distribution/feature-packs/server-feature-pack/assembly.xml
@@ -59,15 +59,4 @@
</includes>
</fileSet>
</fileSets>
-
- <files>
- <file>
- <source>src/main/resources/content/standalone/configuration/keycloak-server.json</source>
- <outputDirectory>content/domain/servers/server-one/configuration</outputDirectory>
- </file>
- <file>
- <source>src/main/resources/content/standalone/configuration/keycloak-server.json</source>
- <outputDirectory>content/domain/servers/server-two/configuration</outputDirectory>
- </file>
- </files>
</assembly>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
index e769a7b..de03ed8 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
@@ -55,6 +55,7 @@
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
<module name="org.jboss.resteasy.resteasy-crypto"/>
<module name="org.jboss.resteasy.resteasy-multipart-provider"/>
+ <module name="org.jboss.dmr"/>
<module name="javax.servlet.api"/>
<module name="com.fasterxml.jackson.core.jackson-core"/>
<module name="com.fasterxml.jackson.core.jackson-annotations"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
index 9f44e59..d82e1a9 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
@@ -28,6 +28,8 @@
</resources>
<dependencies>
+ <module name="com.fasterxml.jackson.core.jackson-core"/>
+ <module name="com.fasterxml.jackson.core.jackson-databind"/>
<module name="javax.api"/>
<module name="org.jboss.staxmapper"/>
<module name="org.jboss.as.controller"/>
diff --git a/distribution/server-overlay/assembly.xml b/distribution/server-overlay/assembly.xml
index 325cadf..162bd00 100755
--- a/distribution/server-overlay/assembly.xml
+++ b/distribution/server-overlay/assembly.xml
@@ -85,10 +85,6 @@
<files>
<file>
- <source>${project.build.directory}/unpacked/keycloak-${project.version}/standalone/configuration/keycloak-server.json</source>
- <outputDirectory>standalone/configuration</outputDirectory>
- </file>
- <file>
<source>${project.build.directory}/unpacked/keycloak-${project.version}/bin/add-user-keycloak.sh</source>
<outputDirectory>bin</outputDirectory>
<destName>add-user-keycloak.sh</destName>
@@ -98,6 +94,11 @@
<outputDirectory>bin</outputDirectory>
<destName>add-user-keycloak.bat</destName>
</file>
+ <file>
+ <source>${project.build.directory}/cli/default-keycloak-subsys-config.cli</source>
+ <outputDirectory>bin</outputDirectory>
+ <destName>default-keycloak-subsys-config.cli</destName>
+ </file>
</files>
</assembly>
diff --git a/distribution/server-overlay/cli/keycloak-install.cli b/distribution/server-overlay/cli/keycloak-install.cli
index c29cd5f..dbb2c32 100644
--- a/distribution/server-overlay/cli/keycloak-install.cli
+++ b/distribution/server-overlay/cli/keycloak-install.cli
@@ -1,5 +1,5 @@
embed-server --server-config=standalone.xml
-/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
+/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",jta=false,driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
/subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
/subsystem=infinispan/cache-container=keycloak/local-cache=realms:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=users:add()
@@ -11,4 +11,4 @@ embed-server --server-config=standalone.xml
/subsystem=infinispan/cache-container=keycloak/local-cache=authorization:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/eviction=EVICTION:add(max-entries=100,strategy=LRU)
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
-/subsystem=keycloak-server:add(web-context=auth)
\ No newline at end of file
+run-batch --file=default-keycloak-subsys-config.cli
\ No newline at end of file
diff --git a/distribution/server-overlay/cli/keycloak-install-ha.cli b/distribution/server-overlay/cli/keycloak-install-ha.cli
index a84a34a..bc7d863 100644
--- a/distribution/server-overlay/cli/keycloak-install-ha.cli
+++ b/distribution/server-overlay/cli/keycloak-install-ha.cli
@@ -1,5 +1,5 @@
embed-server --server-config=standalone-ha.xml
-/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
+/subsystem=datasources/data-source=KeycloakDS/:add(connection-url="jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE",jta=false,driver-name=h2,jndi-name=java:jboss/datasources/KeycloakDS,password=sa,user-name=sa,use-java-context=true)
/subsystem=infinispan/cache-container=keycloak:add(jndi-name="infinispan/Keycloak")
/subsystem=infinispan/cache-container=keycloak/transport=TRANSPORT:add(lock-timeout=60000)
/subsystem=infinispan/cache-container=keycloak/invalidation-cache=realms:add(mode="SYNC")
@@ -11,4 +11,4 @@ embed-server --server-config=standalone-ha.xml
/subsystem=infinispan/cache-container=keycloak/distributed-cache=authorization:add(mode="SYNC",owners="1")
/subsystem=infinispan/cache-container=keycloak/replicated-cache=work:add(mode="SYNC")
/extension=org.keycloak.keycloak-server-subsystem/:add(module=org.keycloak.keycloak-server-subsystem)
-/subsystem=keycloak-server:add(web-context=auth)
\ No newline at end of file
+run-batch --file=default-keycloak-subsys-config.cli
distribution/server-overlay/pom.xml 19(+19 -0)
diff --git a/distribution/server-overlay/pom.xml b/distribution/server-overlay/pom.xml
index 96400d1..900bc29 100755
--- a/distribution/server-overlay/pom.xml
+++ b/distribution/server-overlay/pom.xml
@@ -46,6 +46,25 @@
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
+ <id>unpack</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wildfly-server-subsystem</artifactId>
+ <version>${project.version}</version>
+ <type>jar</type>
+ <includes>cli/*.cli</includes>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ <execution>
<id>unpack-server-dist</id>
<phase>prepare-package</phase>
<goals>
services/pom.xml 5(+5 -0)
diff --git a/services/pom.xml b/services/pom.xml
index a8a009d..57d7984 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -73,6 +73,11 @@
</dependency>
<dependency>
+ <groupId>org.wildfly.core</groupId>
+ <artifactId>wildfly-controller</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index dbfc99c..66a643c 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -56,12 +56,17 @@ import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.*;
+import org.jboss.dmr.ModelNode;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakApplication extends Application {
+ // This param name is defined again in Keycloak Server Subsystem class
+ // org.keycloak.subsystem.server.extension.KeycloakServerDeploymentProcessor. We have this value in
+ // two places to avoid dependency between Keycloak Subsystem and Keycloak Services module.
+ public static final String KEYCLOAK_CONFIG_PARAM_NAME = "org.keycloak.server-subsystem.Config";
private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
@@ -73,7 +78,7 @@ public class KeycloakApplication extends Application {
public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
try {
- loadConfig();
+ loadConfig(context);
this.contextPath = context.getContextPath();
this.sessionFactory = createSessionFactory();
@@ -209,12 +214,18 @@ public class KeycloakApplication extends Application {
return uriInfo.getBaseUriBuilder().replacePath(getContextPath()).build();
}
- public static void loadConfig() {
+ public static void loadConfig(ServletContext context) {
try {
JsonNode node = null;
+
+ String dmrConfig = loadDmrConfig(context);
+ if (dmrConfig != null) {
+ node = new ObjectMapper().readTree(dmrConfig);
+ logger.loadingFrom("standalone.xml or domain.xml");
+ }
String configDir = System.getProperty("jboss.server.config.dir");
- if (configDir != null) {
+ if (node == null && configDir != null) {
File f = new File(configDir + File.separator + "keycloak-server.json");
if (f.isFile()) {
logger.loadingFrom(f.getAbsolutePath());
@@ -233,14 +244,24 @@ public class KeycloakApplication extends Application {
if (node != null) {
Properties properties = new SystemEnvProperties();
Config.init(new JsonConfigProvider(node, properties));
- return;
} else {
- throw new RuntimeException("Config 'keycloak-server.json' not found");
+ throw new RuntimeException("Keycloak config not found.");
}
} catch (IOException e) {
throw new RuntimeException("Failed to load config", e);
}
}
+
+ private static String loadDmrConfig(ServletContext context) {
+ String dmrConfig = context.getInitParameter(KEYCLOAK_CONFIG_PARAM_NAME);
+ if (dmrConfig == null) return null;
+
+ ModelNode dmrConfigNode = ModelNode.fromString(dmrConfig);
+ if (dmrConfigNode.asPropertyList().isEmpty()) return null;
+
+ // note that we need to resolve expressions BEFORE we convert to JSON
+ return dmrConfigNode.resolve().toJSONString(true);
+ }
public static KeycloakSessionFactory createSessionFactory() {
DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory();
diff --git a/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java b/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java
index fd1574a..f7b5a1d 100755
--- a/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java
+++ b/services/src/main/java/org/keycloak/truststore/SSLSocketFactory.java
@@ -29,7 +29,7 @@ import java.net.Socket;
* <p>
* This SSLSocketFactory can only use truststore configured by TruststoreProvider after the ProviderFactory was
* initialized using standard Spi load / init mechanism. That will only happen if "truststore" provider is configured
- * in keycloak-server.json.
+ * in standalone.xml or domain.xml.
* <p>
* If TruststoreProvider is not available this SSLSocketFactory will delegate all operations to javax.net.ssl.SSLSocketFactory.getDefault().
*
diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl
new file mode 100644
index 0000000..f32c036
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl
@@ -0,0 +1,65 @@
+<!--
+~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+~ and other contributors as indicated by the @author tags.
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ version="2.0"
+ exclude-result-prefixes="xalan">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:variable name="nsKS" select="'urn:jboss:domain:keycloak-server'"/>
+ <xsl:variable name="truststoreDefinition">
+ <spi name="truststore">
+ <provider name="file" enabled="true">
+ <properties>
+ <property name="file" value="${{jboss.home.dir}}/standalone/configuration/keycloak.truststore"/>
+ <property name="password" value="secret"/>
+ <property name="hostname-verification-policy" value="WILDCARD"/>
+ <property name="disabled" value="false"/>
+ </properties>
+ </provider>
+ </spi>
+ </xsl:variable>
+
+ <!--inject provider-->
+ <xsl:template match="//*[local-name()='providers']/*[local-name()='provider']">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ <provider>
+ <xsl:text>module:org.keycloak.testsuite.integration-arquillian-testsuite-providers</xsl:text>
+ </provider>
+ </xsl:template>
+
+ <!--inject truststore-->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsKS)]">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ <xsl:copy-of select="$truststoreDefinition"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <!--copy everything else-->
+ <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/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
index 4fab304..eae0512 100644
--- a/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/auth-server/jboss/pom.xml
@@ -143,74 +143,6 @@
</executions>
</plugin>
<plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-antrun-plugin</artifactId>
- <version>1.8</version>
- <executions>
- <execution>
- <id>inject-into-keycloak-server-json</id>
- <phase>process-resources</phase>
- <goals>
- <goal>run</goal>
- </goals>
- <configuration>
- <target>
- <ant antfile="../build.xml" inheritRefs="true">
- <target name="inject-provider"/>
- </ant>
- </target>
- </configuration>
- </execution>
- <execution>
- <id>inject-truststore-into-keycloak-server-json</id>
- <phase>process-resources</phase>
- <goals>
- <goal>run</goal>
- </goals>
- <configuration>
- <target>
- <ant antfile="../build-truststore.xml" inheritRefs="true">
- <target name="inject-truststore"/>
- </ant>
- </target>
- </configuration>
- </execution>
- </executions>
- <dependencies>
- <dependency>
- <groupId>ant-contrib</groupId>
- <artifactId>ant-contrib</artifactId>
- <version>1.0b3</version>
- <exclusions>
- <exclusion>
- <groupId>ant</groupId>
- <artifactId>ant</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.apache.ant</groupId>
- <artifactId>ant-apache-bsf</artifactId>
- <version>1.9.3</version>
- </dependency>
- <dependency>
- <groupId>org.apache.bsf</groupId>
- <artifactId>bsf-api</artifactId>
- <version>3.1</version>
- </dependency>
- <dependency>
- <groupId>rhino</groupId>
- <artifactId>js</artifactId>
- <version>1.7R2</version>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
- </plugin>
- <plugin>
<artifactId>maven-enforcer-plugin</artifactId>
</plugin>
<plugin>
@@ -247,6 +179,25 @@
</transformationSets>
</configuration>
</execution>
+ <execution>
+ <id>inject-provider-and-truststore</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${auth.server.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>${common.resources}/keycloak-server-subsystem.xsl</stylesheet>
+ <outputDir>${auth.server.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
</executions>
</plugin>
<plugin>
@@ -318,7 +269,6 @@
</build>
</profile>
-
<!-- OPT-IN -->
<profile>
<id>server-overlay</id>
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 02c7a70..a753ae9 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -684,7 +684,7 @@ ldap.custom-user-ldap-filter.tooltip=Additional LDAP Filter for filtering search
search-scope=Search Scope
ldap.search-scope.tooltip=For one level, we search for users just in DNs specified by User DNs. For subtree, we search in whole of their subtree. See LDAP documentation for more details
use-truststore-spi=Use Truststore SPI
-ldap.use-truststore-spi.tooltip=Specifies whether LDAP connection will use the truststore SPI with the truststore configured in keycloak-server.json. 'Always' means that it will always use it. 'Never' means that it won't use it. 'Only for ldaps' means that it will use if your connection URL use ldaps. Note even if keycloak-server.json is not configured, the default Java cacerts or certificate specified by 'javax.net.ssl.trustStore' property will be used.
+ldap.use-truststore-spi.tooltip=Specifies whether LDAP connection will use the truststore SPI with the truststore configured in standalone.xml/domain.xml. 'Always' means that it will always use it. 'Never' means that it won't use it. 'Only for ldaps' means that it will use if your connection URL use ldaps. Note even if standalone.xml/domain.xml is not configured, the default Java cacerts or certificate specified by 'javax.net.ssl.trustStore' property will be used.
connection-pooling=Connection Pooling
ldap.connection-pooling.tooltip=Does Keycloak should use connection pooling for accessing LDAP server
ldap.pagination.tooltip=Does the LDAP server support pagination.
wildfly/server-subsystem/pom.xml 25(+24 -1)
diff --git a/wildfly/server-subsystem/pom.xml b/wildfly/server-subsystem/pom.xml
index 5601739..05b0fc0 100755
--- a/wildfly/server-subsystem/pom.xml
+++ b/wildfly/server-subsystem/pom.xml
@@ -47,12 +47,35 @@
<include>**/*TestCase.java</include>
</includes>
</configuration>
- </plugin>
+ </plugin>
</plugins>
+ <filters>
+ <filter>src/main/config/default-server-subsys-config.properties</filter>
+ </filters>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
</build>
<dependencies>
<dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.wildfly.core</groupId>
<artifactId>wildfly-controller</artifactId>
<scope>provided</scope>
diff --git a/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties b/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
new file mode 100644
index 0000000..2355ab0
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/config/default-server-subsys-config.properties
@@ -0,0 +1,73 @@
+# IMPORTANT: If you change this file you should also make equivalent changes
+# to src/main/resources/cli/default-keycloak-subsys-config.cli
+# The CLI file is packaged with the subsystem and extracted by the overlay distribution.
+
+keycloak.server.subsys.default.config=\
+<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">\
+ <web-context>auth</web-context>\
+ <providers>\
+ <provider>classpath:${jboss.home.dir}/providers/*</provider>\
+ </providers>\
+ <master-realm-name>master</master-realm-name>\
+ <scheduled-task-interval>900</scheduled-task-interval>\
+ <theme>\
+ <staticMaxAge>2592000</staticMaxAge>\
+ <cacheThemes>true</cacheThemes>\
+ <cacheTemplates>true</cacheTemplates>\
+ <dir>${jboss.home.dir}/themes</dir>\
+ </theme>\
+ <spi name="eventsStore">\
+ <default-provider>jpa</default-provider>\
+ <provider name="jpa" enabled="true">\
+ <properties>\
+ <property name="exclude-events" value="["REFRESH_TOKEN"]"/>\
+ </properties>\
+ </provider>\
+ </spi>\
+ <spi name="realm">\
+ <default-provider>jpa</default-provider>\
+ </spi>\
+ <spi name="user">\
+ <default-provider>jpa</default-provider>\
+ </spi>\
+ <spi name="userCache">\
+ <provider name="default" enabled="true"/>\
+ </spi>\
+ <spi name="userSessionPersister">\
+ <default-provider>jpa</default-provider>\
+ </spi>\
+ <spi name="authorizationPersister">\
+ <default-provider>jpa</default-provider>\
+ </spi>\
+ <spi name="timer">\
+ <default-provider>basic</default-provider>\
+ </spi>\
+ <spi name="connectionsHttpClient">\
+ <provider name="default" enabled="true"/>\
+ </spi>\
+ <spi name="connectionsJpa">\
+ <provider name="default" enabled="true">\
+ <properties>\
+ <property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>\
+ <property name="initializeEmpty" value="true"/>\
+ <property name="migrationStrategy" value="update"/>\
+ <property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>\
+ </properties>\
+ </provider>\
+ </spi>\
+ <spi name="realmCache">\
+ <provider name="default" enabled="true"/>\
+ </spi>\
+ <spi name="connectionsInfinispan">\
+ <default-provider>default</default-provider>\
+ <provider name="default" enabled="true">\
+ <properties>\
+ <property name="cacheContainer" value="java:comp/env/infinispan/Keycloak"/>\
+ </properties>\
+ </provider>\
+ </spi>\
+ <spi name="jta-lookup">\
+ <default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>\
+ <provider name="jboss" enabled="true"/>\
+ </spi>\
+</subsystem>\
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ModulesListAttributeBuilder.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ModulesListAttributeBuilder.java
new file mode 100644
index 0000000..d47ea10
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ModulesListAttributeBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.attributes;
+
+import org.jboss.as.controller.StringListAttributeDefinition;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ModulesListAttributeBuilder extends StringListAttributeDefinition.Builder {
+ public ModulesListAttributeBuilder() {
+ super("modules");
+ setAllowExpression(true);
+ setAllowNull(true);
+ }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ProvidersListAttributeBuilder.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ProvidersListAttributeBuilder.java
new file mode 100644
index 0000000..2b4952e
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/attributes/ProvidersListAttributeBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.attributes;
+
+import org.jboss.as.controller.StringListAttributeDefinition;
+import org.jboss.dmr.ModelNode;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProvidersListAttributeBuilder extends StringListAttributeDefinition.Builder {
+ public ProvidersListAttributeBuilder() {
+ super("providers");
+ ModelNode provider = new ModelNode();
+ provider.add("classpath:${jboss.home.dir}/providers/*");
+ this.defaultValue = provider;
+ setAllowExpression(true);
+ setAllowNull(true);
+ }
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/JsonConfigConverter.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/JsonConfigConverter.java
new file mode 100644
index 0000000..aa4d3f5
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/JsonConfigConverter.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.subsystem.server.extension;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION;
+
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.PROVIDERS;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.MASTER_REALM_NAME;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.SCHEDULED_TASK_INTERVAL;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.CACHE_TEMPLATES;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.CACHE_THEMES;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.DEFAULT;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.DIR;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.MODULES;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.STATIC_MAX_AGE;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.WELCOME_THEME;
+
+/**
+ * Converts json representation of Keycloak config to DMR operations.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class JsonConfigConverter {
+
+ private static final List<String> NON_SPI_LIST = new ArrayList<>();
+
+ static {
+ NON_SPI_LIST.add("providers");
+ NON_SPI_LIST.add("admin");
+ NON_SPI_LIST.add("theme");
+ NON_SPI_LIST.add("scheduled");
+ }
+
+ /**
+ * Convert keycloak-server.json to DMR operations that write to standalone.xml
+ * or domain.xml.
+ *
+ * @param json The json representation of the config.
+ * @param subsysAddress The management model address of the keycloak-server subsystem.
+ * @return A list of DMR operations.
+ * @throws IOException If the json can not be parsed.
+ */
+ public static List<ModelNode> convertJsonConfig(String json, PathAddress subsysAddress) throws IOException {
+ List<ModelNode> list = new ArrayList<>();
+
+ JsonNode root = new ObjectMapper().readTree(json);
+
+ list.add(masterRealmName(root, subsysAddress));
+ list.add(scheduledTaskInterval(root, subsysAddress));
+ list.add(providers(root, subsysAddress));
+ list.add(theme(root, subsysAddress.append(ThemeResourceDefinition.TAG_NAME,
+ ThemeResourceDefinition.RESOURCE_NAME)));
+ list.addAll(spis(root, subsysAddress));
+
+ return list;
+ }
+
+ private static ModelNode masterRealmName(JsonNode root, PathAddress addr) {
+ JsonNode targetNode = getNode(root, "admin", "realm");
+ String value = MASTER_REALM_NAME.getDefaultValue().asString();
+ if (targetNode != null) value = targetNode.asText(value);
+
+ ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr);
+ op.get("name").set(MASTER_REALM_NAME.getName());
+ op.get("value").set(value);
+ return op;
+ }
+
+ private static ModelNode scheduledTaskInterval(JsonNode root, PathAddress addr) {
+ JsonNode targetNode = getNode(root, "scheduled", "interval");
+ Long value = SCHEDULED_TASK_INTERVAL.getDefaultValue().asLong();
+ if (targetNode != null) value = targetNode.asLong(value);
+
+ ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr);
+ op.get("name").set(SCHEDULED_TASK_INTERVAL.getName());
+ op.get("value").set(value);
+ return op;
+ }
+
+ private static ModelNode providers(JsonNode root, PathAddress addr) {
+ JsonNode targetNode = getNode(root, "providers");
+ ModelNode value = PROVIDERS.getDefaultValue();
+ if (targetNode != null && targetNode.isArray()) {
+ value = new ModelNode();
+ for (JsonNode node : targetNode) {
+ value.add(node.asText());
+ }
+ }
+
+ ModelNode op = Util.createOperation(WRITE_ATTRIBUTE_OPERATION, addr);
+ op.get("name").set(PROVIDERS.getName());
+ op.get("value").set(value);
+ return op;
+ }
+
+ private static ModelNode theme(JsonNode root, PathAddress addr) {
+ JsonNode themeNode = getNode(root, "theme");
+ ModelNode op = Util.createAddOperation(addr);
+
+ JsonNode targetNode = getNode(themeNode, "staticMaxAge");
+ Long lValue = STATIC_MAX_AGE.getDefaultValue().asLong();
+ if (targetNode != null) lValue = targetNode.asLong(lValue);
+ op.get(STATIC_MAX_AGE.getName()).set(lValue);
+
+ targetNode = getNode(themeNode, "cacheTemplates");
+ Boolean bValue = CACHE_TEMPLATES.getDefaultValue().asBoolean();
+ if (targetNode != null) bValue = targetNode.asBoolean(bValue);
+ op.get(CACHE_TEMPLATES.getName()).set(bValue);
+
+ targetNode = getNode(themeNode, "cacheThemes");
+ bValue = CACHE_THEMES.getDefaultValue().asBoolean();
+ if (targetNode != null) bValue = targetNode.asBoolean(bValue);
+ op.get(CACHE_THEMES.getName()).set(bValue);
+
+ targetNode = getNode(themeNode, "folder", "dir");
+ String sValue = DIR.getDefaultValue().asString();
+ if (targetNode != null) sValue = targetNode.asText(sValue);
+ op.get(DIR.getName()).set(sValue);
+
+ targetNode = getNode(themeNode, "welcomeTheme");
+ if (targetNode != null) op.get(WELCOME_THEME.getName()).set(targetNode.asText());
+
+ targetNode = getNode(themeNode, "default");
+ if (targetNode != null) op.get(DEFAULT.getName()).set(targetNode.asText());
+
+ targetNode = getNode(themeNode, "module", "modules");
+ if (targetNode != null && targetNode.isArray()) {
+ op.get(MODULES.getName()).set(themeModules(targetNode));
+ }
+
+ return op;
+ }
+
+ private static ModelNode themeModules(JsonNode modulesNode) {
+ ModelNode modules = new ModelNode();
+ for (JsonNode node : modulesNode) {
+ modules.add(node.asText());
+ }
+ return modules;
+ }
+
+ private static Collection<ModelNode> spis(JsonNode root, PathAddress addr) {
+ List<ModelNode> spis = new ArrayList<>();
+
+ Iterator<String> spiIterator = root.fieldNames();
+ while (spiIterator.hasNext()) {
+ String spiName = spiIterator.next();
+ if (NON_SPI_LIST.contains(spiName)) continue;
+
+ PathAddress spiAddr = addr.append("spi", spiName);
+ spis.addAll(spi(root, spiAddr, spiName));
+ }
+
+ return spis;
+ }
+
+ private static List<ModelNode> spi(JsonNode root, PathAddress spiAddr, String spiName) {
+ List<ModelNode> spiAndProviders = new ArrayList<>();
+ ModelNode op = Util.createAddOperation(spiAddr);
+ spiAndProviders.add(op);
+
+ Iterator<String> providerIterator = root.get(spiName).fieldNames();
+ while (providerIterator.hasNext()) {
+ String providerName = providerIterator.next();
+ if ("provider".equals(providerName)) {
+ op.get(SpiResourceDefinition.DEFAULT_PROVIDER.getName()).set(getNode(root, spiName, "provider").asText());
+ } else {
+ PathAddress providerAddr = spiAddr.append("provider", providerName);
+ spiAndProviders.add(spiProvider(getNode(root, spiName, providerName), providerAddr));
+ }
+ }
+
+ return spiAndProviders;
+ }
+
+ private static ModelNode spiProvider(JsonNode providerNode, PathAddress providerAddr) {
+ ModelNode op = Util.createAddOperation(providerAddr);
+
+ ModelNode properties = new ModelNode();
+
+ Iterator<String> propNames = providerNode.fieldNames();
+ while (propNames.hasNext()) {
+ String propName = propNames.next();
+
+ if ("enabled".equals(propName)) {
+ op.get(ProviderResourceDefinition.ENABLED.getName()).set(providerNode.get(propName).asBoolean());
+ } else {
+ if (providerNode.get(propName).isArray()) {
+ properties.get(propName).set(makeArrayText(providerNode.get(propName)));
+ } else {
+ properties.get(propName).set(providerNode.get(propName).asText());
+ }
+ }
+ }
+
+ if (properties.isDefined() && !properties.asPropertyList().isEmpty()) {
+ op.get("properties").set(properties);
+ }
+
+ if (!op.hasDefined(ProviderResourceDefinition.ENABLED.getName())) {
+ op.get(ProviderResourceDefinition.ENABLED.getName()).set(ProviderResourceDefinition.ENABLED.getDefaultValue());
+ }
+
+ return op;
+ }
+
+ private static String makeArrayText(JsonNode arrayNode) {
+ StringBuilder builder = new StringBuilder("[");
+
+ Iterator<JsonNode> nodes = arrayNode.iterator();
+ while (nodes.hasNext()) {
+ JsonNode node = nodes.next();
+ builder.append("\"");
+ builder.append(node.asText());
+ builder.append("\"");
+ if (nodes.hasNext()) builder.append(",");
+ }
+ builder.append("]");
+
+ return builder.toString();
+ }
+
+ private static JsonNode getNode(JsonNode root, String... path) {
+ if (root == null) {
+ return null;
+ }
+ JsonNode n = root;
+ for (String p : path) {
+ n = n.get(p);
+ if (n == null) {
+ return null;
+ }
+ }
+ return n;
+ }
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java
index 03a2fe2..0eec2ee 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakAdapterConfigService.java
@@ -16,9 +16,15 @@
*/
package org.keycloak.subsystem.server.extension;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.dmr.ModelNode;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import org.jboss.dmr.Property;
+
/**
* This service keeps track of the entire Keycloak management model so as to provide
- * adapter configuration to each deployment at deploy time.
+ * configuration to the Keycloak Server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/
@@ -27,6 +33,8 @@ public final class KeycloakAdapterConfigService {
static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
static final String DEPLOYMENT_NAME = "keycloak-server.war";
+
+ static ModelNode fullConfig = new ModelNode();
private String webContext;
@@ -34,6 +42,127 @@ public final class KeycloakAdapterConfigService {
private KeycloakAdapterConfigService() {
}
+ void updateConfig(ModelNode operation, ModelNode config) {
+ PathAddress address = PathAddress.pathAddress(operation.get(ADDRESS));
+ address = address.subAddress(1); // remove root (subsystem=keycloak-server)
+
+ ModelNode newConfig = fullConfig.clone();
+ ModelNode subNode = newConfig;
+ for (PathElement pathElement : address) {
+ subNode = subNode.get(pathElement.getKey(), pathElement.getValue());
+ }
+
+ subNode.set(config.clone());
+
+ // remove undefined properties
+ for (Property prop : subNode.asPropertyList()) {
+ if (!prop.getValue().isDefined()) {
+ subNode.remove(prop.getName());
+ }
+ }
+
+ fullConfig = newConfig;
+ }
+
+ ModelNode getConfig() {
+ ModelNode copy = fullConfig.clone();
+ //System.out.println("******** BEFORE *************");
+ //System.out.println(copy);
+ //System.out.println("*****************************");
+ copy.remove("web-context");
+ massageScheduledTaskInterval(copy);
+ massageMasterRealm(copy);
+ massageTheme(copy);
+ massageSpis(copy);
+ //System.out.println("******** JSON *************");
+ //System.out.println(copy.resolve().toJSONString(false));
+ //System.out.println("**********************");
+ return copy;
+ }
+
+ // The "massage" methods rearrange the model so that everything will
+ // be where the Keycloak server's Config interface expects it to be.
+
+ private void massageScheduledTaskInterval(ModelNode copy) {
+ if (!copy.hasDefined("scheduled-task-intervale")) return;
+ ModelNode taskInterval = copy.remove("scheduled-task-interval");
+ copy.get("scheduled", "interval").set(taskInterval);
+ }
+
+ private void massageMasterRealm(ModelNode copy) {
+ if (!copy.hasDefined("master-realm-name")) return;
+ ModelNode master = copy.remove("master-realm-name");
+ copy.get("admin", "realm").set(master);
+ }
+
+ private void massageTheme(ModelNode copy) {
+ if (!copy.hasDefined("theme")) return;
+ if (!copy.get("theme").hasDefined("defaults")) return;
+
+ ModelNode themeDefaults = copy.get("theme", "defaults");
+ copy.get("theme").set(themeDefaults);
+
+ if (copy.has("theme", "dir")) {
+ ModelNode dir = copy.get("theme", "dir");
+ copy.get("theme", "folder", "dir").set(dir);
+ copy.get("theme").remove("dir");
+ }
+
+ if (copy.has("theme", "modules")) {
+ ModelNode modules = copy.get("theme").remove("modules");
+ copy.get("theme", "module", "modules").set(modules);
+ }
+ }
+
+ private void massageSpis(ModelNode copy) {
+ if (!copy.hasDefined("spi")) return;
+ ModelNode spis = copy.remove("spi");
+
+ for (Property prop : spis.asPropertyList()) {
+ ModelNode spi = prop.getValue();
+
+ if (spi.has("provider")) {
+ massageProviders(spi);
+ }
+
+ if (spi.has("default-provider")) {
+ ModelNode defaultProvider = spi.remove("default-provider");
+ spi.get("provider").set(defaultProvider);
+ }
+
+ copy.get(prop.getName()).set(spi);
+ }
+ }
+
+ private void massageProviders(ModelNode spi) {
+ if (!spi.hasDefined("provider")) return;
+ ModelNode providers = spi.remove("provider");
+ for (Property prop : providers.asPropertyList()) {
+ ModelNode provider = prop.getValue();
+ if (provider.has("properties")) {
+ massageProviderProps(provider);
+ }
+ spi.get(prop.getName()).set(provider);
+ }
+ }
+
+ private void massageProviderProps(ModelNode provider) {
+ if (!provider.hasDefined("properties")) return;
+ ModelNode providerProps = provider.remove("properties");
+ for (Property prop : providerProps.asPropertyList()) {
+ ModelNode value = prop.getValue();
+ if (isArray(value.asString().trim())) {
+ provider.get(prop.getName()).set(ModelNode.fromString(value.asString()).asList());
+ } else {
+ provider.get(prop.getName()).set(value);
+ }
+ }
+ }
+
+ private boolean isArray(String value) {
+ return value.startsWith("[") && value.endsWith("]");
+ }
+
void setWebContext(String webContext) {
this.webContext = webContext;
}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java
index c6a3cdb..bdfe47c 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakExtension.java
@@ -26,6 +26,7 @@ import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
import org.jboss.as.controller.parsing.ExtensionParsingContext;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
import static org.keycloak.subsystem.server.logging.KeycloakLogger.ROOT_LOGGER;
@@ -44,6 +45,10 @@ public class KeycloakExtension implements Extension {
private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1,1,0);
+
+ static final ThemeResourceDefinition THEME_DEFINITION = new ThemeResourceDefinition();
+ static final SpiResourceDefinition SPI_DEFINITION = new SpiResourceDefinition();
+ static final ProviderResourceDefinition PROVIDER_DEFINITION = new ProviderResourceDefinition();
static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@@ -69,7 +74,11 @@ public class KeycloakExtension implements Extension {
ROOT_LOGGER.debug("Activating Keycloak Extension");
final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION);
- subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+ ManagementResourceRegistration subsystemRegistration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+ subsystemRegistration.registerSubModel(THEME_DEFINITION);
+ ManagementResourceRegistration spiRegistration = subsystemRegistration.registerSubModel(SPI_DEFINITION);
+ spiRegistration.registerSubModel(PROVIDER_DEFINITION);
+
subsystem.registerXMLElementWriter(PARSER);
}
}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
index f99cab3..1bc2211 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakServerDeploymentProcessor.java
@@ -16,41 +16,78 @@
*/
package org.keycloak.subsystem.server.extension;
+import java.util.ArrayList;
+import java.util.List;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.web.common.WarMetaData;
+import org.jboss.metadata.javaee.spec.ParamValueMetaData;
+import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
/**
- * DUP responsible for setting the web context of a Keycloak auth server.
+ * DUP responsible for setting the web context of a Keycloak auth server and
+ * passing the Keycloak configuration to the Keycloak server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class KeycloakServerDeploymentProcessor implements DeploymentUnitProcessor {
+ // This param name is defined again in Keycloak Services class
+ // org.keycloak.services.resources.KeycloakApplication. We have this value in
+ // two places to avoid dependency between Keycloak Subsystem and Keyclaok Services module.
+ public static final String KEYCLOAK_CONFIG_PARAM_NAME = "org.keycloak.server-subsystem.Config";
+
private static final ServiceName cacheContainerService = ServiceName.of("jboss", "infinispan", "keycloak");
-
+
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
- KeycloakAdapterConfigService config = KeycloakAdapterConfigService.INSTANCE;
+ KeycloakAdapterConfigService configService = KeycloakAdapterConfigService.INSTANCE;
String deploymentName = deploymentUnit.getName();
- if (!config.isKeycloakServerDeployment(deploymentName)) {
+ if (!configService.isKeycloakServerDeployment(deploymentName)) {
return;
}
final EEModuleDescription description = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
- String webContext = config.getWebContext();
+ String webContext = configService.getWebContext();
if (webContext == null) {
throw new DeploymentUnitProcessingException("Can't determine web context/module for Keycloak Server");
}
description.setModuleName(webContext);
addInfinispanCaches(phaseContext);
+ addConfiguration(deploymentUnit, configService);
+ }
+
+ private void addConfiguration(DeploymentUnit deploymentUnit, KeycloakAdapterConfigService configService) throws DeploymentUnitProcessingException {
+ WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+ if (warMetaData == null) {
+ throw new DeploymentUnitProcessingException("WarMetaData not found for KeycloakServer.");
+ }
+
+ JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+ if (webMetaData == null) {
+ webMetaData = new JBossWebMetaData();
+ warMetaData.setMergedJBossWebMetaData(webMetaData);
+ }
+
+ List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
+ if (contextParams == null) {
+ contextParams = new ArrayList<ParamValueMetaData>();
+ }
+
+ ParamValueMetaData param = new ParamValueMetaData();
+ param.setParamName(KEYCLOAK_CONFIG_PARAM_NAME);
+ param.setParamValue(configService.getConfig().toString());
+ contextParams.add(param);
+
+ webMetaData.setContextParams(contextParams);
}
private void addInfinispanCaches(DeploymentPhaseContext context) {
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
index 3c6dc34..6d75e28 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
@@ -62,9 +62,10 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
}, OperationContext.Stage.RUNTIME);
}
- protected void populateModel(final OperationContext context, final ModelNode operation, final Resource resource) throws OperationFailedException {
+ @Override
+ protected void populateModel(final OperationContext context, final ModelNode operation, final Resource resource) throws OperationFailedException {
ModelNode model = resource.getModel();
-
+
// set attribute values from parsed model
for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
attrDef.validateAndSet(operation, model);
@@ -89,5 +90,7 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
ServerUtil serverUtil = new ServerUtil(operation);
serverUtil.addStepToUploadServerWar(context);
KeycloakAdapterConfigService.INSTANCE.setWebContext(webContext);
+
+ KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
}
}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java
index 6fcee7b..3dc4917 100644
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemDefinition.java
@@ -29,6 +29,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.jboss.as.controller.StringListAttributeDefinition;
+import org.keycloak.subsystem.server.attributes.ProvidersListAttributeBuilder;
/**
* Definition of subsystem=keycloak-server.
@@ -44,15 +46,34 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
.setRestartAllServices()
.build();
- static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
+ static final StringListAttributeDefinition PROVIDERS = new ProvidersListAttributeBuilder().build();
+
+ static final SimpleAttributeDefinition MASTER_REALM_NAME =
+ new SimpleAttributeDefinitionBuilder("master-realm-name", ModelType.STRING, true)
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode("master"))
+ .setRestartAllServices()
+ .build();
+
+ static final SimpleAttributeDefinition SCHEDULED_TASK_INTERVAL =
+ new SimpleAttributeDefinitionBuilder("scheduled-task-interval", ModelType.LONG, true)
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode("900"))
+ .setRestartAllServices()
+ .build();
+
+ static final List<AttributeDefinition> ALL_ATTRIBUTES = new ArrayList<AttributeDefinition>();
static {
ALL_ATTRIBUTES.add(WEB_CONTEXT);
+ ALL_ATTRIBUTES.add(PROVIDERS);
+ ALL_ATTRIBUTES.add(MASTER_REALM_NAME);
+ ALL_ATTRIBUTES.add(SCHEDULED_TASK_INTERVAL);
}
- private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
+ private static final Map<String, AttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, AttributeDefinition>();
static {
- for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
+ for (AttributeDefinition def : ALL_ATTRIBUTES) {
DEFINITION_LOOKUP.put(def.getXmlName(), def);
}
}
@@ -71,6 +92,7 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+ resourceRegistration.registerOperationHandler(MigrateJsonOperation.DEFINITION, new MigrateJsonOperation());
}
@Override
@@ -81,7 +103,7 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
}
}
- public static SimpleAttributeDefinition lookup(String name) {
+ public static AttributeDefinition lookup(String name) {
return DEFINITION_LOOKUP.get(name);
}
}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java
index 6db6e57..0f4ea8c 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemParser.java
@@ -29,9 +29,26 @@ import org.jboss.staxmapper.XMLExtendedStreamWriter;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.List;
+import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.PropertiesAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.dmr.Property;
import static org.keycloak.subsystem.server.extension.KeycloakExtension.PATH_SUBSYSTEM;
import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.WEB_CONTEXT;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.PROVIDERS;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.MASTER_REALM_NAME;
+import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.SCHEDULED_TASK_INTERVAL;
+
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.MODULES;
+
+import static org.keycloak.subsystem.server.extension.SpiResourceDefinition.DEFAULT_PROVIDER;
+
+import static org.keycloak.subsystem.server.extension.ProviderResourceDefinition.ENABLED;
+import static org.keycloak.subsystem.server.extension.ProviderResourceDefinition.PROPERTIES;
/**
* The subsystem parser, which uses stax to read and write to and from xml
@@ -51,12 +68,116 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
if (reader.getLocalName().equals(WEB_CONTEXT.getXmlName())) {
WEB_CONTEXT.parseAndSetParameter(reader.getElementText(), addKeycloakSub, reader);
+ } else if (reader.getLocalName().equals(PROVIDERS.getXmlName())) {
+ readProviders(reader, addKeycloakSub);
+ } else if (reader.getLocalName().equals(MASTER_REALM_NAME.getXmlName())) {
+ MASTER_REALM_NAME.parseAndSetParameter(reader.getElementText(), addKeycloakSub, reader);
+ } else if (reader.getLocalName().equals(SCHEDULED_TASK_INTERVAL.getXmlName())) {
+ SCHEDULED_TASK_INTERVAL.parseAndSetParameter(reader.getElementText(), addKeycloakSub, reader);
+ } else if (reader.getLocalName().equals(ThemeResourceDefinition.TAG_NAME)) {
+ readTheme(list, reader);
+ } else if (reader.getLocalName().equals(SpiResourceDefinition.TAG_NAME)) {
+ readSpi(list, reader);
} else {
throw new XMLStreamException("Unknown keycloak-server subsystem tag: " + reader.getLocalName());
}
}
}
-
+
+ private void readProviders(final XMLExtendedStreamReader reader, ModelNode addKeycloakSub) throws XMLStreamException {
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ PROVIDERS.parseAndAddParameterElement(reader.getElementText(),addKeycloakSub, reader);
+ }
+ }
+
+ private void readTheme(final List<ModelNode> list, final XMLExtendedStreamReader reader) throws XMLStreamException {
+ ModelNode addThemeDefaults = new ModelNode();
+ addThemeDefaults.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+ PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+ PathElement.pathElement(ThemeResourceDefinition.TAG_NAME, ThemeResourceDefinition.RESOURCE_NAME));
+ addThemeDefaults.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+ list.add(addThemeDefaults);
+
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ String tagName = reader.getLocalName();
+ if (MODULES.getName().equals(tagName)) {
+ readModules(reader, addThemeDefaults);
+ continue;
+ }
+
+ SimpleAttributeDefinition def = KeycloakExtension.THEME_DEFINITION.lookup(tagName);
+ if (def == null) throw new XMLStreamException("Unknown theme tag " + tagName);
+ def.parseAndSetParameter(reader.getElementText(), addThemeDefaults, reader);
+ }
+ }
+
+ private void readModules(final XMLExtendedStreamReader reader, ModelNode addThemeDefaults) throws XMLStreamException {
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ MODULES.parseAndAddParameterElement(reader.getElementText(),addThemeDefaults, reader);
+ }
+ }
+
+ private void readSpi(final List<ModelNode> list, final XMLExtendedStreamReader reader) throws XMLStreamException {
+ String spiName = ParseUtils.requireAttributes(reader, "name")[0];
+ ModelNode addSpi = new ModelNode();
+ addSpi.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+ PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+ PathElement.pathElement(SpiResourceDefinition.TAG_NAME, spiName));
+ addSpi.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+ list.add(addSpi);
+
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ if (reader.getLocalName().equals(DEFAULT_PROVIDER.getXmlName())) {
+ DEFAULT_PROVIDER.parseAndSetParameter(reader.getElementText(), addSpi, reader);
+ } else if (reader.getLocalName().equals(ProviderResourceDefinition.TAG_NAME)) {
+ readProvider(list, spiName, reader);
+ }
+ }
+ }
+
+ private void readProvider(final List<ModelNode> list, String spiName, final XMLExtendedStreamReader reader) throws XMLStreamException {
+ String[] attributes = ParseUtils.requireAttributes(reader, "name", ENABLED.getXmlName());
+ String providerName = attributes[0];
+ String enabled = attributes[1];
+
+ ModelNode addProvider = new ModelNode();
+ addProvider.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+ PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+ PathElement.pathElement(SpiResourceDefinition.TAG_NAME, spiName),
+ PathElement.pathElement(ProviderResourceDefinition.TAG_NAME, providerName));
+ addProvider.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+ addProvider.get(ENABLED.getName()).set(Boolean.valueOf(enabled));
+ list.add(addProvider);
+
+ while (nextTag(reader) != END_ELEMENT) {
+ if (reader.getLocalName().equals(PROPERTIES.getXmlName())) {
+ readProperties(PROPERTIES, addProvider, reader);
+ }
+ }
+ }
+
+ private void readProperties(final PropertiesAttributeDefinition attrDef, ModelNode addOp, final XMLExtendedStreamReader reader) throws XMLStreamException {
+ while (nextTag(reader) != END_ELEMENT) {
+ int attrCount = reader.getAttributeCount();
+ if (attrCount != 2) throw new XMLStreamException("Property must have only two attributes");
+ String name = "";
+ String value = "";
+ for (int i=0 ; i < 2; i++) {
+ String attrName = reader.getAttributeLocalName(i);
+ String attrValue = reader.getAttributeValue(i);
+ if (attrName.equals("name")) {
+ name = attrValue;
+ } else if (attrName.equals("value")) {
+ value = attrValue;
+ } else {
+ throw new XMLStreamException("Property can only have attributes named 'name' and 'value'");
+ }
+ }
+ attrDef.parseAndAddParameterElement(name, value, addOp, reader);
+ nextTag(reader);
+ }
+ }
+
// used for debugging
private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException {
return reader.nextTag();
@@ -69,9 +190,64 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
writeWebContext(writer, context);
+ writeList(writer, context.getModelNode(), PROVIDERS, "provider");
+ writeAdmin(writer, context);
+ writeScheduledTaskInterval(writer, context);
+ writeThemeDefaults(writer, context);
+ writeSpis(writer, context);
+ writer.writeEndElement();
+ }
+
+ private void writeThemeDefaults(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+ if (!context.getModelNode().get(ThemeResourceDefinition.TAG_NAME).isDefined()) {
+ return;
+ }
+
+ writer.writeStartElement(ThemeResourceDefinition.TAG_NAME);
+ ModelNode themeElements = context.getModelNode().get(ThemeResourceDefinition.TAG_NAME, ThemeResourceDefinition.RESOURCE_NAME);
+ for (AttributeDefinition def : ThemeResourceDefinition.ALL_ATTRIBUTES) {
+ if (themeElements.hasDefined(def.getName())) {
+ if (def == MODULES) {
+ ModelNode themeContext = context.getModelNode().get("theme", "defaults");
+ writeList(writer, themeContext, def, "module");
+ } else {
+ def.marshallAsElement(themeElements, writer);
+ }
+ }
+ }
writer.writeEndElement();
}
+ private void writeSpis(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+ if (!context.getModelNode().get(SpiResourceDefinition.TAG_NAME).isDefined()) {
+ return;
+ }
+
+ for (Property spi : context.getModelNode().get(SpiResourceDefinition.TAG_NAME).asPropertyList()) {
+ writer.writeStartElement(SpiResourceDefinition.TAG_NAME);
+ writer.writeAttribute("name", spi.getName());
+ ModelNode spiElements = spi.getValue();
+ DEFAULT_PROVIDER.marshallAsElement(spiElements, writer);
+ writeProviders(writer, spiElements);
+ writer.writeEndElement();
+ }
+ }
+
+ private void writeProviders(XMLExtendedStreamWriter writer, ModelNode spiElements) throws XMLStreamException {
+ if (!spiElements.get(ProviderResourceDefinition.TAG_NAME).isDefined()) {
+ return;
+ }
+
+ for (Property provider : spiElements.get(ProviderResourceDefinition.TAG_NAME).asPropertyList()) {
+ writer.writeStartElement(ProviderResourceDefinition.TAG_NAME);
+ writer.writeAttribute("name", provider.getName());
+ ModelNode providerElements = provider.getValue();
+ ENABLED.marshallAsAttribute(providerElements, writer);
+ PROPERTIES.marshallAsElement(providerElements, writer);
+ writer.writeEndElement();
+ }
+ }
+
private void writeWebContext(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
if (!context.getModelNode().get(WEB_CONTEXT.getName()).isDefined()) {
return;
@@ -79,4 +255,36 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
WEB_CONTEXT.marshallAsElement(context.getModelNode(), writer);
}
-}
+
+ private void writeAdmin(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+ if (!context.getModelNode().get(MASTER_REALM_NAME.getName()).isDefined()) {
+ return;
+ }
+
+ MASTER_REALM_NAME.marshallAsElement(context.getModelNode(), writer);
+ }
+
+ private void writeScheduledTaskInterval(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+ if (!context.getModelNode().get(SCHEDULED_TASK_INTERVAL.getName()).isDefined()) {
+ return;
+ }
+
+ SCHEDULED_TASK_INTERVAL.marshallAsElement(context.getModelNode(), writer);
+ }
+
+ private void writeList(XMLExtendedStreamWriter writer, ModelNode context, AttributeDefinition def, String elementName) throws XMLStreamException {
+ if (!context.get(def.getName()).isDefined()) {
+ return;
+ }
+
+ writer.writeStartElement(def.getXmlName());
+ ModelNode modules = context.get(def.getName());
+ for (ModelNode module : modules.asList()) {
+ writer.writeStartElement(elementName);
+ writer.writeCharacters(module.asString());
+ writer.writeEndElement();
+ }
+ writer.writeEndElement();
+ }
+
+}
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java
index 8469198..3304616 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemWriteAttributeHandler.java
@@ -17,7 +17,6 @@
package org.keycloak.subsystem.server.extension;
import org.jboss.as.controller.AttributeDefinition;
-import org.jboss.as.controller.SimpleAttributeDefinition;
import java.util.List;
import org.jboss.as.controller.ModelOnlyWriteAttributeHandler;
@@ -33,7 +32,7 @@ import org.jboss.dmr.ModelNode;
*/
public class KeycloakSubsystemWriteAttributeHandler extends ModelOnlyWriteAttributeHandler { //extends ReloadRequiredWriteAttributeHandler {
- public KeycloakSubsystemWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
+ public KeycloakSubsystemWriteAttributeHandler(List<AttributeDefinition> definitions) {
this(definitions.toArray(new AttributeDefinition[definitions.size()]));
}
@@ -59,7 +58,7 @@ public class KeycloakSubsystemWriteAttributeHandler extends ModelOnlyWriteAttrib
}
private boolean attribNotChanging(String attributeName, ModelNode newValue, ModelNode oldValue) {
- SimpleAttributeDefinition attribDef = KeycloakSubsystemDefinition.lookup(attributeName);
+ AttributeDefinition attribDef = KeycloakSubsystemDefinition.lookup(attributeName);
if (!oldValue.isDefined()) {
oldValue = attribDef.getDefaultValue();
}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/MigrateJsonOperation.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/MigrateJsonOperation.java
new file mode 100644
index 0000000..92c229d
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/MigrateJsonOperation.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationDefinition;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.OperationStepHandler;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+
+/**
+ * This operation provides a migration path from keycloak-server.json to
+ * standalone.xml or domain.xml.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class MigrateJsonOperation implements OperationStepHandler {
+ public static final String OPERATION_NAME = "migrate-json";
+
+ private static final String CONFIG_DIR = System.getProperty("jboss.server.config.dir");
+ private static final Path DEFAULT_CONFIG_FILE = Paths.get(CONFIG_DIR, "keycloak-server.json");
+
+ private static final AttributeDefinition FILE_ATTRIBUTE = SimpleAttributeDefinitionBuilder.create("file", ModelType.BYTES, true).build();
+ public static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OPERATION_NAME, KeycloakExtension.getResourceDescriptionResolver())
+ .setRuntimeOnly()
+ .setReadOnly()
+ .setReplyType(ModelType.STRING)
+ .setParameters(FILE_ATTRIBUTE)
+ .build();
+
+ private String localConfig() throws IOException {
+ if (Files.notExists(DEFAULT_CONFIG_FILE)) return null;
+ return new String(Files.readAllBytes(DEFAULT_CONFIG_FILE));
+ }
+
+ private String readConfig(ModelNode operation) throws IOException {
+ ModelNode file = operation.get(FILE_ATTRIBUTE.getName());
+ if (file.isDefined() && file.asBytes().length > 0) {
+ return new String(file.asBytes());
+ }
+
+ String localConfig = localConfig();
+ if (localConfig != null) return localConfig;
+
+ throw new IOException("Can not find json file to migrate");
+ }
+
+ @Override
+ public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
+ List<ModelNode> ops = null;
+ try {
+ PathAddress currentAddr = context.getCurrentAddress();
+ ops = JsonConfigConverter.convertJsonConfig(readConfig(operation), currentAddr);
+ } catch (IOException ioe) {
+ throw new OperationFailedException(ioe);
+ }
+
+ for (ModelNode op : ops) {
+ PathAddress addr = PathAddress.pathAddress(op.get(ADDRESS));
+ String opName = op.get(OP).asString();
+ context.addStep(op,
+ context.getRootResourceRegistration().getOperationHandler(addr, opName),
+ OperationContext.Stage.MODEL);
+ }
+
+ context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
+ }
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceAddHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceAddHandler.java
new file mode 100644
index 0000000..7db883c
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceAddHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.OperationFailedException;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.dmr.ModelNode;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProviderResourceAddHandler extends AbstractAddStepHandler {
+
+ public static ProviderResourceAddHandler INSTANCE = new ProviderResourceAddHandler();
+
+ private ProviderResourceAddHandler() {
+ }
+
+ @Override
+ protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+ // TODO: localize exception. get id number
+ if (!operation.get(OP).asString().equals(ADD)) {
+ throw new OperationFailedException("Unexpected operation for add SPI. operation=" + operation.toString());
+ }
+
+ ProviderResourceDefinition.ENABLED.validateAndSet(operation, model);
+ ProviderResourceDefinition.PROPERTIES.validateAndSet(operation, model);
+
+ KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
+ }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceDefinition.java
new file mode 100644
index 0000000..68ba9bb
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceDefinition.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.PropertiesAttributeDefinition;
+import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProviderResourceDefinition extends SimpleResourceDefinition {
+
+ public static final String TAG_NAME = "provider";
+
+ protected static final SimpleAttributeDefinition ENABLED =
+ new SimpleAttributeDefinitionBuilder("enabled", ModelType.BOOLEAN, true)
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(true))
+ .setAllowNull(false)
+ .setRestartAllServices()
+ .build();
+
+ static final PropertiesAttributeDefinition PROPERTIES =
+ new PropertiesAttributeDefinition.Builder("properties", true)
+ .setRestartAllServices()
+ .setAllowExpression(true)
+ .build();
+
+ protected static final ReloadRequiredWriteAttributeHandler WRITE_ATTR_HANDLER = new ReloadRequiredWriteAttributeHandler(ENABLED);
+
+ protected ProviderResourceDefinition() {
+ super(PathElement.pathElement(TAG_NAME),
+ KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+ ProviderResourceAddHandler.INSTANCE,
+ ProviderResourceRemoveHandler.INSTANCE
+ );
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+ resourceRegistration.registerReadWriteAttribute(ENABLED, null, WRITE_ATTR_HANDLER);
+ resourceRegistration.registerReadWriteAttribute(PROPERTIES, null, WRITE_ATTR_HANDLER);
+ }
+
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceRemoveHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceRemoveHandler.java
new file mode 100644
index 0000000..21d8217
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ProviderResourceRemoveHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ProviderResourceRemoveHandler extends AbstractRemoveStepHandler {
+
+ public static ProviderResourceRemoveHandler INSTANCE = new ProviderResourceRemoveHandler();
+
+ private ProviderResourceRemoveHandler() {}
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceAddHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceAddHandler.java
new file mode 100644
index 0000000..3d41791
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceAddHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.OperationFailedException;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.dmr.ModelNode;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class SpiResourceAddHandler extends AbstractAddStepHandler {
+
+ public static SpiResourceAddHandler INSTANCE = new SpiResourceAddHandler();
+
+ private SpiResourceAddHandler() {}
+
+ @Override
+ protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+ // TODO: localize exception. get id number
+ if (!operation.get(OP).asString().equals(ADD)) {
+ throw new OperationFailedException("Unexpected operation for add SPI. operation=" + operation.toString());
+ }
+
+ SpiResourceDefinition.DEFAULT_PROVIDER.validateAndSet(operation, model);
+
+ KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
+ }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceDefinition.java
new file mode 100644
index 0000000..db15ba4
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceDefinition.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelType;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class SpiResourceDefinition extends SimpleResourceDefinition {
+
+ public static final String TAG_NAME = "spi";
+
+ protected static final SimpleAttributeDefinition DEFAULT_PROVIDER =
+ new SimpleAttributeDefinitionBuilder("default-provider", ModelType.STRING, true)
+ .setAllowExpression(true)
+ .setRestartAllServices()
+ .build();
+
+ protected static final ReloadRequiredWriteAttributeHandler WRITE_ATTR_HANDLER = new ReloadRequiredWriteAttributeHandler(DEFAULT_PROVIDER);
+
+ protected SpiResourceDefinition() {
+ super(PathElement.pathElement(TAG_NAME),
+ KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+ SpiResourceAddHandler.INSTANCE,
+ SpiResourceRemoveHandler.INSTANCE
+ );
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+ resourceRegistration.registerReadWriteAttribute(DEFAULT_PROVIDER, null, WRITE_ATTR_HANDLER);
+ }
+
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceRemoveHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceRemoveHandler.java
new file mode 100644
index 0000000..468a40a
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/SpiResourceRemoveHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class SpiResourceRemoveHandler extends AbstractRemoveStepHandler {
+
+ public static SpiResourceRemoveHandler INSTANCE = new SpiResourceRemoveHandler();
+
+ private SpiResourceRemoveHandler() {}
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceAddHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceAddHandler.java
new file mode 100644
index 0000000..b955101
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceAddHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.dmr.ModelNode;
+import static org.keycloak.subsystem.server.extension.ThemeResourceDefinition.ALL_ATTRIBUTES;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ThemeResourceAddHandler extends AbstractAddStepHandler {
+
+ public static ThemeResourceAddHandler INSTANCE = new ThemeResourceAddHandler();
+
+ private ThemeResourceAddHandler() {}
+
+ @Override
+ protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+ // TODO: localize exception. get id number
+ if (!operation.get(OP).asString().equals(ADD)) {
+ throw new OperationFailedException("Unexpected operation for add Theme. operation=" + operation.toString());
+ }
+
+ PathAddress address = PathAddress.pathAddress(operation.get(ADDRESS));
+ PathElement last = address.getLastElement();
+ if (!last.getValue().equals(ThemeResourceDefinition.RESOURCE_NAME)) {
+ throw new OperationFailedException("Theme resource with name " + last.getValue() + " not allowed.");
+ }
+
+ for (AttributeDefinition def : ALL_ATTRIBUTES) {
+ def.validateAndSet(operation, model);
+ }
+
+ KeycloakAdapterConfigService.INSTANCE.updateConfig(operation, model);
+ }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceDefinition.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceDefinition.java
new file mode 100644
index 0000000..94dca67
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceDefinition.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.StringListAttributeDefinition;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+import org.keycloak.subsystem.server.attributes.ModulesListAttributeBuilder;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ThemeResourceDefinition extends SimpleResourceDefinition {
+
+ public static final String TAG_NAME = "theme";
+
+ // This is the internal name of the singleton resource
+ public static final String RESOURCE_NAME = "defaults";
+
+ // NOTE: All attributes must be SimpleAttributeDefinition. If that needs to
+ // change then refactor starting with lookup() method below.
+ static final SimpleAttributeDefinition STATIC_MAX_AGE =
+ new SimpleAttributeDefinitionBuilder("staticMaxAge", ModelType.LONG, true)
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode("2592000"))
+ .setRestartAllServices()
+ .build();
+
+ static final SimpleAttributeDefinition CACHE_THEMES =
+ new SimpleAttributeDefinitionBuilder("cacheThemes", ModelType.BOOLEAN, true)
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(true))
+ .setAllowNull(false)
+ .setRestartAllServices()
+ .build();
+
+ static final SimpleAttributeDefinition CACHE_TEMPLATES =
+ new SimpleAttributeDefinitionBuilder("cacheTemplates", ModelType.BOOLEAN, true)
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(true))
+ .setAllowNull(false)
+ .setRestartAllServices()
+ .build();
+
+ static final SimpleAttributeDefinition WELCOME_THEME =
+ new SimpleAttributeDefinitionBuilder("welcomeTheme", ModelType.STRING, true)
+ .setAllowExpression(true)
+ .setRestartAllServices()
+ .build();
+
+ static final SimpleAttributeDefinition DEFAULT =
+ new SimpleAttributeDefinitionBuilder("default", ModelType.STRING, true)
+ .setAllowExpression(true)
+ .setRestartAllServices()
+ .build();
+
+ static final SimpleAttributeDefinition DIR =
+ new SimpleAttributeDefinitionBuilder("dir", ModelType.STRING, true)
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode("${jboss.home.dir}/themes"))
+ .setRestartAllServices()
+ .build();
+
+ static final StringListAttributeDefinition MODULES = new ModulesListAttributeBuilder().build();
+
+ static final List<AttributeDefinition> ALL_ATTRIBUTES = new ArrayList<>();
+
+ static {
+ ALL_ATTRIBUTES.add(STATIC_MAX_AGE);
+ ALL_ATTRIBUTES.add(CACHE_THEMES);
+ ALL_ATTRIBUTES.add(CACHE_TEMPLATES);
+ ALL_ATTRIBUTES.add(WELCOME_THEME);
+ ALL_ATTRIBUTES.add(DEFAULT);
+ ALL_ATTRIBUTES.add(DIR);
+ ALL_ATTRIBUTES.add(MODULES);
+ }
+
+ private static final Map<String, AttributeDefinition> DEFINITION_LOOKUP = new HashMap<>();
+ static {
+ for (AttributeDefinition def : ALL_ATTRIBUTES) {
+ DEFINITION_LOOKUP.put(def.getXmlName(), def);
+ }
+ }
+
+ protected static final ReloadRequiredWriteAttributeHandler WRITE_ATTR_HANDLER = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
+
+ protected ThemeResourceDefinition() {
+ super(PathElement.pathElement(TAG_NAME),
+ KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+ ThemeResourceAddHandler.INSTANCE,
+ ThemeResourceRemoveHandler.INSTANCE
+ );
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+
+ for (AttributeDefinition def : ALL_ATTRIBUTES) {
+ resourceRegistration.registerReadWriteAttribute(def, null, WRITE_ATTR_HANDLER);
+ }
+ }
+
+ public static SimpleAttributeDefinition lookup(String name) {
+ return (SimpleAttributeDefinition)DEFINITION_LOOKUP.get(name);
+ }
+
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceRemoveHandler.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceRemoveHandler.java
new file mode 100644
index 0000000..797c564
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/ThemeResourceRemoveHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.server.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class ThemeResourceRemoveHandler extends AbstractRemoveStepHandler {
+
+ public static ThemeResourceRemoveHandler INSTANCE = new ThemeResourceRemoveHandler();
+
+ private ThemeResourceRemoveHandler() {}
+
+}
diff --git a/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli b/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
new file mode 100644
index 0000000..6adef01
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/resources/cli/default-keycloak-subsys-config.cli
@@ -0,0 +1,21 @@
+/subsystem=keycloak-server:add(web-context=auth,master-realm-name=master,scheduled-task-interval=900,providers=[classpath:${jboss.home.dir}/providers/*])
+/subsystem=keycloak-server/theme=defaults/:add(dir=${jboss.home.dir}/themes,staticMaxAge=2592000,cacheTemplates=true,cacheThemes=true)
+/subsystem=keycloak-server/spi=eventsStore/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=eventsStore/provider=jpa/:add(properties={exclude-events => "[\"REFRESH_TOKEN\"]"},enabled=true)
+/subsystem=keycloak-server/spi=realm/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=user/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=userCache/:add
+/subsystem=keycloak-server/spi=userCache/provider=default/:add(enabled=true)
+/subsystem=keycloak-server/spi=userSessionPersister/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=authorizationPersister/:add(default-provider=jpa)
+/subsystem=keycloak-server/spi=timer/:add(default-provider=basic)
+/subsystem=keycloak-server/spi=connectionsHttpClient/:add
+/subsystem=keycloak-server/spi=connectionsHttpClient/provider=default/:add(enabled=true)
+/subsystem=keycloak-server/spi=connectionsJpa/:add
+/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:add(properties={dataSource => "java:jboss/datasources/KeycloakDS",initializeEmpty => "true",migrationStrategy => "update",migrationExport => "${jboss.home.dir}/keycloak-database-update.sql"},enabled=true)
+/subsystem=keycloak-server/spi=realmCache/:add
+/subsystem=keycloak-server/spi=realmCache/provider=default/:add(enabled=true)
+/subsystem=keycloak-server/spi=connectionsInfinispan/:add(default-provider=default)
+/subsystem=keycloak-server/spi=connectionsInfinispan/provider=default/:add(properties={cacheContainer => "java:comp/env/infinispan/Keycloak"},enabled=true)
+/subsystem=keycloak-server/spi=jta-lookup/:add(default-provider=${keycloak.jta.lookup.provider:jboss})
+/subsystem=keycloak-server/spi=jta-lookup/provider=jboss/:add(enabled=true)
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/resources/default-config/keycloak-server-default-config.xml b/wildfly/server-subsystem/src/main/resources/default-config/keycloak-server-default-config.xml
new file mode 100644
index 0000000..e520611
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/resources/default-config/keycloak-server-default-config.xml
@@ -0,0 +1 @@
+${keycloak.server.subsys.default.config}
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties b/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties
index bf29d06..8d5c5a7 100755
--- a/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties
+++ b/wildfly/server-subsystem/src/main/resources/org/keycloak/subsystem/server/extension/LocalDescriptions.properties
@@ -15,7 +15,38 @@
# limitations under the License.
#
+keycloak-server.migrate-json=Migrate keycloak-server.json to standalone.xml or domain.xml
+keycloak-server.migrate-json.file=Optional local path to keycloak-server.json
+
keycloak-server.subsystem=Keycloak subsystem
keycloak-server.subsystem.add=Operation Adds Keycloak subsystem
keycloak-server.subsystem.remove=Operation removes Keycloak subsystem
keycloak-server.subsystem.web-context=Web context where Keycloak server is bound. Default value is 'auth'.
+keycloak-server.subsystem.providers=Paths to search for Keycloak provider jars.
+keycloak-server.subsystem.master-realm-name=The name of the master admin realm.
+keycloak-server.subsystem.scheduled-task-interval=The interval (in seconds) to run scheduled tasks.
+keycloak-server.subsystem.spi=A Service Provider type.
+keycloak-server.subsystem.theme=Theme configuration properties.
+
+keycloak-server.theme=Theme configuration properties.
+keycloak-server.theme.add=Add the theme config properties.
+keycloak-server.theme.remove=Remove the theme config properties.
+keycloak-server.theme.staticMaxAge=Maximum time the browser should cache theme resources. A value of -1 will disable caching.
+keycloak-server.theme.cacheThemes=If true, themes are cached.
+keycloak-server.theme.cacheTemplates=If true, theme templates are cached.
+keycloak-server.theme.welcomeTheme=The welcome theme.
+keycloak-server.theme.default=The default theme to use if no theme is specified for a realm.
+keycloak-server.theme.dir=Directory where themes can be located.
+keycloak-server.theme.modules=List of modules containing themes.
+
+keycloak-server.spi=A Service Provider type.
+keycloak-server.spi.add=Add an spi.
+keycloak-server.spi.remove=Remove an spi.
+keycloak-server.spi.default-provider=The default provider for the spi.
+keycloak-server.spi.provider=A provider for the spi.
+
+keycloak-server.provider=A provider for the spi.
+keycloak-server.provider.add=Add a provider.
+keycloak-server.provider.remove=Remove a provider.
+keycloak-server.provider.enabled=Enable or disable the provider.
+keycloak-server.provider.properties=The properties for the provider.
diff --git a/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd b/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd
index df02124..fa9f9e0 100755
--- a/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd
+++ b/wildfly/server-subsystem/src/main/resources/schema/wildfly-keycloak-server_1_1.xsd
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
- ~ and other contributors as indicated by the @author tags.
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
+~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+~ and other contributors as indicated by the @author tags.
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:jboss:domain:keycloak-server:1.1"
@@ -35,8 +35,73 @@
]]>
</xs:documentation>
</xs:annotation>
- <xs:choice minOccurs="0" maxOccurs="1">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="web-context" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="providers" type="providersLocationType" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="master-realm-name" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="scheduled-task-interval" type="xs:long" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="theme" type="themeDefaultsType" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="spi" type="spiType" minOccurs="0" maxOccurs="unbounded"/>
</xs:choice>
+
+ </xs:complexType>
+
+ <xs:complexType name="providersLocationType">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="provider" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:choice>
+ </xs:complexType>
+
+ <xs:complexType name="themeDefaultsType">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="staticMaxAge" type="xs:long" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="cacheThemes" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="cacheTemplates" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="welcomeTheme" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="default" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="dir" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="modules" type="modulesType" minOccurs="0" maxOccurs="1"/>
+ </xs:choice>
+ </xs:complexType>
+
+ <xs:complexType name="modulesType">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="module" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:choice>
+ </xs:complexType>
+
+ <xs:complexType name="spiType">
+ <xs:sequence>
+ <xs:element name="default-provider" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="provider" type="providerType" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="providerType">
+ <xs:sequence>
+ <xs:element name="properties" type="properties" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ <xs:attribute name="name" use="required"/>
+ <xs:attribute name="enabled" use="required"/>
+ </xs:complexType>
+
+ <xs:complexType name="properties">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="property" type="propertyType" maxOccurs="unbounded"/>
+ <xs:element name="list" type="listType" maxOccurs="unbounded"/>
+ </xs:choice>
+ </xs:complexType>
+
+ <xs:complexType name="propertyType">
+ <xs:attribute name="name" use="required"/>
+ <xs:attribute name="value" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="listType">
+ <xs:sequence>
+ <xs:element name="value" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" use="required"/>
</xs:complexType>
</xs:schema>
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml
index c269c31..67bf03e 100644
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-server.xml
@@ -1,25 +1,23 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
- ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
- ~ and other contributors as indicated by the @author tags.
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
+~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+~ and other contributors as indicated by the @author tags.
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
<!-- Template used by WildFly build when directed to include Keycloak subsystem in a configuration. -->
<config>
- <extension-module>org.keycloak.keycloak-server-subsystem</extension-module>
- <subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
- <web-context>auth</web-context>
- </subsystem>
+ <extension-module>org.keycloak.keycloak-server-subsystem</extension-module>
+ ${keycloak.server.subsys.default.config}
</config>
diff --git a/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/JsonConfigConverterTestCase.java b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/JsonConfigConverterTestCase.java
new file mode 100644
index 0000000..4c6bf04
--- /dev/null
+++ b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/JsonConfigConverterTestCase.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.subsystem.server.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.dmr.ModelNode;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
+ */
+public class JsonConfigConverterTestCase {
+
+ private final PathElement domainRoot = PathElement.pathElement("profile", "auth-server-clustered");
+ private final PathAddress domainAddress = PathAddress.pathAddress(domainRoot)
+ .append(KeycloakExtension.PATH_SUBSYSTEM);
+ private final PathAddress standaloneAddress = PathAddress.pathAddress(KeycloakExtension.PATH_SUBSYSTEM);
+
+ @Test
+ public void testConvertJsonStandaloneWithModules() throws Exception {
+ String json = basicJsonConfig(true);
+ List<ModelNode> expResult = expectedOperations(true, false);
+
+ List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, standaloneAddress);
+ assertEquals(expResult, result);
+ }
+
+ @Test
+ public void testConvertJsonStandaloneWithoutModules() throws Exception {
+ String json = basicJsonConfig(false);
+ List<ModelNode> expResult = expectedOperations(false, false);
+
+ List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, standaloneAddress);
+ assertEquals(expResult, result);
+ }
+
+ @Test
+ public void testConvertJsonDomainWithModules() throws Exception {
+ String json = basicJsonConfig(true);
+ List<ModelNode> expResult = expectedOperations(true, true);
+
+ List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, domainAddress);
+ assertEquals(expResult, result);
+ }
+
+ @Test
+ public void testConvertJsonDomainWithoutModules() throws Exception {
+ String json = basicJsonConfig(false);
+ List<ModelNode> expResult = expectedOperations(false, true);
+
+ List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, domainAddress);
+ assertEquals(expResult, result);
+ }
+
+ private String basicJsonConfig(boolean includeModules) {
+ String basicConfig =
+ "{\n"
+ + " \"providers\": [\n"
+ + " \"classpath:${jboss.home.dir}/providers/*\"\n"
+ + " ],\n"
+ + "\n"
+ + " \"admin\": {\n"
+ + " \"realm\": \"master\"\n"
+ + " },\n"
+ + "\n"
+ + " \"eventsStore\": {\n"
+ + " \"provider\": \"jpa\",\n"
+ + " \"jpa\": {\n"
+ + " \"exclude-events\": [ \"REFRESH_TOKEN\" ]\n"
+ + " }\n"
+ + " },\n"
+ + "\n"
+ + " \"realm\": {\n"
+ + " \"provider\": \"jpa\"\n"
+ + " },\n"
+ + "\n"
+ + " \"user\": {\n"
+ + " \"provider\": \"jpa\"\n"
+ + " },\n"
+ + "\n"
+ + " \"userCache\": {\n"
+ + " \"default\" : {\n"
+ + " \"enabled\": true\n"
+ + " }\n"
+ + " },\n"
+ + "\n"
+ + " \"userSessionPersister\": {\n"
+ + " \"provider\": \"jpa\"\n"
+ + " },\n"
+ + "\n"
+ + " \"authorizationPersister\": {\n"
+ + " \"provider\": \"jpa\"\n"
+ + " },\n"
+ + "\n"
+ + " \"timer\": {\n"
+ + " \"provider\": \"basic\"\n"
+ + " },\n"
+ + "\n"
+ + " \"theme\": {\n"
+ + " \"staticMaxAge\": 2592001,\n"
+ + " \"cacheTemplates\": false,\n"
+ + " \"cacheThemes\": false,\n"
+ + " \"welcomeTheme\": \"welcome\",\n"
+ + " \"default\": \"default\",\n"
+ + " \"folder\": {\n"
+ + " \"dir\": \"${jboss.home.dir}/themes\"\n";
+
+
+ if (includeModules) {
+ basicConfig +=
+ " },\n"
+ + " \"module\": {\n"
+ + " \"modules\": [ \"org.keycloak.example.themes\" ]\n"
+ + " }\n";
+ } else {
+ basicConfig +=
+ " }\n";
+ }
+
+ basicConfig +=
+ " },\n"
+ + "\n"
+ + " \"scheduled\": {\n"
+ + " \"interval\": 900\n"
+ + " },\n"
+ + "\n"
+ + " \"connectionsHttpClient\": {\n"
+ + " \"default\": {}\n"
+ + " },\n"
+ + "\n"
+ + " \"connectionsJpa\": {\n"
+ + " \"default\": {\n"
+ + " \"dataSource\": \"java:jboss/datasources/KeycloakDS\",\n"
+ + " \"databaseSchema\": \"update\"\n"
+ + " }\n"
+ + " },\n"
+ + "\n"
+ + " \"realmCache\": {\n"
+ + " \"default\" : {\n"
+ + " \"enabled\": true\n"
+ + " }\n"
+ + " },\n"
+ + "\n"
+ + " \"connectionsInfinispan\": {\n"
+ + " \"provider\": \"default\",\n"
+ + " \"default\": {\n"
+ + " \"cacheContainer\" : \"java:comp/env/infinispan/Keycloak\"\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+
+ return basicConfig;
+ }
+
+ private List<ModelNode> expectedOperations(boolean includeModules, boolean isDomain) {
+ List<ModelNode> ops = new ArrayList<>();
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"write-attribute\",\n" +
+ " \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
+ " \"name\" => \"master-realm-name\",\n" +
+ " \"value\" => \"master\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"write-attribute\",\n" +
+ " \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
+ " \"name\" => \"scheduled-task-interval\",\n" +
+ " \"value\" => 900L\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"write-attribute\",\n" +
+ " \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
+ " \"name\" => \"providers\",\n" +
+ " \"value\" => [\"classpath:${jboss.home.dir}/providers/*\"]\n" +
+ "}"
+ ));
+
+ if (includeModules) {
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"theme\" => \"defaults\")\n" +
+ " ],\n" +
+ " \"staticMaxAge\" => 2592001L,\n" +
+ " \"cacheTemplates\" => false,\n" +
+ " \"cacheThemes\" => false,\n" +
+ " \"dir\" => \"${jboss.home.dir}/themes\",\n" +
+ " \"welcomeTheme\" => \"welcome\",\n" +
+ " \"default\" => \"default\",\n" +
+ " \"modules\" => [\"org.keycloak.example.themes\"]\n" +
+ "}"
+ ));
+ } else {
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"theme\" => \"defaults\")\n" +
+ " ],\n" +
+ " \"staticMaxAge\" => 2592001L,\n" +
+ " \"cacheTemplates\" => false,\n" +
+ " \"cacheThemes\" => false,\n" +
+ " \"dir\" => \"${jboss.home.dir}/themes\",\n" +
+ " \"welcomeTheme\" => \"welcome\",\n" +
+ " \"default\" => \"default\",\n" +
+ "}"
+ ));
+ }
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"eventsStore\")\n" +
+ " ],\n" +
+ " \"default-provider\" => \"jpa\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"eventsStore\"),\n" +
+ " (\"provider\" => \"jpa\")\n" +
+ " ],\n" +
+ " \"properties\" => {\"exclude-events\" => \"[\\\"REFRESH_TOKEN\\\"]\"},\n" +
+ " \"enabled\" => true\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"realm\")\n" +
+ " ],\n" +
+ " \"default-provider\" => \"jpa\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"user\")\n" +
+ " ],\n" +
+ " \"default-provider\" => \"jpa\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"userCache\")\n" +
+ " ]\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"userCache\"),\n" +
+ " (\"provider\" => \"default\")\n" +
+ " ],\n" +
+ " \"enabled\" => true\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"userSessionPersister\")\n" +
+ " ],\n" +
+ " \"default-provider\" => \"jpa\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"authorizationPersister\")\n" +
+ " ],\n" +
+ " \"default-provider\" => \"jpa\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"timer\")\n" +
+ " ],\n" +
+ " \"default-provider\" => \"basic\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"connectionsHttpClient\")\n" +
+ " ]\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"connectionsHttpClient\"),\n" +
+ " (\"provider\" => \"default\")\n" +
+ " ],\n" +
+ " \"enabled\" => true\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"connectionsJpa\")\n" +
+ " ]\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"connectionsJpa\"),\n" +
+ " (\"provider\" => \"default\")\n" +
+ " ],\n" +
+ " \"properties\" => {\n" +
+ " \"dataSource\" => \"java:jboss/datasources/KeycloakDS\",\n" +
+ " \"databaseSchema\" => \"update\"\n" +
+ " },\n" +
+ " \"enabled\" => true\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"realmCache\")\n" +
+ " ]\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"realmCache\"),\n" +
+ " (\"provider\" => \"default\")\n" +
+ " ],\n" +
+ " \"enabled\" => true\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"connectionsInfinispan\")\n" +
+ " ],\n" +
+ " \"default-provider\" => \"default\"\n" +
+ "}"
+ ));
+
+ ops.add(ModelNode.fromString(
+ "{\n" +
+ " \"operation\" => \"add\",\n" +
+ " \"address\" => [\n" +
+ " (\"subsystem\" => \"keycloak-server\"),\n" +
+ " (\"spi\" => \"connectionsInfinispan\"),\n" +
+ " (\"provider\" => \"default\")\n" +
+ " ],\n" +
+ " \"properties\" => {\"cacheContainer\" => \"java:comp/env/infinispan/Keycloak\"},\n" +
+ " \"enabled\" => true\n" +
+ "}"
+ ));
+
+ if (isDomain) { // prepend the domain root
+ for (ModelNode op : ops) {
+ PathAddress addr = PathAddress.pathAddress(op.get(ADDRESS));
+ PathAddress domainAddr = PathAddress.pathAddress(domainRoot).append(addr);
+ op.get(ADDRESS).set(domainAddr.toModelNode());
+ }
+ }
+
+ return ops;
+ }
+}
\ No newline at end of file
diff --git a/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java
index c84acc2..0bc4110 100755
--- a/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java
+++ b/wildfly/server-subsystem/src/test/java/org/keycloak/subsystem/server/extension/SubsystemParsingTestCase.java
@@ -17,10 +17,9 @@
package org.keycloak.subsystem.server.extension;
import java.io.IOException;
+import java.util.Properties;
import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest;
-import org.jboss.dmr.ModelNode;
-import org.junit.Test;
/**
* Tests all management expects for subsystem, parsing, marshaling, model definition and other
@@ -38,14 +37,14 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
super(KeycloakExtension.SUBSYSTEM_NAME, new KeycloakExtension());
}
- @Test
- public void testJson() throws Exception {
- ModelNode node = new ModelNode();
- node.get("web-context").set("auth");
-
- System.out.println("json=" + node.toJSONString(false));
+ @Override
+ protected Properties getResolvedProperties() {
+ Properties properties = new Properties();
+ properties.put("jboss.home.dir", System.getProperty("java.io.tmpdir"));
+ properties.put("keycloak.jta.lookup.provider", "jboss");
+ return properties;
}
-
+
@Override
protected String getSubsystemXml() throws IOException {
return readResource("keycloak-server-1.1.xml");
diff --git a/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml b/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml
index 103e162..e520611 100644
--- a/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml
+++ b/wildfly/server-subsystem/src/test/resources/org/keycloak/subsystem/server/extension/keycloak-server-1.1.xml
@@ -1,20 +1 @@
-<!--
- ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
- ~ and other contributors as indicated by the @author tags.
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
- <web-context>auth</web-context>
-</subsystem>
\ No newline at end of file
+${keycloak.server.subsys.default.config}
\ No newline at end of file