diff --git a/api/src/main/java/org/apache/cloudstack/context/CallContext.java b/api/src/main/java/org/apache/cloudstack/context/CallContext.java index fcfb5b6b1e00..b8e8868b61db 100644 --- a/api/src/main/java/org/apache/cloudstack/context/CallContext.java +++ b/api/src/main/java/org/apache/cloudstack/context/CallContext.java @@ -66,6 +66,7 @@ protected Stack initialValue() { private final Map apiResourcesUuids = new HashMap<>(); private Project project; private String apiName; + private String requestRemoteAddress; static EntityManager s_entityMgr; @@ -401,6 +402,14 @@ public Map getContextParameters() { return context; } + public String getRequestRemoteAddress() { + return requestRemoteAddress; + } + + public void setRequestRemoteAddress(String remoteAddress) { + this.requestRemoteAddress = remoteAddress; + } + public void putContextParameters(Map details){ if (details == null) return; for(Map.Entryentry : details.entrySet()){ diff --git a/server/src/main/java/com/cloud/api/ApiServlet.java b/server/src/main/java/com/cloud/api/ApiServlet.java index bfeb3d276e4e..ba6094ee12a7 100644 --- a/server/src/main/java/com/cloud/api/ApiServlet.java +++ b/server/src/main/java/com/cloud/api/ApiServlet.java @@ -216,6 +216,7 @@ void processRequestInContext(final HttpServletRequest req, final HttpServletResp HttpUtils.RESPONSE_TYPE_XML, ApiServer.JSONcontentType.value()); return; } + CallContext.current().setRequestRemoteAddress(req.getServerName()); final StringBuilder auditTrailSb = new StringBuilder(128); auditTrailSb.append(" ").append(remoteAddress.getHostAddress()); diff --git a/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java b/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java index d6b5dbb18f9f..da1bca516ed4 100644 --- a/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java @@ -29,13 +29,16 @@ import com.github.mustachejava.MustacheFactory; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDao; import org.apache.cloudstack.resourcedetail.UserDetailVO; import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao; import org.apache.cloudstack.utils.mailing.MailAddress; import org.apache.cloudstack.utils.mailing.SMTPMailProperties; import org.apache.cloudstack.utils.mailing.SMTPMailSender; +import org.apache.http.conn.util.InetAddressUtils; import javax.inject.Inject; import javax.naming.ConfigurationException; @@ -45,6 +48,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -65,6 +69,9 @@ public class UserPasswordResetManagerImpl extends ManagerBase implements UserPas @Inject private UserDao userDao; + @Inject + private GuiThemeDetailsDao guiThemeDetailsDao; + private SMTPMailSender mailSender; public static ConfigKey PasswordResetMailTemplate = @@ -182,26 +189,12 @@ public void setResetTokenAndSend(UserAccount userAccount) { final String email = userAccount.getEmail(); final String username = userAccount.getUsername(); final String subject = "Password Reset Request"; - String domainUrl = UserPasswordResetDomainURL.value(); - if (StringUtils.isBlank(domainUrl)) { - String mgmtServerAddr = ManagementServerAddresses.value().split(",")[0]; - if (ServerProperties.isHttpsEnabled()) { - domainUrl = "https://" + mgmtServerAddr + ":" + ServerProperties.getHttpsPort(); - } else { - domainUrl = "http://" + mgmtServerAddr + ":" + ServerProperties.getHttpPort(); - } - } else if (!domainUrl.startsWith("http://") && !domainUrl.startsWith("https://")) { - if (ServerProperties.isHttpsEnabled()) { - domainUrl = "https://" + domainUrl; - } else { - domainUrl = "http://" + domainUrl; - } - } - - domainUrl = domainUrl.replaceAll("/+$", ""); + String requestDomain = CallContext.current().getRequestRemoteAddress(); + String resetLinkDomain = getResetLinkDomain(requestDomain); + String formattedResetLinkDomain = formatResetLinkDomain(resetLinkDomain); String resetLink = String.format("%s/client/#/user/resetPassword?username=%s&token=%s", - domainUrl, username, resetToken); + formattedResetLinkDomain, username, resetToken); String content = getMessageBody(userAccount, resetToken, resetLink); SMTPMailProperties mailProperties = new SMTPMailProperties(); @@ -222,6 +215,44 @@ public void setResetTokenAndSend(UserAccount userAccount) { userAccount, userAccount.getAccountId(), userAccount.getDomainId(), email, resetTokenExpiryTime); } + private String getResetLinkDomain(String requestDomain) { + if (StringUtils.isNotBlank(requestDomain)) { + logger.debug("Searching for GUI theme with common name that matches the request's domain: [{}]", requestDomain); + List commonNameDetails = guiThemeDetailsDao.listGuiThemeIdsByCommonName(requestDomain); + + if (!commonNameDetails.isEmpty()) { + logger.debug("GUI theme with ID {} was found; using request's domain for password reset link.", commonNameDetails.get(0)); + return requestDomain; + } else { + logger.debug("No GUI theme was found with a common name that matches the request's domain."); + } + } + + String configurationDomain = UserPasswordResetDomainURL.value(); + if (StringUtils.isNotBlank(configurationDomain)) { + logger.debug("Defaulting reset link's domain to the [{}] configuration value: [{}].", UserPasswordResetDomainURL.key(), UserPasswordResetDomainURL.value()); + return configurationDomain; + } + + logger.debug("Using the first IP address in the [{}] configuration for the reset password email domain because the [{}] configuration is not defined.", ManagementServerAddresses.key(), UserPasswordResetDomainURL.key()); + return ManagementServerAddresses.value().split(",")[0]; + } + + private String formatResetLinkDomain(String resetLinkDomain) { + String protocol = ServerProperties.isHttpsEnabled() ? "https" : "http"; + + if (InetAddressUtils.isIPv4Address(resetLinkDomain)) { + int port = protocol.equals("https") ? ServerProperties.getHttpsPort() : ServerProperties.getHttpPort(); + resetLinkDomain = resetLinkDomain + ":" + port; + } + + if (!resetLinkDomain.startsWith("http")) { + resetLinkDomain = protocol + "://" + resetLinkDomain; + } + + return resetLinkDomain.replaceAll("/+$", ""); + } + @Override public boolean validateAndResetPassword(UserAccount user, String token, String password) { UserDetailVO resetTokenDetail = userDetailsDao.findDetail(user.getId(), PasswordResetToken);