KEYCLOAK-3196: Use WildFly management model for server configuration.
This commit is contained in:
parent
3be47dee37
commit
3493aa4ab7
39 changed files with 2225 additions and 189 deletions
|
@ -59,6 +59,65 @@
|
|||
<xsl:apply-templates select="node()|@*"/>
|
||||
<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="databaseSchema" value="update"/>
|
||||
</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>
|
||||
</subsystem>
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
{
|
||||
"providers": [
|
||||
"classpath:${jboss.home.dir}/providers/*"
|
||||
],
|
||||
|
||||
"admin": {
|
||||
"realm": "master"
|
||||
},
|
||||
|
||||
"eventsStore": {
|
||||
"provider": "jpa",
|
||||
"jpa": {
|
||||
"exclude-events": [ "REFRESH_TOKEN" ]
|
||||
}
|
||||
},
|
||||
|
||||
"realm": {
|
||||
"provider": "jpa"
|
||||
},
|
||||
|
||||
"user": {
|
||||
"provider": "jpa"
|
||||
},
|
||||
|
||||
"userCache": {
|
||||
"default" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"userSessionPersister": {
|
||||
"provider": "jpa"
|
||||
},
|
||||
|
||||
"authorizationPersister": {
|
||||
"provider": "jpa"
|
||||
},
|
||||
|
||||
"timer": {
|
||||
"provider": "basic"
|
||||
},
|
||||
|
||||
"theme": {
|
||||
"staticMaxAge": 2592000,
|
||||
"cacheTemplates": true,
|
||||
"cacheThemes": true,
|
||||
"folder": {
|
||||
"dir": "${jboss.home.dir}/themes"
|
||||
}
|
||||
},
|
||||
|
||||
"scheduled": {
|
||||
"interval": 900
|
||||
},
|
||||
|
||||
"connectionsHttpClient": {
|
||||
"default": {}
|
||||
},
|
||||
|
||||
"connectionsJpa": {
|
||||
"default": {
|
||||
"dataSource": "java:jboss/datasources/KeycloakDS",
|
||||
"initializeEmpty": true,
|
||||
"migrationStrategy": "update",
|
||||
"migrationExport": "${jboss.home.dir}/keycloak-database-update.sql"
|
||||
}
|
||||
},
|
||||
|
||||
"realmCache": {
|
||||
"default" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"connectionsInfinispan": {
|
||||
"provider": "default",
|
||||
"default": {
|
||||
"cacheContainer" : "java:comp/env/infinispan/Keycloak"
|
||||
}
|
||||
},
|
||||
|
||||
"jta-lookup": {
|
||||
"provider": "${keycloak.jta.lookup.provider:jboss}",
|
||||
"jboss" : {
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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"/>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -84,10 +84,6 @@
|
|||
</fileSets>
|
||||
|
||||
<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>
|
||||
|
|
|
@ -11,4 +11,22 @@ 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)
|
||||
/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",databaseSchema => "update"},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)
|
||||
|
|
|
@ -11,4 +11,22 @@ 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)
|
||||
/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",databaseSchema => "update"},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)
|
||||
|
|
|
@ -72,6 +72,11 @@
|
|||
<artifactId>twitter4j-core</artifactId>
|
||||
</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>
|
||||
|
|
|
@ -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());
|
||||
|
@ -235,12 +246,23 @@ public class KeycloakApplication extends Application {
|
|||
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();
|
||||
|
|
|
@ -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().
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -52,6 +52,14 @@
|
|||
</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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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()
|
||||
.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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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() {}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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() {}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {}
|
||||
|
||||
}
|
|
@ -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=Foo
|
||||
keycloak-server.theme.cacheThemes=foo
|
||||
keycloak-server.theme.cacheTemplates=foo
|
||||
keycloak-server.theme.welcomeTheme=foo
|
||||
keycloak-server.theme.default=foo
|
||||
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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,25 +1,84 @@
|
|||
<?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>
|
||||
<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="databaseSchema" value="update"/>
|
||||
</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>
|
||||
</subsystem>
|
||||
</config>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,13 @@ 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"));
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getSubsystemXml() throws IOException {
|
||||
return readResource("keycloak-server-1.1.xml");
|
||||
|
|
|
@ -1,20 +1,79 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<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="databaseSchema" value="update"/>
|
||||
</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>
|
||||
</subsystem>
|
Loading…
Reference in a new issue