This is an automated email from the ASF dual-hosted git repository.

DaanHoogland pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 6f30b0d5837 Custom login base domain using GUI whitelabel themes 
(#13412)
6f30b0d5837 is described below

commit 6f30b0d58374505021ec62820bccfad58d6db9c5
Author: Henrique Sato <[email protected]>
AuthorDate: Thu Jul 2 15:19:31 2026 -0300

    Custom login base domain using GUI whitelabel themes (#13412)
---
 .../org/apache/cloudstack/api/ApiConstants.java    |  1 +
 .../command/user/gui/theme/CreateGuiThemeCmd.java  |  8 +++++
 .../command/user/gui/theme/UpdateGuiThemeCmd.java  |  8 +++++
 .../cloudstack/api/response/GuiThemeResponse.java  | 13 +++++++++
 .../apache/cloudstack/gui/theme/GuiThemeJoin.java  |  2 ++
 .../cloudstack/gui/theme/GuiThemeJoinVO.java       |  8 +++++
 .../apache/cloudstack/gui/theme/GuiThemeVO.java    | 11 ++++++-
 .../resources/META-INF/db/schema-42210to42300.sql  |  3 ++
 .../META-INF/db/views/cloud.gui_themes_view.sql    |  1 +
 .../main/java/com/cloud/api/ApiResponseHelper.java |  1 +
 .../cloudstack/gui/theme/GuiThemeServiceImpl.java  | 34 +++++++++++++++-------
 .../gui/theme/GuiThemeServiceImplTest.java         | 21 +++++++++++++
 ui/src/utils/guiTheme.js                           |  5 ++++
 ui/src/views/auth/Login.vue                        | 27 +++++++++--------
 14 files changed, 120 insertions(+), 23 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java 
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index b8e06984884..15025621a33 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -1408,6 +1408,7 @@ public class ApiConstants {
     public static final String CSS = "css";
 
     public static final String JSON_CONFIGURATION = "jsonconfiguration";
+    public static final String LOGIN_BASE_DOMAIN = "loginbasedomain";
 
     public static final String COMMON_NAMES = "commonnames";
 
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java
index 8566b413cc1..2e3849cc850 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java
@@ -57,6 +57,10 @@ public class CreateGuiThemeCmd extends BaseCmd {
             "wildcard) separated by comma that can retrieve the theme; e.g.: 
*acme.com,acme2.com")
     private String commonNames;
 
+    @Parameter(name = ApiConstants.LOGIN_BASE_DOMAIN, type = 
CommandType.STRING, length = 65535, description = "The ACS domain to be used as 
base " +
+            "for the login when accessing the GUI through the common name 
defined in the theme. If a common name is not defined, this parameter is 
ignored on the GUI.")
+    private String loginBaseDomain;
+
     @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, 
length = 65535, description = "A set of domain UUIDs (also known as ID for " +
             "the end-user) separated by comma that can retrieve the theme.")
     private String domainIds;
@@ -93,6 +97,10 @@ public class CreateGuiThemeCmd extends BaseCmd {
         return commonNames;
     }
 
+    public String getLoginBaseDomain() {
+        return loginBaseDomain;
+    }
+
     public String getDomainIds() {
         return domainIds;
     }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java
 
b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java
index daef2235ce8..e50961aa30d 100644
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java
@@ -60,6 +60,10 @@ public class UpdateGuiThemeCmd extends BaseCmd {
             "wildcard) separated by comma that can retrieve the theme; e.g.: 
*acme.com,acme2.com")
     private String commonNames;
 
+    @Parameter(name = ApiConstants.LOGIN_BASE_DOMAIN, type = 
CommandType.STRING, length = 65535, description = "The ACS domain to be used as 
base for " +
+            "the login when accessing the GUI through the common name defined 
in the theme. If a common name is not defined, this parameter is ignored on the 
GUI.")
+    private String loginBaseDomain;
+
     @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, 
length = 65535, description = "A set of domain UUIDs (also known as ID for " +
             "the end-user) separated by comma that can retrieve the theme.")
     private String domainIds;
@@ -96,6 +100,10 @@ public class UpdateGuiThemeCmd extends BaseCmd {
         return jsonConfiguration;
     }
 
+    public String getLoginBaseDomain() {
+        return loginBaseDomain;
+    }
+
     public String getCommonNames() {
         return commonNames;
     }
diff --git 
a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java 
b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java
index fe8a85b4176..3c53bdb9d93 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java
@@ -52,6 +52,11 @@ public class GuiThemeResponse extends BaseResponse {
     @Param(description = "A set of Common Names (CN) (fixed or wildcard) 
separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com")
     private String commonNames;
 
+    @SerializedName(ApiConstants.LOGIN_BASE_DOMAIN)
+    @Param(description = "The ACS domain to be used as base for the login when 
accessing the GUI through the common name defined in the theme. If a " +
+            "common name is not defined, this parameter is ignored on the 
GUI.")
+    private String loginBaseDomain;
+
     @SerializedName(ApiConstants.DOMAIN_IDS)
     @Param(description = "A set of domain UUIDs (also known as ID for the 
end-user) separated by comma that can retrieve the theme.")
     private String domainIds;
@@ -176,4 +181,12 @@ public class GuiThemeResponse extends BaseResponse {
     public void setRemoved(Date removed) {
         this.removed = removed;
     }
+
+    public String getLoginBaseDomain() {
+        return loginBaseDomain;
+    }
+
+    public void setLoginBaseDomain(String loginBaseDomain) {
+        this.loginBaseDomain = loginBaseDomain;
+    }
 }
diff --git 
a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java 
b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java
index e54d53138ef..cb3708cda37 100644
--- a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java
+++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java
@@ -44,4 +44,6 @@ public interface GuiThemeJoin extends InternalIdentity, 
Identity {
     Date getCreated();
 
     Date getRemoved();
+
+    String getLoginBaseDomain();
 }
diff --git 
a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java
 
b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java
index 2df23b3d106..45bad5a44d7 100644
--- 
a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java
+++ 
b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java
@@ -63,6 +63,9 @@ public class GuiThemeJoinVO implements GuiThemeJoin {
     @Column(name = "is_public")
     private boolean isPublic;
 
+    @Column(name = "login_base_domain")
+    private String loginBaseDomain;
+
     @Column(name = GenericDao.CREATED_COLUMN, nullable = false)
     @Temporal(value = TemporalType.TIMESTAMP)
     private Date created;
@@ -138,4 +141,9 @@ public class GuiThemeJoinVO implements GuiThemeJoin {
     public Date getRemoved() {
         return removed;
     }
+
+    @Override
+    public String getLoginBaseDomain() {
+        return loginBaseDomain;
+    }
 }
diff --git 
a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java 
b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java
index 887e3886f6c..729071f60b8 100644
--- 
a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java
+++ 
b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java
@@ -59,6 +59,9 @@ public class GuiThemeVO implements GuiTheme {
     @Column(name = "recursive_domains")
     private boolean recursiveDomains = false;
 
+    @Column(name = "login_base_domain", length = 65535)
+    private String loginBaseDomain;
+
     @Column(name = GenericDao.CREATED_COLUMN, nullable = false)
     @Temporal(value = TemporalType.TIMESTAMP)
     private Date created;
@@ -71,7 +74,8 @@ public class GuiThemeVO implements GuiTheme {
 
     }
 
-    public GuiThemeVO(String name, String description, String css, String 
jsonConfiguration, boolean recursiveDomains, boolean isPublic, Date created, 
Date removed) {
+    public GuiThemeVO(String name, String description, String css, String 
jsonConfiguration, boolean recursiveDomains,
+        boolean isPublic, Date created, String loginBaseDomain, Date removed) {
         this.name = name;
         this.description = description;
         this.css = css;
@@ -79,6 +83,7 @@ public class GuiThemeVO implements GuiTheme {
         this.recursiveDomains = recursiveDomains;
         this.isPublic = isPublic;
         this.created = created;
+        this.loginBaseDomain = loginBaseDomain;
         this.removed = removed;
     }
 
@@ -186,4 +191,8 @@ public class GuiThemeVO implements GuiTheme {
     public String toString() {
         return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, 
"uuid", "name", "description", "isPublic", "recursiveDomains");
     }
+
+    public void setLoginBaseDomain(String loginBaseDomain) {
+        this.loginBaseDomain = loginBaseDomain;
+    }
 }
diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
index bc90085469c..97c40115b5a 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
@@ -451,6 +451,9 @@ SELECT uuid(), role_id, 'quotaResourceStatement', 
permission, sort_order
 FROM cloud.role_permissions rp
 WHERE rule = 'quotaStatement' AND NOT EXISTS(SELECT 1 FROM 
cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 
'quotaResourceStatement');
 
+--- Gui theme login base domain
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.gui_themes', 'login_base_domain', 
'TEXT DEFAULT NULL');
+
 -- Add description for secondary IP addresses
 CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nic_secondary_ips', 'description', 
'VARCHAR(2048) DEFAULT NULL');
 
diff --git 
a/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql 
b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql
index 3173274623e..5f49cffbf1f 100644
--- 
a/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql
+++ 
b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql
@@ -27,6 +27,7 @@ SELECT
     `cloud`.`gui_themes`.`description` AS `description`,
     `cloud`.`gui_themes`.`css` AS `css`,
     `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`,
+    `cloud`.`gui_themes`.`login_base_domain` AS `login_base_domain`,
     (SELECT group_concat(gtd.`value` separator ',') FROM 
`cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'commonName' AND 
gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) common_names,
     (SELECT group_concat(gtd.`value` separator ',') FROM 
`cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'domain' AND 
gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) domains,
     (SELECT group_concat(gtd.`value` separator ',') FROM 
`cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'account' AND 
gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) accounts,
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java 
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 8c51d992684..e27339941f6 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -5721,6 +5721,7 @@ protected Map<String, ResourceIcon> 
getResourceIconsUsingOsCategory(List<Templat
 
         
guiThemeResponse.setJsonConfiguration(guiThemeJoin.getJsonConfiguration());
         guiThemeResponse.setCss(guiThemeJoin.getCss());
+        guiThemeResponse.setLoginBaseDomain(guiThemeJoin.getLoginBaseDomain());
         guiThemeResponse.setResponseName("guithemes");
 
         return guiThemeResponse;
diff --git 
a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java 
b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java
index 9a92b9bef01..5e95bb6a994 100644
--- 
a/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java
+++ 
b/server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java
@@ -39,6 +39,7 @@ import org.apache.cloudstack.gui.theme.dao.GuiThemeDao;
 import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDao;
 import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao;
 import 
org.apache.cloudstack.gui.theme.json.config.validator.JsonConfigValidator;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -126,21 +127,18 @@ public class GuiThemeServiceImpl implements 
GuiThemeService {
         String providedAccountIds = cmd.getAccountIds();
         boolean isPublic = cmd.getPublic();
         Boolean recursiveDomains = cmd.getRecursiveDomains();
+        String baseDomainName = cmd.getLoginBaseDomain();
 
         CallContext.current().setEventDetails(String.format("Name: %s, 
AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", name, 
providedAccountIds,
                 providedDomainIds, recursiveDomains, commonNames));
 
-        if (StringUtils.isAllBlank(css, jsonConfiguration)) {
-            throw new CloudRuntimeException("Either the `css` or 
`jsonConfiguration` parameter must be informed.");
-        }
-
-        validateParameters(jsonConfiguration, providedDomainIds, 
providedAccountIds, commonNames, null);
+        validateParameters(css, jsonConfiguration, providedDomainIds, 
providedAccountIds, commonNames, baseDomainName, null);
 
         if (shouldSetGuiThemeToPrivate(providedDomainIds, providedAccountIds)) 
{
             isPublic = false;
         }
 
-        GuiThemeVO guiThemeVO = new GuiThemeVO(name, description, css, 
jsonConfiguration, recursiveDomains, isPublic, new Date(), null);
+        GuiThemeVO guiThemeVO = new GuiThemeVO(name, description, css, 
jsonConfiguration, recursiveDomains, isPublic, new Date(), 
cmd.getLoginBaseDomain(), null);
         guiThemeDao.persist(guiThemeVO);
         persistGuiThemeDetails(guiThemeVO.getId(), commonNames, 
providedDomainIds, providedAccountIds);
         return guiThemeJoinDao.findById(guiThemeVO.getId());
@@ -224,7 +222,11 @@ public class GuiThemeServiceImpl implements 
GuiThemeService {
         return guiThemeJoinDao.listGuiThemes(id, name, commonName, domainUuid, 
accountUuid, listAll, showRemoved, showPublic);
     }
 
-    protected void validateParameters(String jsonConfig, String domainIds, 
String accountIds, String commonNames, Long idOfThemeToBeUpdated) {
+    protected void validateParameters(String css, String jsonConfig, String 
domainIds, String accountIds, String commonNames, String loginBaseDomain, Long 
idOfThemeToBeUpdated) {
+        if (StringUtils.isAllBlank(css, jsonConfig, loginBaseDomain)) {
+            throw new CloudRuntimeException("At least one of the `css`, 
`jsonconfiguration`, or `loginbasedomain` parameters must be informed.");
+        }
+
         if (isConsideredDefaultTheme(commonNames, domainIds, accountIds)) {
             checkIfDefaultThemeIsAllowed(commonNames, domainIds, accountIds, 
idOfThemeToBeUpdated);
         }
@@ -232,6 +234,7 @@ public class GuiThemeServiceImpl implements GuiThemeService 
{
         validateObjectUuids(accountIds, Account.class);
         validateObjectUuids(domainIds, Domain.class);
         jsonConfigValidator.validateJsonConfiguration(jsonConfig);
+        validateLoginBaseDomain(loginBaseDomain, commonNames);
     }
 
     /**
@@ -254,6 +257,12 @@ public class GuiThemeServiceImpl implements 
GuiThemeService {
         }
     }
 
+    protected void validateLoginBaseDomain(String loginBaseDomain, String 
commonNames) {
+        if (loginBaseDomain != null && StringUtils.isBlank(commonNames)) {
+            throw new CloudRuntimeException("Parameter `loginBaseDomain` must 
be provided with `commonNames`.");
+        }
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_GUI_THEME_UPDATE, 
eventDescription = "Updating GUI theme")
     public GuiThemeJoin updateGuiTheme(UpdateGuiThemeCmd cmd) {
@@ -267,23 +276,24 @@ public class GuiThemeServiceImpl implements 
GuiThemeService {
         String commonNames = cmd.getCommonNames() == null ? 
guiThemeJoinVO.getCommonNames() : cmd.getCommonNames();
         String providedDomainIds = cmd.getDomainIds() == null ? 
guiThemeJoinVO.getDomains() : cmd.getDomainIds();
         String providedAccountIds = cmd.getAccountIds() == null ? 
guiThemeJoinVO.getAccounts() : cmd.getAccountIds();
+        String baseDomainName = 
ObjectUtils.defaultIfNull(cmd.getLoginBaseDomain(), 
guiThemeJoinVO.getLoginBaseDomain());
         Boolean isPublic = cmd.getIsPublic();
         Boolean recursiveDomains = cmd.getRecursiveDomains();
 
         CallContext.current().setEventDetails(String.format("ID: %s, Name: %s, 
AccountIDs: %s, DomainIDs: %s, RecursiveDomains: %s, CommonNames: %s", 
guiThemeId, name,
                 providedAccountIds, providedDomainIds, recursiveDomains, 
commonNames));
 
-        validateParameters(jsonConfiguration, providedDomainIds, 
providedAccountIds, commonNames, guiThemeId);
+        validateParameters(css, jsonConfiguration, providedDomainIds, 
providedAccountIds, commonNames, baseDomainName, guiThemeId);
 
         if (shouldSetGuiThemeToPrivate(providedDomainIds, providedAccountIds)) 
{
             isPublic = false;
         }
 
-        return persistGuiTheme(guiThemeId, name, description, css, 
jsonConfiguration, commonNames, providedDomainIds, providedAccountIds, 
isPublic, recursiveDomains);
+        return persistGuiTheme(guiThemeId, name, description, css, 
jsonConfiguration, commonNames, providedDomainIds, providedAccountIds, 
isPublic, recursiveDomains, baseDomainName);
     }
 
     protected GuiThemeJoinVO persistGuiTheme(Long guiThemeId, String name, 
String description, String css, String jsonConfiguration, String commonNames, 
String providedDomainIds,
-                                             String providedAccountIds, 
Boolean isPublic, Boolean recursiveDomains){
+                                         String providedAccountIds, Boolean 
isPublic, Boolean recursiveDomains, String loginBaseDomain){
         return Transaction.execute((TransactionCallback<GuiThemeJoinVO>) 
status -> {
             GuiThemeVO guiThemeVO = guiThemeDao.findById(guiThemeId);
 
@@ -311,6 +321,10 @@ public class GuiThemeServiceImpl implements 
GuiThemeService {
                 guiThemeVO.setRecursiveDomains(recursiveDomains);
             }
 
+            if (loginBaseDomain != null) {
+                guiThemeVO.setLoginBaseDomain(loginBaseDomain);
+            }
+
             logger.trace("Persisting GUI theme [{}] with CSS [{}] and JSON 
configuration [{}].", guiThemeVO, guiThemeVO.getCss(), 
guiThemeVO.getJsonConfiguration());
 
             guiThemeDao.persist(guiThemeVO);
diff --git 
a/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java
 
b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java
index c47fdadd32c..c7579040be2 100644
--- 
a/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java
+++ 
b/server/src/test/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImplTest.java
@@ -65,6 +65,7 @@ public class GuiThemeServiceImplTest {
 
     private static final String ACCOUNT_IDS = "4,5,6";
 
+    private static final String LOGIN_BASE_DOMAIN = "acmedomain";
     private static final String BLANK_STRING = "";
 
     @Test
@@ -172,6 +173,26 @@ public class GuiThemeServiceImplTest {
         guiThemeServiceSpy.validateObjectUuids(ACCOUNT_IDS, Account.class);
     }
 
+    @Test
+    public void 
validateLoginBaseDomainTestBaseDomainIsNullCommonNamesIsNullShouldNotThrowCloudRuntimeException()
 {
+        guiThemeServiceSpy.validateLoginBaseDomain(null, null);
+    }
+
+    @Test
+    public void 
validateLoginBaseDomainTestBaseDomainIsNullCommonNamesIsNotNullShouldNotThrowCloudRuntimeException()
 {
+        guiThemeServiceSpy.validateLoginBaseDomain(null, COMMON_NAME);
+    }
+
+    @Test
+    public void 
validateLoginBaseDomainTestBaseDomainIsNotNullCommonNamesIsNotNullShouldNotThrowCloudRuntimeException()
 {
+        guiThemeServiceSpy.validateLoginBaseDomain(LOGIN_BASE_DOMAIN, 
COMMON_NAME);
+    }
+
+    @Test(expected = CloudRuntimeException.class)
+    public void 
validateLoginBaseDomainTestBaseDomainIsNotNullCommonNamesIsNullShouldNotThrowCloudRuntimeException()
 {
+        guiThemeServiceSpy.validateLoginBaseDomain(LOGIN_BASE_DOMAIN, null);
+    }
+
     @Test
     public void checkIfDefaultThemeIsAllowedTestThemeIsNotConsideredDefault() {
         
Mockito.when(guiThemeServiceSpy.isConsideredDefaultTheme(Mockito.anyString(), 
Mockito.anyString(), Mockito.anyString())).thenReturn(false);
diff --git a/ui/src/utils/guiTheme.js b/ui/src/utils/guiTheme.js
index 438ce9333a4..dab82287d11 100644
--- a/ui/src/utils/guiTheme.js
+++ b/ui/src/utils/guiTheme.js
@@ -63,6 +63,11 @@ async function applyDynamicCustomization (response) {
     jsonConfig = JSON.parse(response?.jsonconfiguration)
   }
 
+  vueProps.$config.loginBaseDomain = ''
+  if (response?.loginbasedomain) {
+    vueProps.$config.loginBaseDomain = response.loginbasedomain
+  }
+
   // Sets custom GUI fields only if is not nullish.
   vueProps.$config.appTitle = jsonConfig?.appTitle ?? vueProps.$config.appTitle
   vueProps.$config.footer = jsonConfig?.footer ?? vueProps.$config.footer
diff --git a/ui/src/views/auth/Login.vue b/ui/src/views/auth/Login.vue
index acb874dc75b..202ae20f16f 100644
--- a/ui/src/views/auth/Login.vue
+++ b/ui/src/views/auth/Login.vue
@@ -390,11 +390,20 @@ export default {
     },
     handleDomain () {
       const values = toRaw(this.form)
-      if (!values.domain) {
-        this.$store.commit('SET_DOMAIN_USED_TO_LOGIN', '/')
-      } else {
-        this.$store.commit('SET_DOMAIN_USED_TO_LOGIN', values.domain)
+      const domain = this.getLoginDomain(values.domain)
+      this.$store.commit('SET_DOMAIN_USED_TO_LOGIN', domain)
+    },
+    getLoginDomain (domain) {
+      if (this.$config.loginBaseDomain) {
+        if (domain) {
+          return this.$config.loginBaseDomain + '/' + domain
+        }
+        return this.$config.loginBaseDomain
       }
+      if (domain) {
+        return domain
+      }
+      return '/'
     },
     getGitHubUrl (from) {
       const rootURl = 'https://github.com/login/oauth/authorize'
@@ -457,10 +466,7 @@ export default {
           delete loginParams.username
           loginParams[!this.state.loginType ? 'email' : 'username'] = 
values.username
           loginParams.password = values.password
-          loginParams.domain = values.domain
-          if (!loginParams.domain) {
-            loginParams.domain = '/'
-          }
+          loginParams.domain = this.getLoginDomain(values.domain)
           this.Login(loginParams)
             .then((res) => this.loginSuccess(res))
             .catch(err => {
@@ -489,10 +495,7 @@ export default {
         loginParams.email = this.email
         loginParams.provider = provider
         loginParams.secretcode = this.secretcode
-        loginParams.domain = values.domain
-        if (!loginParams.domain) {
-          loginParams.domain = '/'
-        }
+        loginParams.domain = this.getLoginDomain(values.domain)
         this.OauthLogin(loginParams)
           .then((res) => this.loginSuccess(res))
           .catch(err => {

Reply via email to