keycloak-aplcache

KEYCLOAK-6289 Add ThemeSelectorSPI

1/17/2018 5:55:24 AM

Changes

Details

diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
index 0348b68..c239fb2 100755
--- a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
+++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java
@@ -160,4 +160,11 @@ public interface KeycloakSession {
      */
     KeyManager keys();
 
+    /**
+     * Theme manager
+     *
+     * @return
+     */
+    ThemeManager theme();
+
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/ThemeManager.java b/server-spi/src/main/java/org/keycloak/models/ThemeManager.java
new file mode 100644
index 0000000..399b628
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/ThemeManager.java
@@ -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);
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java
new file mode 100755
index 0000000..8d62605
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProvider.java
@@ -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);
+
+}
diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProviderFactory.java b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProviderFactory.java
new file mode 100755
index 0000000..9931921
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorProviderFactory.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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ThemeSelectorProviderFactory extends ProviderFactory<ThemeSelectorProvider> {
+}
diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorSpi.java b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorSpi.java
new file mode 100644
index 0000000..d821af7
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/theme/ThemeSelectorSpi.java
@@ -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;
+    }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java
index 34de97b..7909d96 100755
--- a/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java
@@ -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);
 
 }
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 6db13f7..b781ca3 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,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
diff --git a/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java b/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java
index 58f0306..b62381b 100755
--- a/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java
+++ b/services/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailTemplateProvider.java
@@ -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 {
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
index 8957f70..64fc15b 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/FreeMarkerAccountProvider.java
@@ -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);
     }
 
     /**
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
index 2adbb05..d78b517 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -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);
     }
 
     /**
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
index 67cce1f..0cc81c1 100644
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java
@@ -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 {
diff --git a/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java b/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java
index d743cc0..80ecdbd 100644
--- a/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java
+++ b/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java
@@ -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;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java b/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java
index 9aef0d0..8efd090 100644
--- a/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java
+++ b/services/src/main/java/org/keycloak/services/resources/account/AccountLoader.java
@@ -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);
         }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
index a550a71..8060095 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
@@ -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) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
index 377697a..51057df 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
@@ -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);
 
diff --git a/services/src/main/java/org/keycloak/services/resources/ThemeResource.java b/services/src/main/java/org/keycloak/services/resources/ThemeResource.java
index 5c29bfa..67b96cf 100644
--- a/services/src/main/java/org/keycloak/services/resources/ThemeResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/ThemeResource.java
@@ -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();
diff --git a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
index 93bde2f..f6e464e 100755
--- a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java
@@ -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);
         }
diff --git a/services/src/main/java/org/keycloak/services/util/P3PHelper.java b/services/src/main/java/org/keycloak/services/util/P3PHelper.java
index 84e2e21..4579007 100644
--- a/services/src/main/java/org/keycloak/services/util/P3PHelper.java
+++ b/services/src/main/java/org/keycloak/services/util/P3PHelper.java
@@ -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");
diff --git a/services/src/main/java/org/keycloak/theme/DefaultThemeManager.java b/services/src/main/java/org/keycloak/theme/DefaultThemeManager.java
new file mode 100644
index 0000000..c539eea
--- /dev/null
+++ b/services/src/main/java/org/keycloak/theme/DefaultThemeManager.java
@@ -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);
+    }
+}
diff --git a/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
new file mode 100644
index 0000000..5023942
--- /dev/null
+++ b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
@@ -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() {
+    }
+}
diff --git a/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProviderFactory.java b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProviderFactory.java
new file mode 100644
index 0000000..d49e0d2
--- /dev/null
+++ b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProviderFactory.java
@@ -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";
+    }
+}
diff --git a/services/src/main/java/org/keycloak/theme/ExtendingThemeManagerFactory.java b/services/src/main/java/org/keycloak/theme/ExtendingThemeManagerFactory.java
old mode 100755
new mode 100644
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory
new file mode 100644
index 0000000..fccc28f
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory
@@ -0,0 +1 @@
+org.keycloak.theme.DefaultThemeSelectorProviderFactory
\ No newline at end of file