diff --git a/repository/src/main/java/org/alfresco/repo/client/config/ClientAppConfig.java b/repository/src/main/java/org/alfresco/repo/client/config/ClientAppConfig.java
index b6c651b3a4..bc6df000d8 100644
--- a/repository/src/main/java/org/alfresco/repo/client/config/ClientAppConfig.java
+++ b/repository/src/main/java/org/alfresco/repo/client/config/ClientAppConfig.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,10 +26,12 @@
package org.alfresco.repo.client.config;
+import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.util.PropertyCheck;
+import org.alfresco.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
@@ -41,6 +43,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Pattern;
/**
* This class picks up all the loaded properties passed to it and uses a naming
@@ -59,25 +62,35 @@ import java.util.concurrent.ConcurrentMap;
* for example, sharedLinkBaseUrl and templateAssetsUrl properties, then the following
* needs to be put into a properties file.
*
- * - repo.client-app.MyClientName.sharedLinkBaseUrl=http://localhost:8080/MyClientName/s
- * - repo.client-app.MyClientName.templateAssetsUrl=http://localhost:8080/MyClientName/assets
+ * - {@code repo.client-app.MyClientName.sharedLinkBaseUrl=http://localhost:8080/MyClientName/s}
+ * - {@code repo.client-app.MyClientName.templateAssetsUrl=http://localhost:8080/MyClientName/assets}
*
* The default property file is alfresco/client/config/repo-clients-apps.properties which
* could be overridden (or add new clients) by alfresco-global properties file.
+ *
+ * Note: The {@literal Url} is a special property which can be used as a placeholder.
+ * For example,
+ *
+ * - {@code repo.client-app.MyClientName.MyClientNameUrl=${repoBaseUrl}/entrypoint}
+ * - {@code repo.client-app.MyClientName.somePropName=${MyClientNameUrl}/some-page}
+ *
+ *
*
* @author Jamal Kaabi-Mofrad
*/
public class ClientAppConfig extends AbstractLifecycleBean
{
- private static final Log logger = LogFactory.getLog(ClientAppConfig.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(ClientAppConfig.class);
public static final String PREFIX = "repo.client-app.";
public static final String PROP_TEMPLATE_ASSETS_URL = "templateAssetsUrl";
+ public static final String SHARE_PLACEHOLDER = "${shareUrl}";
+
private Properties defaultProperties;
private Properties globalProperties;
- private ConcurrentMap clients = new ConcurrentHashMap<>();
+ private final ConcurrentMap clients = new ConcurrentHashMap<>();
public ClientAppConfig()
{
@@ -117,6 +130,17 @@ public class ClientAppConfig extends AbstractLifecycleBean
return clients.get(name);
}
+ /**
+ * Checks whether a client with the given name exists or not.
+ *
+ * @param name the name of the client to check
+ * @return true if the client with the given name exists; false otherwise
+ */
+ public boolean exists(String name)
+ {
+ return clients.containsKey(name);
+ }
+
@Override
protected void onBootstrap(ApplicationEvent event)
{
@@ -127,9 +151,9 @@ public class ClientAppConfig extends AbstractLifecycleBean
processPropertyKeys(mergedProperties, clientsNames, propsNames);
clients.putAll(processClients(clientsNames, propsNames, mergedProperties));
- if (logger.isDebugEnabled())
+ if (LOGGER.isDebugEnabled())
{
- logger.debug("All bootstrapped repo clients apps: " + clients);
+ LOGGER.debug("All bootstrapped repo clients apps: " + clients);
}
}
@@ -232,7 +256,7 @@ public class ClientAppConfig extends AbstractLifecycleBean
}
if (StringUtils.isEmpty(templateAssetsUrl) && config.isEmpty())
{
- logger.warn("Client-app [" + clientName + "] can not be registered as it needs at least one property with a valid value.");
+ LOGGER.warn("Client-app [" + clientName + "] can not be registered as it needs at least one property with a valid value.");
continue;
}
// As the required values are valid, create the client data
@@ -275,7 +299,7 @@ public class ClientAppConfig extends AbstractLifecycleBean
private void logMalformedPropertyKey(String propName, String reason)
{
reason = (StringUtils.isBlank(reason)) ? "" : " " + reason;
- logger.warn("Ignoring client app config (malformed property key) [" + propName + "]." + reason);
+ LOGGER.warn("Ignoring client app config (malformed property key) [" + propName + "]." + reason);
}
private void logMalformedPropertyKey(String propName)
@@ -285,9 +309,9 @@ public class ClientAppConfig extends AbstractLifecycleBean
private void logInvalidPropertyValue(String propName, String propValue)
{
- if (logger.isDebugEnabled())
+ if (LOGGER.isDebugEnabled())
{
- logger.debug("Ignoring client app config (invalid value) [" + propValue + "] for the property:" + propName);
+ LOGGER.debug("Ignoring client app config (invalid value) [" + propValue + "] for the property:" + propName);
}
}
@@ -298,13 +322,18 @@ public class ClientAppConfig extends AbstractLifecycleBean
public static class ClientApp
{
+ private static final Logger LOGGER = LoggerFactory.getLogger(ClientApp.class);
private final String name;
+ private final String clientUrlPropKey;
+ private final Pattern clientUrlPlaceholderPattern;
private final String templateAssetsUrl;
private final Map properties;
public ClientApp(String name, String templateAssetsUrl, Map properties)
{
this.name = name;
+ this.clientUrlPropKey = name + "Url"; // E.g. 'workspaceUrl' in 'repo.client-app.workspace.workspaceUrl'
+ this.clientUrlPlaceholderPattern = Pattern.compile("\\$\\{" + clientUrlPropKey + '}'); // E.g. ${workspaceUrl}
this.templateAssetsUrl = templateAssetsUrl;
this.properties = new HashMap<>(properties);
}
@@ -314,11 +343,77 @@ public class ClientAppConfig extends AbstractLifecycleBean
return name;
}
+ public String getClientUrlPropKey()
+ {
+ return clientUrlPropKey;
+ }
+
+ public String getClientUrl()
+ {
+ String url = properties.get(getClientUrlPropKey());
+ if (StringUtils.isEmpty(url) && "share".equals(name))
+ {
+ return SHARE_PLACEHOLDER;
+ }
+ return url;
+ }
+
+ /**
+ * Resolves the client URL that has one of the following defined URL placeholders:
+ *
+ * - {@literal ${repoBaseUrl}}
+ * - {@literal ${shareUrl}}
+ *
+ *
+ * @param sysAdminParams SysAdminParams object to retrieve configurable system parameters
+ * @return the resolved property value if applicable
+ */
+ public String getResolvedClientUrl(SysAdminParams sysAdminParams)
+ {
+ String resolvedUrl;
+ String clientUrl = getClientUrl();
+ if (!StringUtils.isEmpty(clientUrl) && clientUrl.contains(SHARE_PLACEHOLDER))
+ {
+ resolvedUrl = UrlUtil.replaceShareUrlPlaceholder(clientUrl, sysAdminParams);
+ }
+ else
+ {
+ resolvedUrl = UrlUtil.replaceRepoBaseUrlPlaceholder(clientUrl, sysAdminParams);
+ }
+
+ LOGGER.debug("Resolved clientUrl [" + clientUrl + "] to [" + resolvedUrl + "] for the client: " + name);
+ return resolvedUrl;
+ }
+
+ public Pattern getClientUrlPlaceholderPattern()
+ {
+ return clientUrlPlaceholderPattern;
+ }
+
public String getTemplateAssetsUrl()
{
return templateAssetsUrl;
}
+ /**
+ * Resolves the template assets URL that has the following defined URL placeholder:
+ *
+ * - {@literal ${Url}}
+ *
+ *
+ * @param sysAdminParams SysAdminParams object to retrieve configurable system parameters
+ * @return the resolved property value if applicable
+ */
+ public String getResolvedTemplateAssetsUrl(SysAdminParams sysAdminParams)
+ {
+ String resolvedUrl = UrlUtil.replaceUrlPlaceholder(getClientUrlPlaceholderPattern(), getTemplateAssetsUrl(),
+ getResolvedClientUrl(sysAdminParams));
+
+ LOGGER.debug("Resolved template assets [" + getTemplateAssetsUrl() + "] URL to [" + resolvedUrl
+ + "] for the client: " + name);
+ return resolvedUrl;
+ }
+
public Map getProperties()
{
return Collections.unmodifiableMap(properties);
@@ -329,6 +424,27 @@ public class ClientAppConfig extends AbstractLifecycleBean
return properties.get(propName);
}
+ /**
+ * Resolves the property that has the following defined URL placeholder:
+ *
+ * - {@literal ${Url}}
+ *
+ *
+ * @param propName the property name to search for
+ * @param sysAdminParams SysAdminParams object to retrieve configurable system parameters
+ * @return the resolved property value if applicable
+ */
+ public String getResolvedProperty(String propName, SysAdminParams sysAdminParams)
+ {
+ String property = getProperty(propName);
+ String resolvedProp = UrlUtil.replaceUrlPlaceholder(getClientUrlPlaceholderPattern(), property,
+ getResolvedClientUrl(sysAdminParams));
+
+ LOGGER.debug("Resolved [" + property + "] URL to [" + resolvedProp + "] for the [" + propName
+ + "] property of the client: " + name);
+ return resolvedProp;
+ }
+
@Override
public boolean equals(Object o)
{
@@ -356,6 +472,8 @@ public class ClientAppConfig extends AbstractLifecycleBean
{
final StringBuilder sb = new StringBuilder(250);
sb.append("ClientApp [name=").append(name)
+ .append(", clientUrlPropKey=").append(clientUrlPropKey)
+ .append(", clientUrlPlaceholderPattern=").append(clientUrlPlaceholderPattern.pattern())
.append(", templateAssetsUrl=").append(templateAssetsUrl)
.append(", properties=").append(properties)
.append(']');
diff --git a/repository/src/main/java/org/alfresco/repo/invitation/InvitationServiceImpl.java b/repository/src/main/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
index bb8ea5d91d..87737da627 100644
--- a/repository/src/main/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
+++ b/repository/src/main/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -1875,7 +1875,6 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
* @param siteName
* @param role
* @param runAsUser
- * @param siteService
* @param overrideExisting
*/
public void addSiteMembership(final String invitee, final String siteName, final String role, final String runAsUser, final boolean overrideExisting)
@@ -2111,11 +2110,11 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE, resourceType.toString());
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_CLIENT_NAME, clientName);
- if(clientName != null && clientAppConfig.getClient(clientName) != null)
+ if(clientName != null && clientAppConfig.exists(clientName))
{
ClientAppConfig.ClientApp client = clientAppConfig.getClient(clientName);
- workflowProps.put(WorkflowModelModeratedInvitation.WF_TEMPLATE_ASSETS_URL, client.getTemplateAssetsUrl());
- workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, client.getProperty("workspaceUrl"));
+ workflowProps.put(WorkflowModelModeratedInvitation.WF_TEMPLATE_ASSETS_URL, client.getResolvedTemplateAssetsUrl(sysAdminParams));
+ workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, client.getResolvedClientUrl(sysAdminParams));
}
// get the moderated workflow
diff --git a/repository/src/main/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java b/repository/src/main/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java
index add6dd7557..bea8a969ab 100644
--- a/repository/src/main/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java
+++ b/repository/src/main/java/org/alfresco/repo/invitation/activiti/SendModeratedInviteDelegate.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2016 Alfresco Software Limited
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -72,7 +72,7 @@ public class SendModeratedInviteDelegate extends AbstractInvitationDelegate
Map variables = execution.getVariables();
String clientName = (String) variables.get(WorkflowModelModeratedInvitation.wfVarClientName);
- if(clientName != null && clientAppConfig.getClient(clientName) != null)
+ if(clientName != null && clientAppConfig.exists(clientName))
{
ClientAppConfig.ClientApp clientApp = clientAppConfig.getClient(clientName);
final String path = clientApp.getProperty("inviteModeratedTemplatePath");
diff --git a/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java b/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java
index 2102a2bf93..50eccb8849 100644
--- a/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java
+++ b/repository/src/main/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java
@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -934,13 +934,13 @@ public class QuickShareServiceImpl implements QuickShareService,
Map templateModel = new HashMap<>(6);
templateModel.put(FTL_SENDER_FIRST_NAME, senderFirstName);
templateModel.put(FTL_SENDER_LAST_NAME, senderLastName);
- final String sharedNodeUrl = getUrl(clientApp.getProperty(CONFIG_SHARED_LINK_BASE_URL), CONFIG_SHARED_LINK_BASE_URL)
- + '/' + emailRequest.getSharedId();
+ String resolvedUrl = getUrl(clientApp.getResolvedProperty(CONFIG_SHARED_LINK_BASE_URL, sysAdminParams), CONFIG_SHARED_LINK_BASE_URL);
+ final String sharedNodeUrl = resolvedUrl + '/' + emailRequest.getSharedId();
templateModel.put(FTL_SHARED_NODE_URL, sharedNodeUrl);
templateModel.put(FTL_SHARED_NODE_NAME, emailRequest.getSharedNodeName());
templateModel.put(FTL_SENDER_MESSAGE, emailRequest.getSenderMessage());
- final String templateAssetsUrl = getUrl(clientApp.getTemplateAssetsUrl(), ClientAppConfig.PROP_TEMPLATE_ASSETS_URL);
+ final String templateAssetsUrl = getUrl(clientApp.getResolvedTemplateAssetsUrl(sysAdminParams), ClientAppConfig.PROP_TEMPLATE_ASSETS_URL);
templateModel.put(FTL_TEMPLATE_ASSETS_URL, templateAssetsUrl);
// Set the email details
@@ -1048,8 +1048,7 @@ public class QuickShareServiceImpl implements QuickShareService,
{
url = url.substring(0, url.length() - 1);
}
- // Replace '${shareUrl} placeholder if it does exist.
- return UrlUtil.replaceShareUrlPlaceholder(url, sysAdminParams);
+ return url;
}
private T getDefaultIfNull(T defaultValue, T newValue)
diff --git a/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordService.java b/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordService.java
index b8781b2486..d7f1fe3629 100644
--- a/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordService.java
+++ b/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordService.java
@@ -32,13 +32,11 @@ import org.alfresco.repo.client.config.ClientAppNotFoundException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordDetails;
/**
- * @deprecated from 7.1.0
* Reset password service.
*
* @author Jamal Kaabi-Mofrad
* @since 5.2.1
*/
-@Deprecated
public interface ResetPasswordService
{
/**
diff --git a/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImpl.java b/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImpl.java
index 530002af89..c74e8cf738 100644
--- a/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImpl.java
+++ b/repository/src/main/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImpl.java
@@ -2,23 +2,23 @@
* #%L
* Alfresco Remote API
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
- * This file is part of the Alfresco software.
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail. Otherwise, the software is
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
- *
+ *
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
* #L%
@@ -61,8 +61,8 @@ import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.webscripts.WebScriptException;
@@ -74,17 +74,15 @@ import java.util.Locale;
import java.util.Map;
/**
- * @deprecated from 7.1.0
- * *
+ *
* Reset password implementation based on workflow.
*
* @author Jamal Kaabi-Mofrad
* @since 5.2.1
*/
-@Deprecated
public class ResetPasswordServiceImpl implements ResetPasswordService
{
- private static final Log LOGGER = LogFactory.getLog(ResetPasswordServiceImpl.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(ResetPasswordServiceImpl.class);
private static final String TIMER_END = "PT1H";
private static final String WORKFLOW_DESCRIPTION_KEY = "resetpasswordwf_resetpassword.resetpassword.workflow.description";
@@ -196,6 +194,7 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
ParameterCheck.mandatoryString("clientName", clientName);
String userEmail = validateUserAndGetEmail(userId);
+ validateClient(clientName);
// Get the (latest) workflow definition for reset-password.
WorkflowDefinition wfDefinition = workflowService.getDefinitionByName(WorkflowModelResetPassword.WORKFLOW_DEFINITION_NAME);
@@ -342,8 +341,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
}
else if (!username.equals(userId))
{
- throw new InvalidResetPasswordWorkflowException("The given user id [" + userId + "] does not match the person's user id [" + username
- + "] who requested the password reset.");
+ throw new InvalidResetPasswordWorkflowException(
+ "The given user id [" + userId + "] does not match the person's user id [" + username
+ + "] who requested the password reset.");
}
}
@@ -352,12 +352,16 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
{
ParameterCheck.mandatoryString("clientName", clientName);
- ClientApp clientApp = clientAppConfig.getClient(clientName);
- if (clientApp == null)
+ validateClient(clientName);
+ return clientAppConfig.getClient(clientName);
+ }
+
+ private void validateClient(String clientName)
+ {
+ if (!clientAppConfig.exists(clientName))
{
throw new ClientAppNotFoundException("Client was not found [" + clientName + "]");
}
- return clientApp;
}
@@ -383,9 +387,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
.setUserName(userName)
.setUserEmail(toEmail)
.setTemplatePath(templatePath)
- .setTemplateAssetsUrl(clientApp.getTemplateAssetsUrl())
.setEmailSubject(emailSubject)
- .setTemplateModel(emailTemplateModel);
+ .setTemplateModel(emailTemplateModel)
+ .setClientApp(clientApp);
sendEmail(emailRequest);
}
@@ -400,7 +404,7 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
final String userName = (String) execution.getVariable(WorkflowModelResetPassword.WF_PROP_USERNAME_ACTIVITI);
// But we cannot get the password from the execution as we have intentionally not stored the password there.
- // Instead we recover the password from the specific task in which it was set.
+ // Instead, we recover the password from the specific task in which it was set.
List activitiTasks = activitiTaskService.createTaskQuery().taskDefinitionKey(WorkflowModelResetPassword.TASK_RESET_PASSWORD)
.processInstanceId(execution.getProcessInstanceId()).list();
if (activitiTasks.size() != 1)
@@ -448,9 +452,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
.setUserName(userName)
.setUserEmail(userEmail)
.setTemplatePath(templatePath)
- .setTemplateAssetsUrl(clientApp.getTemplateAssetsUrl())
.setEmailSubject(emailSubject)
- .setTemplateModel(emailTemplateModel);
+ .setTemplateModel(emailTemplateModel)
+ .setClientApp(clientApp);
sendEmail(emailRequest);
}
@@ -460,7 +464,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
// Prepare the email
Map templateModel = new HashMap<>();
// Replace '${shareUrl}' placeholder if it does exist.
- final String templateAssetsUrl = getUrl(emailRequest.getTemplateAssetsUrl(), ClientAppConfig.PROP_TEMPLATE_ASSETS_URL);
+ ClientApp clientApp = emailRequest.getClientApp();
+ final String templateAssetsUrl = getUrl(clientApp.getResolvedTemplateAssetsUrl(sysAdminParams),
+ ClientAppConfig.PROP_TEMPLATE_ASSETS_URL, clientApp.getName());
templateModel.put(FTL_TEMPLATE_ASSETS_URL, templateAssetsUrl);
if (emailRequest.getTemplateModel() != null)
{
@@ -489,11 +495,11 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
actionService.executeAction(mailAction, null, false, sendEmailAsynchronously);
}
- private String getUrl(String url, String propName)
+ private String getUrl(String url, String propName, String clientName)
{
- if (url == null)
+ if (StringUtils.isEmpty(url))
{
- LOGGER.warn("The url for the property [" + propName + "] is not configured.");
+ LOGGER.warn("The url for the property [" + propName + "] is not configured for the client: " + clientName);
return "";
}
@@ -501,7 +507,7 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
{
url = url.substring(0, url.length() - 1);
}
- return UrlUtil.replaceShareUrlPlaceholder(url, sysAdminParams);
+ return url;
}
protected String getResetPasswordEmailTemplate(ClientApp clientApp)
@@ -521,22 +527,23 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
{
StringBuilder sb = new StringBuilder(100);
- String pageUrl = clientApp.getProperty("resetPasswordPageUrl");
+ String pageUrl = clientApp.getResolvedProperty("resetPasswordPageUrl", sysAdminParams);
if (StringUtils.isEmpty(pageUrl))
{
sb.append(UrlUtil.getShareUrl(sysAdminParams));
+ LOGGER.warn("'resetPasswordPageUrl' property is not set for the client [" + clientApp.getName() + "]. The default base url of Share will be used [" + sb + "]");
- LOGGER.warn("'resetPasswordPageUrl' property is not set for the client [" + clientApp.getName()
- + "]. The default base url of Share will be used [" + sb.toString() + "]");
}
else
{
// We pass an empty string as we know that the pageUrl is not null
- sb.append(getUrl(pageUrl, ""));
+ sb.append(getUrl(pageUrl, "resetPasswordPageUrl", clientApp.getName()));
}
- sb.append("?key=").append(key)
- .append("&id=").append(BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, id));
+ sb.append("?key=")
+ .append(key)
+ .append("&id=")
+ .append(BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, id));
return sb.toString();
}
@@ -616,10 +623,10 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
private String userEmail;
private String fromEmail;
private String templatePath;
- private String templateAssetsUrl;
private Map templateModel;
private String emailSubject;
private boolean ignoreSendFailure = true;
+ private ClientApp clientApp;
public String getUserName()
{
@@ -665,17 +672,6 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
return this;
}
- public String getTemplateAssetsUrl()
- {
- return templateAssetsUrl;
- }
-
- public ResetPasswordEmailDetails setTemplateAssetsUrl(String templateAssetsUrl)
- {
- this.templateAssetsUrl = templateAssetsUrl;
- return this;
- }
-
public Map getTemplateModel()
{
return templateModel;
@@ -709,18 +705,29 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
return this;
}
+ public ClientApp getClientApp()
+ {
+ return clientApp;
+ }
+
+ public ResetPasswordEmailDetails setClientApp(ClientApp clientApp)
+ {
+ this.clientApp = clientApp;
+ return this;
+ }
+
@Override
public String toString()
{
- final StringBuilder sb = new StringBuilder(250);
+ final StringBuilder sb = new StringBuilder(300);
sb.append("ResetPasswordEmailDetails [userName=").append(userName)
.append(", userEmail=").append(userEmail)
.append(", fromEmail=").append(fromEmail)
.append(", templatePath=").append(templatePath)
- .append(", templateAssetsUrl=").append(templateAssetsUrl)
.append(", templateModel=").append(templateModel)
.append(", emailSubject=").append(emailSubject)
.append(", ignoreSendFailure=").append(ignoreSendFailure)
+ .append(", clientApp=").append(clientApp)
.append(']');
return sb.toString();
}
diff --git a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowModelResetPassword.java b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowModelResetPassword.java
index c44017ab38..3dc24fe4a7 100644
--- a/repository/src/main/java/org/alfresco/repo/workflow/WorkflowModelResetPassword.java
+++ b/repository/src/main/java/org/alfresco/repo/workflow/WorkflowModelResetPassword.java
@@ -29,12 +29,10 @@ package org.alfresco.repo.workflow;
import org.alfresco.service.namespace.QName;
/**
- * @deprecated from 7.1.0
*
* @author Jamal Kaabi-Mofrad
* @since 5.2.1
*/
-@Deprecated
public interface WorkflowModelResetPassword
{
// namespace
diff --git a/repository/src/main/java/org/alfresco/util/UrlUtil.java b/repository/src/main/java/org/alfresco/util/UrlUtil.java
index f040c31858..8d9490d13a 100644
--- a/repository/src/main/java/org/alfresco/util/UrlUtil.java
+++ b/repository/src/main/java/org/alfresco/util/UrlUtil.java
@@ -2,23 +2,23 @@
* #%L
* Alfresco Repository
* %%
- * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
- * This file is part of the Alfresco software.
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail. Otherwise, the software is
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
- *
+ *
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
* #L%
@@ -32,43 +32,62 @@ import java.util.regex.Pattern;
/**
* Alfresco URL related utility functions.
- *
+ *
* @since 3.5
*/
public class UrlUtil
{
// ${shareUrl} placeholder
public static final Pattern PATTERN = Pattern.compile("\\$\\{shareUrl\\}");
+
// ${alfrescoUrl} placeholder
public static final Pattern REPO_PATTERN = Pattern.compile("\\$\\{alfrescoUrl\\}");
+
+ // ${repoBaseUrl} placeholder
+ public static final Pattern REPO_BASE_PATTERN = Pattern.compile("\\$\\{repoBaseUrl\\}");
+
/**
- * Builds up the Url to Alfresco based on the settings in the
- * {@link SysAdminParams}.
- * @return Alfresco Url such as https://col.ab.or.ate/alfresco/
- * or http://localhost:8080/alfresco/
+ * Builds up the Url to Alfresco root url based on the settings in the
+ * {@link SysAdminParams}.
+ * @return Alfresco base Url such as {@code https://col.ab.or.ate}
+ * or {@code http://localhost:8080}
+ */
+ public static String getAlfrescoBaseUrl(SysAdminParams sysAdminParams)
+ {
+ return buildBaseUrl(
+ sysAdminParams.getAlfrescoProtocol(),
+ sysAdminParams.getAlfrescoHost(),
+ sysAdminParams.getAlfrescoPort());
+ }
+
+ /**
+ * Builds up the Url to Alfresco context based on the settings in the
+ * {@link SysAdminParams}.
+ * @return Alfresco Url such as {@code https://col.ab.or.ate/alfresco}
+ * or {@code http://localhost:8080/alfresco}
*/
public static String getAlfrescoUrl(SysAdminParams sysAdminParams)
{
return buildUrl(
- sysAdminParams.getAlfrescoProtocol(),
- sysAdminParams.getAlfrescoHost(),
- sysAdminParams.getAlfrescoPort(),
- sysAdminParams.getAlfrescoContext());
+ sysAdminParams.getAlfrescoProtocol(),
+ sysAdminParams.getAlfrescoHost(),
+ sysAdminParams.getAlfrescoPort(),
+ sysAdminParams.getAlfrescoContext());
}
-
+
/**
- * Builds up the Url to Share based on the settings in the
- * {@link SysAdminParams}.
- * @return Alfresco Url such as https://col.ab.or.ate/share/
- * or http://localhost:8081/share/
+ * Builds up the Url to Share based on the settings in the
+ * {@link SysAdminParams}.
+ * @return Alfresco Url such as {@code https://col.ab.or.ate/share}
+ * or {@code http://localhost:8081/share}
*/
public static String getShareUrl(SysAdminParams sysAdminParams)
{
return buildUrl(
- sysAdminParams.getShareProtocol(),
- sysAdminParams.getShareHost(),
- sysAdminParams.getSharePort(),
- sysAdminParams.getShareContext());
+ sysAdminParams.getShareProtocol(),
+ sysAdminParams.getShareHost(),
+ sysAdminParams.getSharePort(),
+ sysAdminParams.getShareContext());
}
@@ -80,8 +99,8 @@ public class UrlUtil
/**
* Builds URL to Api-Explorer based on the request only if the URL property is not provided
* {@link SysAdminParams}.
- * @return Rest-Api Url such as https://col.ab.or.ate/api-explorer/
- * or http://localhost:8082/api-explorer/
+ * @return Rest-Api Url such as {@code https://col.ab.or.ate/api-explorer}
+ * or {@code http://localhost:8082/api-explorer}
*/
public static String getApiExplorerUrl(SysAdminParams sysAdminParams, String requestURL, String requestURI)
{
@@ -124,6 +143,12 @@ public class UrlUtil
}
protected static String buildUrl(String protocol, String host, int port, String context)
+ {
+ String baseUrl = buildBaseUrl(protocol, host, port);
+ return baseUrl + '/' + context;
+ }
+
+ protected static String buildBaseUrl(String protocol, String host, int port)
{
StringBuilder url = new StringBuilder();
url.append(protocol);
@@ -142,8 +167,33 @@ public class UrlUtil
url.append(':');
url.append(port);
}
- url.append('/');
- url.append(context);
return url.toString();
}
+
+ /**
+ * Replaces the repo base url placeholder, namely {@literal ${repoBaseUrl}}, with value based on the settings in the
+ * {@link SysAdminParams}.
+ *
+ * @param value the string value which contains the repoBase url placeholder
+ * @param sysAdminParams the {@code SysAdminParams} object
+ * @return if the given {@code value} contains repoBase url placeholder,
+ * the placeholder is replaced with repoBase url; otherwise, the given {@code value} is simply returned
+ */
+ public static String replaceRepoBaseUrlPlaceholder(String value, SysAdminParams sysAdminParams)
+ {
+ if (value != null)
+ {
+ return REPO_BASE_PATTERN.matcher(value).replaceAll(getAlfrescoBaseUrl(sysAdminParams));
+ }
+ return value;
+ }
+
+ public static String replaceUrlPlaceholder(Pattern pattern, String value, String replacement)
+ {
+ if (value != null)
+ {
+ return pattern.matcher(value).replaceAll(replacement);
+ }
+ return null;
+ }
}
diff --git a/repository/src/main/resources/alfresco/client/config/repo-clients-apps.properties b/repository/src/main/resources/alfresco/client/config/repo-clients-apps.properties
index 16e27a74e1..c2f71a4802 100644
--- a/repository/src/main/resources/alfresco/client/config/repo-clients-apps.properties
+++ b/repository/src/main/resources/alfresco/client/config/repo-clients-apps.properties
@@ -5,8 +5,8 @@
# a NodeRef of the template
# a class path of the template
-# template assets url for share client
-repo.client-app.share.templateAssetsUrl=${shareUrl}/res/components/images/
+# template assets url for share client. i.e. The source url for the images/logs within the email
+repo.client-app.share.templateAssetsUrl=${shareUrl}/res/components/images
# shared-link (quickShare) base url
repo.client-app.share.sharedLinkBaseUrl=${shareUrl}/s
# shared-link email template path
@@ -20,6 +20,14 @@ repo.client-app.share.resetPasswordPageUrl=${shareUrl}/page/reset-password
repo.client-app.share.confirmResetPasswordTemplatePath=
### Digital workspace template configurations
+repo.client-app.workspace.workspaceUrl=${repoBaseUrl}/workspace
repo.client-app.workspace.inviteModeratedTemplatePath=
-repo.client-app.workspace.workspaceUrl=workspace
-repo.client-app.workspace.templateAssetsUrl=${alfrescoUrl}/images
+# template assets url for workspace client. i.e. The source url for the images/logs within the email
+repo.client-app.workspace.templateAssetsUrl=${workspaceUrl}/images
+
+# reset password request email template path
+repo.client-app.workspace.requestResetPasswordTemplatePath=alfresco/templates/reset-password-email-templates/forgot-password-email-template.ftl
+# reset password UI page url
+repo.client-app.workspace.resetPasswordPageUrl=${workspaceUrl}/reset-password
+# reset password confirmation email template path
+repo.client-app.workspace.confirmResetPasswordTemplatePath=
diff --git a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
index 999f2e16ca..0f128d287f 100644
--- a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
+++ b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java
@@ -250,7 +250,8 @@ import org.junit.runners.Suite;
org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.class,
- org.alfresco.repo.tagging.TaggingServiceImplUnitTest.class
+ org.alfresco.repo.tagging.TaggingServiceImplUnitTest.class,
+ org.alfresco.repo.client.config.ClientAppConfigUnitTest.class
})
public class AllUnitTestsSuite
{
diff --git a/repository/src/test/java/org/alfresco/repo/client/config/ClientAppConfigTest.java b/repository/src/test/java/org/alfresco/repo/client/config/ClientAppConfigTest.java
index b111486301..cce090c81f 100644
--- a/repository/src/test/java/org/alfresco/repo/client/config/ClientAppConfigTest.java
+++ b/repository/src/test/java/org/alfresco/repo/client/config/ClientAppConfigTest.java
@@ -31,16 +31,12 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import org.alfresco.repo.client.config.ClientAppConfig.ClientApp;
-import org.alfresco.service.cmr.repository.TemporalSourceOptions;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.testing.category.LuceneTests;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
-import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Collections;
import java.util.Map;
@@ -53,15 +49,15 @@ import java.util.Map;
@Category(LuceneTests.class)
public class ClientAppConfigTest
{
- private ApplicationContext context;
private ClientAppConfig clientAppConfig;
@Before
public void setUp() throws Exception
{
ApplicationContextHelper.closeApplicationContext();
- context = ApplicationContextHelper.getApplicationContext(new String[] { ApplicationContextHelper.CONFIG_LOCATIONS[0],
- "classpath:org/alfresco/repo/client/config/test-repo-clients-apps-context.xml" });
+ ApplicationContext context = ApplicationContextHelper.getApplicationContext(
+ new String[] { ApplicationContextHelper.CONFIG_LOCATIONS[0],
+ "classpath:org/alfresco/repo/client/config/test-repo-clients-apps-context.xml" });
clientAppConfig = context.getBean("clientAppConfigTest", ClientAppConfig.class);
}
diff --git a/repository/src/test/java/org/alfresco/repo/client/config/ClientAppConfigUnitTest.java b/repository/src/test/java/org/alfresco/repo/client/config/ClientAppConfigUnitTest.java
new file mode 100644
index 0000000000..0f431eb880
--- /dev/null
+++ b/repository/src/test/java/org/alfresco/repo/client/config/ClientAppConfigUnitTest.java
@@ -0,0 +1,266 @@
+/*
+ * #%L
+ * Alfresco Repository
+ * %%
+ * Copyright (C) 2005 - 2023 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.repo.client.config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.alfresco.repo.admin.SysAdminParams;
+import org.alfresco.repo.client.config.ClientAppConfig.ClientApp;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+/**
+ * @author Jamal Kaabi-Mofrad
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ClientAppConfigUnitTest
+{
+ private ClientAppConfig clientAppConfig;
+ @Mock
+ private SysAdminParams sysAdminParams;
+
+ private AtomicBoolean initialised;
+
+ @Before
+ public void setUp() throws Exception
+
+ { // This in not initialised yet. i.e. the properties are not processed yet.
+ // The processing will start when you call the 'onBootstrap()' method
+ clientAppConfig = buildClientAppConfig();
+
+ initialised = new AtomicBoolean(false);
+
+ when(sysAdminParams.getAlfrescoProtocol()).thenReturn("http");
+ when(sysAdminParams.getAlfrescoHost()).thenReturn("localhost");
+ when(sysAdminParams.getAlfrescoPort()).thenReturn(8080);
+
+ when(sysAdminParams.getShareProtocol()).thenReturn("http");
+ when(sysAdminParams.getShareHost()).thenReturn("localhost");
+ when(sysAdminParams.getSharePort()).thenReturn(8081);
+ when(sysAdminParams.getShareContext()).thenReturn("share");
+ }
+
+ private ClientAppConfig buildClientAppConfig()
+ {
+ Properties defaultProps = getWorkspaceAppProperties();
+ defaultProps.putAll(getCoolAppProperties());
+ defaultProps.putAll(getShareProperties());
+ Properties globalProps = new Properties();
+
+ ClientAppConfig config = new ClientAppConfig();
+ config.setDefaultProperties(defaultProps);
+ config.setGlobalProperties(globalProps);
+ return config;
+ }
+
+ @Test
+ public void testWorkspaceClient()
+ {
+ ClientApp client = getClientApp("workspace");
+ assertEquals("workspace", client.getName());
+ assertEquals("${workspaceUrl}/images", client.getTemplateAssetsUrl());
+ assertEquals("${repoBaseUrl}/workspace", client.getClientUrl());
+ assertEquals("workspaceUrl", client.getClientUrlPropKey());
+ assertEquals("\\$\\{workspaceUrl}", client.getClientUrlPlaceholderPattern()
+ .pattern());
+
+ Map properties = client.getProperties();
+ assertNotNull(properties);
+ assertNull("Not Set", properties.get("inviteModeratedTemplatePath"));
+ assertEquals("alfresco/templates/test-email-templates/test-email-template.ftl",
+ properties.get("requestResetPasswordTemplatePath"));
+ assertEquals("${workspaceUrl}/reset-password", properties.get("resetPasswordPageUrl"));
+ assertEquals("some/path", properties.get("confirmResetPasswordTemplatePath"));
+ }
+
+ @Test
+ public void testCoolAppClient()
+ {
+ ClientApp client = getClientApp("coolApp");
+ assertEquals("coolApp", client.getName());
+ assertEquals("${coolAppUrl}/images", client.getTemplateAssetsUrl());
+ assertEquals("http://localhost:8090/cool-app", client.getClientUrl());
+ assertEquals("coolAppUrl", client.getClientUrlPropKey());
+ assertEquals("\\$\\{coolAppUrl}", client.getClientUrlPlaceholderPattern()
+ .pattern());
+
+ Map properties = client.getProperties();
+ assertNotNull(properties);
+ assertEquals("${coolAppUrl}/page-one/page-two", properties.get("testPropUrl"));
+ }
+
+ @Test
+ public void resolveWorkspacePlaceholders()
+ {
+ ClientApp client = getClientApp("workspace");
+ // Raw properties
+ assertEquals("${repoBaseUrl}/workspace", client.getClientUrl());
+ assertEquals("workspaceUrl", client.getClientUrlPropKey());
+ assertEquals("\\$\\{workspaceUrl}", client.getClientUrlPlaceholderPattern()
+ .pattern());
+ assertEquals("${workspaceUrl}/images", client.getTemplateAssetsUrl());
+ assertEquals("${workspaceUrl}/reset-password", client.getProperty("resetPasswordPageUrl"));
+
+ // Resolved properties
+ // String clientUrl = UrlUtil.replaceRepoBaseUrlPlaceholder(client.getClientUrl(), sysAdminParams);
+ assertEquals("http://localhost:8080/workspace", client.getResolvedClientUrl(sysAdminParams));
+ assertEquals("http://localhost:8080/workspace/images", client.getResolvedTemplateAssetsUrl(sysAdminParams));
+ assertEquals("http://localhost:8080/workspace/reset-password",
+ client.getResolvedProperty("resetPasswordPageUrl", sysAdminParams));
+ }
+
+ @Test
+ public void resolveCoolAppPlaceholders()
+ {
+ ClientApp client = getClientApp("coolApp");
+ // Resolved properties
+ assertEquals("http://localhost:8090/cool-app", client.getResolvedClientUrl(sysAdminParams));
+ assertEquals("http://localhost:8090/cool-app/images", client.getResolvedTemplateAssetsUrl(sysAdminParams));
+ assertEquals("http://localhost:8090/cool-app/page-one/page-two",
+ client.getResolvedProperty("testPropUrl", sysAdminParams));
+ }
+
+ @Test
+ public void resolveSharePlaceholders()
+ {
+ ClientApp client = getClientApp("share");
+ // Resolved properties
+ assertEquals("http://localhost:8081/share", client.getResolvedClientUrl(sysAdminParams));
+ assertEquals("http://localhost:8081/share/res/components/images",
+ client.getResolvedTemplateAssetsUrl(sysAdminParams));
+ assertEquals("http://localhost:8081/share/page/reset-password",
+ client.getResolvedProperty("resetPasswordPageUrl", sysAdminParams));
+ assertEquals("http://localhost:8081/share/s", client.getResolvedProperty("sharedLinkBaseUrl", sysAdminParams));
+ }
+
+ @Test
+ public void testClientsPropertiesOverride()
+ {
+ Properties globalProps = new Properties();
+ globalProps.put("repo.client-app.workspace.workspaceUrl", "https://develop.envalfresco.com/#");
+ globalProps.put("repo.client-app.share.shareUrl", "https://develop.envalfresco.com/share");
+ globalProps.put("repo.client-app.coolApp.coolAppUrl", "https://develop.envalfresco.com/eval/cool-app");
+
+ clientAppConfig.setGlobalProperties(globalProps);
+
+ /*
+ * Workspace client URL Override
+ */
+ ClientApp workspaceClient = getClientApp("workspace");
+ // Resolved properties
+ assertEquals("https://develop.envalfresco.com/#", workspaceClient.getResolvedClientUrl(sysAdminParams));
+ assertEquals("https://develop.envalfresco.com/#/images",
+ workspaceClient.getResolvedTemplateAssetsUrl(sysAdminParams));
+ assertEquals("https://develop.envalfresco.com/#/reset-password",
+ workspaceClient.getResolvedProperty("resetPasswordPageUrl", sysAdminParams));
+
+ /*
+ * Share client URL Override
+ */
+ ClientApp shareClient = getClientApp("share");
+ // Resolved properties
+ assertEquals("https://develop.envalfresco.com/share", shareClient.getResolvedClientUrl(sysAdminParams));
+ assertEquals("https://develop.envalfresco.com/share/res/components/images",
+ shareClient.getResolvedTemplateAssetsUrl(sysAdminParams));
+ assertEquals("https://develop.envalfresco.com/share/page/reset-password",
+ shareClient.getResolvedProperty("resetPasswordPageUrl", sysAdminParams));
+ assertEquals("https://develop.envalfresco.com/share/s",
+ shareClient.getResolvedProperty("sharedLinkBaseUrl", sysAdminParams));
+
+ /*
+ * coolApp client URL Override
+ */
+ ClientApp coolAppClient = getClientApp("coolApp");
+ // Resolved properties
+ assertEquals("https://develop.envalfresco.com/eval/cool-app",
+ coolAppClient.getResolvedClientUrl(sysAdminParams));
+ assertEquals("https://develop.envalfresco.com/eval/cool-app/images",
+ coolAppClient.getResolvedTemplateAssetsUrl(sysAdminParams));
+ assertEquals("https://develop.envalfresco.com/eval/cool-app/page-one/page-two",
+ coolAppClient.getResolvedProperty("testPropUrl", sysAdminParams));
+ }
+
+ private Properties getWorkspaceAppProperties()
+ {
+ Properties props = new Properties();
+ props.put("repo.client-app.workspace.inviteModeratedTemplatePath", "");
+ props.put("repo.client-app.workspace.workspaceUrl", "${repoBaseUrl}/workspace");
+ props.put("repo.client-app.workspace.templateAssetsUrl", "${workspaceUrl}/images");
+ props.put("repo.client-app.workspace.requestResetPasswordTemplatePath",
+ "alfresco/templates/test-email-templates/test-email-template.ftl");
+ props.put("repo.client-app.workspace.resetPasswordPageUrl", "${workspaceUrl}/reset-password");
+ props.put("repo.client-app.workspace.confirmResetPasswordTemplatePath", "some/path");
+
+ return props;
+ }
+
+ private Properties getShareProperties()
+ {
+ Properties props = new Properties();
+ props.put("repo.client-app.share.templateAssetsUrl", "${shareUrl}/res/components/images");
+ props.put("repo.client-app.share.resetPasswordPageUrl", "${shareUrl}/page/reset-password");
+ props.put("repo.client-app.share.sharedLinkBaseUrl", "${shareUrl}/s");
+
+ return props;
+ }
+
+ private Properties getCoolAppProperties()
+ {
+ Properties props = new Properties();
+ props.put("repo.client-app.coolApp.coolAppUrl", "http://localhost:8090/cool-app");
+ props.put("repo.client-app.coolApp.templateAssetsUrl", "${coolAppUrl}/images");
+ props.put("repo.client-app.coolApp.testPropUrl", "${coolAppUrl}/page-one/page-two");
+
+ return props;
+ }
+
+ private ClientApp getClientApp(String clientName)
+ {
+ if (!initialised.get())
+ {
+ clientAppConfig.onBootstrap(null);
+ initialised.set(true);
+ }
+ Map clients = clientAppConfig.getClients();
+ assertFalse(clients.isEmpty());
+ assertTrue(clientName + " client is expected.", clientAppConfig.exists(clientName));
+ ClientApp client = clients.get(clientName);
+ assertNotNull(clientName + " client can't be null.", client);
+ return client;
+ }
+}
diff --git a/repository/src/test/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImplTest.java b/repository/src/test/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImplTest.java
index a4b4e94c54..ff27ce82fa 100644
--- a/repository/src/test/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImplTest.java
+++ b/repository/src/test/java/org/alfresco/repo/security/authentication/ResetPasswordServiceImplTest.java
@@ -30,6 +30,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import org.alfresco.model.ContentModel;
+import org.alfresco.repo.admin.SysAdminParams;
+import org.alfresco.repo.client.config.ClientAppConfig;
+import org.alfresco.repo.client.config.ClientAppConfig.ClientApp;
import org.alfresco.repo.client.config.ClientAppNotFoundException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.InvalidResetPasswordWorkflowException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordDetails;
@@ -47,6 +50,7 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.alfresco.util.TestHelper;
+import org.alfresco.util.UrlUtil;
import org.alfresco.util.email.EmailUtil;
import org.alfresco.util.test.junitrules.ApplicationContextInit;
import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule;
@@ -88,9 +92,12 @@ public class ResetPasswordServiceImplTest
private static PersonService personService;
private static Properties globalProperties;
private static WorkflowService workflowService;
+ private static ClientAppConfig clientAppConfig;
+ private static SysAdminParams sysAdminParams;
- private static TestPerson testPerson;
- private static EmailUtil emailUtil;
+ private static TestPerson testPerson1;
+ private static TestPerson testPerson2;
+ private static EmailUtil emailUtil;
@BeforeClass
public static void initStaticData() throws Exception
@@ -103,36 +110,57 @@ public class ResetPasswordServiceImplTest
personService = APP_CONTEXT_INIT.getApplicationContext().getBean("personService", PersonService.class);
globalProperties = APP_CONTEXT_INIT.getApplicationContext().getBean("global-properties", Properties.class);
workflowService = APP_CONTEXT_INIT.getApplicationContext().getBean("WorkflowService", WorkflowService.class);
+ clientAppConfig = APP_CONTEXT_INIT.getApplicationContext().getBean("clientAppConfig", ClientAppConfig.class);
+ sysAdminParams = APP_CONTEXT_INIT.getApplicationContext().getBean("sysAdminParams", SysAdminParams.class);
emailUtil = new EmailUtil(APP_CONTEXT_INIT.getApplicationContext());
emailUtil.reset();
- String userName = "jane.doe" + System.currentTimeMillis();
- testPerson = new TestPerson()
- .setUserName(userName)
+ String userName1 = "jane.doe" + System.currentTimeMillis();
+ testPerson1 = new TestPerson()
+ .setUserName(userName1)
.setFirstName("Jane")
- .setLastName("doe")
+ .setLastName("Doe")
.setPassword("password")
- .setEmail(userName + "@example.com");
+ .setEmail(userName1 + "@example.com");
+ String userName2 = "sara.blogs" + System.currentTimeMillis();
+ testPerson2 = new TestPerson()
+ .setUserName(userName2)
+ .setFirstName("Sara")
+ .setLastName("Blogs")
+ .setPassword("password")
+ .setEmail(userName2 + "@example.com");
+
+ AuthenticationUtil.setRunAsUserSystem();
transactionHelper.doInTransaction((RetryingTransactionCallback) () ->
{
- createUser(testPerson);
+ createUser(testPerson1);
+ createUser(testPerson2);
return null;
});
-
+ // Restore authentication to pre-test state.
+ try
+ {
+ AuthenticationUtil.popAuthentication();
+ }
+ catch(EmptyStackException e)
+ {
+ // Nothing to do.
+ }
}
@AfterClass
public static void cleanUp()
{
- resetPasswordService.setSendEmailAsynchronously(Boolean.valueOf(
+ resetPasswordService.setSendEmailAsynchronously(Boolean.parseBoolean(
globalProperties.getProperty("system.reset-password.sendEmailAsynchronously")));
resetPasswordService.setDefaultEmailSender((String) globalProperties.get("system.email.sender.default"));
AuthenticationUtil.setRunAsUserSystem();
transactionHelper.doInTransaction(() ->
{
- personService.deletePerson(testPerson.userName);
+ personService.deletePerson(testPerson1.userName);
+ personService.deletePerson(testPerson2.userName);
return null;
});
@@ -157,21 +185,21 @@ public class ResetPasswordServiceImplTest
public void testResetPassword() throws Exception
{
// Try the credential before change of password
- authenticateUser(testPerson.userName, testPerson.password);
+ authenticateUser(testPerson1.userName, testPerson1.password);
// Make sure to run as system
AuthenticationUtil.clearCurrentSecurityContext();
AuthenticationUtil.setRunAsUserSystem();
// Request password reset
- resetPasswordService.requestReset(testPerson.userName, "share");
+ resetPasswordService.requestReset(testPerson1.userName, "share");
assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount());
// Check the email
MimeMessage msg = emailUtil.getLastEmail();
assertNotNull("There should be an email.", msg);
assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length);
// Check the recipient is the person who requested the reset password
- assertEquals(testPerson.email, msg.getAllRecipients()[0].toString());
+ assertEquals(testPerson1.email, msg.getAllRecipients()[0].toString());
//Check the sender is what we set as default
assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString());
// There should be a subject
@@ -192,7 +220,7 @@ public class ResetPasswordServiceImplTest
emailUtil.reset();
// Now that we have got the email, try to reset the password
ResetPasswordDetails passwordDetails = new ResetPasswordDetails()
- .setUserId(testPerson.userName)
+ .setUserId(testPerson1.userName)
.setPassword("newPassword")
.setWorkflowId(pair.getFirst())
.setWorkflowKey(pair.getSecond());
@@ -204,7 +232,7 @@ public class ResetPasswordServiceImplTest
assertNotNull("There should be an email.", msg);
assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length);
// Check the recipient is the person who requested the reset password
- assertEquals(testPerson.email, msg.getAllRecipients()[0].toString());
+ assertEquals(testPerson1.email, msg.getAllRecipients()[0].toString());
// Check the sender is what we set as default
assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString());
// There should be a subject
@@ -215,12 +243,12 @@ public class ResetPasswordServiceImplTest
assertEquals(msg.getSubject(), I18NUtil.getMessage(emailSubjectKey));
// Try the old credential
- TestHelper.assertThrows(() -> authenticateUser(testPerson.userName, testPerson.password),
+ TestHelper.assertThrows(() -> authenticateUser(testPerson1.userName, testPerson1.password),
AuthenticationException.class,
"As the user changed her password, the authentication should have failed.");
// Try the new credential
- authenticateUser(testPerson.userName, "newPassword");
+ authenticateUser(testPerson1.userName, "newPassword");
// Make sure to run as system
AuthenticationUtil.clearCurrentSecurityContext();
@@ -237,12 +265,12 @@ public class ResetPasswordServiceImplTest
public void testRequestResetPasswordInvalid() throws Exception
{
// Request password reset
- TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson.userName, null),
+ TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson1.userName, null),
IllegalArgumentException.class,
"Client name is mandatory.");
// Request password reset
- TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson.userName, "TestClient" + System.currentTimeMillis()),
+ TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson1.userName, "TestClient" + System.currentTimeMillis()),
ClientAppNotFoundException.class,
"Client is not found.");
assertEquals("No email should have been sent.", 0, emailUtil.getSentCount());
@@ -260,23 +288,23 @@ public class ResetPasswordServiceImplTest
assertEquals("No email should have been sent.", 0, emailUtil.getSentCount());
// Disable the user
- enableUser(testPerson.userName, false);
+ enableUser(testPerson1.userName, false);
// Request password reset
- TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson.userName, "share"),
+ TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson1.userName, "share"),
ResetPasswordWorkflowInvalidUserException.class,
"user is disabled.");
assertEquals("No email should have been sent.", 0, emailUtil.getSentCount());
// Enable the user
- enableUser(testPerson.userName, true);
+ enableUser(testPerson1.userName, true);
}
@Test
public void testResetPasswordInvalid() throws Exception
{
// Request password reset
- resetPasswordService.requestReset(testPerson.userName, "share");
+ resetPasswordService.requestReset(testPerson1.userName, "share");
assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount());
// Check the email
MimeMessage msg = emailUtil.getLastEmail();
@@ -307,7 +335,7 @@ public class ResetPasswordServiceImplTest
IllegalArgumentException.class,
"User id is mandatory.");
- passwordDetails.setUserId(testPerson.userName)
+ passwordDetails.setUserId(testPerson1.userName)
.setPassword(null); // Password is not provided
TestHelper.assertThrows(() -> resetPasswordService.initiateResetPassword(passwordDetails),
IllegalArgumentException.class,
@@ -363,7 +391,7 @@ public class ResetPasswordServiceImplTest
// Set the duration for 1 second
resetPasswordService.setTimerEnd("PT1S");
// Request password reset
- resetPasswordService.requestReset(testPerson.userName, "share");
+ resetPasswordService.requestReset(testPerson1.userName, "share");
assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount());
// Check the reset password url.
@@ -377,7 +405,7 @@ public class ResetPasswordServiceImplTest
emailUtil.reset();
// Now that we have got the email, try to reset the password
ResetPasswordDetails passwordDetails = new ResetPasswordDetails()
- .setUserId(testPerson.userName)
+ .setUserId(testPerson1.userName)
.setPassword("newPassword")
.setWorkflowId(pair.getFirst())
.setWorkflowKey(pair.getSecond());
@@ -396,6 +424,110 @@ public class ResetPasswordServiceImplTest
}
}
+ @Test
+ public void testResetPasswordForClientWorkspace() throws Exception
+ {
+ // Try the credential before change of password
+ authenticateUser(testPerson2.userName, testPerson2.password);
+
+ // Make sure to run as system
+ AuthenticationUtil.clearCurrentSecurityContext();
+ AuthenticationUtil.setRunAsUserSystem();
+
+ // Request password reset
+ resetPasswordService.requestReset(testPerson2.userName, "workspace");
+ assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount());
+ // Check the email
+ MimeMessage msg = emailUtil.getLastEmail();
+ assertNotNull("There should be an email.", msg);
+ assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length);
+ // Check the recipient is the person who requested the reset password
+ assertEquals(testPerson2.email, msg.getAllRecipients()[0].toString());
+ //Check the sender is what we set as default
+ assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString());
+ // There should be a subject
+ assertNotNull("There should be a subject.", msg.getSubject());
+ // Check the default email subject - (check that we are sending the right email)
+ String emailSubjectKey = getDeclaredField(SendResetPasswordEmailDelegate.class, "EMAIL_SUBJECT_KEY");
+ assertNotNull(emailSubjectKey);
+ assertEquals(msg.getSubject(), I18NUtil.getMessage(emailSubjectKey));
+
+ // Check the reset password url.
+ String resetPasswordUrl = (String) emailUtil.getLastEmailTemplateModelValue("reset_password_url");
+ assertNotNull("Wrong email is sent.", resetPasswordUrl);
+ // Get the workflow id and key
+ Pair pair = getWorkflowIdAndKeyFromUrl(resetPasswordUrl);
+ assertNotNull("Workflow Id can't be null.", pair.getFirst());
+ assertNotNull("Workflow Key can't be null.", pair.getSecond());
+
+ emailUtil.reset();
+ // Now that we have got the email, try to reset the password
+ ResetPasswordDetails passwordDetails = new ResetPasswordDetails()
+ .setUserId(testPerson2.userName)
+ .setPassword("strongPassword")
+ .setWorkflowId(pair.getFirst())
+ .setWorkflowKey(pair.getSecond());
+
+ resetPasswordService.initiateResetPassword(passwordDetails);
+ assertEquals("A reset password confirmation email should have been sent.", 1, emailUtil.getSentCount());
+ // Check the email
+ msg = emailUtil.getLastEmail();
+ assertNotNull("There should be an email.", msg);
+ assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length);
+ // Check the recipient is the person who requested the reset password
+ assertEquals(testPerson2.email, msg.getAllRecipients()[0].toString());
+ // Check the sender is what we set as default
+ assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString());
+ // There should be a subject
+ assertNotNull("There should be a subject.", msg.getSubject());
+ // Check the default email subject - (check that we are sending the right email)
+ emailSubjectKey = getDeclaredField(SendResetPasswordConfirmationEmailDelegate.class, "EMAIL_SUBJECT_KEY");
+ assertNotNull(emailSubjectKey);
+ assertEquals(msg.getSubject(), I18NUtil.getMessage(emailSubjectKey));
+
+ // Try the old credential
+ TestHelper.assertThrows(() -> authenticateUser(testPerson2.userName, testPerson2.password),
+ AuthenticationException.class,
+ "As the user changed her password, the authentication should have failed.");
+
+ // Try the new credential
+ authenticateUser(testPerson2.userName, "strongPassword");
+
+ // Make sure to run as system
+ AuthenticationUtil.clearCurrentSecurityContext();
+ AuthenticationUtil.setRunAsUserSystem();
+ emailUtil.reset();
+ // Try reset again with the used workflow
+ TestHelper.assertThrows(() -> resetPasswordService.initiateResetPassword(passwordDetails),
+ InvalidResetPasswordWorkflowException.class,
+ "The workflow instance is not active (it has already been used).");
+ assertEquals("No email should have been sent.", 0, emailUtil.getSentCount());
+ }
+
+ @Test
+ public void testCreateResetPasswordUrl()
+ {
+ /* Out of the box we have share and workspace registered as the clients.
+ * See: alfresco/client/config/repo-clients-apps.properties file.
+ */
+
+ // Share client
+ ClientApp share = clientAppConfig.getClient("share");
+ String shareResetPasswordUrl =
+ resetPasswordService.createResetPasswordUrl(share, "workflow-id-123", "workflow-key-123");
+ String shareExpectedUrl = UrlUtil.getShareUrl(sysAdminParams)
+ + "/page/reset-password?key=workflow-key-123&id=activiti$workflow-id-123";
+ assertEquals(shareExpectedUrl, shareResetPasswordUrl);
+
+ // Workspace client
+ ClientApp workspace = clientAppConfig.getClient("workspace");
+ String workspaceResetPasswordUrl =
+ resetPasswordService.createResetPasswordUrl(workspace, "workflow-id-456", "workflow-key-456");
+ String workspaceExpectedUrl = UrlUtil.getAlfrescoBaseUrl(sysAdminParams)
+ + "/workspace/reset-password?key=workflow-key-456&id=activiti$workflow-id-456";
+ assertEquals(workspaceExpectedUrl, workspaceResetPasswordUrl);
+ }
+
private boolean isActive(String workflowId)
{
WorkflowInstance workflowInstance = workflowService.getWorkflowById(workflowId);