Details
diff --git a/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
index 5023942..44171ae 100644
--- a/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
+++ b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
@@ -2,10 +2,14 @@ package org.keycloak.theme;
import org.keycloak.Config;
import org.keycloak.common.Version;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.KeycloakSession;
public class DefaultThemeSelectorProvider implements ThemeSelectorProvider {
+ public static final String LOGIN_THEME_KEY = "login_theme";
+
private final KeycloakSession session;
public DefaultThemeSelectorProvider(KeycloakSession session) {
@@ -17,11 +21,29 @@ public class DefaultThemeSelectorProvider implements ThemeSelectorProvider {
String name = null;
switch (type) {
- case ACCOUNT:
- name = session.getContext().getRealm().getAccountTheme();
+ case WELCOME:
+ name = Config.scope("theme").get("welcomeTheme");
break;
case LOGIN:
- name = session.getContext().getRealm().getLoginTheme();
+ ClientModel client = session.getContext().getClient();
+ if (client != null) {
+ name = client.getAttribute(LOGIN_THEME_KEY);
+
+ if (name == null || name.isEmpty()) {
+ ClientTemplateModel clientTemplate = client.getClientTemplate();
+ if (clientTemplate != null) {
+ name = clientTemplate.getAttribute(LOGIN_THEME_KEY);
+ }
+ }
+ }
+
+ if (name == null) {
+ name = session.getContext().getRealm().getLoginTheme();
+ }
+
+ break;
+ case ACCOUNT:
+ name = session.getContext().getRealm().getAccountTheme();
break;
case EMAIL:
name = session.getContext().getRealm().getEmailTheme();
@@ -29,12 +51,9 @@ public class DefaultThemeSelectorProvider implements ThemeSelectorProvider {
case ADMIN:
name = session.getContext().getRealm().getAdminTheme();
break;
- case WELCOME:
- name = Config.scope("theme").get("welcomeTheme");
- break;
}
- if (name == null) {
+ if (name == null || name.isEmpty()) {
name = Config.scope("theme").get("default", Version.NAME.toLowerCase());
}
@@ -44,4 +63,6 @@ public class DefaultThemeSelectorProvider implements ThemeSelectorProvider {
@Override
public void close() {
}
+
}
+
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java
new file mode 100644
index 0000000..eaccb2a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java
@@ -0,0 +1,74 @@
+package org.keycloak.testsuite.forms;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.pages.LoginPage;
+
+import java.util.HashMap;
+
+import static org.junit.Assert.assertEquals;
+
+public class ThemeSelectorTest extends AbstractTestRealmKeycloakTest {
+
+ @Page
+ protected LoginPage loginPage;
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ }
+
+ @Test
+ public void clientOverride() {
+ loginPage.open();
+ assertEquals("keycloak", detectTheme());
+
+ ClientRepresentation rep = testRealm().clients().findByClientId("test-app").get(0);
+ rep.getAttributes().put("login_theme", "base");
+ testRealm().clients().get(rep.getId()).update(rep);
+
+ loginPage.open();
+ assertEquals("base", detectTheme());
+
+ rep.getAttributes().put("login_theme", "");
+ testRealm().clients().get(rep.getId()).update(rep);
+ }
+
+ @Test
+ public void clientTemplateOverride() {
+ ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation();
+ templateRep.setName("loginTheme");
+ templateRep.setAttributes(new HashMap<>());
+ templateRep.getAttributes().put("login_theme", "base");
+
+ String templateId = ApiUtil.getCreatedId(testRealm().clientTemplates().create(templateRep));
+
+ loginPage.open();
+ assertEquals("keycloak", detectTheme());
+
+ ClientRepresentation rep = testRealm().clients().findByClientId("test-app").get(0);
+ rep.setClientTemplate("loginTheme");
+ testRealm().clients().get(rep.getId()).update(rep);
+
+ loginPage.open();
+ assertEquals("base", detectTheme());
+
+ rep.setClientTemplate("NONE");
+ testRealm().clients().get(rep.getId()).update(rep);
+
+ testRealm().clientTemplates().get(templateId).remove();
+ }
+
+ private String detectTheme() {
+ if(driver.getPageSource().contains("/login/keycloak/css/login.css")) {
+ return "keycloak";
+ } else {
+ return "base";
+ }
+ }
+
+}
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 88d765a..6943b4e 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -45,6 +45,17 @@
<kc-tooltip>{{:: 'consent-required.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
+ <label class="col-md-2 control-label" for="loginTheme">{{:: 'login-theme' | translate}}</label>
+ <div class="col-sm-6">
+ <select class="form-control" id="loginTheme"
+ ng-model="clientEdit.attributes['login_theme']"
+ ng-options="o.name as o.name for o in serverInfo.themes.login">
+ <option value="" selected></option>
+ </select>
+ </div>
+ <kc-tooltip>{{:: 'login-theme.tooltip' | translate}}</kc-tooltip>
+ </div>
+ <div class="form-group">
<label class="col-md-2 control-label" for="protocol">{{:: 'client-protocol' | translate}}</label>
<div class="col-sm-6">
<div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-template-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-template-detail.html
index 91af170..c7a6bb2 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-template-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-template-detail.html
@@ -25,6 +25,17 @@
<kc-tooltip>{{:: 'client-template.description.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
+ <label class="col-md-2 control-label" for="loginTheme">{{:: 'login-theme' | translate}}</label>
+ <div class="col-sm-6">
+ <select class="form-control" id="loginTheme"
+ ng-model="template.attributes['login_theme']"
+ ng-options="o.name as o.name for o in serverInfo.themes.login">
+ <option value="" selected></option>
+ </select>
+ </div>
+ <kc-tooltip>{{:: 'login-theme.tooltip' | translate}}</kc-tooltip>
+ </div>
+ <div class="form-group">
<label class="col-md-2 control-label" for="protocol">{{:: 'protocol' | translate}}</label>
<div class="col-sm-6">
<div>