KEYCLOAK-6289 Add ThemeSelectorSPI
This commit is contained in:
parent
81a4ba854c
commit
35ada9d636
24 changed files with 286 additions and 38 deletions
|
@ -27,12 +27,12 @@ import java.util.Set;
|
|||
*/
|
||||
public interface ThemeProvider extends Provider {
|
||||
|
||||
public int getProviderPriority();
|
||||
int getProviderPriority();
|
||||
|
||||
public Theme getTheme(String name, Theme.Type type) throws IOException;
|
||||
Theme getTheme(String name, Theme.Type type) throws IOException;
|
||||
|
||||
public Set<String> nameSet(Theme.Type type);
|
||||
Set<String> nameSet(Theme.Type type);
|
||||
|
||||
public boolean hasTheme(String name, Theme.Type type);
|
||||
boolean hasTheme(String name, Theme.Type type);
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ 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
|
||||
|
|
|
@ -160,4 +160,11 @@ public interface KeycloakSession {
|
|||
*/
|
||||
KeyManager keys();
|
||||
|
||||
/**
|
||||
* Theme manager
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ThemeManager theme();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import org.keycloak.theme.Theme;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ThemeManager {
|
||||
|
||||
/**
|
||||
* Returns the theme for the specified type. The theme is determined by the theme selector.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
Theme getTheme(Theme.Type type) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the specified theme for the specified type.
|
||||
*
|
||||
* @param name
|
||||
* @param type
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
Theme getTheme(String name, Theme.Type type) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a set of all theme names for the specified type.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
Set<String> nameSet(Theme.Type type);
|
||||
|
||||
}
|
|
@ -28,23 +28,23 @@ import java.util.Properties;
|
|||
*/
|
||||
public interface Theme {
|
||||
|
||||
public enum Type { LOGIN, ACCOUNT, ADMIN, EMAIL, WELCOME, COMMON };
|
||||
enum Type { LOGIN, ACCOUNT, ADMIN, EMAIL, WELCOME, COMMON };
|
||||
|
||||
public String getName();
|
||||
String getName();
|
||||
|
||||
public String getParentName();
|
||||
String getParentName();
|
||||
|
||||
public String getImportName();
|
||||
String getImportName();
|
||||
|
||||
public Type getType();
|
||||
Type getType();
|
||||
|
||||
public URL getTemplate(String name) throws IOException;
|
||||
URL getTemplate(String name) throws IOException;
|
||||
|
||||
public InputStream getTemplateAsStream(String name) throws IOException;
|
||||
InputStream getTemplateAsStream(String name) throws IOException;
|
||||
|
||||
public URL getResource(String path) throws IOException;
|
||||
URL getResource(String path) throws IOException;
|
||||
|
||||
public InputStream getResourceAsStream(String path) throws IOException;
|
||||
InputStream getResourceAsStream(String path) throws IOException;
|
||||
|
||||
/**
|
||||
* Same as getMessages(baseBundlename, locale), but uses a default baseBundlename
|
||||
|
@ -54,7 +54,7 @@ public interface Theme {
|
|||
* @return The localized messages from the bundle.
|
||||
* @throws IOException If bundle can not be read.
|
||||
*/
|
||||
public Properties getMessages(Locale locale) throws IOException;
|
||||
Properties getMessages(Locale locale) throws IOException;
|
||||
|
||||
/**
|
||||
* Retrieve localized messages from a message bundle.
|
||||
|
@ -65,8 +65,8 @@ public interface Theme {
|
|||
* @return The localized messages from the bundle.
|
||||
* @throws IOException If bundle can not be read.
|
||||
*/
|
||||
public Properties getMessages(String baseBundlename, Locale locale) throws IOException;
|
||||
Properties getMessages(String baseBundlename, Locale locale) throws IOException;
|
||||
|
||||
public Properties getProperties() throws IOException;
|
||||
Properties getProperties() throws IOException;
|
||||
|
||||
}
|
35
server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java
Executable file
35
server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java
Executable file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface ThemeSelectorProvider extends Provider {
|
||||
|
||||
/**
|
||||
* Return the theme name to use for the specified type
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
String getThemeName(Theme.Type type);
|
||||
|
||||
}
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface ThemeSelectorProviderFactory extends ProviderFactory<ThemeSelectorProvider> {
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package org.keycloak.theme;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
public class ThemeSelectorSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "themeSelector";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return ThemeSelectorProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return ThemeSelectorProviderFactory.class;
|
||||
}
|
||||
}
|
|
@ -208,8 +208,7 @@ public class FreeMarkerEmailTemplateProvider implements EmailTemplateProvider {
|
|||
}
|
||||
|
||||
protected Theme getTheme() throws IOException {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
return themeProvider.getTheme(realm.getEmailTheme(), Theme.Type.EMAIL);
|
||||
return session.theme().getTheme(Theme.Type.EMAIL);
|
||||
}
|
||||
|
||||
protected void send(String subjectKey, List<Object> subjectAttributes, String template, Map<String, Object> attributes) throws EmailException {
|
||||
|
|
|
@ -192,8 +192,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
|
|||
* @throws IOException in case of Theme loading problem
|
||||
*/
|
||||
protected Theme getTheme() throws IOException {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
return themeProvider.getTheme(realm.getAccountTheme(), Theme.Type.ACCOUNT);
|
||||
return session.theme().getTheme(Theme.Type.ACCOUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -261,8 +261,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
* @throws IOException in case of Theme loading problem
|
||||
*/
|
||||
protected Theme getTheme() throws IOException {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
return themeProvider.getTheme(realm.getLoginTheme(), Theme.Type.LOGIN);
|
||||
return session.theme().getTheme(Theme.Type.LOGIN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
import org.keycloak.models.KeycloakTransactionManager;
|
||||
import org.keycloak.models.KeyManager;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.ThemeManager;
|
||||
import org.keycloak.models.UserCredentialManager;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
|
@ -36,6 +37,7 @@ import org.keycloak.provider.ProviderFactory;
|
|||
import org.keycloak.sessions.AuthenticationSessionProvider;
|
||||
import org.keycloak.storage.UserStorageManager;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.theme.DefaultThemeManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -62,6 +64,7 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
private UserFederatedStorageProvider userFederatedStorageProvider;
|
||||
private KeycloakContext context;
|
||||
private KeyManager keyManager;
|
||||
private ThemeManager themeManager;
|
||||
|
||||
public DefaultKeycloakSession(DefaultKeycloakSessionFactory factory) {
|
||||
this.factory = factory;
|
||||
|
@ -253,6 +256,14 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
|||
return keyManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThemeManager theme() {
|
||||
if (themeManager == null) {
|
||||
themeManager = new DefaultThemeManager(this);
|
||||
}
|
||||
return themeManager;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for (Provider p : providers.values()) {
|
||||
try {
|
||||
|
|
|
@ -72,8 +72,7 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
|
|||
try {
|
||||
RealmModel realm = resolveRealm();
|
||||
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
Theme theme = themeProvider.getTheme(realm.getLoginTheme(), Theme.Type.LOGIN);
|
||||
Theme theme = session.theme().getTheme(Theme.Type.LOGIN);
|
||||
|
||||
Locale locale = LocaleHelper.getLocale(session, realm, null);
|
||||
|
||||
|
@ -119,6 +118,8 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
|
|||
realm = realmManager.getRealmByName(Config.getAdminRealm());
|
||||
}
|
||||
|
||||
session.getContext().setRealm(realm);
|
||||
|
||||
return realm;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,8 +61,7 @@ public class ThemeResource {
|
|||
}
|
||||
|
||||
try {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
Theme theme = themeProvider.getTheme(themeName, Theme.Type.valueOf(themType.toUpperCase()));
|
||||
Theme theme = session.theme().getTheme(themeName, Theme.Type.valueOf(themType.toUpperCase()));
|
||||
InputStream resource = theme.getResourceAsStream(path);
|
||||
if (resource != null) {
|
||||
return Response.ok(resource).type(MimeTypeUtil.getContentType(path)).cacheControl(CacheControlUtil.getDefaultCacheControl()).build();
|
||||
|
|
|
@ -208,10 +208,8 @@ public class WelcomeResource {
|
|||
}
|
||||
|
||||
private Theme getTheme() {
|
||||
Config.Scope config = Config.scope("theme");
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
try {
|
||||
return themeProvider.getTheme(config.get("welcomeTheme"), Theme.Type.WELCOME);
|
||||
return session.theme().getTheme(Theme.Type.WELCOME);
|
||||
} catch (IOException e) {
|
||||
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
|
|
@ -93,8 +93,7 @@ public class AccountLoader {
|
|||
|
||||
private Theme getTheme(KeycloakSession session) {
|
||||
try {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
return themeProvider.getTheme(session.getContext().getRealm().getAccountTheme(), Theme.Type.ACCOUNT);
|
||||
return session.theme().getTheme(Theme.Type.ACCOUNT);
|
||||
} catch (IOException e) {
|
||||
throw new InternalServerErrorException(e);
|
||||
}
|
||||
|
|
|
@ -248,8 +248,7 @@ public class AdminRoot {
|
|||
}
|
||||
|
||||
public static Theme getTheme(KeycloakSession session, RealmModel realm) throws IOException {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
return themeProvider.getTheme(realm.getAdminTheme(), Theme.Type.ADMIN);
|
||||
return session.theme().getTheme(Theme.Type.ADMIN);
|
||||
}
|
||||
|
||||
public static Properties getMessages(KeycloakSession session, RealmModel realm, String lang) {
|
||||
|
|
|
@ -163,11 +163,10 @@ public class ServerInfoAdminResource {
|
|||
}
|
||||
|
||||
private void setThemes(ServerInfoRepresentation info) {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
info.setThemes(new HashMap<String, List<ThemeInfoRepresentation>>());
|
||||
|
||||
for (Theme.Type type : Theme.Type.values()) {
|
||||
List<String> themeNames = new LinkedList<>(themeProvider.nameSet(type));
|
||||
List<String> themeNames = new LinkedList<>(session.theme().nameSet(type));
|
||||
Collections.sort(themeNames);
|
||||
|
||||
if (!Profile.isFeatureEnabled(Profile.Feature.ACCOUNT2)) {
|
||||
|
@ -179,7 +178,7 @@ public class ServerInfoAdminResource {
|
|||
|
||||
for (String name : themeNames) {
|
||||
try {
|
||||
Theme theme = themeProvider.getTheme(name, type);
|
||||
Theme theme = session.theme().getTheme(name, type);
|
||||
ThemeInfoRepresentation ti = new ThemeInfoRepresentation();
|
||||
ti.setName(name);
|
||||
|
||||
|
|
|
@ -39,8 +39,7 @@ public class P3PHelper {
|
|||
|
||||
public static void addP3PHeader(KeycloakSession session) {
|
||||
try {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
Theme theme = themeProvider.getTheme(session.getContext().getRealm().getLoginTheme(), Theme.Type.LOGIN);
|
||||
Theme theme = session.theme().getTheme(Theme.Type.LOGIN);
|
||||
|
||||
Locale locale = LocaleHelper.getLocaleFromCookie(session);
|
||||
String p3pValue = theme.getMessages(locale).getProperty("p3pPolicy");
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package org.keycloak.theme;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ThemeManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class DefaultThemeManager implements ThemeManager {
|
||||
|
||||
private KeycloakSession session;
|
||||
|
||||
public DefaultThemeManager(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Theme getTheme(Theme.Type type) throws IOException {
|
||||
String name = session.getProvider(ThemeSelectorProvider.class).getThemeName(type);
|
||||
return getTheme(name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Theme getTheme(String name, Theme.Type type) throws IOException {
|
||||
return session.getProvider(ThemeProvider.class, "extending").getTheme(name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> nameSet(Theme.Type type) {
|
||||
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
|
||||
return themeProvider.nameSet(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package org.keycloak.theme;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
public class DefaultThemeSelectorProvider implements ThemeSelectorProvider {
|
||||
|
||||
private final KeycloakSession session;
|
||||
|
||||
public DefaultThemeSelectorProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getThemeName(Theme.Type type) {
|
||||
String name = null;
|
||||
|
||||
switch (type) {
|
||||
case ACCOUNT:
|
||||
name = session.getContext().getRealm().getAccountTheme();
|
||||
break;
|
||||
case LOGIN:
|
||||
name = session.getContext().getRealm().getLoginTheme();
|
||||
break;
|
||||
case EMAIL:
|
||||
name = session.getContext().getRealm().getEmailTheme();
|
||||
break;
|
||||
case ADMIN:
|
||||
name = session.getContext().getRealm().getAdminTheme();
|
||||
break;
|
||||
case WELCOME:
|
||||
name = Config.scope("theme").get("welcomeTheme");
|
||||
break;
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
name = Config.scope("theme").get("default", Version.NAME.toLowerCase());
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package org.keycloak.theme;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
public class DefaultThemeSelectorProviderFactory implements ThemeSelectorProviderFactory {
|
||||
|
||||
@Override
|
||||
public ThemeSelectorProvider create(KeycloakSession session) {
|
||||
return new DefaultThemeSelectorProvider(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "default";
|
||||
}
|
||||
}
|
0
services/src/main/java/org/keycloak/theme/ExtendingThemeManagerFactory.java
Executable file → Normal file
0
services/src/main/java/org/keycloak/theme/ExtendingThemeManagerFactory.java
Executable file → Normal file
|
@ -0,0 +1 @@
|
|||
org.keycloak.theme.DefaultThemeSelectorProviderFactory
|
Loading…
Reference in a new issue