ACA-4619: fixed dynamic client app config. That is to get the resolved url based on the client name.

This commit is contained in:
Jamal Kaabi-Mofrad
2023-04-13 18:32:09 +01:00
parent c4a4aedad7
commit 14e21eb62d
13 changed files with 718 additions and 146 deletions

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -26,10 +26,12 @@
package org.alfresco.repo.client.config; package org.alfresco.repo.client.config;
import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
import org.alfresco.util.UrlUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log; import org.slf4j.Logger;
import org.apache.commons.logging.LogFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.springframework.extensions.surf.util.AbstractLifecycleBean;
@@ -41,6 +43,7 @@ import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; 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 * 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, <i>sharedLinkBaseUrl</i> and <i>templateAssetsUrl</i> properties, then the following * for example, <i>sharedLinkBaseUrl</i> and <i>templateAssetsUrl</i> properties, then the following
* needs to be put into a properties file. * needs to be put into a properties file.
* <ul> * <ul>
* <li>repo.client-app.MyClientName.sharedLinkBaseUrl=http://localhost:8080/MyClientName/s</li> * <li>{@code repo.client-app.MyClientName.sharedLinkBaseUrl=http://localhost:8080/MyClientName/s}</li>
* <li>repo.client-app.MyClientName.templateAssetsUrl=http://localhost:8080/MyClientName/assets</li> * <li>{@code repo.client-app.MyClientName.templateAssetsUrl=http://localhost:8080/MyClientName/assets}</li>
* </ul> * </ul>
* The default property file is <b>alfresco/client/config/repo-clients-apps.properties</b> which * The default property file is <b>alfresco/client/config/repo-clients-apps.properties</b> which
* could be overridden (or add new clients) by <b>alfresco-global</b> properties file. * could be overridden (or add new clients) by <b>alfresco-global</b> properties file.
* <p>
* <b>Note:</b> The {@literal <clientName>Url} is a special property which can be used as a placeholder.
* For example,
* <ul>
* <li>{@code repo.client-app.MyClientName.MyClientNameUrl=${repoBaseUrl}/entrypoint}</li>
* <li>{@code repo.client-app.MyClientName.somePropName=${MyClientNameUrl}/some-page}</li>
* </ul>
* </p>
* *
* @author Jamal Kaabi-Mofrad * @author Jamal Kaabi-Mofrad
*/ */
public class ClientAppConfig extends AbstractLifecycleBean 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 PREFIX = "repo.client-app.";
public static final String PROP_TEMPLATE_ASSETS_URL = "templateAssetsUrl"; public static final String PROP_TEMPLATE_ASSETS_URL = "templateAssetsUrl";
public static final String SHARE_PLACEHOLDER = "${shareUrl}";
private Properties defaultProperties; private Properties defaultProperties;
private Properties globalProperties; private Properties globalProperties;
private ConcurrentMap<String, ClientApp> clients = new ConcurrentHashMap<>(); private final ConcurrentMap<String, ClientApp> clients = new ConcurrentHashMap<>();
public ClientAppConfig() public ClientAppConfig()
{ {
@@ -117,6 +130,17 @@ public class ClientAppConfig extends AbstractLifecycleBean
return clients.get(name); 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 @Override
protected void onBootstrap(ApplicationEvent event) protected void onBootstrap(ApplicationEvent event)
{ {
@@ -127,9 +151,9 @@ public class ClientAppConfig extends AbstractLifecycleBean
processPropertyKeys(mergedProperties, clientsNames, propsNames); processPropertyKeys(mergedProperties, clientsNames, propsNames);
clients.putAll(processClients(clientsNames, propsNames, mergedProperties)); 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()) 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; continue;
} }
// As the required values are valid, create the client data // 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) private void logMalformedPropertyKey(String propName, String reason)
{ {
reason = (StringUtils.isBlank(reason)) ? "" : " " + 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) private void logMalformedPropertyKey(String propName)
@@ -285,9 +309,9 @@ public class ClientAppConfig extends AbstractLifecycleBean
private void logInvalidPropertyValue(String propName, String propValue) 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 public static class ClientApp
{ {
private static final Logger LOGGER = LoggerFactory.getLogger(ClientApp.class);
private final String name; private final String name;
private final String clientUrlPropKey;
private final Pattern clientUrlPlaceholderPattern;
private final String templateAssetsUrl; private final String templateAssetsUrl;
private final Map<String, String> properties; private final Map<String, String> properties;
public ClientApp(String name, String templateAssetsUrl, Map<String, String> properties) public ClientApp(String name, String templateAssetsUrl, Map<String, String> properties)
{ {
this.name = name; 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.templateAssetsUrl = templateAssetsUrl;
this.properties = new HashMap<>(properties); this.properties = new HashMap<>(properties);
} }
@@ -314,11 +343,77 @@ public class ClientAppConfig extends AbstractLifecycleBean
return name; 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:
* <ul>
* <li>{@literal ${repoBaseUrl}}</li>
* <li>{@literal ${shareUrl}}</li>
* </ul>
*
* @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() public String getTemplateAssetsUrl()
{ {
return templateAssetsUrl; return templateAssetsUrl;
} }
/**
* Resolves the template assets URL that has the following defined URL placeholder:
* <ul>
* <li>{@literal ${<clientName>Url}}</li>
* </ul>
*
* @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<String, String> getProperties() public Map<String, String> getProperties()
{ {
return Collections.unmodifiableMap(properties); return Collections.unmodifiableMap(properties);
@@ -329,6 +424,27 @@ public class ClientAppConfig extends AbstractLifecycleBean
return properties.get(propName); return properties.get(propName);
} }
/**
* Resolves the property that has the following defined URL placeholder:
* <ul>
* <li>{@literal ${<clientName>Url}}</li>
* </ul>
*
* @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 @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {
@@ -356,6 +472,8 @@ public class ClientAppConfig extends AbstractLifecycleBean
{ {
final StringBuilder sb = new StringBuilder(250); final StringBuilder sb = new StringBuilder(250);
sb.append("ClientApp [name=").append(name) sb.append("ClientApp [name=").append(name)
.append(", clientUrlPropKey=").append(clientUrlPropKey)
.append(", clientUrlPlaceholderPattern=").append(clientUrlPlaceholderPattern.pattern())
.append(", templateAssetsUrl=").append(templateAssetsUrl) .append(", templateAssetsUrl=").append(templateAssetsUrl)
.append(", properties=").append(properties) .append(", properties=").append(properties)
.append(']'); .append(']');

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * 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 siteName
* @param role * @param role
* @param runAsUser * @param runAsUser
* @param siteService
* @param overrideExisting * @param overrideExisting
*/ */
public void addSiteMembership(final String invitee, final String siteName, final String role, final String runAsUser, final boolean 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_RESOURCE_TYPE, resourceType.toString());
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_CLIENT_NAME, clientName); 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); ClientAppConfig.ClientApp client = clientAppConfig.getClient(clientName);
workflowProps.put(WorkflowModelModeratedInvitation.WF_TEMPLATE_ASSETS_URL, client.getTemplateAssetsUrl()); workflowProps.put(WorkflowModelModeratedInvitation.WF_TEMPLATE_ASSETS_URL, client.getResolvedTemplateAssetsUrl(sysAdminParams));
workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, client.getProperty("workspaceUrl")); workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, client.getResolvedClientUrl(sysAdminParams));
} }
// get the moderated workflow // get the moderated workflow

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -72,7 +72,7 @@ public class SendModeratedInviteDelegate extends AbstractInvitationDelegate
Map<String, Object> variables = execution.getVariables(); Map<String, Object> variables = execution.getVariables();
String clientName = (String) variables.get(WorkflowModelModeratedInvitation.wfVarClientName); 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); ClientAppConfig.ClientApp clientApp = clientAppConfig.getClient(clientName);
final String path = clientApp.getProperty("inviteModeratedTemplatePath"); final String path = clientApp.getProperty("inviteModeratedTemplatePath");

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -934,13 +934,13 @@ public class QuickShareServiceImpl implements QuickShareService,
Map<String, Serializable> templateModel = new HashMap<>(6); Map<String, Serializable> templateModel = new HashMap<>(6);
templateModel.put(FTL_SENDER_FIRST_NAME, senderFirstName); templateModel.put(FTL_SENDER_FIRST_NAME, senderFirstName);
templateModel.put(FTL_SENDER_LAST_NAME, senderLastName); templateModel.put(FTL_SENDER_LAST_NAME, senderLastName);
final String sharedNodeUrl = getUrl(clientApp.getProperty(CONFIG_SHARED_LINK_BASE_URL), CONFIG_SHARED_LINK_BASE_URL) String resolvedUrl = getUrl(clientApp.getResolvedProperty(CONFIG_SHARED_LINK_BASE_URL, sysAdminParams), CONFIG_SHARED_LINK_BASE_URL);
+ '/' + emailRequest.getSharedId(); final String sharedNodeUrl = resolvedUrl + '/' + emailRequest.getSharedId();
templateModel.put(FTL_SHARED_NODE_URL, sharedNodeUrl); templateModel.put(FTL_SHARED_NODE_URL, sharedNodeUrl);
templateModel.put(FTL_SHARED_NODE_NAME, emailRequest.getSharedNodeName()); templateModel.put(FTL_SHARED_NODE_NAME, emailRequest.getSharedNodeName());
templateModel.put(FTL_SENDER_MESSAGE, emailRequest.getSenderMessage()); 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); templateModel.put(FTL_TEMPLATE_ASSETS_URL, templateAssetsUrl);
// Set the email details // Set the email details
@@ -1048,8 +1048,7 @@ public class QuickShareServiceImpl implements QuickShareService,
{ {
url = url.substring(0, url.length() - 1); url = url.substring(0, url.length() - 1);
} }
// Replace '${shareUrl} placeholder if it does exist. return url;
return UrlUtil.replaceShareUrlPlaceholder(url, sysAdminParams);
} }
private <T> T getDefaultIfNull(T defaultValue, T newValue) private <T> T getDefaultIfNull(T defaultValue, T newValue)

View File

@@ -32,13 +32,11 @@ import org.alfresco.repo.client.config.ClientAppNotFoundException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordDetails; import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordDetails;
/** /**
* @deprecated from 7.1.0
* Reset password service. * Reset password service.
* *
* @author Jamal Kaabi-Mofrad * @author Jamal Kaabi-Mofrad
* @since 5.2.1 * @since 5.2.1
*/ */
@Deprecated
public interface ResetPasswordService public interface ResetPasswordService
{ {
/** /**

View File

@@ -2,23 +2,23 @@
* #%L * #%L
* Alfresco Remote API * 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. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
@@ -61,8 +61,8 @@ import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
import org.alfresco.util.UrlUtil; import org.alfresco.util.UrlUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log; import org.slf4j.Logger;
import org.apache.commons.logging.LogFactory; import org.slf4j.LoggerFactory;
import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptException;
@@ -74,17 +74,15 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
/** /**
* @deprecated from 7.1.0 *
* *
* Reset password implementation based on workflow. * Reset password implementation based on workflow.
* *
* @author Jamal Kaabi-Mofrad * @author Jamal Kaabi-Mofrad
* @since 5.2.1 * @since 5.2.1
*/ */
@Deprecated
public class ResetPasswordServiceImpl implements ResetPasswordService 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 TIMER_END = "PT1H";
private static final String WORKFLOW_DESCRIPTION_KEY = "resetpasswordwf_resetpassword.resetpassword.workflow.description"; 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); ParameterCheck.mandatoryString("clientName", clientName);
String userEmail = validateUserAndGetEmail(userId); String userEmail = validateUserAndGetEmail(userId);
validateClient(clientName);
// Get the (latest) workflow definition for reset-password. // Get the (latest) workflow definition for reset-password.
WorkflowDefinition wfDefinition = workflowService.getDefinitionByName(WorkflowModelResetPassword.WORKFLOW_DEFINITION_NAME); WorkflowDefinition wfDefinition = workflowService.getDefinitionByName(WorkflowModelResetPassword.WORKFLOW_DEFINITION_NAME);
@@ -342,8 +341,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
} }
else if (!username.equals(userId)) else if (!username.equals(userId))
{ {
throw new InvalidResetPasswordWorkflowException("The given user id [" + userId + "] does not match the person's user id [" + username throw new InvalidResetPasswordWorkflowException(
+ "] who requested the password reset."); "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); ParameterCheck.mandatoryString("clientName", clientName);
ClientApp clientApp = clientAppConfig.getClient(clientName); validateClient(clientName);
if (clientApp == null) return clientAppConfig.getClient(clientName);
}
private void validateClient(String clientName)
{
if (!clientAppConfig.exists(clientName))
{ {
throw new ClientAppNotFoundException("Client was not found [" + clientName + "]"); throw new ClientAppNotFoundException("Client was not found [" + clientName + "]");
} }
return clientApp;
} }
@@ -383,9 +387,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
.setUserName(userName) .setUserName(userName)
.setUserEmail(toEmail) .setUserEmail(toEmail)
.setTemplatePath(templatePath) .setTemplatePath(templatePath)
.setTemplateAssetsUrl(clientApp.getTemplateAssetsUrl())
.setEmailSubject(emailSubject) .setEmailSubject(emailSubject)
.setTemplateModel(emailTemplateModel); .setTemplateModel(emailTemplateModel)
.setClientApp(clientApp);
sendEmail(emailRequest); sendEmail(emailRequest);
} }
@@ -400,7 +404,7 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
final String userName = (String) execution.getVariable(WorkflowModelResetPassword.WF_PROP_USERNAME_ACTIVITI); 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. // 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<Task> activitiTasks = activitiTaskService.createTaskQuery().taskDefinitionKey(WorkflowModelResetPassword.TASK_RESET_PASSWORD) List<Task> activitiTasks = activitiTaskService.createTaskQuery().taskDefinitionKey(WorkflowModelResetPassword.TASK_RESET_PASSWORD)
.processInstanceId(execution.getProcessInstanceId()).list(); .processInstanceId(execution.getProcessInstanceId()).list();
if (activitiTasks.size() != 1) if (activitiTasks.size() != 1)
@@ -448,9 +452,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
.setUserName(userName) .setUserName(userName)
.setUserEmail(userEmail) .setUserEmail(userEmail)
.setTemplatePath(templatePath) .setTemplatePath(templatePath)
.setTemplateAssetsUrl(clientApp.getTemplateAssetsUrl())
.setEmailSubject(emailSubject) .setEmailSubject(emailSubject)
.setTemplateModel(emailTemplateModel); .setTemplateModel(emailTemplateModel)
.setClientApp(clientApp);
sendEmail(emailRequest); sendEmail(emailRequest);
} }
@@ -460,7 +464,9 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
// Prepare the email // Prepare the email
Map<String, Serializable> templateModel = new HashMap<>(); Map<String, Serializable> templateModel = new HashMap<>();
// Replace '${shareUrl}' placeholder if it does exist. // 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); templateModel.put(FTL_TEMPLATE_ASSETS_URL, templateAssetsUrl);
if (emailRequest.getTemplateModel() != null) if (emailRequest.getTemplateModel() != null)
{ {
@@ -489,11 +495,11 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
actionService.executeAction(mailAction, null, false, sendEmailAsynchronously); 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 ""; return "";
} }
@@ -501,7 +507,7 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
{ {
url = url.substring(0, url.length() - 1); url = url.substring(0, url.length() - 1);
} }
return UrlUtil.replaceShareUrlPlaceholder(url, sysAdminParams); return url;
} }
protected String getResetPasswordEmailTemplate(ClientApp clientApp) protected String getResetPasswordEmailTemplate(ClientApp clientApp)
@@ -521,22 +527,23 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
{ {
StringBuilder sb = new StringBuilder(100); StringBuilder sb = new StringBuilder(100);
String pageUrl = clientApp.getProperty("resetPasswordPageUrl"); String pageUrl = clientApp.getResolvedProperty("resetPasswordPageUrl", sysAdminParams);
if (StringUtils.isEmpty(pageUrl)) if (StringUtils.isEmpty(pageUrl))
{ {
sb.append(UrlUtil.getShareUrl(sysAdminParams)); 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 else
{ {
// We pass an empty string as we know that the pageUrl is not null // 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) sb.append("?key=")
.append("&id=").append(BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, id)); .append(key)
.append("&id=")
.append(BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, id));
return sb.toString(); return sb.toString();
} }
@@ -616,10 +623,10 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
private String userEmail; private String userEmail;
private String fromEmail; private String fromEmail;
private String templatePath; private String templatePath;
private String templateAssetsUrl;
private Map<String, Serializable> templateModel; private Map<String, Serializable> templateModel;
private String emailSubject; private String emailSubject;
private boolean ignoreSendFailure = true; private boolean ignoreSendFailure = true;
private ClientApp clientApp;
public String getUserName() public String getUserName()
{ {
@@ -665,17 +672,6 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
return this; return this;
} }
public String getTemplateAssetsUrl()
{
return templateAssetsUrl;
}
public ResetPasswordEmailDetails setTemplateAssetsUrl(String templateAssetsUrl)
{
this.templateAssetsUrl = templateAssetsUrl;
return this;
}
public Map<String, Serializable> getTemplateModel() public Map<String, Serializable> getTemplateModel()
{ {
return templateModel; return templateModel;
@@ -709,18 +705,29 @@ public class ResetPasswordServiceImpl implements ResetPasswordService
return this; return this;
} }
public ClientApp getClientApp()
{
return clientApp;
}
public ResetPasswordEmailDetails setClientApp(ClientApp clientApp)
{
this.clientApp = clientApp;
return this;
}
@Override @Override
public String toString() public String toString()
{ {
final StringBuilder sb = new StringBuilder(250); final StringBuilder sb = new StringBuilder(300);
sb.append("ResetPasswordEmailDetails [userName=").append(userName) sb.append("ResetPasswordEmailDetails [userName=").append(userName)
.append(", userEmail=").append(userEmail) .append(", userEmail=").append(userEmail)
.append(", fromEmail=").append(fromEmail) .append(", fromEmail=").append(fromEmail)
.append(", templatePath=").append(templatePath) .append(", templatePath=").append(templatePath)
.append(", templateAssetsUrl=").append(templateAssetsUrl)
.append(", templateModel=").append(templateModel) .append(", templateModel=").append(templateModel)
.append(", emailSubject=").append(emailSubject) .append(", emailSubject=").append(emailSubject)
.append(", ignoreSendFailure=").append(ignoreSendFailure) .append(", ignoreSendFailure=").append(ignoreSendFailure)
.append(", clientApp=").append(clientApp)
.append(']'); .append(']');
return sb.toString(); return sb.toString();
} }

View File

@@ -29,12 +29,10 @@ package org.alfresco.repo.workflow;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
* @deprecated from 7.1.0
* *
* @author Jamal Kaabi-Mofrad * @author Jamal Kaabi-Mofrad
* @since 5.2.1 * @since 5.2.1
*/ */
@Deprecated
public interface WorkflowModelResetPassword public interface WorkflowModelResetPassword
{ {
// namespace // namespace

View File

@@ -2,23 +2,23 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited * Copyright (C) 2005 - 2023 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
@@ -32,43 +32,62 @@ import java.util.regex.Pattern;
/** /**
* Alfresco URL related utility functions. * Alfresco URL related utility functions.
* *
* @since 3.5 * @since 3.5
*/ */
public class UrlUtil public class UrlUtil
{ {
// ${shareUrl} placeholder // ${shareUrl} placeholder
public static final Pattern PATTERN = Pattern.compile("\\$\\{shareUrl\\}"); public static final Pattern PATTERN = Pattern.compile("\\$\\{shareUrl\\}");
// ${alfrescoUrl} placeholder // ${alfrescoUrl} placeholder
public static final Pattern REPO_PATTERN = Pattern.compile("\\$\\{alfrescoUrl\\}"); 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 * Builds up the Url to Alfresco root url based on the settings in the
* {@link SysAdminParams}. * {@link SysAdminParams}.
* @return Alfresco Url such as https://col.ab.or.ate/alfresco/ * @return Alfresco base Url such as {@code https://col.ab.or.ate}
* or http://localhost:8080/alfresco/ * 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) public static String getAlfrescoUrl(SysAdminParams sysAdminParams)
{ {
return buildUrl( return buildUrl(
sysAdminParams.getAlfrescoProtocol(), sysAdminParams.getAlfrescoProtocol(),
sysAdminParams.getAlfrescoHost(), sysAdminParams.getAlfrescoHost(),
sysAdminParams.getAlfrescoPort(), sysAdminParams.getAlfrescoPort(),
sysAdminParams.getAlfrescoContext()); sysAdminParams.getAlfrescoContext());
} }
/** /**
* Builds up the Url to Share based on the settings in the * Builds up the Url to Share based on the settings in the
* {@link SysAdminParams}. * {@link SysAdminParams}.
* @return Alfresco Url such as https://col.ab.or.ate/share/ * @return Alfresco Url such as {@code https://col.ab.or.ate/share}
* or http://localhost:8081/share/ * or {@code http://localhost:8081/share}
*/ */
public static String getShareUrl(SysAdminParams sysAdminParams) public static String getShareUrl(SysAdminParams sysAdminParams)
{ {
return buildUrl( return buildUrl(
sysAdminParams.getShareProtocol(), sysAdminParams.getShareProtocol(),
sysAdminParams.getShareHost(), sysAdminParams.getShareHost(),
sysAdminParams.getSharePort(), sysAdminParams.getSharePort(),
sysAdminParams.getShareContext()); 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 * Builds URL to Api-Explorer based on the request only if the URL property is not provided
* {@link SysAdminParams}. * {@link SysAdminParams}.
* @return Rest-Api Url such as https://col.ab.or.ate/api-explorer/ * @return Rest-Api Url such as {@code https://col.ab.or.ate/api-explorer}
* or http://localhost:8082/api-explorer/ * or {@code http://localhost:8082/api-explorer}
*/ */
public static String getApiExplorerUrl(SysAdminParams sysAdminParams, String requestURL, String requestURI) 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) 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(); StringBuilder url = new StringBuilder();
url.append(protocol); url.append(protocol);
@@ -142,8 +167,33 @@ public class UrlUtil
url.append(':'); url.append(':');
url.append(port); url.append(port);
} }
url.append('/');
url.append(context);
return url.toString(); 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;
}
} }

View File

@@ -5,8 +5,8 @@
# a NodeRef of the template # a NodeRef of the template
# a class path of the template # a class path of the template
# template assets url for share client # 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/ repo.client-app.share.templateAssetsUrl=${shareUrl}/res/components/images
# shared-link (quickShare) base url # shared-link (quickShare) base url
repo.client-app.share.sharedLinkBaseUrl=${shareUrl}/s repo.client-app.share.sharedLinkBaseUrl=${shareUrl}/s
# shared-link email template path # shared-link email template path
@@ -20,6 +20,14 @@ repo.client-app.share.resetPasswordPageUrl=${shareUrl}/page/reset-password
repo.client-app.share.confirmResetPasswordTemplatePath= repo.client-app.share.confirmResetPasswordTemplatePath=
### Digital workspace template configurations ### Digital workspace template configurations
repo.client-app.workspace.workspaceUrl=${repoBaseUrl}/workspace
repo.client-app.workspace.inviteModeratedTemplatePath= repo.client-app.workspace.inviteModeratedTemplatePath=
repo.client-app.workspace.workspaceUrl=workspace # template assets url for workspace client. i.e. The source url for the images/logs within the email
repo.client-app.workspace.templateAssetsUrl=${alfrescoUrl}/images 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=

View File

@@ -250,7 +250,8 @@ import org.junit.runners.Suite;
org.alfresco.repo.event2.RepoEvent2UnitSuite.class, org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.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 public class AllUnitTestsSuite
{ {

View File

@@ -31,16 +31,12 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import org.alfresco.repo.client.config.ClientAppConfig.ClientApp; import org.alfresco.repo.client.config.ClientAppConfig.ClientApp;
import org.alfresco.service.cmr.repository.TemporalSourceOptions;
import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.testing.category.LuceneTests; import org.alfresco.util.testing.category.LuceneTests;
import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.experimental.categories.Category; import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@@ -53,15 +49,15 @@ import java.util.Map;
@Category(LuceneTests.class) @Category(LuceneTests.class)
public class ClientAppConfigTest public class ClientAppConfigTest
{ {
private ApplicationContext context;
private ClientAppConfig clientAppConfig; private ClientAppConfig clientAppConfig;
@Before @Before
public void setUp() throws Exception public void setUp() throws Exception
{ {
ApplicationContextHelper.closeApplicationContext(); ApplicationContextHelper.closeApplicationContext();
context = ApplicationContextHelper.getApplicationContext(new String[] { ApplicationContextHelper.CONFIG_LOCATIONS[0], ApplicationContext context = ApplicationContextHelper.getApplicationContext(
"classpath:org/alfresco/repo/client/config/test-repo-clients-apps-context.xml" }); new String[] { ApplicationContextHelper.CONFIG_LOCATIONS[0],
"classpath:org/alfresco/repo/client/config/test-repo-clients-apps-context.xml" });
clientAppConfig = context.getBean("clientAppConfigTest", ClientAppConfig.class); clientAppConfig = context.getBean("clientAppConfigTest", ClientAppConfig.class);
} }

View File

@@ -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 <http://www.gnu.org/licenses/>.
* #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<String, String> 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<String, String> 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<String, ClientApp> 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;
}
}

View File

@@ -30,6 +30,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import org.alfresco.model.ContentModel; 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.client.config.ClientAppNotFoundException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.InvalidResetPasswordWorkflowException; import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.InvalidResetPasswordWorkflowException;
import org.alfresco.repo.security.authentication.ResetPasswordServiceImpl.ResetPasswordDetails; 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.GUID;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.alfresco.util.TestHelper; import org.alfresco.util.TestHelper;
import org.alfresco.util.UrlUtil;
import org.alfresco.util.email.EmailUtil; import org.alfresco.util.email.EmailUtil;
import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.alfresco.util.test.junitrules.ApplicationContextInit;
import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule;
@@ -88,9 +92,12 @@ public class ResetPasswordServiceImplTest
private static PersonService personService; private static PersonService personService;
private static Properties globalProperties; private static Properties globalProperties;
private static WorkflowService workflowService; private static WorkflowService workflowService;
private static ClientAppConfig clientAppConfig;
private static SysAdminParams sysAdminParams;
private static TestPerson testPerson; private static TestPerson testPerson1;
private static EmailUtil emailUtil; private static TestPerson testPerson2;
private static EmailUtil emailUtil;
@BeforeClass @BeforeClass
public static void initStaticData() throws Exception public static void initStaticData() throws Exception
@@ -103,36 +110,57 @@ public class ResetPasswordServiceImplTest
personService = APP_CONTEXT_INIT.getApplicationContext().getBean("personService", PersonService.class); personService = APP_CONTEXT_INIT.getApplicationContext().getBean("personService", PersonService.class);
globalProperties = APP_CONTEXT_INIT.getApplicationContext().getBean("global-properties", Properties.class); globalProperties = APP_CONTEXT_INIT.getApplicationContext().getBean("global-properties", Properties.class);
workflowService = APP_CONTEXT_INIT.getApplicationContext().getBean("WorkflowService", WorkflowService.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 = new EmailUtil(APP_CONTEXT_INIT.getApplicationContext());
emailUtil.reset(); emailUtil.reset();
String userName = "jane.doe" + System.currentTimeMillis(); String userName1 = "jane.doe" + System.currentTimeMillis();
testPerson = new TestPerson() testPerson1 = new TestPerson()
.setUserName(userName) .setUserName(userName1)
.setFirstName("Jane") .setFirstName("Jane")
.setLastName("doe") .setLastName("Doe")
.setPassword("password") .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<Void>) () -> transactionHelper.doInTransaction((RetryingTransactionCallback<Void>) () ->
{ {
createUser(testPerson); createUser(testPerson1);
createUser(testPerson2);
return null; return null;
}); });
// Restore authentication to pre-test state.
try
{
AuthenticationUtil.popAuthentication();
}
catch(EmptyStackException e)
{
// Nothing to do.
}
} }
@AfterClass @AfterClass
public static void cleanUp() public static void cleanUp()
{ {
resetPasswordService.setSendEmailAsynchronously(Boolean.valueOf( resetPasswordService.setSendEmailAsynchronously(Boolean.parseBoolean(
globalProperties.getProperty("system.reset-password.sendEmailAsynchronously"))); globalProperties.getProperty("system.reset-password.sendEmailAsynchronously")));
resetPasswordService.setDefaultEmailSender((String) globalProperties.get("system.email.sender.default")); resetPasswordService.setDefaultEmailSender((String) globalProperties.get("system.email.sender.default"));
AuthenticationUtil.setRunAsUserSystem(); AuthenticationUtil.setRunAsUserSystem();
transactionHelper.doInTransaction(() -> transactionHelper.doInTransaction(() ->
{ {
personService.deletePerson(testPerson.userName); personService.deletePerson(testPerson1.userName);
personService.deletePerson(testPerson2.userName);
return null; return null;
}); });
@@ -157,21 +185,21 @@ public class ResetPasswordServiceImplTest
public void testResetPassword() throws Exception public void testResetPassword() throws Exception
{ {
// Try the credential before change of password // Try the credential before change of password
authenticateUser(testPerson.userName, testPerson.password); authenticateUser(testPerson1.userName, testPerson1.password);
// Make sure to run as system // Make sure to run as system
AuthenticationUtil.clearCurrentSecurityContext(); AuthenticationUtil.clearCurrentSecurityContext();
AuthenticationUtil.setRunAsUserSystem(); AuthenticationUtil.setRunAsUserSystem();
// Request password reset // 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()); assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount());
// Check the email // Check the email
MimeMessage msg = emailUtil.getLastEmail(); MimeMessage msg = emailUtil.getLastEmail();
assertNotNull("There should be an email.", msg); assertNotNull("There should be an email.", msg);
assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length); assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length);
// Check the recipient is the person who requested the reset password // 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 //Check the sender is what we set as default
assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString()); assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString());
// There should be a subject // There should be a subject
@@ -192,7 +220,7 @@ public class ResetPasswordServiceImplTest
emailUtil.reset(); emailUtil.reset();
// Now that we have got the email, try to reset the password // Now that we have got the email, try to reset the password
ResetPasswordDetails passwordDetails = new ResetPasswordDetails() ResetPasswordDetails passwordDetails = new ResetPasswordDetails()
.setUserId(testPerson.userName) .setUserId(testPerson1.userName)
.setPassword("newPassword") .setPassword("newPassword")
.setWorkflowId(pair.getFirst()) .setWorkflowId(pair.getFirst())
.setWorkflowKey(pair.getSecond()); .setWorkflowKey(pair.getSecond());
@@ -204,7 +232,7 @@ public class ResetPasswordServiceImplTest
assertNotNull("There should be an email.", msg); assertNotNull("There should be an email.", msg);
assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length); assertEquals("Should've been only one email recipient.", 1, msg.getAllRecipients().length);
// Check the recipient is the person who requested the reset password // 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 // Check the sender is what we set as default
assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString()); assertEquals(DEFAULT_SENDER, msg.getFrom()[0].toString());
// There should be a subject // There should be a subject
@@ -215,12 +243,12 @@ public class ResetPasswordServiceImplTest
assertEquals(msg.getSubject(), I18NUtil.getMessage(emailSubjectKey)); assertEquals(msg.getSubject(), I18NUtil.getMessage(emailSubjectKey));
// Try the old credential // Try the old credential
TestHelper.assertThrows(() -> authenticateUser(testPerson.userName, testPerson.password), TestHelper.assertThrows(() -> authenticateUser(testPerson1.userName, testPerson1.password),
AuthenticationException.class, AuthenticationException.class,
"As the user changed her password, the authentication should have failed."); "As the user changed her password, the authentication should have failed.");
// Try the new credential // Try the new credential
authenticateUser(testPerson.userName, "newPassword"); authenticateUser(testPerson1.userName, "newPassword");
// Make sure to run as system // Make sure to run as system
AuthenticationUtil.clearCurrentSecurityContext(); AuthenticationUtil.clearCurrentSecurityContext();
@@ -237,12 +265,12 @@ public class ResetPasswordServiceImplTest
public void testRequestResetPasswordInvalid() throws Exception public void testRequestResetPasswordInvalid() throws Exception
{ {
// Request password reset // Request password reset
TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson.userName, null), TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson1.userName, null),
IllegalArgumentException.class, IllegalArgumentException.class,
"Client name is mandatory."); "Client name is mandatory.");
// Request password reset // Request password reset
TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson.userName, "TestClient" + System.currentTimeMillis()), TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson1.userName, "TestClient" + System.currentTimeMillis()),
ClientAppNotFoundException.class, ClientAppNotFoundException.class,
"Client is not found."); "Client is not found.");
assertEquals("No email should have been sent.", 0, emailUtil.getSentCount()); 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()); assertEquals("No email should have been sent.", 0, emailUtil.getSentCount());
// Disable the user // Disable the user
enableUser(testPerson.userName, false); enableUser(testPerson1.userName, false);
// Request password reset // Request password reset
TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson.userName, "share"), TestHelper.assertThrows(() -> resetPasswordService.requestReset(testPerson1.userName, "share"),
ResetPasswordWorkflowInvalidUserException.class, ResetPasswordWorkflowInvalidUserException.class,
"user is disabled."); "user is disabled.");
assertEquals("No email should have been sent.", 0, emailUtil.getSentCount()); assertEquals("No email should have been sent.", 0, emailUtil.getSentCount());
// Enable the user // Enable the user
enableUser(testPerson.userName, true); enableUser(testPerson1.userName, true);
} }
@Test @Test
public void testResetPasswordInvalid() throws Exception public void testResetPasswordInvalid() throws Exception
{ {
// Request password reset // 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()); assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount());
// Check the email // Check the email
MimeMessage msg = emailUtil.getLastEmail(); MimeMessage msg = emailUtil.getLastEmail();
@@ -307,7 +335,7 @@ public class ResetPasswordServiceImplTest
IllegalArgumentException.class, IllegalArgumentException.class,
"User id is mandatory."); "User id is mandatory.");
passwordDetails.setUserId(testPerson.userName) passwordDetails.setUserId(testPerson1.userName)
.setPassword(null); // Password is not provided .setPassword(null); // Password is not provided
TestHelper.assertThrows(() -> resetPasswordService.initiateResetPassword(passwordDetails), TestHelper.assertThrows(() -> resetPasswordService.initiateResetPassword(passwordDetails),
IllegalArgumentException.class, IllegalArgumentException.class,
@@ -363,7 +391,7 @@ public class ResetPasswordServiceImplTest
// Set the duration for 1 second // Set the duration for 1 second
resetPasswordService.setTimerEnd("PT1S"); resetPasswordService.setTimerEnd("PT1S");
// Request password reset // 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()); assertEquals("A reset password email should have been sent.", 1, emailUtil.getSentCount());
// Check the reset password url. // Check the reset password url.
@@ -377,7 +405,7 @@ public class ResetPasswordServiceImplTest
emailUtil.reset(); emailUtil.reset();
// Now that we have got the email, try to reset the password // Now that we have got the email, try to reset the password
ResetPasswordDetails passwordDetails = new ResetPasswordDetails() ResetPasswordDetails passwordDetails = new ResetPasswordDetails()
.setUserId(testPerson.userName) .setUserId(testPerson1.userName)
.setPassword("newPassword") .setPassword("newPassword")
.setWorkflowId(pair.getFirst()) .setWorkflowId(pair.getFirst())
.setWorkflowKey(pair.getSecond()); .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<String, String> 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) private boolean isActive(String workflowId)
{ {
WorkflowInstance workflowInstance = workflowService.getWorkflowById(workflowId); WorkflowInstance workflowInstance = workflowService.getWorkflowById(workflowId);