diff --git a/common/src/main/java/org/keycloak/common/Profile.java b/common/src/main/java/org/keycloak/common/Profile.java index fdf35b2be2..0761472477 100755 --- a/common/src/main/java/org/keycloak/common/Profile.java +++ b/common/src/main/java/org/keycloak/common/Profile.java @@ -22,6 +22,7 @@ import static org.keycloak.common.Profile.Type.DEPRECATED; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.Arrays; import java.util.HashSet; import java.util.Properties; import java.util.Set; @@ -89,6 +90,11 @@ public class Profile { break; } } + + if ((!disabledFeatures.contains(Feature.ADMIN2) || !disabledFeatures.contains(Feature.ADMIN)) && disabledFeatures.contains(Feature.ADMIN_API)) { + throw new RuntimeException(String.format("Invalid value for feature: %s needs to be enabled because it is required by feature %s.", + Feature.ADMIN_API, Arrays.asList(Feature.ADMIN, Feature.ADMIN2))); + } } private static Profile getInstance() { @@ -153,6 +159,22 @@ public class Profile { ACCOUNT2("New Account Management Console", Type.DEFAULT), ACCOUNT_API("Account Management REST API", Type.DEFAULT), ADMIN_FINE_GRAINED_AUTHZ("Fine-Grained Admin Permissions", Type.PREVIEW), + /** + * Controls the availability of the Admin REST-API. + */ + ADMIN_API("Admin API", Type.DEFAULT), + + /** + * Controls the availability of the legacy admin-console. + * Note that the admin-console requires the {@link #ADMIN_API} feature. + */ + @Deprecated + ADMIN("Legacy Admin Console", Type.DEPRECATED), + + /** + * Controls the availability of the admin-console. + * Note that the admin-console requires the {@link #ADMIN_API} feature. + */ ADMIN2("New Admin Console", Type.DEFAULT), DOCKER("Docker Registry protocol", Type.DISABLED_BY_DEFAULT), IMPERSONATION("Ability for admins to impersonate users", Type.DEFAULT), @@ -297,4 +319,4 @@ public class Profile { } } -} +} \ No newline at end of file diff --git a/common/src/test/java/org/keycloak/common/ProfileTest.java b/common/src/test/java/org/keycloak/common/ProfileTest.java index a33607d11f..8163312ca1 100644 --- a/common/src/test/java/org/keycloak/common/ProfileTest.java +++ b/common/src/test/java/org/keycloak/common/ProfileTest.java @@ -24,7 +24,7 @@ public class ProfileTest { @Test public void checkDefaultsKeycloak() { Assert.assertEquals("community", Profile.getName()); - assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DYNAMIC_SCOPES, Profile.Feature.DOCKER, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE, Feature.CLIENT_SECRET_ROTATION, Feature.UPDATE_EMAIL); + assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN, Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DYNAMIC_SCOPES, Profile.Feature.DOCKER, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE, Feature.CLIENT_SECRET_ROTATION, Feature.UPDATE_EMAIL); assertEquals(Profile.getPreviewFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.DECLARATIVE_USER_PROFILE, Feature.CLIENT_SECRET_ROTATION, Feature.UPDATE_EMAIL); } @@ -36,7 +36,7 @@ public class ProfileTest { Profile.init(); Assert.assertEquals("product", Profile.getName()); - assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DYNAMIC_SCOPES, Profile.Feature.DOCKER, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE, Feature.CLIENT_SECRET_ROTATION, Feature.UPDATE_EMAIL); + assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN, Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DYNAMIC_SCOPES, Profile.Feature.DOCKER, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE, Feature.CLIENT_SECRET_ROTATION, Feature.UPDATE_EMAIL); assertEquals(Profile.getPreviewFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.RECOVERY_CODES, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.DECLARATIVE_USER_PROFILE, Feature.CLIENT_SECRET_ROTATION, Feature.UPDATE_EMAIL); System.setProperty("keycloak.profile", "community"); diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/resources/QuarkusWelcomeResource.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/resources/QuarkusWelcomeResource.java index deb36889a6..7bc37401a2 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/resources/QuarkusWelcomeResource.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/resources/QuarkusWelcomeResource.java @@ -19,6 +19,7 @@ package org.keycloak.quarkus.runtime.services.resources; import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.common.ClientConnection; +import org.keycloak.common.Profile; import org.keycloak.common.Version; import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.MimeTypeUtil; @@ -173,6 +174,7 @@ public class QuarkusWelcomeResource { Map map = new HashMap<>(); + map.put("adminConsoleEnabled", isAdminConsoleEnabled()); map.put("productName", Version.NAME); map.put("productNameFull", Version.NAME_FULL); @@ -250,6 +252,10 @@ public class QuarkusWelcomeResource { return shouldBootstrap.get(); } + private static boolean isAdminConsoleEnabled() { + return Profile.isFeatureEnabled(Profile.Feature.ADMIN2) || Profile.isFeatureEnabled(Profile.Feature.ADMIN); + } + private boolean isLocal() { try { ClientConnection clientConnection = session.getContext().getConnection(); diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt index 58365f1286..8ae1b9cafd 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testBuildHelp.unix.approved.txt @@ -44,18 +44,18 @@ Transaction: Feature: --features Enables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. --features-disabled Disables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. HTTP/TLS: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelp.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelp.unix.approved.txt index 3886962672..df5fcdb8f2 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelp.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelp.unix.approved.txt @@ -67,18 +67,18 @@ Transaction: Feature: --features Enables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. --features-disabled Disables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. Hostname: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt index 16bd3213f6..1faaa3ff51 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartDevHelpAll.unix.approved.txt @@ -128,18 +128,18 @@ Transaction: Feature: --features Enables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. --features-disabled Disables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. Hostname: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelp.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelp.unix.approved.txt index e38c2deb5e..87ed2c982b 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelp.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelp.unix.approved.txt @@ -73,18 +73,18 @@ Transaction: Feature: --features Enables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. --features-disabled Disables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. Hostname: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt index 0f266218a9..eec38c0312 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/approvals/cli/help/HelpCommandTest.testStartHelpAll.unix.approved.txt @@ -134,18 +134,18 @@ Transaction: Feature: --features Enables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. --features-disabled Disables a set of one or more features. Possible values are: authorization, - account2, account-api, admin-fine-grained-authz, admin2, docker, - impersonation, openshift-integration, scripts, token-exchange, web-authn, - client-policies, ciba, map-storage, par, declarative-user-profile, - dynamic-scopes, client-secret-rotation, step-up-authentication, - recovery-codes, update-email, preview. + account2, account-api, admin-fine-grained-authz, admin-api, admin, admin2, + docker, impersonation, openshift-integration, scripts, token-exchange, + web-authn, client-policies, ciba, map-storage, par, + declarative-user-profile, dynamic-scopes, client-secret-rotation, + step-up-authentication, recovery-codes, update-email, preview. Hostname: diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 09c019a960..d690f74634 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -19,8 +19,8 @@ package org.keycloak.services.resources; import com.fasterxml.jackson.core.type.TypeReference; import org.jboss.logging.Logger; import org.keycloak.Config; +import org.keycloak.common.Profile; import org.keycloak.common.crypto.CryptoIntegration; -import org.keycloak.common.util.BouncyIntegration; import org.keycloak.common.util.Resteasy; import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.config.ConfigProviderFactory; @@ -101,7 +101,9 @@ public class KeycloakApplication extends Application { singletons.add(new RobotsResource()); singletons.add(new RealmsResource()); - singletons.add(new AdminRoot()); + if (Profile.isFeatureEnabled(Profile.Feature.ADMIN_API)) { + singletons.add(new AdminRoot()); + } classes.add(ThemeResource.class); classes.add(JsResource.class); 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 e6f330e09c..8b0fcd8d0a 100755 --- a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java +++ b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java @@ -18,6 +18,7 @@ package org.keycloak.services.resources; import org.jboss.logging.Logger; import org.keycloak.common.ClientConnection; +import org.keycloak.common.Profile; import org.keycloak.common.Version; import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.MimeTypeUtil; @@ -176,6 +177,7 @@ public class WelcomeResource { Map map = new HashMap<>(); + map.put("adminConsoleEnabled", isAdminConsoleEnabled()); map.put("productName", Version.NAME); map.put("productNameFull", Version.NAME_FULL); @@ -215,6 +217,10 @@ public class WelcomeResource { } } + private static boolean isAdminConsoleEnabled() { + return Profile.isFeatureEnabled(Profile.Feature.ADMIN2) || Profile.isFeatureEnabled(Profile.Feature.ADMIN); + } + private Theme getTheme() { try { return session.theme().getTheme(Theme.Type.WELCOME); 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 dcb88bc086..1c816fbd63 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 @@ -23,9 +23,9 @@ import javax.ws.rs.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import javax.ws.rs.NotAuthorizedException; import org.keycloak.common.ClientConnection; +import org.keycloak.common.Profile; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInputException; -import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.oidc.TokenManager; @@ -97,6 +97,11 @@ public class AdminRoot { */ @GET public Response masterRealmAdminConsoleRedirect() { + + if (!isAdminConsoleEnabled()) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + RealmModel master = new RealmManager(session).getKeycloakAdminstrationRealm(); return Response.status(302).location( session.getContext().getUri(UrlType.ADMIN).getBaseUriBuilder().path(AdminRoot.class).path(AdminRoot.class, "getAdminConsole").path("/").build(master.getName()) @@ -112,6 +117,11 @@ public class AdminRoot { @Path("index.{html:html}") // expression is actually "index.html" but this is a hack to get around jax-doclet bug @GET public Response masterRealmAdminConsoleRedirectHtml() { + + if (!isAdminConsoleEnabled()) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + return masterRealmAdminConsoleRedirect(); } @@ -142,6 +152,11 @@ public class AdminRoot { */ @Path("{realm}/console") public AdminConsole getAdminConsole(final @PathParam("realm") String name) { + + if (!isAdminConsoleEnabled()) { + throw new NotFoundException(); + } + RealmManager realmManager = new RealmManager(session); RealmModel realm = locateRealm(name, realmManager); AdminConsole service = new AdminConsole(realm); @@ -198,6 +213,11 @@ public class AdminRoot { */ @Path("realms") public Object getRealmsAdmin(@Context final HttpHeaders headers) { + + if (!isAdminApiEnabled()) { + throw new NotFoundException(); + } + if (request.getHttpMethod().equals(HttpMethod.OPTIONS)) { return new AdminCorsPreflightService(request); } @@ -222,6 +242,11 @@ public class AdminRoot { */ @Path("serverinfo") public Object getServerInfo(@Context final HttpHeaders headers) { + + if (!isAdminApiEnabled()) { + throw new NotFoundException(); + } + if (request.getHttpMethod().equals(HttpMethod.OPTIONS)) { return new AdminCorsPreflightService(request); } @@ -277,4 +302,11 @@ public class AdminRoot { } } + private static boolean isAdminApiEnabled() { + return Profile.isFeatureEnabled(Profile.Feature.ADMIN_API); + } + + private static boolean isAdminConsoleEnabled() { + return Profile.isFeatureEnabled(Profile.Feature.ADMIN2) || Profile.isFeatureEnabled(Profile.Feature.ADMIN); + } } diff --git a/themes/src/main/resources/theme/keycloak/welcome/index.ftl b/themes/src/main/resources/theme/keycloak/welcome/index.ftl index 8bf272f22f..29daacc918 100755 --- a/themes/src/main/resources/theme/keycloak/welcome/index.ftl +++ b/themes/src/main/resources/theme/keycloak/welcome/index.ftl @@ -52,6 +52,7 @@

Welcome to ${productNameFull}

+ <#if adminConsoleEnabled>
<#if successMessage?has_content> @@ -93,6 +94,7 @@ +
+ <#-- adminConsoleEnabled -->