From 505cf5b25184c59ca85de63b082eb12383eba1ab Mon Sep 17 00:00:00 2001 From: stianst Date: Tue, 6 Feb 2018 06:49:10 +0100 Subject: [PATCH] KEYCLOAK-6519 Theme resource provider --- .../main/module.xml | 1 + .../provider/KeycloakDeploymentInfo.java | 56 ++++++++ .../provider/ProviderLoaderFactory.java | 2 +- .../services/org.keycloak.provider.Spi | 1 - .../main/java/org/keycloak/theme/Theme.java | 4 - .../keycloak/theme/ThemeResourceProvider.java | 56 ++++++++ .../theme/ThemeResourceProviderFactory.java | 26 ++++ .../org/keycloak/theme/ThemeResourceSpi.java | 48 +++++++ .../services/org.keycloak.provider.Spi | 4 +- .../provider/DefaultProviderLoader.java | 41 ++++-- .../DefaultProviderLoaderFactory.java | 4 +- .../FileSystemProviderLoaderFactory.java | 4 +- .../keycloak/provider/ProviderManager.java | 23 ++-- .../DefaultKeycloakSessionFactory.java | 3 +- .../org/keycloak/theme/ClassLoaderTheme.java | 10 -- ...vider.java => ClasspathThemeProvider.java} | 4 +- .../theme/ClasspathThemeProviderFactory.java | 121 ++++++++++++++++++ ...ClasspathThemeResourceProviderFactory.java | 55 ++++++++ .../keycloak/theme/ExtendingThemeManager.java | 65 ++++------ .../java/org/keycloak/theme/FolderTheme.java | 16 +-- .../theme/JarThemeProviderFactory.java | 71 +--------- .../theme/TestThemeResourceProvider.java | 11 ++ ...eycloak.theme.ThemeResourceProviderFactory | 1 + .../theme-resources/resources/test.js | 1 + .../theme-resources/templates/test.ftl | 1 + .../theme/ThemeResourceProviderTest.java | 53 ++++++++ .../wildfly/ModuleProviderLoaderFactory.java | 5 +- .../wildfly/ModuleThemeProviderFactory.java | 12 +- .../KeycloakProviderDependencyProcessor.java | 87 +++++++------ .../KeycloakProviderDeploymentProcessor.java | 19 ++- 30 files changed, 587 insertions(+), 218 deletions(-) create mode 100644 server-spi-private/src/main/java/org/keycloak/provider/KeycloakDeploymentInfo.java create mode 100755 server-spi/src/main/java/org/keycloak/theme/ThemeResourceProvider.java create mode 100644 server-spi/src/main/java/org/keycloak/theme/ThemeResourceProviderFactory.java create mode 100755 server-spi/src/main/java/org/keycloak/theme/ThemeResourceSpi.java rename services/src/main/java/org/keycloak/theme/{JarThemeProvider.java => ClasspathThemeProvider.java} (91%) create mode 100755 services/src/main/java/org/keycloak/theme/ClasspathThemeProviderFactory.java create mode 100644 services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/theme/TestThemeResourceProvider.java create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.theme.ThemeResourceProviderFactory create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/resources/test.js create mode 100644 testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/templates/test.ftl create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml index d82e1a9dde..d038c7b186 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml @@ -43,6 +43,7 @@ + diff --git a/server-spi-private/src/main/java/org/keycloak/provider/KeycloakDeploymentInfo.java b/server-spi-private/src/main/java/org/keycloak/provider/KeycloakDeploymentInfo.java new file mode 100644 index 0000000000..8b6c99a6e3 --- /dev/null +++ b/server-spi-private/src/main/java/org/keycloak/provider/KeycloakDeploymentInfo.java @@ -0,0 +1,56 @@ +package org.keycloak.provider; + +public class KeycloakDeploymentInfo { + + private String name; + private boolean services; + private boolean themes; + private boolean themeResources; + + public boolean isProvider() { + return services || themes || themeResources; + } + + public boolean hasServices() { + return services; + } + + public static KeycloakDeploymentInfo create() { + return new KeycloakDeploymentInfo(); + } + + private KeycloakDeploymentInfo() { + } + + public KeycloakDeploymentInfo name(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + public KeycloakDeploymentInfo services() { + this.services = true; + return this; + } + + public boolean hasThemes() { + return themes; + } + + public KeycloakDeploymentInfo themes() { + this.themes = true; + return this; + } + + public boolean hasThemeResources() { + return themeResources; + } + + public KeycloakDeploymentInfo themeResources() { + themeResources = true; + return this; + } +} diff --git a/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java index 85a5e170d5..7f4cded84d 100644 --- a/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java @@ -24,6 +24,6 @@ public interface ProviderLoaderFactory { boolean supports(String type); - ProviderLoader create(ClassLoader baseClassLoader, String resource); + ProviderLoader create(KeycloakDeploymentInfo info, ClassLoader baseClassLoader, String resource); } diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi index a63b206527..9fae0fd5ba 100755 --- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -48,7 +48,6 @@ org.keycloak.email.EmailSenderSpi org.keycloak.email.EmailTemplateSpi org.keycloak.executors.ExecutorsSpi org.keycloak.theme.ThemeSpi -org.keycloak.theme.ThemeSelectorSpi org.keycloak.truststore.TruststoreSpi org.keycloak.connections.httpclient.HttpClientSpi org.keycloak.models.cache.CacheRealmProviderSpi diff --git a/server-spi/src/main/java/org/keycloak/theme/Theme.java b/server-spi/src/main/java/org/keycloak/theme/Theme.java index 660029eef7..c37b7e79df 100755 --- a/server-spi/src/main/java/org/keycloak/theme/Theme.java +++ b/server-spi/src/main/java/org/keycloak/theme/Theme.java @@ -40,10 +40,6 @@ public interface Theme { URL getTemplate(String name) throws IOException; - InputStream getTemplateAsStream(String name) throws IOException; - - URL getResource(String path) throws IOException; - InputStream getResourceAsStream(String path) throws IOException; /** diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeResourceProvider.java b/server-spi/src/main/java/org/keycloak/theme/ThemeResourceProvider.java new file mode 100755 index 0000000000..d647f54136 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/theme/ThemeResourceProvider.java @@ -0,0 +1,56 @@ +/* + * 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.theme; + +import org.keycloak.provider.Provider; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Set; + +/** + * A theme resource provider can be used to load additional templates and resources. An example use of this would be + * a custom authenticator that requires an additional template and a JavaScript file. + * + * The theme is searched for templates and resources first. Theme resource providers are only searched if the template + * or resource is not found. This allows overriding templates and resources from theme resource providers in the theme. + * + * @author Stian Thorgersen + */ +public interface ThemeResourceProvider extends Provider { + + /** + * Load the template for the specific name + * + * @param name the template name + * @return the URL of the template, or null if the template is unknown + * @throws IOException + */ + URL getTemplate(String name) throws IOException; + + /** + * Load the resource for the specific path + * + * @param path the resource path + * @return an InputStream to read the resource, or null if the resource is unknown + * @throws IOException + */ + InputStream getResourceAsStream(String path) throws IOException; + +} diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeResourceProviderFactory.java b/server-spi/src/main/java/org/keycloak/theme/ThemeResourceProviderFactory.java new file mode 100644 index 0000000000..9acccb314d --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/theme/ThemeResourceProviderFactory.java @@ -0,0 +1,26 @@ +/* + * 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.theme; + +import org.keycloak.provider.ProviderFactory; + +/** + * @author Stian Thorgersen + */ +public interface ThemeResourceProviderFactory extends ProviderFactory { +} diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeResourceSpi.java b/server-spi/src/main/java/org/keycloak/theme/ThemeResourceSpi.java new file mode 100755 index 0000000000..c0ecd821c6 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/theme/ThemeResourceSpi.java @@ -0,0 +1,48 @@ +/* + * 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.theme; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Stian Thorgersen + */ +public class ThemeResourceSpi implements Spi { + + @Override + public boolean isInternal() { + return false; + } + + @Override + public String getName() { + return "themeResource"; + } + + @Override + public Class getProviderClass() { + return ThemeResourceProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return ThemeResourceProviderFactory.class; + } +} diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi index c7ee4865c7..86539b5e99 100755 --- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -32,4 +32,6 @@ # limitations under the License. # -org.keycloak.storage.UserStorageProviderSpi \ No newline at end of file +org.keycloak.storage.UserStorageProviderSpi +org.keycloak.theme.ThemeResourceSpi +org.keycloak.theme.ThemeSelectorSpi \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java b/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java index 5f4b19db6b..ced747ac78 100644 --- a/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java +++ b/services/src/main/java/org/keycloak/provider/DefaultProviderLoader.java @@ -17,6 +17,12 @@ package org.keycloak.provider; +import org.keycloak.theme.ClasspathThemeProviderFactory; +import org.keycloak.theme.ClasspathThemeResourceProviderFactory; +import org.keycloak.theme.ThemeResourceSpi; +import org.keycloak.theme.ThemeSpi; + +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.ServiceLoader; @@ -26,27 +32,46 @@ import java.util.ServiceLoader; */ public class DefaultProviderLoader implements ProviderLoader { + private KeycloakDeploymentInfo info; private ClassLoader classLoader; - public DefaultProviderLoader(ClassLoader classLoader) { + public DefaultProviderLoader(KeycloakDeploymentInfo info, ClassLoader classLoader) { + this.info = info; this.classLoader = classLoader; } @Override public List loadSpis() { - LinkedList list = new LinkedList<>(); - for (Spi spi : ServiceLoader.load(Spi.class, classLoader)) { - list.add(spi); + if (info.hasServices()) { + LinkedList list = new LinkedList<>(); + for (Spi spi : ServiceLoader.load(Spi.class, classLoader)) { + list.add(spi); + } + return list; + } else { + return Collections.emptyList(); } - return list; } @Override public List load(Spi spi) { - LinkedList list = new LinkedList(); - for (ProviderFactory f : ServiceLoader.load(spi.getProviderFactoryClass(), classLoader)) { - list.add(f); + List list = new LinkedList<>(); + if (info.hasServices()) { + for (ProviderFactory f : ServiceLoader.load(spi.getProviderFactoryClass(), classLoader)) { + list.add(f); + } } + + if (spi.getClass().equals(ThemeResourceSpi.class) && info.hasThemeResources()) { + ClasspathThemeResourceProviderFactory resourceProviderFactory = new ClasspathThemeResourceProviderFactory(info.getName(), classLoader); + list.add(resourceProviderFactory); + } + + if (spi.getClass().equals(ThemeSpi.class) && info.hasThemes()) { + ClasspathThemeProviderFactory themeProviderFactory = new ClasspathThemeProviderFactory(info.getName(), classLoader); + list.add(themeProviderFactory); + } + return list; } diff --git a/services/src/main/java/org/keycloak/provider/DefaultProviderLoaderFactory.java b/services/src/main/java/org/keycloak/provider/DefaultProviderLoaderFactory.java index 0062a61f0b..1786370615 100644 --- a/services/src/main/java/org/keycloak/provider/DefaultProviderLoaderFactory.java +++ b/services/src/main/java/org/keycloak/provider/DefaultProviderLoaderFactory.java @@ -28,8 +28,8 @@ public class DefaultProviderLoaderFactory implements ProviderLoaderFactory { } @Override - public ProviderLoader create(ClassLoader baseClassLoader, String resource) { - return new DefaultProviderLoader(baseClassLoader); + public ProviderLoader create(KeycloakDeploymentInfo info, ClassLoader baseClassLoader, String resource) { + return new DefaultProviderLoader(info, baseClassLoader); } } diff --git a/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java b/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java index 414bea80f3..f7c10e2e77 100644 --- a/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java +++ b/services/src/main/java/org/keycloak/provider/FileSystemProviderLoaderFactory.java @@ -38,8 +38,8 @@ public class FileSystemProviderLoaderFactory implements ProviderLoaderFactory { } @Override - public ProviderLoader create(ClassLoader baseClassLoader, String resource) { - return new DefaultProviderLoader(createClassLoader(baseClassLoader, resource.split(";"))); + public ProviderLoader create(KeycloakDeploymentInfo info, ClassLoader baseClassLoader, String resource) { + return new DefaultProviderLoader(info, createClassLoader(baseClassLoader, resource.split(";"))); } private static URLClassLoader createClassLoader(ClassLoader parent, String... files) { diff --git a/services/src/main/java/org/keycloak/provider/ProviderManager.java b/services/src/main/java/org/keycloak/provider/ProviderManager.java index 9db3181026..e7659a4c2c 100644 --- a/services/src/main/java/org/keycloak/provider/ProviderManager.java +++ b/services/src/main/java/org/keycloak/provider/ProviderManager.java @@ -21,11 +21,13 @@ import org.keycloak.common.util.MultivaluedHashMap; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.ServiceLoader; +import java.util.Set; /** * @author Stian Thorgersen @@ -38,7 +40,7 @@ public class ProviderManager { private MultivaluedHashMap, ProviderFactory> cache = new MultivaluedHashMap<>(); - public ProviderManager(ClassLoader baseClassLoader, String... resources) { + public ProviderManager(KeycloakDeploymentInfo info, ClassLoader baseClassLoader, String... resources) { List factories = new LinkedList(); for (ProviderLoaderFactory f : ServiceLoader.load(ProviderLoaderFactory.class, getClass().getClassLoader())) { factories.add(f); @@ -46,7 +48,7 @@ public class ProviderManager { logger.debugv("Provider loaders {0}", factories); - loaders.add(new DefaultProviderLoader(baseClassLoader)); + loaders.add(new DefaultProviderLoader(info, baseClassLoader)); if (resources != null) { for (String r : resources) { @@ -56,7 +58,8 @@ public class ProviderManager { boolean found = false; for (ProviderLoaderFactory f : factories) { if (f.supports(type)) { - loaders.add(f.create(baseClassLoader, resource)); + KeycloakDeploymentInfo resourceInfo = KeycloakDeploymentInfo.create().services(); + loaders.add(f.create(resourceInfo, baseClassLoader, resource)); found = true; break; } @@ -67,11 +70,6 @@ public class ProviderManager { } } } - - public ProviderManager(ClassLoader baseClassLoader) { - loaders.add(new DefaultProviderLoader(baseClassLoader)); - } - public synchronized List loadSpis() { // Use a map to prevent duplicates, since the loaders may have overlapping classpaths. Map spiMap = new HashMap<>(); @@ -88,15 +86,16 @@ public class ProviderManager { public synchronized List load(Spi spi) { if (!cache.containsKey(spi.getProviderClass())) { - IdentityHashMap factoryClasses = new IdentityHashMap(); + + Set loaded = new HashSet<>(); for (ProviderLoader loader : loaders) { List f = loader.load(spi); if (f != null) { for (ProviderFactory pf: f) { - // make sure there are no duplicates - if (!factoryClasses.containsKey(pf.getClass())) { + String uniqueId = spi.getName() + "-" + pf.getId(); + if (!loaded.contains(uniqueId)) { cache.add(spi.getProviderClass(), pf); - factoryClasses.put(pf.getClass(), pf); + loaded.add(uniqueId); } } } diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index 21da694ca7..78738b3e62 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -22,6 +22,7 @@ import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.provider.EnvironmentDependentProviderFactory; +import org.keycloak.provider.KeycloakDeploymentInfo; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEventListener; @@ -72,7 +73,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr public void init() { serverStartupTimestamp = System.currentTimeMillis(); - ProviderManager pm = new ProviderManager(getClass().getClassLoader(), Config.scope().getArray("providers")); + ProviderManager pm = new ProviderManager(KeycloakDeploymentInfo.create().services(), getClass().getClassLoader(), Config.scope().getArray("providers")); spis.addAll(pm.loadSpis()); factoriesMap = loadFactories(pm); for (ProviderManager manager : ProviderManagerRegistry.SINGLETON.getPreBoot()) { diff --git a/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java b/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java index 24f215a50e..82ad64a584 100644 --- a/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java +++ b/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java @@ -104,16 +104,6 @@ public class ClassLoaderTheme implements Theme { return classLoader.getResource(templateRoot + name); } - @Override - public InputStream getTemplateAsStream(String name) { - return classLoader.getResourceAsStream(templateRoot + name); - } - - @Override - public URL getResource(String path) { - return classLoader.getResource(resourceRoot + path); - } - @Override public InputStream getResourceAsStream(String path) { return classLoader.getResourceAsStream(resourceRoot + path); diff --git a/services/src/main/java/org/keycloak/theme/JarThemeProvider.java b/services/src/main/java/org/keycloak/theme/ClasspathThemeProvider.java similarity index 91% rename from services/src/main/java/org/keycloak/theme/JarThemeProvider.java rename to services/src/main/java/org/keycloak/theme/ClasspathThemeProvider.java index bd53f1181f..8e7c58135b 100755 --- a/services/src/main/java/org/keycloak/theme/JarThemeProvider.java +++ b/services/src/main/java/org/keycloak/theme/ClasspathThemeProvider.java @@ -25,11 +25,11 @@ import java.util.Set; /** * @author Stian Thorgersen */ -public class JarThemeProvider implements ThemeProvider { +public class ClasspathThemeProvider implements ThemeProvider { private Map> themes; - public JarThemeProvider(Map> themes) { + public ClasspathThemeProvider(Map> themes) { this.themes = themes; } diff --git a/services/src/main/java/org/keycloak/theme/ClasspathThemeProviderFactory.java b/services/src/main/java/org/keycloak/theme/ClasspathThemeProviderFactory.java new file mode 100755 index 0000000000..0bd0891e31 --- /dev/null +++ b/services/src/main/java/org/keycloak/theme/ClasspathThemeProviderFactory.java @@ -0,0 +1,121 @@ +/* + * 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.theme; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.util.JsonSerialization; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Stian Thorgersen + */ +public class ClasspathThemeProviderFactory implements ThemeProviderFactory { + + public static final String KEYCLOAK_THEMES_JSON = "META-INF/keycloak-themes.json"; + protected static Map> themes = new HashMap<>(); + + private String id; + + public ClasspathThemeProviderFactory(String id) { + this.id = id; + } + + public ClasspathThemeProviderFactory(String id, ClassLoader classLoader) { + this.id = id; + loadThemes(classLoader, classLoader.getResourceAsStream(KEYCLOAK_THEMES_JSON)); + } + + public static class ThemeRepresentation { + private String name; + private String[] types; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String[] getTypes() { + return types; + } + + public void setTypes(String[] types) { + this.types = types; + } + } + + public static class ThemesRepresentation { + private ThemeRepresentation[] themes; + + public ThemeRepresentation[] getThemes() { + return themes; + } + + public void setThemes(ThemeRepresentation[] themes) { + this.themes = themes; + } + } + + @Override + public ThemeProvider create(KeycloakSession session) { + return new ClasspathThemeProvider(themes); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return id; + } + + protected void loadThemes(ClassLoader classLoader, InputStream themesInputStream) { + try { + ThemesRepresentation themesRep = JsonSerialization.readValue(themesInputStream, ThemesRepresentation.class); + + for (ThemeRepresentation themeRep : themesRep.getThemes()) { + for (String t : themeRep.getTypes()) { + Theme.Type type = Theme.Type.valueOf(t.toUpperCase()); + if (!themes.containsKey(type)) { + themes.put(type, new HashMap<>()); + } + themes.get(type).put(themeRep.getName(), new ClassLoaderTheme(themeRep.getName(), type, classLoader)); + } + } + } catch (Exception e) { + throw new RuntimeException("Failed to load themes", e); + } + } + +} diff --git a/services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java b/services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java new file mode 100644 index 0000000000..760bbace6e --- /dev/null +++ b/services/src/main/java/org/keycloak/theme/ClasspathThemeResourceProviderFactory.java @@ -0,0 +1,55 @@ +package org.keycloak.theme; + +import org.keycloak.Config; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +public class ClasspathThemeResourceProviderFactory implements ThemeResourceProviderFactory, ThemeResourceProvider { + + public static final String THEME_RESOURCES_TEMPLATES = "theme-resources/templates/"; + public static final String THEME_RESOURCES_RESOURCES = "theme-resources/resources/"; + private final String id; + private final ClassLoader classLoader; + + public ClasspathThemeResourceProviderFactory(String id, ClassLoader classLoader) { + this.id = id; + this.classLoader = classLoader; + } + + @Override + public ThemeResourceProvider create(KeycloakSession session) { + return this; + } + + @Override + public URL getTemplate(String name) throws IOException { + return classLoader.getResource(THEME_RESOURCES_TEMPLATES + name); + } + + @Override + public InputStream getResourceAsStream(String path) throws IOException { + return classLoader.getResourceAsStream(THEME_RESOURCES_RESOURCES + path); + } + + @Override + public String getId() { + return id; + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + +} diff --git a/services/src/main/java/org/keycloak/theme/ExtendingThemeManager.java b/services/src/main/java/org/keycloak/theme/ExtendingThemeManager.java index 3aa6cdc791..b580a4a316 100755 --- a/services/src/main/java/org/keycloak/theme/ExtendingThemeManager.java +++ b/services/src/main/java/org/keycloak/theme/ExtendingThemeManager.java @@ -111,31 +111,27 @@ public class ExtendingThemeManager implements ThemeProvider { private Theme loadTheme(String name, Theme.Type type) throws IOException { Theme theme = findTheme(name, type); - if (theme != null && (theme.getParentName() != null || theme.getImportName() != null)) { - List themes = new LinkedList<>(); - themes.add(theme); + List themes = new LinkedList<>(); + themes.add(theme); - if (theme.getImportName() != null) { - String[] s = theme.getImportName().split("/"); - themes.add(findTheme(s[1], Theme.Type.valueOf(s[0].toUpperCase()))); - } + if (theme.getImportName() != null) { + String[] s = theme.getImportName().split("/"); + themes.add(findTheme(s[1], Theme.Type.valueOf(s[0].toUpperCase()))); + } - if (theme.getParentName() != null) { - for (String parentName = theme.getParentName(); parentName != null; parentName = theme.getParentName()) { - theme = findTheme(parentName, type); - themes.add(theme); + if (theme.getParentName() != null) { + for (String parentName = theme.getParentName(); parentName != null; parentName = theme.getParentName()) { + theme = findTheme(parentName, type); + themes.add(theme); - if (theme.getImportName() != null) { - String[] s = theme.getImportName().split("/"); - themes.add(findTheme(s[1], Theme.Type.valueOf(s[0].toUpperCase()))); - } + if (theme.getImportName() != null) { + String[] s = theme.getImportName().split("/"); + themes.add(findTheme(s[1], Theme.Type.valueOf(s[0].toUpperCase()))); } } - - return new ExtendingTheme(themes); - } else { - return theme; } + + return new ExtendingTheme(themes, session.getAllProviders(ThemeResourceProvider.class)); } @Override @@ -178,13 +174,15 @@ public class ExtendingThemeManager implements ThemeProvider { public static class ExtendingTheme implements Theme { private List themes; + private Set themeResourceProviders; private Properties properties; private ConcurrentHashMap> messages = new ConcurrentHashMap<>(); - public ExtendingTheme(List themes) { + public ExtendingTheme(List themes, Set themeResourceProviders) { this.themes = themes; + this.themeResourceProviders = themeResourceProviders; } @Override @@ -215,29 +213,14 @@ public class ExtendingThemeManager implements ThemeProvider { return template; } } - return null; - } - @Override - public InputStream getTemplateAsStream(String name) throws IOException { - for (Theme t : themes) { - InputStream template = t.getTemplateAsStream(name); + for (ThemeResourceProvider t : themeResourceProviders) { + URL template = t.getTemplate(name); if (template != null) { return template; } } - return null; - } - - @Override - public URL getResource(String path) throws IOException { - for (Theme t : themes) { - URL resource = t.getResource(path); - if (resource != null) { - return resource; - } - } return null; } @@ -249,6 +232,14 @@ public class ExtendingThemeManager implements ThemeProvider { return resource; } } + + for (ThemeResourceProvider t : themeResourceProviders) { + InputStream resource = t.getResourceAsStream(path); + if (resource != null) { + return resource; + } + } + return null; } diff --git a/services/src/main/java/org/keycloak/theme/FolderTheme.java b/services/src/main/java/org/keycloak/theme/FolderTheme.java index 04621d7528..f15bddbf33 100644 --- a/services/src/main/java/org/keycloak/theme/FolderTheme.java +++ b/services/src/main/java/org/keycloak/theme/FolderTheme.java @@ -87,13 +87,7 @@ public class FolderTheme implements Theme { } @Override - public InputStream getTemplateAsStream(String name) throws IOException { - URL url = getTemplate(name); - return url != null ? url.openStream() : null; - } - - @Override - public URL getResource(String path) throws IOException { + public InputStream getResourceAsStream(String path) throws IOException { if (File.separatorChar != '/') { path = path.replace('/', File.separatorChar); } @@ -102,16 +96,10 @@ public class FolderTheme implements Theme { if (!file.isFile() || !file.getCanonicalPath().startsWith(resourcesDir.getCanonicalPath())) { return null; } else { - return file.toURI().toURL(); + return file.toURI().toURL().openStream(); } } - @Override - public InputStream getResourceAsStream(String path) throws IOException { - URL url = getResource(path); - return url != null ? url.openStream() : null; - } - @Override public Properties getMessages(Locale locale) throws IOException { return getMessages("messages", locale); diff --git a/services/src/main/java/org/keycloak/theme/JarThemeProviderFactory.java b/services/src/main/java/org/keycloak/theme/JarThemeProviderFactory.java index 4a2bb26b6e..83cbb58bf5 100755 --- a/services/src/main/java/org/keycloak/theme/JarThemeProviderFactory.java +++ b/services/src/main/java/org/keycloak/theme/JarThemeProviderFactory.java @@ -32,47 +32,15 @@ import java.util.Map; /** * @author Stian Thorgersen */ -public class JarThemeProviderFactory implements ThemeProviderFactory { +public class JarThemeProviderFactory extends ClasspathThemeProviderFactory { - protected static final String KEYCLOAK_THEMES_JSON = "META-INF/keycloak-themes.json"; - protected static Map> themes = new HashMap<>(); - - public static class ThemeRepresentation { - private String name; - private String[] types; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String[] getTypes() { - return types; - } - - public void setTypes(String[] types) { - this.types = types; - } - } - - public static class ThemesRepresentation { - private ThemeRepresentation[] themes; - - public ThemeRepresentation[] getThemes() { - return themes; - } - - public void setThemes(ThemeRepresentation[] themes) { - this.themes = themes; - } + public JarThemeProviderFactory() { + super("jar"); } @Override public ThemeProvider create(KeycloakSession session) { - return new JarThemeProvider(themes); + return new ClasspathThemeProvider(themes); } @Override @@ -88,35 +56,4 @@ public class JarThemeProviderFactory implements ThemeProviderFactory { } } - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public void close() { - } - - @Override - public String getId() { - return "jar"; - } - - protected void loadThemes(ClassLoader classLoader, InputStream themesInputStream) { - try { - ThemesRepresentation themesRep = JsonSerialization.readValue(themesInputStream, ThemesRepresentation.class); - - for (ThemeRepresentation themeRep : themesRep.getThemes()) { - for (String t : themeRep.getTypes()) { - Theme.Type type = Theme.Type.valueOf(t.toUpperCase()); - if (!themes.containsKey(type)) { - themes.put(type, new HashMap()); - } - themes.get(type).put(themeRep.getName(), new ClassLoaderTheme(themeRep.getName(), type, classLoader)); - } - } - } catch (Exception e) { - throw new RuntimeException("Failed to load themes", e); - } - } - } diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/theme/TestThemeResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/theme/TestThemeResourceProvider.java new file mode 100644 index 0000000000..e621b82f8a --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/theme/TestThemeResourceProvider.java @@ -0,0 +1,11 @@ +package org.keycloak.testsuite.theme; + +import org.keycloak.theme.ClasspathThemeResourceProviderFactory; + +public class TestThemeResourceProvider extends ClasspathThemeResourceProviderFactory { + + public TestThemeResourceProvider() { + super("test-resources", TestThemeResourceProvider.class.getClassLoader()); + } + +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.theme.ThemeResourceProviderFactory b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.theme.ThemeResourceProviderFactory new file mode 100644 index 0000000000..4da0a77f57 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/services/org.keycloak.theme.ThemeResourceProviderFactory @@ -0,0 +1 @@ +org.keycloak.testsuite.theme.TestThemeResourceProvider \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/resources/test.js b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/resources/test.js new file mode 100644 index 0000000000..b5d5243b91 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/resources/test.js @@ -0,0 +1 @@ +console.debug('hello'); \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/templates/test.ftl b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/templates/test.ftl new file mode 100644 index 0000000000..5124d86069 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme-resources/templates/test.ftl @@ -0,0 +1 @@ +Hello! \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java new file mode 100644 index 0000000000..5373cb06ba --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/theme/ThemeResourceProviderTest.java @@ -0,0 +1,53 @@ +package org.keycloak.testsuite.theme; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Assert; +import org.junit.Test; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractTestRealmKeycloakTest; +import org.keycloak.testsuite.runonserver.RunOnServerDeployment; +import org.keycloak.theme.Theme; +import org.keycloak.theme.ThemeProvider; + +import java.io.IOException; + +public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest { + + @Deployment + public static WebArchive deploy() { + return RunOnServerDeployment.create(ThemeResourceProviderTest.class, AbstractTestRealmKeycloakTest.class); + } + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + + } + + @Test + public void getTheme() { + testingClient.server().run(session -> { + try { + ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending"); + Theme theme = extending.getTheme("base", Theme.Type.LOGIN); + Assert.assertNotNull(theme.getTemplate("test.ftl")); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + }); + } + + @Test + public void getResourceAsStream() { + testingClient.server().run(session -> { + try { + ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending"); + Theme theme = extending.getTheme("base", Theme.Type.LOGIN); + Assert.assertNotNull(theme.getResourceAsStream("test.js")); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + }); + } + +} diff --git a/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java b/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java index b0d66c2817..32a45b8219 100755 --- a/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java +++ b/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleProviderLoaderFactory.java @@ -21,6 +21,7 @@ import org.jboss.modules.Module; import org.jboss.modules.ModuleClassLoader; import org.jboss.modules.ModuleIdentifier; import org.keycloak.provider.DefaultProviderLoader; +import org.keycloak.provider.KeycloakDeploymentInfo; import org.keycloak.provider.ProviderLoader; import org.keycloak.provider.ProviderLoaderFactory; @@ -35,11 +36,11 @@ public class ModuleProviderLoaderFactory implements ProviderLoaderFactory { } @Override - public ProviderLoader create(ClassLoader baseClassLoader, String resource) { + public ProviderLoader create(KeycloakDeploymentInfo info, ClassLoader baseClassLoader, String resource) { try { Module module = Module.getContextModuleLoader().loadModule(ModuleIdentifier.fromString(resource)); ModuleClassLoader classLoader = module.getClassLoader(); - return new DefaultProviderLoader(classLoader); + return new DefaultProviderLoader(info, classLoader); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleThemeProviderFactory.java b/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleThemeProviderFactory.java index 72d2e1d73d..d1e6ce5fcc 100644 --- a/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleThemeProviderFactory.java +++ b/wildfly/extensions/src/main/java/org/keycloak/provider/wildfly/ModuleThemeProviderFactory.java @@ -21,12 +21,17 @@ import org.jboss.modules.Module; import org.jboss.modules.ModuleClassLoader; import org.jboss.modules.ModuleIdentifier; import org.keycloak.Config; +import org.keycloak.theme.ClasspathThemeProviderFactory; import org.keycloak.theme.JarThemeProviderFactory; /** * @author Stian Thorgersen */ -public class ModuleThemeProviderFactory extends JarThemeProviderFactory { +public class ModuleThemeProviderFactory extends ClasspathThemeProviderFactory { + + public ModuleThemeProviderFactory() { + super("module"); + } @Override public void init(Config.Scope config) { @@ -44,9 +49,4 @@ public class ModuleThemeProviderFactory extends JarThemeProviderFactory { } } - @Override - public String getId() { - return "module"; - } - } diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java index 7def4d1345..3d6e367a56 100644 --- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java +++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java @@ -24,12 +24,12 @@ import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.as.server.deployment.module.ModuleDependency; import org.jboss.as.server.deployment.module.ModuleSpecification; import org.jboss.as.server.deployment.module.ResourceRoot; -import org.jboss.logging.Logger; import org.jboss.modules.Module; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoader; import org.jboss.vfs.VirtualFile; import org.jboss.vfs.util.AbstractVirtualFileFilterWithAttributes; +import org.keycloak.provider.KeycloakDeploymentInfo; import java.io.IOException; import java.util.List; @@ -48,8 +48,6 @@ public class KeycloakProviderDependencyProcessor implements DeploymentUnitProces private static final ModuleIdentifier RESTEASY = ModuleIdentifier.create("org.jboss.resteasy.resteasy-jaxrs"); private static final ModuleIdentifier APACHE = ModuleIdentifier.create("org.apache.httpcomponents"); - private static final Logger logger = Logger.getLogger(KeycloakProviderDependencyProcessor.class); - @Override public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); @@ -60,53 +58,66 @@ public class KeycloakProviderDependencyProcessor implements DeploymentUnitProces return; } - if (!isKeycloakProviderDeployment(deploymentUnit)) return; - - final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION); - final ModuleLoader moduleLoader = Module.getBootModuleLoader(); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false)); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false)); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_SERVER_SPI, false, false, false, false)); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_SERVER_SPI_PRIVATE, false, false, false, false)); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, JAXRS, false, false, false, false)); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, RESTEASY, false, false, false, false)); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE, false, false, false, false)); - moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JPA, false, false, false, false)); - - + KeycloakDeploymentInfo info = getKeycloakProviderDeploymentInfo(deploymentUnit); + if (info.hasServices()) { + final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION); + final ModuleLoader moduleLoader = Module.getBootModuleLoader(); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_SERVER_SPI, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_SERVER_SPI_PRIVATE, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, JAXRS, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, RESTEASY, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JPA, false, false, false, false)); + } } public KeycloakProviderDependencyProcessor() { super(); } - public static boolean isKeycloakProviderDeployment(DeploymentUnit du) { - final ResourceRoot resourceRoot = du.getAttachment(Attachments.DEPLOYMENT_ROOT); - if (resourceRoot == null) { - return false; - } - final VirtualFile deploymentRoot = resourceRoot.getRoot(); - if (deploymentRoot == null || !deploymentRoot.exists()) { - return false; - } - VirtualFile services = deploymentRoot.getChild("META-INF/services"); - if (!services.exists()) return false; - try { - List archives = services.getChildren(new AbstractVirtualFileFilterWithAttributes(){ - @Override - public boolean accepts(VirtualFile file) { - return file.getName().startsWith("org.keycloak"); - } - }); - return !archives.isEmpty(); - } catch (IOException e) { + public static KeycloakDeploymentInfo getKeycloakProviderDeploymentInfo(DeploymentUnit du) { + KeycloakDeploymentInfo info = KeycloakDeploymentInfo.create(); + info.name(du.getName()); + final ResourceRoot resourceRoot = du.getAttachment(Attachments.DEPLOYMENT_ROOT); + if (resourceRoot != null) { + final VirtualFile deploymentRoot = resourceRoot.getRoot(); + if (deploymentRoot != null && deploymentRoot.exists()) { + if (deploymentRoot.getChild("META-INF/keycloak-themes.json").exists() && deploymentRoot.getChild("theme").exists()) { + info.themes(); + } + + if (deploymentRoot.getChild("theme-resources").exists()) { + info.themeResources(); + } + + VirtualFile services = deploymentRoot.getChild("META-INF/services"); + if(services.exists()) { + try { + List archives = services.getChildren(new AbstractVirtualFileFilterWithAttributes() { + @Override + public boolean accepts(VirtualFile file) { + return file.getName().startsWith("org.keycloak"); + } + }); + if (!archives.isEmpty()) { + info.services(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } } - return false; + + return info; } @Override public void undeploy(DeploymentUnit context) { } + } diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java index fcd1b463d7..bf74732dc7 100644 --- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java +++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java @@ -24,6 +24,7 @@ import org.jboss.as.server.deployment.DeploymentUnitProcessingException; import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.logging.Logger; import org.jboss.modules.Module; +import org.keycloak.provider.KeycloakDeploymentInfo; import org.keycloak.provider.ProviderManager; import org.keycloak.provider.ProviderManagerRegistry; @@ -46,16 +47,14 @@ public class KeycloakProviderDeploymentProcessor implements DeploymentUnitProces return; } - if (!KeycloakProviderDependencyProcessor.isKeycloakProviderDeployment(deploymentUnit)) return; - - logger.infov("Deploying Keycloak provider: {0}", deploymentUnit.getName()); - final Module module = deploymentUnit.getAttachment(Attachments.MODULE); - ProviderManager pm = new ProviderManager(module.getClassLoader()); - ProviderManagerRegistry.SINGLETON.deploy(pm); - deploymentUnit.putAttachment(ATTACHMENT_KEY, pm); - - - + KeycloakDeploymentInfo info = KeycloakProviderDependencyProcessor.getKeycloakProviderDeploymentInfo(deploymentUnit); + if (info.isProvider()) { + logger.infov("Deploying Keycloak provider: {0}", deploymentUnit.getName()); + final Module module = deploymentUnit.getAttachment(Attachments.MODULE); + ProviderManager pm = new ProviderManager(info, module.getClassLoader()); + ProviderManagerRegistry.SINGLETON.deploy(pm); + deploymentUnit.putAttachment(ATTACHMENT_KEY, pm); + } } public KeycloakProviderDeploymentProcessor() {