Compare commits

...

4 Commits

Author SHA1 Message Date
Mohinish Sah
dc32451c28 fixed resolved url and updated the images path 2023-05-04 14:08:17 +05:30
Jamal Kaabi-Mofrad
4e354449ab ACA-4619: Minor: fixed Typo. 2023-04-13 18:50:20 +01:00
Jamal Kaabi-Mofrad
902fe505fa ACA-4619: Clean up QuickShareServiceImpl. 2023-04-13 18:34:55 +01:00
Jamal Kaabi-Mofrad
14e21eb62d ACA-4619: fixed dynamic client app config. That is to get the resolved url based on the client name. 2023-04-13 18:32:09 +01:00
16 changed files with 1348 additions and 397 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

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,81 @@ 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;
}
LOGGER.info("INSIDE getClientUrl ****--------------------->"+url);
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)
{
System.out.println("Inside getResolvedClientUrl--------------------->");
String resolvedUrl;
String clientUrl = getClientUrl();
System.out.println("clientUrl is ****--------------------->"+clientUrl);
if (!StringUtils.isEmpty(clientUrl) && clientUrl.contains(SHARE_PLACEHOLDER))
{
resolvedUrl = UrlUtil.replaceShareUrlPlaceholder(clientUrl, sysAdminParams);
}
else
{
resolvedUrl = UrlUtil.replaceRepoBaseUrlPlaceholder(clientUrl, sysAdminParams);
}
System.out.println("))))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 UrlUtil.replaceRepoBaseUrlPlaceholder(resolvedUrl, sysAdminParams);
}
public Map<String, String> getProperties() public Map<String, String> getProperties()
{ {
return Collections.unmodifiableMap(properties); return Collections.unmodifiableMap(properties);
@@ -329,6 +428,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 +476,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,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%
@@ -28,17 +28,15 @@ package org.alfresco.repo.quickshare;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import org.alfresco.sync.events.types.ActivityEvent; import org.alfresco.sync.events.types.ActivityEvent;
import org.alfresco.sync.events.types.Event;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.model.QuickShareModel; import org.alfresco.model.QuickShareModel;
import org.alfresco.sync.repo.Client; import org.alfresco.sync.repo.Client;
@@ -51,7 +49,6 @@ import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails; import org.alfresco.repo.copy.CopyDetails;
import org.alfresco.repo.copy.CopyServicePolicies; import org.alfresco.repo.copy.CopyServicePolicies;
import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback; import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback;
import org.alfresco.sync.repo.events.EventPreparator;
import org.alfresco.sync.repo.events.EventPublisher; import org.alfresco.sync.repo.events.EventPublisher;
import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.BehaviourFilter;
@@ -101,7 +98,6 @@ import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair; import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck; import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
import org.alfresco.util.UrlUtil;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -113,16 +109,16 @@ import org.safehaus.uuid.UUIDGenerator;
/** /**
* QuickShare Service implementation. * QuickShare Service implementation.
* * <p>
* In addition to the quick share service, this class also provides a BeforeDeleteNodePolicy and * In addition to the quick share service, this class also provides a BeforeDeleteNodePolicy and
* OnCopyNodePolicy for content with the QuickShare aspect. * OnCopyNodePolicy for content with the QuickShare aspect.
* *
* @author Alex Miller, janv, Jamal Kaabi-Mofrad * @author Alex Miller, janv, Jamal Kaabi-Mofrad
*/ */
public class QuickShareServiceImpl implements QuickShareService, public class QuickShareServiceImpl implements QuickShareService,
NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.BeforeDeleteNodePolicy,
CopyServicePolicies.OnCopyNodePolicy, CopyServicePolicies.OnCopyNodePolicy,
NodeServicePolicies.OnRestoreNodePolicy NodeServicePolicies.OnRestoreNodePolicy
{ {
private static final Log logger = LogFactory.getLog(QuickShareServiceImpl.class); private static final Log logger = LogFactory.getLog(QuickShareServiceImpl.class);
@@ -134,7 +130,6 @@ public class QuickShareServiceImpl implements QuickShareService,
private static final String FTL_SENDER_FIRST_NAME = "sender_first_name"; private static final String FTL_SENDER_FIRST_NAME = "sender_first_name";
private static final String FTL_SENDER_LAST_NAME = "sender_last_name"; private static final String FTL_SENDER_LAST_NAME = "sender_last_name";
private static final String FTL_TEMPLATE_ASSETS_URL = "template_assets_url"; private static final String FTL_TEMPLATE_ASSETS_URL = "template_assets_url";
private static final String CONFIG_SHARED_LINK_BASE_URL = "sharedLinkBaseUrl"; private static final String CONFIG_SHARED_LINK_BASE_URL = "sharedLinkBaseUrl";
private static final String DEFAULT_EMAIL_SUBJECT = "quickshare.notifier.email.subject"; private static final String DEFAULT_EMAIL_SUBJECT = "quickshare.notifier.email.subject";
private static final String EMAIL_TEMPLATE_REF ="alfresco/templates/quickshare-email-templates/quickshare-email.default.template.ftl"; private static final String EMAIL_TEMPLATE_REF ="alfresco/templates/quickshare-email-templates/quickshare-email.default.template.ftl";
@@ -177,9 +172,9 @@ public class QuickShareServiceImpl implements QuickShareService,
*/ */
public void setDictionaryService(DictionaryService dictionaryService) public void setDictionaryService(DictionaryService dictionaryService)
{ {
this.dictionaryService = dictionaryService; this.dictionaryService = dictionaryService;
} }
/** /**
* Set the node service * Set the node service
*/ */
@@ -195,15 +190,15 @@ public class QuickShareServiceImpl implements QuickShareService,
{ {
this.permissionService = permissionService; this.permissionService = permissionService;
} }
/** /**
* Set the person service * Set the person service
*/ */
public void setPersonService(PersonService personService) public void setPersonService(PersonService personService)
{ {
this.personService = personService; this.personService = personService;
} }
/** /**
* Set the policy component * Set the policy component
*/ */
@@ -215,11 +210,11 @@ public class QuickShareServiceImpl implements QuickShareService,
/** /**
* Set the tenant service * Set the tenant service
*/ */
public void setTenantService(TenantService tenantService) public void setTenantService(TenantService tenantService)
{ {
this.tenantService = tenantService; this.tenantService = tenantService;
} }
/** /**
* Set the thumbnail service * Set the thumbnail service
*/ */
@@ -227,7 +222,7 @@ public class QuickShareServiceImpl implements QuickShareService,
{ {
this.thumbnailService = thumbnailService; this.thumbnailService = thumbnailService;
} }
/** /**
* Set the eventPublisher * Set the eventPublisher
*/ */
@@ -394,14 +389,14 @@ public class QuickShareServiceImpl implements QuickShareService,
// Register interest in the beforeDeleteNode policy - note: currently for content only !! // Register interest in the beforeDeleteNode policy - note: currently for content only !!
policyComponent.bindClassBehaviour( policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
ContentModel.TYPE_CONTENT, ContentModel.TYPE_CONTENT,
new JavaBehaviour(this, "beforeDeleteNode")); new JavaBehaviour(this, "beforeDeleteNode"));
//Register interest in the onCopyNodePolicy to block copying of quick share metadta //Register interest in the onCopyNodePolicy to block copying of quick share metadta
policyComponent.bindClassBehaviour( policyComponent.bindClassBehaviour(
CopyServicePolicies.OnCopyNodePolicy.QNAME, CopyServicePolicies.OnCopyNodePolicy.QNAME,
QuickShareModel.ASPECT_QSHARE, QuickShareModel.ASPECT_QSHARE,
new JavaBehaviour(this, "getCopyCallback")); new JavaBehaviour(this, "getCopyCallback"));
this.policyComponent.bindClassBehaviour( this.policyComponent.bindClassBehaviour(
@@ -424,7 +419,7 @@ public class QuickShareServiceImpl implements QuickShareService,
//Check the node is the correct type //Check the node is the correct type
final QName typeQName = nodeService.getType(nodeRef); final QName typeQName = nodeService.getType(nodeRef);
if (isSharable(typeQName) == false) if (!isSharable(typeQName))
{ {
throw new InvalidNodeRefException(nodeRef); throw new InvalidNodeRefException(nodeRef);
} }
@@ -438,7 +433,7 @@ public class QuickShareServiceImpl implements QuickShareService,
UUID uuid = UUIDGenerator.getInstance().generateRandomBasedUUID(); UUID uuid = UUIDGenerator.getInstance().generateRandomBasedUUID();
sharedId = Base64.encodeBase64URLSafeString(uuid.toByteArray()); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw) sharedId = Base64.encodeBase64URLSafeString(uuid.toByteArray()); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw)
final Map<QName, Serializable> props = new HashMap<QName, Serializable>(2); final Map<QName, Serializable> props = new HashMap<>(2);
props.put(QuickShareModel.PROP_QSHARE_SHAREDID, sharedId); props.put(QuickShareModel.PROP_QSHARE_SHAREDID, sharedId);
props.put(QuickShareModel.PROP_QSHARE_SHAREDBY, AuthenticationUtil.getRunAsUser()); props.put(QuickShareModel.PROP_QSHARE_SHAREDBY, AuthenticationUtil.getRunAsUser());
@@ -448,13 +443,9 @@ public class QuickShareServiceImpl implements QuickShareService,
try try
{ {
// consumer/contributor should be able to add "shared" aspect (MNT-10366) // consumer/contributor should be able to add "shared" aspect (MNT-10366)
AuthenticationUtil.runAsSystem(new RunAsWork<Void>() AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
{ nodeService.addAspect(nodeRef, QuickShareModel.ASPECT_QSHARE, props);
public Void doWork() return null;
{
nodeService.addAspect(nodeRef, QuickShareModel.ASPECT_QSHARE, props);
return null;
}
}); });
} }
finally finally
@@ -464,27 +455,19 @@ public class QuickShareServiceImpl implements QuickShareService,
final NodeRef tenantNodeRef = tenantService.getName(nodeRef); final NodeRef tenantNodeRef = tenantService.getName(nodeRef);
TenantUtil.runAsDefaultTenant(new TenantRunAsWork<Void>() TenantUtil.runAsDefaultTenant((TenantRunAsWork<Void>) () -> {
{ attributeService.setAttribute(tenantNodeRef, ATTR_KEY_SHAREDIDS_ROOT, sharedId);
public Void doWork() throws Exception return null;
{
attributeService.setAttribute(tenantNodeRef, ATTR_KEY_SHAREDIDS_ROOT, sharedId);
return null;
}
}); });
final StringBuffer sb = new StringBuffer(); String sharedIdStr = "{" + "\"sharedId\":\"" + sharedId + "\"" + "}";
sb.append("{").append("\"sharedId\":\"").append(sharedId).append("\"").append("}");
eventPublisher.publishEvent(new EventPreparator(){ eventPublisher.publishEvent(
@Override (user, networkId, transactionId) -> new ActivityEvent("quickshare", transactionId, networkId,
public Event prepareEvent(String user, String networkId, String transactionId) user, nodeRef.getId(), null,
{ typeQName.toString(),
return new ActivityEvent("quickshare", transactionId, networkId, user, nodeRef.getId(), Client.asType(ClientType.webclient),
null, typeQName.toString(), Client.asType(ClientType.webclient), sb.toString(), sharedIdStr, null, null, 0L, null));
null, null, 0l, null);
}
});
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
{ {
@@ -523,26 +506,25 @@ public class QuickShareServiceImpl implements QuickShareService,
} }
/** /**
* Is this service enable? * Is this service enable?
* @throws QuickShareDisabledException if it isn't. * @throws QuickShareDisabledException if it isn't.
*/ */
private void checkEnabled() private void checkEnabled()
{ {
if (enabled == false) if (!enabled)
{ {
throw new QuickShareDisabledException("QuickShare is disabled system-wide"); throw new QuickShareDisabledException("QuickShare is disabled system-wide");
} }
} }
@SuppressWarnings("unchecked")
@Override @Override
public Map<String, Object> getMetaData(NodeRef nodeRef) public Map<String, Object> getMetaData(NodeRef nodeRef)
{ {
// TODO This functionality MUST be available when quickshare is also disabled, therefor refactor it out from the quickshare package to a more common package. // TODO This functionality MUST be available when quickshare is also disabled, therefor refactor it out from the quickshare package to a more common package.
Map<QName, Serializable> nodeProps = nodeService.getProperties(nodeRef); Map<QName, Serializable> nodeProps = nodeService.getProperties(nodeRef);
ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
String modifierUserName = (String)nodeProps.get(ContentModel.PROP_MODIFIER); String modifierUserName = (String)nodeProps.get(ContentModel.PROP_MODIFIER);
Map<QName, Serializable> personProps = null; Map<QName, Serializable> personProps = null;
if (modifierUserName != null) if (modifierUserName != null)
@@ -557,21 +539,21 @@ public class QuickShareServiceImpl implements QuickShareService,
} }
catch (NoSuchPersonException nspe) catch (NoSuchPersonException nspe)
{ {
// absorb this exception - eg. System (or maybe the user has been deleted) // absorb this exception - e.g. System (or maybe the user has been deleted)
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
{ {
logger.info("MetaDataGet - no such person: "+modifierUserName); logger.info("MetaDataGet - no such person: "+modifierUserName);
} }
} }
} }
Map<String, Object> metadata = new HashMap<String, Object>(8); Map<String, Object> metadata = new HashMap<>(8);
metadata.put("nodeRef", nodeRef.toString()); metadata.put("nodeRef", nodeRef.toString());
metadata.put("name", nodeProps.get(ContentModel.PROP_NAME)); metadata.put("name", nodeProps.get(ContentModel.PROP_NAME));
metadata.put("title", nodeProps.get(ContentModel.PROP_TITLE)); metadata.put("title", nodeProps.get(ContentModel.PROP_TITLE));
metadata.put("description", nodeProps.get(ContentModel.PROP_DESCRIPTION)); metadata.put("description", nodeProps.get(ContentModel.PROP_DESCRIPTION));
if (contentData != null) if (contentData != null)
{ {
metadata.put("mimetype", contentData.getMimetype()); metadata.put("mimetype", contentData.getMimetype());
@@ -581,17 +563,17 @@ public class QuickShareServiceImpl implements QuickShareService,
{ {
metadata.put("size", 0L); metadata.put("size", 0L);
} }
metadata.put("modified", nodeProps.get(ContentModel.PROP_MODIFIED)); metadata.put("modified", nodeProps.get(ContentModel.PROP_MODIFIED));
if (personProps != null) if (personProps != null)
{ {
metadata.put("modifierFirstName", personProps.get(ContentModel.PROP_FIRSTNAME)); metadata.put("modifierFirstName", personProps.get(ContentModel.PROP_FIRSTNAME));
metadata.put("modifierLastName", personProps.get(ContentModel.PROP_LASTNAME)); metadata.put("modifierLastName", personProps.get(ContentModel.PROP_LASTNAME));
} }
// thumbnail defs for this nodeRef // thumbnail defs for this nodeRef
List<String> thumbnailDefs = new ArrayList<String>(7); List<String> thumbnailDefs = new ArrayList<>(7);
if (contentData != null) if (contentData != null)
{ {
// Note: thumbnail defs only appear in this list if they can produce a thumbnail for the content // Note: thumbnail defs only appear in this list if they can produce a thumbnail for the content
@@ -604,18 +586,18 @@ public class QuickShareServiceImpl implements QuickShareService,
} }
} }
metadata.put("thumbnailDefinitions", thumbnailDefs); metadata.put("thumbnailDefinitions", thumbnailDefs);
// thumbnail instances for this nodeRef // thumbnail instances for this nodeRef
List<NodeRef> thumbnailRefs = thumbnailService.getThumbnails(nodeRef, ContentModel.PROP_CONTENT, null, null); List<NodeRef> thumbnailRefs = thumbnailService.getThumbnails(nodeRef, ContentModel.PROP_CONTENT, null, null);
List<String> thumbnailNames = new ArrayList<String>(thumbnailRefs.size()); List<String> thumbnailNames = new ArrayList<>(thumbnailRefs.size());
for (NodeRef thumbnailRef : thumbnailRefs) for (NodeRef thumbnailRef : thumbnailRefs)
{ {
thumbnailNames.add((String)nodeService.getProperty(thumbnailRef, ContentModel.PROP_NAME)); thumbnailNames.add((String)nodeService.getProperty(thumbnailRef, ContentModel.PROP_NAME));
} }
metadata.put("thumbnailNames", thumbnailNames); metadata.put("thumbnailNames", thumbnailNames);
metadata.put("lastThumbnailModificationData", (List<String>)nodeProps.get(ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA)); metadata.put("lastThumbnailModificationData", nodeProps.get(ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA));
if (nodeProps.containsKey(QuickShareModel.PROP_QSHARE_SHAREDID)) if (nodeProps.containsKey(QuickShareModel.PROP_QSHARE_SHAREDID))
{ {
metadata.put("sharedId", nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDID)); metadata.put("sharedId", nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDID));
@@ -629,7 +611,7 @@ public class QuickShareServiceImpl implements QuickShareService,
metadata.put("sharable", sharable); metadata.put("sharable", sharable);
} }
Map<String, Object> model = new HashMap<String, Object>(2); Map<String, Object> model = new HashMap<>(2);
model.put("item", metadata); model.put("item", metadata);
return model; return model;
} }
@@ -637,13 +619,8 @@ public class QuickShareServiceImpl implements QuickShareService,
@Override @Override
public Pair<String, NodeRef> getTenantNodeRefFromSharedId(final String sharedId) public Pair<String, NodeRef> getTenantNodeRefFromSharedId(final String sharedId)
{ {
NodeRef nodeRef = TenantUtil.runAsDefaultTenant(new TenantRunAsWork<NodeRef>() NodeRef nodeRef = TenantUtil.runAsDefaultTenant(
{ () -> (NodeRef) attributeService.getAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId));
public NodeRef doWork() throws Exception
{
return (NodeRef) attributeService.getAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId);
}
});
if (nodeRef == null) if (nodeRef == null)
{ {
@@ -659,7 +636,7 @@ public class QuickShareServiceImpl implements QuickShareService,
sp.setQuery(query); sp.setQuery(query);
sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
List<NodeRef> nodeRefs = null; List<NodeRef> nodeRefs;
ResultSet results = null; ResultSet results = null;
try try
{ {
@@ -697,23 +674,19 @@ public class QuickShareServiceImpl implements QuickShareService,
Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId); Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId);
final String tenantDomain = pair.getFirst(); final String tenantDomain = pair.getFirst();
final NodeRef nodeRef = pair.getSecond(); final NodeRef nodeRef = pair.getSecond();
Map<String, Object> model = TenantUtil.runAsSystemTenant(new TenantRunAsWork<Map<String, Object>>() Map<String, Object> model = TenantUtil.runAsSystemTenant(() -> {
{ checkQuickShareNode(nodeRef);
public Map<String, Object> doWork() throws Exception
{ return getMetaData(nodeRef);
checkQuickShareNode(nodeRef);
return getMetaData(nodeRef);
}
}, tenantDomain); }, tenantDomain);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("QuickShare - retrieved metadata: "+sharedId+" ["+nodeRef+"]["+model+"]"); logger.debug("QuickShare - retrieved metadata: "+sharedId+" ["+nodeRef+"]["+model+"]");
} }
//model.put("nodeRef", nodeRef) //model.put("nodeRef", nodeRef)
return model; return model;
} }
@@ -729,44 +702,40 @@ public class QuickShareServiceImpl implements QuickShareService,
// note: will remove "share" even if node is only being archived (ie. moved to trash) => a subsequent restore will *not* restore the "share" // note: will remove "share" even if node is only being archived (ie. moved to trash) => a subsequent restore will *not* restore the "share"
public void beforeDeleteNode(final NodeRef beforeDeleteNodeRef) public void beforeDeleteNode(final NodeRef beforeDeleteNodeRef)
{ {
AuthenticationUtil.runAsSystem(new RunAsWork<Void>() AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
{ String sharedId = (String)nodeService.getProperty(beforeDeleteNodeRef, QuickShareModel.PROP_QSHARE_SHAREDID);
public Void doWork() throws Exception if (sharedId != null)
{ {
String sharedId = (String)nodeService.getProperty(beforeDeleteNodeRef, QuickShareModel.PROP_QSHARE_SHAREDID); try
if (sharedId != null)
{ {
try Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId);
@SuppressWarnings("unused")
final String tenantDomain = pair.getFirst();
final NodeRef nodeRef = pair.getSecond();
// note: deleted nodeRef might not match, eg. for upload new version -> checkin -> delete working copy
if (nodeRef.equals(beforeDeleteNodeRef))
{ {
Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId); // Disable audit to preserve modifier and modified date
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
@SuppressWarnings("unused") try
final String tenantDomain = pair.getFirst();
final NodeRef nodeRef = pair.getSecond();
// note: deleted nodeRef might not match, eg. for upload new version -> checkin -> delete working copy
if (nodeRef.equals(beforeDeleteNodeRef))
{ {
// Disable audit to preserve modifier and modified date nodeService.removeAspect(nodeRef, QuickShareModel.ASPECT_QSHARE);
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
nodeService.removeAspect(nodeRef, QuickShareModel.ASPECT_QSHARE);
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
removeSharedId(sharedId);
} }
} finally
catch (InvalidSharedIdException ex) {
{ behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
logger.warn("Couldn't find shareId, " + sharedId + ", attributes for node " + beforeDeleteNodeRef); }
removeSharedId(sharedId);
} }
} }
return null; catch (InvalidSharedIdException ex)
{
logger.warn("Couldn't find shareId, " + sharedId + ", attributes for node " + beforeDeleteNodeRef);
}
} }
return null;
}); });
} }
@@ -778,37 +747,29 @@ public class QuickShareServiceImpl implements QuickShareService,
public void onRestoreNode(ChildAssociationRef childAssocRef) public void onRestoreNode(ChildAssociationRef childAssocRef)
{ {
final NodeRef childNodeRef = childAssocRef.getChildRef(); final NodeRef childNodeRef = childAssocRef.getChildRef();
AuthenticationUtil.runAsSystem(new RunAsWork<Void>() AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
{ if (nodeService.hasAspect(childNodeRef, QuickShareModel.ASPECT_QSHARE))
public Void doWork() throws Exception
{ {
if (nodeService.hasAspect(childNodeRef, QuickShareModel.ASPECT_QSHARE)) // Disable audit to preserve modifier and modified date
behaviourFilter.disableBehaviour(childNodeRef, ContentModel.ASPECT_AUDITABLE);
try
{ {
// Disable audit to preserve modifier and modified date nodeService.removeAspect(childNodeRef, QuickShareModel.ASPECT_QSHARE);
behaviourFilter.disableBehaviour(childNodeRef, ContentModel.ASPECT_AUDITABLE); }
try finally
{ {
nodeService.removeAspect(childNodeRef, QuickShareModel.ASPECT_QSHARE); behaviourFilter.enableBehaviour(childNodeRef, ContentModel.ASPECT_AUDITABLE);
}
finally
{
behaviourFilter.enableBehaviour(childNodeRef, ContentModel.ASPECT_AUDITABLE);
}
} }
return null;
} }
return null;
}); });
} }
private void removeSharedId(final String sharedId) private void removeSharedId(final String sharedId)
{ {
TenantUtil.runAsDefaultTenant(new TenantRunAsWork<Void>() TenantUtil.runAsDefaultTenant((TenantRunAsWork<Void>) () -> {
{ attributeService.removeAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId);
public Void doWork() throws Exception return null;
{
attributeService.removeAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId);
return null;
}
}); });
try try
@@ -832,40 +793,36 @@ public class QuickShareServiceImpl implements QuickShareService,
Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId); Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId);
final String tenantDomain = pair.getFirst(); final String tenantDomain = pair.getFirst();
final NodeRef nodeRef = pair.getSecond(); final NodeRef nodeRef = pair.getSecond();
TenantUtil.runAsSystemTenant(new TenantRunAsWork<Void>()
{
public Void doWork() throws Exception
{
QName typeQName = nodeService.getType(nodeRef);
if (! isSharable(typeQName))
{
throw new InvalidNodeRefException(nodeRef);
}
String nodeSharedId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDID);
if (! EqualsHelper.nullSafeEquals(nodeSharedId, sharedId))
{
logger.warn("SharedId mismatch: expected="+sharedId+",actual="+nodeSharedId);
}
// Disable audit to preserve modifier and modified date TenantUtil.runAsSystemTenant((TenantRunAsWork<Void>) () -> {
// And not to create version QName typeQName = nodeService.getType(nodeRef);
// see MNT-15654 if (! isSharable(typeQName))
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); {
try throw new InvalidNodeRefException(nodeRef);
{
nodeService.removeAspect(nodeRef, QuickShareModel.ASPECT_QSHARE);
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
return null;
} }
String nodeSharedId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDID);
if (! EqualsHelper.nullSafeEquals(nodeSharedId, sharedId))
{
logger.warn("SharedId mismatch: expected="+sharedId+",actual="+nodeSharedId);
}
// Disable audit to preserve modifier and modified date
// And not to create version
// see MNT-15654
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
nodeService.removeAspect(nodeRef, QuickShareModel.ASPECT_QSHARE);
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
return null;
}, tenantDomain); }, tenantDomain);
removeSharedId(sharedId); removeSharedId(sharedId);
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
@@ -891,23 +848,19 @@ public class QuickShareServiceImpl implements QuickShareService,
Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId); Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId);
final String tenantDomain = pair.getFirst(); final String tenantDomain = pair.getFirst();
final NodeRef nodeRef = pair.getSecond(); final NodeRef nodeRef = pair.getSecond();
return TenantUtil.runAsTenant(new TenantRunAsWork<Boolean>() return TenantUtil.runAsTenant(() -> {
{ try
public Boolean doWork() throws Exception
{ {
try checkQuickShareNode(nodeRef);
{ return permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED;
checkQuickShareNode(nodeRef); }
return permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED; catch (AccessDeniedException ex)
} {
catch (AccessDeniedException ex) return false;
{
return false;
}
} }
}, tenantDomain); }, tenantDomain);
} }
@Override @Override
@@ -934,18 +887,18 @@ 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
Map<String, Serializable> actionParams = new HashMap<>(); Map<String, Serializable> actionParams = new HashMap<>();
// Email sender. By default the current-user's email address will not be used to send this mail. // Email sender. By default, the current-user's email address will not be used to send this mail.
// However, current-user's first and lastname will be used as the personal name. // However, current-user's first and lastname will be used as the personal name.
actionParams.put(MailActionExecuter.PARAM_FROM, this.defaultEmailSender); actionParams.put(MailActionExecuter.PARAM_FROM, this.defaultEmailSender);
actionParams.put(MailActionExecuter.PARAM_FROM_PERSONAL_NAME, senderFullName); actionParams.put(MailActionExecuter.PARAM_FROM_PERSONAL_NAME, senderFullName);
@@ -981,21 +934,21 @@ public class QuickShareServiceImpl implements QuickShareService,
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
String siteName = getSiteName(nodeRef); String siteName = getSiteName(nodeRef);
boolean isSharedByCurrentUser = currentUser.equals(sharedByUserId); boolean isSharedByCurrentUser = Objects.equals(currentUser, sharedByUserId);
if (siteName != null) if (siteName != null)
{ {
// node belongs to a site - current user must be a manager or collaborator or someone who shared the link // node belongs to a site - current user must be a manager or collaborator or someone who shared the link
String role = siteService.getMembersRole(siteName, currentUser); String role = siteService.getMembersRole(siteName, currentUser);
if (isSharedByCurrentUser || (role != null && (role.equals(SiteModel.SITE_MANAGER) || role.equals(SiteModel.SITE_COLLABORATOR))) if (isSharedByCurrentUser || (role != null && (role.equals(SiteModel.SITE_MANAGER) || role.equals(SiteModel.SITE_COLLABORATOR)))
|| (authorityService.isAdminAuthority(currentUser))) || (authorityService.isAdminAuthority(currentUser)))
{ {
canDeleteSharedLink = true; canDeleteSharedLink = true;
} }
} }
else if (isSharedByCurrentUser || (authorityService.isAdminAuthority(currentUser))) else if (isSharedByCurrentUser || (authorityService.isAdminAuthority(currentUser)))
{ {
// node does not belongs to a site - current user must be the person who shared the link or an admin // node does not belong to a site - current user must be the person who shared the link or an admin
canDeleteSharedLink = true; canDeleteSharedLink = true;
} }
@@ -1015,11 +968,7 @@ public class QuickShareServiceImpl implements QuickShareService,
while (parent != null && !nodeService.getType(parent).equals(SiteModel.TYPE_SITE)) while (parent != null && !nodeService.getType(parent).equals(SiteModel.TYPE_SITE))
{ {
// check that we can read parent name // check that we can read parent name
if (permissionService.hasReadPermission(parent) == AccessStatus.ALLOWED) if (!(permissionService.hasReadPermission(parent) == AccessStatus.ALLOWED))
{
String parentName = (String) nodeService.getProperty(parent,ContentModel.PROP_NAME);
}
else
{ {
return null; return null;
} }
@@ -1048,8 +997,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)
@@ -1134,7 +1082,7 @@ public class QuickShareServiceImpl implements QuickShareService,
{ {
if (toEmails != null) if (toEmails != null)
{ {
this.toEmails = Collections.unmodifiableSet(new HashSet<>(toEmails)); this.toEmails = Set.copyOf(toEmails);
} }
} }
@@ -1239,7 +1187,7 @@ public class QuickShareServiceImpl implements QuickShareService,
} }
final NodeRef expiryActionNodeRef = getQuickShareLinkExpiryActionNode(sharedId); final NodeRef expiryActionNodeRef = getQuickShareLinkExpiryActionNode(sharedId);
// If an expiry action already exists for the specified shared Id, first remove it, before creating a new one. // If an expiry action already exists for the specified sharedId, first remove it, before creating a new one.
if (expiryActionNodeRef != null) if (expiryActionNodeRef != null)
{ {
deleteQuickShareLinkExpiryAction(expiryActionNodeRef); deleteQuickShareLinkExpiryAction(expiryActionNodeRef);
@@ -1247,11 +1195,11 @@ public class QuickShareServiceImpl implements QuickShareService,
// Create the expiry action // Create the expiry action
final QuickShareLinkExpiryAction expiryAction = new QuickShareLinkExpiryActionImpl(java.util.UUID.randomUUID().toString(), sharedId, final QuickShareLinkExpiryAction expiryAction = new QuickShareLinkExpiryActionImpl(java.util.UUID.randomUUID().toString(), sharedId,
"QuickShare link expiry action"); "QuickShare link expiry action");
// Create the persisted schedule // Create the persisted schedule
final ScheduledPersistedAction schedule = scheduledPersistedActionService.createSchedule(expiryAction); final ScheduledPersistedAction schedule = scheduledPersistedActionService.createSchedule(expiryAction);
// first set the scheduledAction so we can set the other information // first set the scheduledAction, so we can set the other information
expiryAction.setSchedule(schedule); expiryAction.setSchedule(schedule);
expiryAction.setScheduleStart(expiryDate); expiryAction.setScheduleStart(expiryDate);
@@ -1273,7 +1221,7 @@ public class QuickShareServiceImpl implements QuickShareService,
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Quick share link expiry action is created for sharedId[" + sharedId + "] and it's scheduled to be executed on: " logger.debug("Quick share link expiry action is created for sharedId[" + sharedId + "] and it's scheduled to be executed on: "
+ expiryDate); + expiryDate);
} }
} }
@@ -1298,7 +1246,7 @@ public class QuickShareServiceImpl implements QuickShareService,
// Delete the expiry action and its related persisted schedule // Delete the expiry action and its related persisted schedule
deleteQuickShareLinkExpiryActionImpl(linkExpiryAction); deleteQuickShareLinkExpiryActionImpl(linkExpiryAction);
// As the method is called directly (ie. not via unshareContent method which removes the aspect properties), // As the method is called directly (i.e. not via unshareContent method which removes the aspect properties),
// then we have to remove the 'expiryDate' property as well. // then we have to remove the 'expiryDate' property as well.
if (sharedNodeRef != null && nodeService.getProperty(sharedNodeRef, QuickShareModel.PROP_QSHARE_EXPIRY_DATE) != null) if (sharedNodeRef != null && nodeService.getProperty(sharedNodeRef, QuickShareModel.PROP_QSHARE_EXPIRY_DATE) != null)
{ {
@@ -1342,7 +1290,7 @@ public class QuickShareServiceImpl implements QuickShareService,
quickShareLinkExpiryActionPersister.deleteQuickShareLinkExpiryAction(linkExpiryAction); quickShareLinkExpiryActionPersister.deleteQuickShareLinkExpiryAction(linkExpiryAction);
} }
private QuickShareLinkExpiryAction attachSchedule(QuickShareLinkExpiryAction quickShareLinkExpiryAction) private void attachSchedule(QuickShareLinkExpiryAction quickShareLinkExpiryAction)
{ {
if (quickShareLinkExpiryAction.getSchedule() == null) if (quickShareLinkExpiryAction.getSchedule() == null)
{ {
@@ -1350,7 +1298,6 @@ public class QuickShareServiceImpl implements QuickShareService,
quickShareLinkExpiryAction.setSchedule(schedule); quickShareLinkExpiryAction.setSchedule(schedule);
} }
return quickShareLinkExpiryAction;
} }
private NodeRef getQuickShareLinkExpiryActionNode(String sharedId) private NodeRef getQuickShareLinkExpiryActionNode(String sharedId)
@@ -1381,54 +1328,53 @@ public class QuickShareServiceImpl implements QuickShareService,
private enum ExpiryDatePeriod private enum ExpiryDatePeriod
{ {
DAYS DAYS
{ {
@Override @Override
int getDuration(DateTime now, DateTime expiryDate) int getDuration(DateTime now, DateTime expiryDate)
{ {
Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate); Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.days()).getDays(); return interval.toPeriod(PeriodType.days()).getDays();
} }
@Override @Override
String getMessage() String getMessage()
{ {
return "day (24 hours)"; return "day (24 hours)";
} }
}, },
HOURS HOURS
{ {
@Override @Override
int getDuration(DateTime now, DateTime expiryDate) int getDuration(DateTime now, DateTime expiryDate)
{ {
Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate); Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.hours()).getHours(); return interval.toPeriod(PeriodType.hours()).getHours();
} }
@Override @Override
String getMessage() String getMessage()
{ {
return "hour"; return "hour";
} }
}, },
MINUTES MINUTES
{ {
@Override @Override
public int getDuration(DateTime now, DateTime expiryDate) public int getDuration(DateTime now, DateTime expiryDate)
{ {
Interval interval = new Interval(now.withMillisOfSecond(0), expiryDate); Interval interval = new Interval(now.withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.minutes()).getMinutes(); return interval.toPeriod(PeriodType.minutes()).getMinutes();
} }
@Override @Override
String getMessage() String getMessage()
{ {
return "minute"; return "minute";
} }
}; };
abstract int getDuration(DateTime now, DateTime expiryDate); abstract int getDuration(DateTime now, DateTime expiryDate);
abstract String getMessage(); abstract String getMessage();
} }
} }

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/logo 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/logo within the email
repo.client-app.workspace.templateAssetsUrl=${alfrescoUrl}/images repo.client-app.workspace.templateAssetsUrl=${repoBaseUrl}/alfresco/images/logo
# 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

@@ -0,0 +1,428 @@
<html>
<head>
<style type="text/css">
td {
font-family: 'Helvetica Neue', Arial, sans-serif;
}
body {
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
width: 100% ! important;
height: 100% !important;
color: #727174;
font-weight: 400;
font-size: 18px;
margin: 0;
}
h1 {
margin: 10px 0;
}
h2 {
color: #727174;
font-weight: 600;
font-size: 22px;
}
a {
color: #0c79bf;
text-decoration: underline;
}
a.linkone {
color: #0c79bf;
text-decoration: underline;
}
.appleLinks a {
color: #0c79bf;
}
.appleLinksWhite a {
color: #0c79bf;
}
.force-full-width {
width: 100% !important;
}
.body-padding {
padding: 0 75px
}
.force-width-80 {
width: 80% !important;
}
p {
Margin-bottom: 1em;
}
.button {
text-align: center;
font-size: 18px;
font-family: sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: none;
}
.button a {
color: #ffffff;
text-decoration: none;
}
img {
max-width: 600px;
outline: none;
text-decoration: none;
-ms-interpolation-mode: bicubic;
}
a img {
border: none;
}
table {
border-collapse: collapse;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
#outlook a {
padding: 0;
}
.ReadMsgBody {
width: 100%;
}
.ExternalClass {
width: 100%;
}
.backgroundTable {
margin: 0 auto;
padding: 0;
width: 100% !important;
}
table td {
border-collapse: collapse;
}
.ExternalClass * {
line-height: 115%;
}
.ExternalClass {
vertical-align: middle
}
/*]]>*/
</style>
<style type="text/css" media="screen">
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { {
@-ms-viewport {
width: 320px;
}
@viewport {
width: 320px;
}
}
</style>
<style type="text/css" media="screen">
@media screen {
* {
font-family: 'Helvetica Neue', 'Arial', 'sans-serif' !important;
}
.w280 {
width: 280px !important;
}
}
</style>
<style type="text/css" media="only screen and (max-width: 480px)">
@media only screen and (max-width: 480px) {
.full {
display: block;
width: 100%;
}
table[class*="w320"] {
width: 320px !important;
}
td[class*="w320"] {
width: 280px !important;
padding-left: 20px !important;
padding-right: 20px !important;
}
img[class*="w320"] {
width: 250px !important;
height: 67px !important;
}
td[class*="mobile-spacing"] {
padding-top: 10px !important;
padding-bottom: 10px !important;
}
*[class*="mobile-hide"] {
display: none !important;
}
*[class*="mobile-br"] {
font-size: 8px !important;
}
*[class*="mobile-brh"] {
font-size: 1px !important;
}
td[class*="mobile-w20"] {
width: 20px !important;
}
img[class*="mobile-w20"] {
width: 20px !important;
}
td[class*="mobile-center"] {
text-align: center !important;
}
table[class*="w100p"] {
width: 100% !important;
}
td[class*="activate-now"] {
padding-right: 0 !important;
padding-top: 20px !important;
}
td[class*="mobile-resize"] {
font-size: 22px !important;
padding-left: 15px !important;
}
}
</style>
</head>
<body offset="0" class="body" style="padding:0; margin:0; display:block; background:#f3f4f4; -webkit-text-size-adjust:none; " bgcolor="#EEEEEE">
<table align="center" cellpadding="0" cellspacing="0" width="100%" height="100%">
<tr>
<td align="center" valign="top" style="background-color:#f3f4f4; " width="100%">
<table cellspacing="0" cellpadding="0" width="600" class="w320">
<tr>
<td align="center" valign="top">
<!--Snippet Block-->
<table border="0" cellspacing="0" cellpadding="0" align="center" width="100%">
<tr>
<td>
<table border="0" cellspacing="0" cellpadding="10" width="100%" align="center">
<tbody>
<tr>
<td align="left">
<table align="center" width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="right">
<div style="display:none;font-size:1px;color:#333333;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;">
-
</div>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<!--Header Block-->
<table bgcolor="#FFFFFF" border="0" cellspacing="0" cellpadding="0" align="center" width="100%"
style="border-left:solid 1px #dedee4; border-right:solid 1px #dedee4; border-bottom:solid 1px #dedee4; border-top:solid 1px #dedee4; width:100%!important; min-width:100%;">
<tr style="width:100%;">
<td>
<table border="0" cellspacing="0" cellpadding="10" align="center" width="100%">
<tr>
<td align="left" valign="middle"><a style="color: #ffffff; text-decoration: none;" href=
"https://www.alfresco.com/" target="_blank"
><span style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; color: #727174;"><img
style="border:none; display:block; border-collapse:collapse; outline:none; text-decoration:none;"
src="${template_assets_url}/hyland_logo.png" border="0"
alt="Alfresco" width="122" height="38"></span></a></td>
</tr>
</table>
</td>
<td align="right" bgcolor="#FFFFFF">
<table border="0" cellspacing="0" cellpadding="10" align="center" width="100%">
<tr>
<td align="right" valign="middle"><span
style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; font-weight:200; color: #727174;">Digital Workspace</span>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!--Break-->
<table width="100%">
<tr>
<td height="10">
<div class="mobile-br">&nbsp;</div>
</td>
</tr>
</table>
<!--Body Block 1-->
<!--Banner Block-->
<table cellspacing="0" cellpadding="0" width="100%" style="border-left:solid 1px #dedee4; border-right:solid 1px #dedee4; ">
<tr>
<td style="border-collapse:collapse; ">
<table cellspacing="0" cellpadding="0" width="100%" style="background-color:#0c79bf; border:none; ">
<tr>
<td style="border-collapse:collapse; ">
<div class="mktEditable" id="Banner Image 1"><span
style="font-family:'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; color: #ffffff;"><img
src="${template_assets_url}/adw_logo.png" alt="Alfresco Products" style="width:100%; border:none;
display:block; border-collapse:collapse; outline:none; text-decoration:none;"></span></div>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!--Copy-->
<table cellspacing="0" cellpadding="0" align="center" bgcolor="#FFFFFF"
style="border-left:solid 1px #dedee4; border-right:solid 1px #dedee4; border-bottom:solid 1px #dedee4; " width="100%">
<tr align="center">
<td style="background-color:#ffffff; " align="center">
<table class="force-width-80" cellspacing="0" cellpadding="12" align="center" bgcolor="#FFFFFF" width="80%">
<tbody>
<tr>
<td align="left">
<!--Headline Text-->
<div class="mktEditable" id="Headline Text 1" align="left" style="vertical-align:middle; "><span
style="color: #727174; margin: 0px; font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 22px; font-weight: 600; text-align: left; text-decoration: none; vertical-align:
middle;"><p>${message("templates.reset-password-email.ftl.title")}</p></span></div>
<div class="mktEditable" id="Body Text 1" align="left">
<span style="color:#727174; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:18px;font-weight:400; text-align:left; text-decoration:none; -webkit-text-size-adjust:none;">
<p>${message("templates.reset-password-email.ftl.detail")}</p>
</span>
</div>
<!--Break-->
<table width="100%">
<tr>
<td height="5">
<div class="mobile-br">&nbsp;</div>
</td>
</tr>
</table>
<!--Button-->
<table style="margin:0 auto; " cellspacing="0" cellpadding="0" width="100%">
<tr>
<td style="text-align:center; margin:0 auto; ">
<div><!--[if mso]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="urn:schemas-microsoft-com:office:word"
style="height:45px;v-text-anchor:middle;width:220px;" stroke="f"
fillcolor="#47AA2">
<w:anchorlock/>
<center>
<![endif]-->
<div class="mktEditable" id="Button Text"><a href="${reset_password_url}" style="background-color:#47aa42; color:#ffffff; display:inline-block; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:18px; font-weight:400; line-height:45px; text-align:center; text-decoration:none; width:220px;
-webkit-text-size-adjust:none;">${message("templates.reset-password-email.ftl.reset_password_button")}</a></div>
<span style="color:#727174; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:12px;font-weight:400; text-align:left; text-decoration:none; -webkit-text-size-adjust:none;">
<p>${message("templates.reset-password-email.ftl.ignore_message")}</p>
<span style="color:#727174; font-family:Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size:12px;font-weight:400; text-align:left; text-decoration:none; -webkit-text-size-adjust:none;">
<hr>
<p>${message("templates.reset-password-email.ftl.having_trouble_clicking_button")}</p>
<p><a href="${reset_password_url}">${reset_password_url}</a></p>
</span>
<!--[if mso]>
</center>
</v:rect>
<![endif]--></div>
</td>
</tr>
</table>
<!--Break-->
<table width="100%">
<tr>
<td height="5">
<div class="mobile-br">&nbsp;</div>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
<!--Break-->
<table width="100%">
<tr>
<td height="10">
<div class="mobile-br">&nbsp;</div>
</td>
</tr>
</table>
<!--Footer Block-->
<table border="0" cellspacing="0" cellpadding="0" align="center" width="100%">
<tr>
<td align="center">
<a style="color: #0c79bf; text-decoration: underline;" href="http://www.alfresco.com/company/contact"
target="_blank"><span
style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10px; color: #0c79bf;">${message("templates.generic-email.ftl.contact_us")}</span></a>
<span style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial,
sans-serif; font-size: 10px; color: #b3b3b8;">&copy; ${date?string["yyyy"]} Alfresco Software, Inc.
${message("templates.generic-email.ftl.copy_right")}</span><br>
<span style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10px; color: #b3b3b8;">Bridge Ave, The Place Maidenhead SL6 1AF United Kingdom</span><br>
<span style="font-family: Gotham, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 10px; color: #b3b3b8;">1825 S Grant St, Suite 900 San Mateo, CA 94402 USA</span><br>
</td>
</tr>
</table>
<!--Break-->
<table width="100%">
<tr>
<td height="10">
<div class="mobile-br">&nbsp;</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!--Litmus Tracking-->
<table style="display: none;">
<tbody>
<tr>
<td style="width: 0px; display: none; overflow: hidden; max-height: 0px;">{{my.Litmus_Code}}</td>
</tr>
</tbody>
</table>
</body>
</html>

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);