KEYCLOAK-8044 Clear theme caches on hot-deploy
This commit is contained in:
parent
d8d81ee162
commit
9e47022116
13 changed files with 347 additions and 380 deletions
|
@ -34,4 +34,6 @@ public interface ThemeManager {
|
||||||
*/
|
*/
|
||||||
Set<String> nameSet(Theme.Type type);
|
Set<String> nameSet(Theme.Type type);
|
||||||
|
|
||||||
|
void clearCache();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,13 @@ public class ProviderManager {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ProviderManager.class);
|
private static final Logger logger = Logger.getLogger(ProviderManager.class);
|
||||||
|
|
||||||
|
private final KeycloakDeploymentInfo info;
|
||||||
private List<ProviderLoader> loaders = new LinkedList<ProviderLoader>();
|
private List<ProviderLoader> loaders = new LinkedList<ProviderLoader>();
|
||||||
private MultivaluedHashMap<Class<? extends Provider>, ProviderFactory> cache = new MultivaluedHashMap<>();
|
private MultivaluedHashMap<Class<? extends Provider>, ProviderFactory> cache = new MultivaluedHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
public ProviderManager(KeycloakDeploymentInfo info, ClassLoader baseClassLoader, String... resources) {
|
public ProviderManager(KeycloakDeploymentInfo info, ClassLoader baseClassLoader, String... resources) {
|
||||||
|
this.info = info;
|
||||||
List<ProviderLoaderFactory> factories = new LinkedList<ProviderLoaderFactory>();
|
List<ProviderLoaderFactory> factories = new LinkedList<ProviderLoaderFactory>();
|
||||||
for (ProviderLoaderFactory f : ServiceLoader.load(ProviderLoaderFactory.class, getClass().getClassLoader())) {
|
for (ProviderLoaderFactory f : ServiceLoader.load(ProviderLoaderFactory.class, getClass().getClassLoader())) {
|
||||||
factories.add(f);
|
factories.add(f);
|
||||||
|
@ -126,4 +128,8 @@ public class ProviderManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized KeycloakDeploymentInfo getInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@ import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||||
import org.keycloak.storage.ClientStorageManager;
|
import org.keycloak.storage.ClientStorageManager;
|
||||||
import org.keycloak.storage.UserStorageManager;
|
import org.keycloak.storage.UserStorageManager;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||||
import org.keycloak.theme.DefaultThemeManager;
|
|
||||||
import org.keycloak.vault.DefaultVaultTranscriber;
|
import org.keycloak.vault.DefaultVaultTranscriber;
|
||||||
import org.keycloak.vault.VaultProvider;
|
import org.keycloak.vault.VaultProvider;
|
||||||
import org.keycloak.vault.VaultTranscriber;
|
import org.keycloak.vault.VaultTranscriber;
|
||||||
|
@ -301,7 +300,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||||
@Override
|
@Override
|
||||||
public ThemeManager theme() {
|
public ThemeManager theme() {
|
||||||
if (themeManager == null) {
|
if (themeManager == null) {
|
||||||
themeManager = new DefaultThemeManager(this);
|
themeManager = factory.getThemeManagerFactory().create(this);
|
||||||
}
|
}
|
||||||
return themeManager;
|
return themeManager;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.provider.ProviderManagerDeployer;
|
||||||
import org.keycloak.provider.ProviderManagerRegistry;
|
import org.keycloak.provider.ProviderManagerRegistry;
|
||||||
import org.keycloak.provider.Spi;
|
import org.keycloak.provider.Spi;
|
||||||
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
|
||||||
|
import org.keycloak.theme.DefaultThemeManagerFactory;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -50,6 +51,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
||||||
private volatile Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<>();
|
private volatile Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<>();
|
||||||
protected CopyOnWriteArrayList<ProviderEventListener> listeners = new CopyOnWriteArrayList<>();
|
protected CopyOnWriteArrayList<ProviderEventListener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
private DefaultThemeManagerFactory themeManagerFactory;
|
||||||
|
|
||||||
// TODO: Likely should be changed to int and use Time.currentTime() to be compatible with all our "time" reps
|
// TODO: Likely should be changed to int and use Time.currentTime() to be compatible with all our "time" reps
|
||||||
protected long serverStartupTimestamp;
|
protected long serverStartupTimestamp;
|
||||||
|
|
||||||
|
@ -101,7 +104,9 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
||||||
|
|
||||||
AdminPermissions.registerListener(this);
|
AdminPermissions.registerListener(this);
|
||||||
|
|
||||||
|
themeManagerFactory = new DefaultThemeManagerFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<Class<? extends Provider>, Map<String, ProviderFactory>> getFactoriesCopy() {
|
protected Map<Class<? extends Provider>, Map<String, ProviderFactory>> getFactoriesCopy() {
|
||||||
Map<Class<? extends Provider>, Map<String, ProviderFactory>> copy = new HashMap<>();
|
Map<Class<? extends Provider>, Map<String, ProviderFactory>> copy = new HashMap<>();
|
||||||
for (Map.Entry<Class<? extends Provider>, Map<String, ProviderFactory>> entry : factoriesMap.entrySet()) {
|
for (Map.Entry<Class<? extends Provider>, Map<String, ProviderFactory>> entry : factoriesMap.entrySet()) {
|
||||||
|
@ -141,6 +146,10 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
||||||
for (ProviderFactory factory : deployed) {
|
for (ProviderFactory factory : deployed) {
|
||||||
factory.postInit(this);
|
factory.postInit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pm.getInfo().hasThemes() || pm.getInfo().hasThemeResources()) {
|
||||||
|
themeManagerFactory.clearCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -166,6 +175,10 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected DefaultThemeManagerFactory getThemeManagerFactory() {
|
||||||
|
return themeManagerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
protected void checkProvider() {
|
protected void checkProvider() {
|
||||||
for (Spi spi : spis) {
|
for (Spi spi : spis) {
|
||||||
String provider = Config.getProvider(spi.getName());
|
String provider = Config.getProvider(spi.getName());
|
||||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
import org.keycloak.services.util.CacheControlUtil;
|
import org.keycloak.services.util.CacheControlUtil;
|
||||||
import org.keycloak.theme.Theme;
|
import org.keycloak.theme.Theme;
|
||||||
import org.keycloak.theme.ThemeProvider;
|
|
||||||
|
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.keycloak.services.managers.AppAuthManager;
|
||||||
import org.keycloak.services.managers.Auth;
|
import org.keycloak.services.managers.Auth;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.theme.Theme;
|
import org.keycloak.theme.Theme;
|
||||||
import org.keycloak.theme.ThemeProvider;
|
|
||||||
|
|
||||||
import javax.ws.rs.HttpMethod;
|
import javax.ws.rs.HttpMethod;
|
||||||
import javax.ws.rs.InternalServerErrorException;
|
import javax.ws.rs.InternalServerErrorException;
|
||||||
|
|
|
@ -53,9 +53,7 @@ import org.keycloak.representations.info.ServerInfoRepresentation;
|
||||||
import org.keycloak.representations.info.SpiInfoRepresentation;
|
import org.keycloak.representations.info.SpiInfoRepresentation;
|
||||||
import org.keycloak.representations.info.SystemInfoRepresentation;
|
import org.keycloak.representations.info.SystemInfoRepresentation;
|
||||||
import org.keycloak.representations.info.ThemeInfoRepresentation;
|
import org.keycloak.representations.info.ThemeInfoRepresentation;
|
||||||
import org.keycloak.storage.user.ImportSynchronization;
|
|
||||||
import org.keycloak.theme.Theme;
|
import org.keycloak.theme.Theme;
|
||||||
import org.keycloak.theme.ThemeProvider;
|
|
||||||
|
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
|
283
services/src/main/java/org/keycloak/theme/DefaultThemeManager.java
Normal file → Executable file
283
services/src/main/java/org/keycloak/theme/DefaultThemeManager.java
Normal file → Executable file
|
@ -1,34 +1,303 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
package org.keycloak.theme;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.common.Version;
|
||||||
|
import org.keycloak.common.util.StringPropertyReplacer;
|
||||||
|
import org.keycloak.common.util.SystemEnvProperties;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ThemeManager;
|
import org.keycloak.models.ThemeManager;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
*/
|
||||||
public class DefaultThemeManager implements ThemeManager {
|
public class DefaultThemeManager implements ThemeManager {
|
||||||
|
|
||||||
private KeycloakSession session;
|
private static final Logger log = Logger.getLogger(DefaultThemeManager.class);
|
||||||
|
|
||||||
public DefaultThemeManager(KeycloakSession session) {
|
private final DefaultThemeManagerFactory factory;
|
||||||
|
private final KeycloakSession session;
|
||||||
|
private List<ThemeProvider> providers;
|
||||||
|
private String defaultTheme;
|
||||||
|
|
||||||
|
public DefaultThemeManager(DefaultThemeManagerFactory factory, KeycloakSession session) {
|
||||||
|
this.factory = factory;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
this.defaultTheme = Config.scope("theme").get("default", Version.NAME.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Theme getTheme(Theme.Type type) throws IOException {
|
public Theme getTheme(Theme.Type type) {
|
||||||
String name = session.getProvider(ThemeSelectorProvider.class).getThemeName(type);
|
String name = session.getProvider(ThemeSelectorProvider.class).getThemeName(type);
|
||||||
return getTheme(name, type);
|
return getTheme(name, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Theme getTheme(String name, Theme.Type type) throws IOException {
|
public Theme getTheme(String name, Theme.Type type) {
|
||||||
return session.getProvider(ThemeProvider.class, "extending").getTheme(name, type);
|
if (name == null) {
|
||||||
|
name = defaultTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
Theme theme = factory.getCachedTheme(name, type);
|
||||||
|
if (theme == null) {
|
||||||
|
theme = loadTheme(name, type);
|
||||||
|
if (theme == null) {
|
||||||
|
theme = loadTheme("keycloak", type);
|
||||||
|
if (theme == null) {
|
||||||
|
theme = loadTheme("base", type);
|
||||||
|
}
|
||||||
|
log.errorv("Failed to find {0} theme {1}, using built-in themes", type, name);
|
||||||
|
} else {
|
||||||
|
theme = factory.addCachedTheme(name, type, theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> nameSet(Theme.Type type) {
|
public Set<String> nameSet(Theme.Type type) {
|
||||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
Set<String> themes = new HashSet<String>();
|
||||||
return themeProvider.nameSet(type);
|
for (ThemeProvider p : getProviders()) {
|
||||||
|
themes.addAll(p.nameSet(type));
|
||||||
|
}
|
||||||
|
return themes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearCache() {
|
||||||
|
factory.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Theme loadTheme(String name, Theme.Type type) {
|
||||||
|
Theme theme = findTheme(name, type);
|
||||||
|
List<Theme> 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.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())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExtendingTheme(themes, session.getAllProviders(ThemeResourceProvider.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Theme findTheme(String name, Theme.Type type) {
|
||||||
|
for (ThemeProvider p : getProviders()) {
|
||||||
|
if (p.hasTheme(name, type)) {
|
||||||
|
try {
|
||||||
|
return p.getTheme(name, type);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.errorv(e, p.getClass() + " failed to load theme, type={0}, name={1}", type, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExtendingTheme implements Theme {
|
||||||
|
|
||||||
|
private List<Theme> themes;
|
||||||
|
private Set<ThemeResourceProvider> themeResourceProviders;
|
||||||
|
|
||||||
|
private Properties properties;
|
||||||
|
|
||||||
|
private ConcurrentHashMap<String, ConcurrentHashMap<Locale, Properties>> messages = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public ExtendingTheme(List<Theme> themes, Set<ThemeResourceProvider> themeResourceProviders) {
|
||||||
|
this.themes = themes;
|
||||||
|
this.themeResourceProviders = themeResourceProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return themes.get(0).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParentName() {
|
||||||
|
return themes.get(0).getParentName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getImportName() {
|
||||||
|
return themes.get(0).getImportName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return themes.get(0).getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getTemplate(String name) throws IOException {
|
||||||
|
for (Theme t : themes) {
|
||||||
|
URL template = t.getTemplate(name);
|
||||||
|
if (template != null) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ThemeResourceProvider t : themeResourceProviders) {
|
||||||
|
URL template = t.getTemplate(name);
|
||||||
|
if (template != null) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getResourceAsStream(String path) throws IOException {
|
||||||
|
for (Theme t : themes) {
|
||||||
|
InputStream resource = t.getResourceAsStream(path);
|
||||||
|
if (resource != null) {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ThemeResourceProvider t : themeResourceProviders) {
|
||||||
|
InputStream resource = t.getResourceAsStream(path);
|
||||||
|
if (resource != null) {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Properties getMessages(Locale locale) throws IOException {
|
||||||
|
return getMessages("messages", locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Properties getMessages(String baseBundlename, Locale locale) throws IOException {
|
||||||
|
if (messages.get(baseBundlename) == null || messages.get(baseBundlename).get(locale) == null) {
|
||||||
|
Properties messages = new Properties();
|
||||||
|
|
||||||
|
Locale parent = getParent(locale);
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
messages.putAll(getMessages(baseBundlename, parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ThemeResourceProvider t : themeResourceProviders ){
|
||||||
|
messages.putAll(t.getMessages(baseBundlename, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
ListIterator<Theme> itr = themes.listIterator(themes.size());
|
||||||
|
while (itr.hasPrevious()) {
|
||||||
|
Properties m = itr.previous().getMessages(baseBundlename, locale);
|
||||||
|
if (m != null) {
|
||||||
|
messages.putAll(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages.putIfAbsent(baseBundlename, new ConcurrentHashMap<Locale, Properties>());
|
||||||
|
this.messages.get(baseBundlename).putIfAbsent(locale, messages);
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
} else {
|
||||||
|
return messages.get(baseBundlename).get(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Properties getProperties() throws IOException {
|
||||||
|
if (properties == null) {
|
||||||
|
Properties properties = new Properties();
|
||||||
|
ListIterator<Theme> itr = themes.listIterator(themes.size());
|
||||||
|
while (itr.hasPrevious()) {
|
||||||
|
Properties p = itr.previous().getProperties();
|
||||||
|
if (p != null) {
|
||||||
|
properties.putAll(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
substituteProperties(properties);
|
||||||
|
this.properties = properties;
|
||||||
|
return properties;
|
||||||
|
} else {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over all string properties defined in "theme.properties" then substitute the value with system property or environment variables.
|
||||||
|
* See {@link StringPropertyReplacer#replaceProperties} for details about the different formats.
|
||||||
|
*/
|
||||||
|
private void substituteProperties(final Properties properties) {
|
||||||
|
for (final String propertyName : properties.stringPropertyNames()) {
|
||||||
|
properties.setProperty(propertyName, StringPropertyReplacer.replaceProperties(properties.getProperty(propertyName), new SystemEnvProperties()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Locale getParent(Locale locale) {
|
||||||
|
if (Locale.ENGLISH.equals(locale)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locale.getVariant() != null && !locale.getVariant().isEmpty()) {
|
||||||
|
return new Locale(locale.getLanguage(), locale.getCountry());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locale.getCountry() != null && !locale.getCountry().isEmpty()) {
|
||||||
|
return new Locale(locale.getLanguage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Locale.ENGLISH;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ThemeProvider> getProviders() {
|
||||||
|
if (providers == null) {
|
||||||
|
providers = new LinkedList(session.getAllProviders(ThemeProvider.class));
|
||||||
|
Collections.sort(providers, (o1, o2) -> o2.getProviderPriority() - o1.getProviderPriority());
|
||||||
|
}
|
||||||
|
|
||||||
|
return providers;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,43 +17,63 @@
|
||||||
|
|
||||||
package org.keycloak.theme;
|
package org.keycloak.theme;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.ThemeManager;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
*/
|
*/
|
||||||
public class ExtendingThemeManagerFactory implements ThemeProviderFactory {
|
public class DefaultThemeManagerFactory {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(DefaultThemeManagerFactory.class);
|
||||||
|
|
||||||
private ConcurrentHashMap<ThemeKey, Theme> themeCache;
|
private ConcurrentHashMap<ThemeKey, Theme> themeCache;
|
||||||
|
|
||||||
@Override
|
public DefaultThemeManagerFactory() {
|
||||||
public ThemeProvider create(KeycloakSession session) {
|
|
||||||
return new ExtendingThemeManager(session, themeCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Config.Scope config) {
|
|
||||||
if(Config.scope("theme").getBoolean("cacheThemes", true)) {
|
if(Config.scope("theme").getBoolean("cacheThemes", true)) {
|
||||||
themeCache = new ConcurrentHashMap<>();
|
themeCache = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public ThemeManager create(KeycloakSession session) {
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
return new DefaultThemeManager(this, session);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Theme getCachedTheme(String name, Theme.Type type) {
|
||||||
public void close() {
|
if (themeCache != null) {
|
||||||
|
DefaultThemeManagerFactory.ThemeKey key = DefaultThemeManagerFactory.ThemeKey.get(name, type);
|
||||||
|
return themeCache.get(key);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Theme addCachedTheme(String name, Theme.Type type, Theme theme) {
|
||||||
public String getId() {
|
if (theme == null) {
|
||||||
return "extending";
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themeCache == null) {
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultThemeManagerFactory.ThemeKey key = DefaultThemeManagerFactory.ThemeKey.get(name, type);
|
||||||
|
if (themeCache.putIfAbsent(key, theme) != null) {
|
||||||
|
theme = themeCache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCache() {
|
||||||
|
if (themeCache != null) {
|
||||||
|
themeCache.clear();
|
||||||
|
log.info("Cleared theme cache");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ThemeKey {
|
public static class ThemeKey {
|
|
@ -1,331 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.jboss.logging.Logger;
|
|
||||||
import org.keycloak.Config;
|
|
||||||
import org.keycloak.common.Version;
|
|
||||||
import org.keycloak.common.util.StringPropertyReplacer;
|
|
||||||
import org.keycloak.common.util.SystemEnvProperties;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ListIterator;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
|
||||||
*/
|
|
||||||
public class ExtendingThemeManager implements ThemeProvider {
|
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(ExtendingThemeManager.class);
|
|
||||||
|
|
||||||
private final KeycloakSession session;
|
|
||||||
private final ConcurrentHashMap<ExtendingThemeManagerFactory.ThemeKey, Theme> themeCache;
|
|
||||||
private List<ThemeProvider> providers;
|
|
||||||
private String defaultTheme;
|
|
||||||
|
|
||||||
public ExtendingThemeManager(KeycloakSession session, ConcurrentHashMap<ExtendingThemeManagerFactory.ThemeKey, Theme> themeCache) {
|
|
||||||
this.session = session;
|
|
||||||
this.themeCache = themeCache;
|
|
||||||
this.defaultTheme = Config.scope("theme").get("default", Version.NAME.toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ThemeProvider> getProviders() {
|
|
||||||
if (providers == null) {
|
|
||||||
providers = new LinkedList();
|
|
||||||
|
|
||||||
for (ThemeProvider p : session.getAllProviders(ThemeProvider.class)) {
|
|
||||||
if (!(p instanceof ExtendingThemeManager)) {
|
|
||||||
if (!p.getClass().equals(ExtendingThemeManager.class)) {
|
|
||||||
providers.add(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(providers, new Comparator<ThemeProvider>() {
|
|
||||||
@Override
|
|
||||||
public int compare(ThemeProvider o1, ThemeProvider o2) {
|
|
||||||
return o2.getProviderPriority() - o1.getProviderPriority();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return providers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getProviderPriority() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Theme getTheme(String name, Theme.Type type) throws IOException {
|
|
||||||
if (name == null) {
|
|
||||||
name = defaultTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (themeCache != null) {
|
|
||||||
ExtendingThemeManagerFactory.ThemeKey key = ExtendingThemeManagerFactory.ThemeKey.get(name, type);
|
|
||||||
Theme theme = themeCache.get(key);
|
|
||||||
if (theme == null) {
|
|
||||||
theme = loadTheme(name, type);
|
|
||||||
if (theme == null) {
|
|
||||||
theme = loadTheme("keycloak", type);
|
|
||||||
if (theme == null) {
|
|
||||||
theme = loadTheme("base", type);
|
|
||||||
}
|
|
||||||
log.errorv("Failed to find {0} theme {1}, using built-in themes", type, name);
|
|
||||||
} else if (themeCache.putIfAbsent(key, theme) != null) {
|
|
||||||
theme = themeCache.get(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return theme;
|
|
||||||
} else {
|
|
||||||
return loadTheme(name, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Theme loadTheme(String name, Theme.Type type) throws IOException {
|
|
||||||
Theme theme = findTheme(name, type);
|
|
||||||
List<Theme> 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.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())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ExtendingTheme(themes, session.getAllProviders(ThemeResourceProvider.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> nameSet(Theme.Type type) {
|
|
||||||
Set<String> themes = new HashSet<String>();
|
|
||||||
for (ThemeProvider p : getProviders()) {
|
|
||||||
themes.addAll(p.nameSet(type));
|
|
||||||
}
|
|
||||||
return themes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasTheme(String name, Theme.Type type) {
|
|
||||||
for (ThemeProvider p : getProviders()) {
|
|
||||||
if (p.hasTheme(name, type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
providers = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Theme findTheme(String name, Theme.Type type) {
|
|
||||||
for (ThemeProvider p : getProviders()) {
|
|
||||||
if (p.hasTheme(name, type)) {
|
|
||||||
try {
|
|
||||||
return p.getTheme(name, type);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.errorv(e, p.getClass() + " failed to load theme, type={0}, name={1}", type, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ExtendingTheme implements Theme {
|
|
||||||
|
|
||||||
private List<Theme> themes;
|
|
||||||
private Set<ThemeResourceProvider> themeResourceProviders;
|
|
||||||
|
|
||||||
private Properties properties;
|
|
||||||
|
|
||||||
private ConcurrentHashMap<String, ConcurrentHashMap<Locale, Properties>> messages = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public ExtendingTheme(List<Theme> themes, Set<ThemeResourceProvider> themeResourceProviders) {
|
|
||||||
this.themes = themes;
|
|
||||||
this.themeResourceProviders = themeResourceProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return themes.get(0).getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getParentName() {
|
|
||||||
return themes.get(0).getParentName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getImportName() {
|
|
||||||
return themes.get(0).getImportName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type getType() {
|
|
||||||
return themes.get(0).getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URL getTemplate(String name) throws IOException {
|
|
||||||
for (Theme t : themes) {
|
|
||||||
URL template = t.getTemplate(name);
|
|
||||||
if (template != null) {
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ThemeResourceProvider t : themeResourceProviders) {
|
|
||||||
URL template = t.getTemplate(name);
|
|
||||||
if (template != null) {
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getResourceAsStream(String path) throws IOException {
|
|
||||||
for (Theme t : themes) {
|
|
||||||
InputStream resource = t.getResourceAsStream(path);
|
|
||||||
if (resource != null) {
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ThemeResourceProvider t : themeResourceProviders) {
|
|
||||||
InputStream resource = t.getResourceAsStream(path);
|
|
||||||
if (resource != null) {
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Properties getMessages(Locale locale) throws IOException {
|
|
||||||
return getMessages("messages", locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Properties getMessages(String baseBundlename, Locale locale) throws IOException {
|
|
||||||
if (messages.get(baseBundlename) == null || messages.get(baseBundlename).get(locale) == null) {
|
|
||||||
Properties messages = new Properties();
|
|
||||||
|
|
||||||
Locale parent = getParent(locale);
|
|
||||||
|
|
||||||
if (parent != null) {
|
|
||||||
messages.putAll(getMessages(baseBundlename, parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ThemeResourceProvider t : themeResourceProviders ){
|
|
||||||
messages.putAll(t.getMessages(baseBundlename, locale));
|
|
||||||
}
|
|
||||||
|
|
||||||
ListIterator<Theme> itr = themes.listIterator(themes.size());
|
|
||||||
while (itr.hasPrevious()) {
|
|
||||||
Properties m = itr.previous().getMessages(baseBundlename, locale);
|
|
||||||
if (m != null) {
|
|
||||||
messages.putAll(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.messages.putIfAbsent(baseBundlename, new ConcurrentHashMap<Locale, Properties>());
|
|
||||||
this.messages.get(baseBundlename).putIfAbsent(locale, messages);
|
|
||||||
|
|
||||||
return messages;
|
|
||||||
} else {
|
|
||||||
return messages.get(baseBundlename).get(locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Properties getProperties() throws IOException {
|
|
||||||
if (properties == null) {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
ListIterator<Theme> itr = themes.listIterator(themes.size());
|
|
||||||
while (itr.hasPrevious()) {
|
|
||||||
Properties p = itr.previous().getProperties();
|
|
||||||
if (p != null) {
|
|
||||||
properties.putAll(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
substituteProperties(properties);
|
|
||||||
this.properties = properties;
|
|
||||||
return properties;
|
|
||||||
} else {
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over all string properties defined in "theme.properties" then substitute the value with system property or environment variables.
|
|
||||||
* See {@link StringPropertyReplacer#replaceProperties} for details about the different formats.
|
|
||||||
*/
|
|
||||||
private void substituteProperties(final Properties properties) {
|
|
||||||
for (final String propertyName : properties.stringPropertyNames()) {
|
|
||||||
properties.setProperty(propertyName, StringPropertyReplacer.replaceProperties(properties.getProperty(propertyName), new SystemEnvProperties()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Locale getParent(Locale locale) {
|
|
||||||
if (Locale.ENGLISH.equals(locale)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locale.getVariant() != null && !locale.getVariant().isEmpty()) {
|
|
||||||
return new Locale(locale.getLanguage(), locale.getCountry());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locale.getCountry() != null && !locale.getCountry().isEmpty()) {
|
|
||||||
return new Locale(locale.getLanguage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Locale.ENGLISH;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -15,6 +15,5 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
org.keycloak.theme.ExtendingThemeManagerFactory
|
|
||||||
org.keycloak.theme.JarThemeProviderFactory
|
org.keycloak.theme.JarThemeProviderFactory
|
||||||
org.keycloak.theme.FolderThemeProviderFactory
|
org.keycloak.theme.FolderThemeProviderFactory
|
|
@ -8,7 +8,6 @@ import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
import org.keycloak.testsuite.util.ContainerAssume;
|
import org.keycloak.testsuite.util.ContainerAssume;
|
||||||
import org.keycloak.theme.Theme;
|
import org.keycloak.theme.Theme;
|
||||||
import org.keycloak.theme.ThemeProvider;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -19,13 +18,16 @@ import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerEx
|
||||||
* @author <a href="mailto:vincent.letarouilly@gmail.com">Vincent Letarouilly</a>
|
* @author <a href="mailto:vincent.letarouilly@gmail.com">Vincent Letarouilly</a>
|
||||||
*/
|
*/
|
||||||
@AuthServerContainerExclude(REMOTE)
|
@AuthServerContainerExclude(REMOTE)
|
||||||
public class ExtendingThemeTest extends AbstractKeycloakTest {
|
public class DefaultThemeManagerTest extends AbstractKeycloakTest {
|
||||||
|
|
||||||
private static final String THEME_NAME = "environment-agnostic";
|
private static final String THEME_NAME = "environment-agnostic";
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
testingClient.server().run(session -> System.setProperty("existing_system_property", "Keycloak is awesome"));
|
testingClient.server().run(session -> {
|
||||||
|
System.setProperty("existing_system_property", "Keycloak is awesome");
|
||||||
|
session.theme().clearCache();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -39,8 +41,7 @@ public class ExtendingThemeTest extends AbstractKeycloakTest {
|
||||||
ContainerAssume.assumeAuthServerUndertow();
|
ContainerAssume.assumeAuthServerUndertow();
|
||||||
testingClient.server().run(session -> {
|
testingClient.server().run(session -> {
|
||||||
try {
|
try {
|
||||||
ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending");
|
Theme theme = session.theme().getTheme(THEME_NAME, Theme.Type.LOGIN);
|
||||||
Theme theme = extending.getTheme(THEME_NAME, Theme.Type.LOGIN);
|
|
||||||
Assert.assertEquals("Keycloak is awesome", theme.getProperties().getProperty("system.property.found"));
|
Assert.assertEquals("Keycloak is awesome", theme.getProperties().getProperty("system.property.found"));
|
||||||
Assert.assertEquals("${missing_system_property}", theme.getProperties().getProperty("system.property.missing"));
|
Assert.assertEquals("${missing_system_property}", theme.getProperties().getProperty("system.property.missing"));
|
||||||
Assert.assertEquals("defaultValue", theme.getProperties().getProperty("system.property.missing.with.default"));
|
Assert.assertEquals("defaultValue", theme.getProperties().getProperty("system.property.missing.with.default"));
|
||||||
|
@ -55,8 +56,7 @@ public class ExtendingThemeTest extends AbstractKeycloakTest {
|
||||||
public void environmentVariablesSubstitutionInThemeProperties() {
|
public void environmentVariablesSubstitutionInThemeProperties() {
|
||||||
testingClient.server().run(session -> {
|
testingClient.server().run(session -> {
|
||||||
try {
|
try {
|
||||||
ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending");
|
Theme theme = session.theme().getTheme(THEME_NAME, Theme.Type.LOGIN);
|
||||||
Theme theme = extending.getTheme(THEME_NAME, Theme.Type.LOGIN);
|
|
||||||
Assert.assertEquals("${env.MISSING_ENVIRONMENT_VARIABLE}", theme.getProperties().getProperty("env.missing"));
|
Assert.assertEquals("${env.MISSING_ENVIRONMENT_VARIABLE}", theme.getProperties().getProperty("env.missing"));
|
||||||
Assert.assertEquals("defaultValue", theme.getProperties().getProperty("env.missingWithDefault"));
|
Assert.assertEquals("defaultValue", theme.getProperties().getProperty("env.missingWithDefault"));
|
||||||
if (System.getenv().containsKey("HOMEPATH")) {
|
if (System.getenv().containsKey("HOMEPATH")) {
|
|
@ -5,14 +5,12 @@ import org.junit.Test;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||||
import org.keycloak.theme.Theme;
|
import org.keycloak.theme.Theme;
|
||||||
import org.keycloak.theme.ThemeProvider;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
|
||||||
|
|
||||||
@AuthServerContainerExclude(AuthServer.REMOTE)
|
@AuthServerContainerExclude(AuthServer.REMOTE)
|
||||||
public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
|
@ -25,8 +23,7 @@ public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
||||||
public void getTheme() {
|
public void getTheme() {
|
||||||
testingClient.server().run(session -> {
|
testingClient.server().run(session -> {
|
||||||
try {
|
try {
|
||||||
ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending");
|
Theme theme = session.theme().getTheme("base", Theme.Type.LOGIN);
|
||||||
Theme theme = extending.getTheme("base", Theme.Type.LOGIN);
|
|
||||||
Assert.assertNotNull(theme.getTemplate("test.ftl"));
|
Assert.assertNotNull(theme.getTemplate("test.ftl"));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Assert.fail(e.getMessage());
|
Assert.fail(e.getMessage());
|
||||||
|
@ -38,8 +35,7 @@ public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
||||||
public void getResourceAsStream() {
|
public void getResourceAsStream() {
|
||||||
testingClient.server().run(session -> {
|
testingClient.server().run(session -> {
|
||||||
try {
|
try {
|
||||||
ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending");
|
Theme theme = session.theme().getTheme("base", Theme.Type.LOGIN);
|
||||||
Theme theme = extending.getTheme("base", Theme.Type.LOGIN);
|
|
||||||
Assert.assertNotNull(theme.getResourceAsStream("test.js"));
|
Assert.assertNotNull(theme.getResourceAsStream("test.js"));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Assert.fail(e.getMessage());
|
Assert.fail(e.getMessage());
|
||||||
|
@ -51,8 +47,7 @@ public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
||||||
public void getMessages() {
|
public void getMessages() {
|
||||||
testingClient.server().run(session -> {
|
testingClient.server().run(session -> {
|
||||||
try {
|
try {
|
||||||
ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending");
|
Theme theme = session.theme().getTheme("base", Theme.Type.LOGIN);
|
||||||
Theme theme = extending.getTheme("base", Theme.Type.LOGIN);
|
|
||||||
Assert.assertNotNull(theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-8818"));
|
Assert.assertNotNull(theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-8818"));
|
||||||
Assert.assertNotEquals("Full name (Theme-resources)", theme.getMessages("messages", Locale.ENGLISH).get("fullName"));
|
Assert.assertNotEquals("Full name (Theme-resources)", theme.getMessages("messages", Locale.ENGLISH).get("fullName"));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -68,8 +63,7 @@ public class ThemeResourceProviderTest extends AbstractTestRealmKeycloakTest {
|
||||||
public void getMessagesLocaleResolving() {
|
public void getMessagesLocaleResolving() {
|
||||||
testingClient.server().run(session -> {
|
testingClient.server().run(session -> {
|
||||||
try {
|
try {
|
||||||
ThemeProvider extending = session.getProvider(ThemeProvider.class, "extending");
|
Theme theme = session.theme().getTheme("base", Theme.Type.LOGIN);
|
||||||
Theme theme = extending.getTheme("base", Theme.Type.LOGIN);
|
|
||||||
Assert.assertEquals("Test en_US_variant", theme.getMessages("messages", new Locale("en", "US", "variant")).get("test.keycloak-12926"));
|
Assert.assertEquals("Test en_US_variant", theme.getMessages("messages", new Locale("en", "US", "variant")).get("test.keycloak-12926"));
|
||||||
Assert.assertEquals("Test en_US", theme.getMessages("messages", new Locale("en", "US")).get("test.keycloak-12926"));
|
Assert.assertEquals("Test en_US", theme.getMessages("messages", new Locale("en", "US")).get("test.keycloak-12926"));
|
||||||
Assert.assertEquals("Test en", theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926"));
|
Assert.assertEquals("Test en", theme.getMessages("messages", Locale.ENGLISH).get("test.keycloak-12926"));
|
||||||
|
|
Loading…
Reference in a new issue