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
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,10 +26,12 @@
package org.alfresco.repo.client.config;
import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
@@ -41,6 +43,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
/**
* This class picks up all the loaded properties passed to it and uses a naming
@@ -59,25 +62,35 @@ import java.util.concurrent.ConcurrentMap;
* for example, <i>sharedLinkBaseUrl</i> and <i>templateAssetsUrl</i> properties, then the following
* needs to be put into a properties file.
* <ul>
* <li>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.sharedLinkBaseUrl=http://localhost:8080/MyClientName/s}</li>
* <li>{@code repo.client-app.MyClientName.templateAssetsUrl=http://localhost:8080/MyClientName/assets}</li>
* </ul>
* 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.
* <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
*/
public class ClientAppConfig extends AbstractLifecycleBean
{
private static final Log logger = LogFactory.getLog(ClientAppConfig.class);
private static final Logger LOGGER = LoggerFactory.getLogger(ClientAppConfig.class);
public static final String PREFIX = "repo.client-app.";
public static final String PROP_TEMPLATE_ASSETS_URL = "templateAssetsUrl";
public static final String SHARE_PLACEHOLDER = "${shareUrl}";
private Properties defaultProperties;
private Properties globalProperties;
private ConcurrentMap<String, ClientApp> clients = new ConcurrentHashMap<>();
private final ConcurrentMap<String, ClientApp> clients = new ConcurrentHashMap<>();
public ClientAppConfig()
{
@@ -117,6 +130,17 @@ public class ClientAppConfig extends AbstractLifecycleBean
return clients.get(name);
}
/**
* Checks whether a client with the given name exists or not.
*
* @param name the name of the client to check
* @return true if the client with the given name exists; false otherwise
*/
public boolean exists(String name)
{
return clients.containsKey(name);
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
@@ -127,9 +151,9 @@ public class ClientAppConfig extends AbstractLifecycleBean
processPropertyKeys(mergedProperties, clientsNames, propsNames);
clients.putAll(processClients(clientsNames, propsNames, mergedProperties));
if (logger.isDebugEnabled())
if (LOGGER.isDebugEnabled())
{
logger.debug("All bootstrapped repo clients apps: " + clients);
LOGGER.debug("All bootstrapped repo clients apps: " + clients);
}
}
@@ -232,7 +256,7 @@ public class ClientAppConfig extends AbstractLifecycleBean
}
if (StringUtils.isEmpty(templateAssetsUrl) && config.isEmpty())
{
logger.warn("Client-app [" + clientName + "] can not be registered as it needs at least one property with a valid value.");
LOGGER.warn("Client-app [" + clientName + "] can not be registered as it needs at least one property with a valid value.");
continue;
}
// As the required values are valid, create the client data
@@ -275,7 +299,7 @@ public class ClientAppConfig extends AbstractLifecycleBean
private void logMalformedPropertyKey(String propName, String reason)
{
reason = (StringUtils.isBlank(reason)) ? "" : " " + reason;
logger.warn("Ignoring client app config (malformed property key) [" + propName + "]." + reason);
LOGGER.warn("Ignoring client app config (malformed property key) [" + propName + "]." + reason);
}
private void logMalformedPropertyKey(String propName)
@@ -285,9 +309,9 @@ public class ClientAppConfig extends AbstractLifecycleBean
private void logInvalidPropertyValue(String propName, String propValue)
{
if (logger.isDebugEnabled())
if (LOGGER.isDebugEnabled())
{
logger.debug("Ignoring client app config (invalid value) [" + propValue + "] for the property:" + propName);
LOGGER.debug("Ignoring client app config (invalid value) [" + propValue + "] for the property:" + propName);
}
}
@@ -298,13 +322,18 @@ public class ClientAppConfig extends AbstractLifecycleBean
public static class ClientApp
{
private static final Logger LOGGER = LoggerFactory.getLogger(ClientApp.class);
private final String name;
private final String clientUrlPropKey;
private final Pattern clientUrlPlaceholderPattern;
private final String templateAssetsUrl;
private final Map<String, String> properties;
public ClientApp(String name, String templateAssetsUrl, Map<String, String> properties)
{
this.name = name;
this.clientUrlPropKey = name + "Url"; // E.g. 'workspaceUrl' in 'repo.client-app.workspace.workspaceUrl'
this.clientUrlPlaceholderPattern = Pattern.compile("\\$\\{" + clientUrlPropKey + '}'); // E.g. ${workspaceUrl}
this.templateAssetsUrl = templateAssetsUrl;
this.properties = new HashMap<>(properties);
}
@@ -314,11 +343,81 @@ public class ClientAppConfig extends AbstractLifecycleBean
return name;
}
public String getClientUrlPropKey()
{
return clientUrlPropKey;
}
public String getClientUrl()
{
String url = properties.get(getClientUrlPropKey());
if (StringUtils.isEmpty(url) && "share".equals(name))
{
return SHARE_PLACEHOLDER;
}
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()
{
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()
{
return Collections.unmodifiableMap(properties);
@@ -329,6 +428,27 @@ public class ClientAppConfig extends AbstractLifecycleBean
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
public boolean equals(Object o)
{
@@ -356,6 +476,8 @@ public class ClientAppConfig extends AbstractLifecycleBean
{
final StringBuilder sb = new StringBuilder(250);
sb.append("ClientApp [name=").append(name)
.append(", clientUrlPropKey=").append(clientUrlPropKey)
.append(", clientUrlPlaceholderPattern=").append(clientUrlPlaceholderPattern.pattern())
.append(", templateAssetsUrl=").append(templateAssetsUrl)
.append(", properties=").append(properties)
.append(']');

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -1875,7 +1875,6 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
* @param siteName
* @param role
* @param runAsUser
* @param siteService
* @param overrideExisting
*/
public void addSiteMembership(final String invitee, final String siteName, final String role, final String runAsUser, final boolean overrideExisting)
@@ -2111,11 +2110,11 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_RESOURCE_TYPE, resourceType.toString());
workflowProps.put(WorkflowModelModeratedInvitation.WF_PROP_CLIENT_NAME, clientName);
if(clientName != null && clientAppConfig.getClient(clientName) != null)
if(clientName != null && clientAppConfig.exists(clientName))
{
ClientAppConfig.ClientApp client = clientAppConfig.getClient(clientName);
workflowProps.put(WorkflowModelModeratedInvitation.WF_TEMPLATE_ASSETS_URL, client.getTemplateAssetsUrl());
workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, client.getProperty("workspaceUrl"));
workflowProps.put(WorkflowModelModeratedInvitation.WF_TEMPLATE_ASSETS_URL, client.getResolvedTemplateAssetsUrl(sysAdminParams));
workflowProps.put(WorkflowModelModeratedInvitation.WF_WORKSPACE_URL, client.getResolvedClientUrl(sysAdminParams));
}
// get the moderated workflow

View File

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

View File

@@ -2,23 +2,23 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2023 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
@@ -28,17 +28,15 @@ package org.alfresco.repo.quickshare;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.alfresco.sync.events.types.ActivityEvent;
import org.alfresco.sync.events.types.Event;
import org.alfresco.model.ContentModel;
import org.alfresco.model.QuickShareModel;
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.CopyServicePolicies;
import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback;
import org.alfresco.sync.repo.events.EventPreparator;
import org.alfresco.sync.repo.events.EventPublisher;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.BehaviourFilter;
@@ -101,7 +98,6 @@ import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.UrlUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -113,16 +109,16 @@ import org.safehaus.uuid.UUIDGenerator;
/**
* QuickShare Service implementation.
*
* <p>
* In addition to the quick share service, this class also provides a BeforeDeleteNodePolicy and
* OnCopyNodePolicy for content with the QuickShare aspect.
*
* @author Alex Miller, janv, Jamal Kaabi-Mofrad
*/
public class QuickShareServiceImpl implements QuickShareService,
NodeServicePolicies.BeforeDeleteNodePolicy,
CopyServicePolicies.OnCopyNodePolicy,
NodeServicePolicies.OnRestoreNodePolicy
NodeServicePolicies.BeforeDeleteNodePolicy,
CopyServicePolicies.OnCopyNodePolicy,
NodeServicePolicies.OnRestoreNodePolicy
{
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_LAST_NAME = "sender_last_name";
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 DEFAULT_EMAIL_SUBJECT = "quickshare.notifier.email.subject";
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)
{
this.dictionaryService = dictionaryService;
this.dictionaryService = dictionaryService;
}
/**
* Set the node service
*/
@@ -195,15 +190,15 @@ public class QuickShareServiceImpl implements QuickShareService,
{
this.permissionService = permissionService;
}
/**
* Set the person service
* Set the person service
*/
public void setPersonService(PersonService personService)
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* Set the policy component
*/
@@ -215,11 +210,11 @@ public class QuickShareServiceImpl implements QuickShareService,
/**
* Set the tenant service
*/
public void setTenantService(TenantService tenantService)
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
/**
* Set the thumbnail service
*/
@@ -227,7 +222,7 @@ public class QuickShareServiceImpl implements QuickShareService,
{
this.thumbnailService = thumbnailService;
}
/**
* Set the eventPublisher
*/
@@ -394,14 +389,14 @@ public class QuickShareServiceImpl implements QuickShareService,
// Register interest in the beforeDeleteNode policy - note: currently for content only !!
policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
ContentModel.TYPE_CONTENT,
new JavaBehaviour(this, "beforeDeleteNode"));
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
ContentModel.TYPE_CONTENT,
new JavaBehaviour(this, "beforeDeleteNode"));
//Register interest in the onCopyNodePolicy to block copying of quick share metadta
policyComponent.bindClassBehaviour(
CopyServicePolicies.OnCopyNodePolicy.QNAME,
QuickShareModel.ASPECT_QSHARE,
CopyServicePolicies.OnCopyNodePolicy.QNAME,
QuickShareModel.ASPECT_QSHARE,
new JavaBehaviour(this, "getCopyCallback"));
this.policyComponent.bindClassBehaviour(
@@ -424,7 +419,7 @@ public class QuickShareServiceImpl implements QuickShareService,
//Check the node is the correct type
final QName typeQName = nodeService.getType(nodeRef);
if (isSharable(typeQName) == false)
if (!isSharable(typeQName))
{
throw new InvalidNodeRefException(nodeRef);
}
@@ -438,7 +433,7 @@ public class QuickShareServiceImpl implements QuickShareService,
UUID uuid = UUIDGenerator.getInstance().generateRandomBasedUUID();
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_SHAREDBY, AuthenticationUtil.getRunAsUser());
@@ -448,13 +443,9 @@ public class QuickShareServiceImpl implements QuickShareService,
try
{
// consumer/contributor should be able to add "shared" aspect (MNT-10366)
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
public Void doWork()
{
nodeService.addAspect(nodeRef, QuickShareModel.ASPECT_QSHARE, props);
return null;
}
AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
nodeService.addAspect(nodeRef, QuickShareModel.ASPECT_QSHARE, props);
return null;
});
}
finally
@@ -464,27 +455,19 @@ public class QuickShareServiceImpl implements QuickShareService,
final NodeRef tenantNodeRef = tenantService.getName(nodeRef);
TenantUtil.runAsDefaultTenant(new TenantRunAsWork<Void>()
{
public Void doWork() throws Exception
{
attributeService.setAttribute(tenantNodeRef, ATTR_KEY_SHAREDIDS_ROOT, sharedId);
return null;
}
TenantUtil.runAsDefaultTenant((TenantRunAsWork<Void>) () -> {
attributeService.setAttribute(tenantNodeRef, ATTR_KEY_SHAREDIDS_ROOT, sharedId);
return null;
});
final StringBuffer sb = new StringBuffer();
sb.append("{").append("\"sharedId\":\"").append(sharedId).append("\"").append("}");
String sharedIdStr = "{" + "\"sharedId\":\"" + sharedId + "\"" + "}";
eventPublisher.publishEvent(new EventPreparator(){
@Override
public Event prepareEvent(String user, String networkId, String transactionId)
{
return new ActivityEvent("quickshare", transactionId, networkId, user, nodeRef.getId(),
null, typeQName.toString(), Client.asType(ClientType.webclient), sb.toString(),
null, null, 0l, null);
}
});
eventPublisher.publishEvent(
(user, networkId, transactionId) -> new ActivityEvent("quickshare", transactionId, networkId,
user, nodeRef.getId(), null,
typeQName.toString(),
Client.asType(ClientType.webclient),
sharedIdStr, null, null, 0L, null));
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.
*/
private void checkEnabled()
{
if (enabled == false)
if (!enabled)
{
throw new QuickShareDisabledException("QuickShare is disabled system-wide");
}
}
@SuppressWarnings("unchecked")
@Override
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.
Map<QName, Serializable> nodeProps = nodeService.getProperties(nodeRef);
ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
String modifierUserName = (String)nodeProps.get(ContentModel.PROP_MODIFIER);
Map<QName, Serializable> personProps = null;
if (modifierUserName != null)
@@ -557,21 +539,21 @@ public class QuickShareServiceImpl implements QuickShareService,
}
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())
{
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("name", nodeProps.get(ContentModel.PROP_NAME));
metadata.put("title", nodeProps.get(ContentModel.PROP_TITLE));
metadata.put("description", nodeProps.get(ContentModel.PROP_DESCRIPTION));
if (contentData != null)
{
metadata.put("mimetype", contentData.getMimetype());
@@ -581,17 +563,17 @@ public class QuickShareServiceImpl implements QuickShareService,
{
metadata.put("size", 0L);
}
metadata.put("modified", nodeProps.get(ContentModel.PROP_MODIFIED));
if (personProps != null)
{
metadata.put("modifierFirstName", personProps.get(ContentModel.PROP_FIRSTNAME));
metadata.put("modifierLastName", personProps.get(ContentModel.PROP_LASTNAME));
}
// thumbnail defs for this nodeRef
List<String> thumbnailDefs = new ArrayList<String>(7);
List<String> thumbnailDefs = new ArrayList<>(7);
if (contentData != null)
{
// 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);
// thumbnail instances for this nodeRef
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)
{
thumbnailNames.add((String)nodeService.getProperty(thumbnailRef, ContentModel.PROP_NAME));
}
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))
{
metadata.put("sharedId", nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDID));
@@ -629,7 +611,7 @@ public class QuickShareServiceImpl implements QuickShareService,
metadata.put("sharable", sharable);
}
Map<String, Object> model = new HashMap<String, Object>(2);
Map<String, Object> model = new HashMap<>(2);
model.put("item", metadata);
return model;
}
@@ -637,13 +619,8 @@ public class QuickShareServiceImpl implements QuickShareService,
@Override
public Pair<String, NodeRef> getTenantNodeRefFromSharedId(final String sharedId)
{
NodeRef nodeRef = TenantUtil.runAsDefaultTenant(new TenantRunAsWork<NodeRef>()
{
public NodeRef doWork() throws Exception
{
return (NodeRef) attributeService.getAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId);
}
});
NodeRef nodeRef = TenantUtil.runAsDefaultTenant(
() -> (NodeRef) attributeService.getAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId));
if (nodeRef == null)
{
@@ -659,7 +636,7 @@ public class QuickShareServiceImpl implements QuickShareService,
sp.setQuery(query);
sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
List<NodeRef> nodeRefs = null;
List<NodeRef> nodeRefs;
ResultSet results = null;
try
{
@@ -697,23 +674,19 @@ public class QuickShareServiceImpl implements QuickShareService,
Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId);
final String tenantDomain = pair.getFirst();
final NodeRef nodeRef = pair.getSecond();
Map<String, Object> model = TenantUtil.runAsSystemTenant(new TenantRunAsWork<Map<String, Object>>()
{
public Map<String, Object> doWork() throws Exception
{
checkQuickShareNode(nodeRef);
return getMetaData(nodeRef);
}
Map<String, Object> model = TenantUtil.runAsSystemTenant(() -> {
checkQuickShareNode(nodeRef);
return getMetaData(nodeRef);
}, tenantDomain);
if (logger.isDebugEnabled())
{
logger.debug("QuickShare - retrieved metadata: "+sharedId+" ["+nodeRef+"]["+model+"]");
}
//model.put("nodeRef", nodeRef)
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"
public void beforeDeleteNode(final NodeRef beforeDeleteNodeRef)
{
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
public Void doWork() throws Exception
AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
String sharedId = (String)nodeService.getProperty(beforeDeleteNodeRef, QuickShareModel.PROP_QSHARE_SHAREDID);
if (sharedId != null)
{
String sharedId = (String)nodeService.getProperty(beforeDeleteNodeRef, QuickShareModel.PROP_QSHARE_SHAREDID);
if (sharedId != null)
try
{
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);
@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))
// Disable audit to preserve modifier and modified date
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
// Disable audit to preserve modifier and modified date
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
nodeService.removeAspect(nodeRef, QuickShareModel.ASPECT_QSHARE);
}
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
removeSharedId(sharedId);
nodeService.removeAspect(nodeRef, QuickShareModel.ASPECT_QSHARE);
}
}
catch (InvalidSharedIdException ex)
{
logger.warn("Couldn't find shareId, " + sharedId + ", attributes for node " + beforeDeleteNodeRef);
finally
{
behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
}
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)
{
final NodeRef childNodeRef = childAssocRef.getChildRef();
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
public Void doWork() throws Exception
AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
if (nodeService.hasAspect(childNodeRef, QuickShareModel.ASPECT_QSHARE))
{
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
behaviourFilter.disableBehaviour(childNodeRef, ContentModel.ASPECT_AUDITABLE);
try
{
nodeService.removeAspect(childNodeRef, QuickShareModel.ASPECT_QSHARE);
}
finally
{
behaviourFilter.enableBehaviour(childNodeRef, ContentModel.ASPECT_AUDITABLE);
}
nodeService.removeAspect(childNodeRef, QuickShareModel.ASPECT_QSHARE);
}
finally
{
behaviourFilter.enableBehaviour(childNodeRef, ContentModel.ASPECT_AUDITABLE);
}
return null;
}
return null;
});
}
private void removeSharedId(final String sharedId)
{
TenantUtil.runAsDefaultTenant(new TenantRunAsWork<Void>()
{
public Void doWork() throws Exception
{
attributeService.removeAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId);
return null;
}
TenantUtil.runAsDefaultTenant((TenantRunAsWork<Void>) () -> {
attributeService.removeAttribute(ATTR_KEY_SHAREDIDS_ROOT, sharedId);
return null;
});
try
@@ -832,40 +793,36 @@ public class QuickShareServiceImpl implements QuickShareService,
Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId);
final String tenantDomain = pair.getFirst();
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
// 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;
TenantUtil.runAsSystemTenant((TenantRunAsWork<Void>) () -> {
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
// 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);
removeSharedId(sharedId);
if (logger.isInfoEnabled())
@@ -891,23 +848,19 @@ public class QuickShareServiceImpl implements QuickShareService,
Pair<String, NodeRef> pair = getTenantNodeRefFromSharedId(sharedId);
final String tenantDomain = pair.getFirst();
final NodeRef nodeRef = pair.getSecond();
return TenantUtil.runAsTenant(new TenantRunAsWork<Boolean>()
{
public Boolean doWork() throws Exception
return TenantUtil.runAsTenant(() -> {
try
{
try
{
checkQuickShareNode(nodeRef);
return permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED;
}
catch (AccessDeniedException ex)
{
return false;
}
checkQuickShareNode(nodeRef);
return permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED;
}
catch (AccessDeniedException ex)
{
return false;
}
}, tenantDomain);
}
@Override
@@ -934,18 +887,18 @@ public class QuickShareServiceImpl implements QuickShareService,
Map<String, Serializable> templateModel = new HashMap<>(6);
templateModel.put(FTL_SENDER_FIRST_NAME, senderFirstName);
templateModel.put(FTL_SENDER_LAST_NAME, senderLastName);
final String sharedNodeUrl = getUrl(clientApp.getProperty(CONFIG_SHARED_LINK_BASE_URL), CONFIG_SHARED_LINK_BASE_URL)
+ '/' + emailRequest.getSharedId();
String resolvedUrl = getUrl(clientApp.getResolvedProperty(CONFIG_SHARED_LINK_BASE_URL, sysAdminParams), CONFIG_SHARED_LINK_BASE_URL);
final String sharedNodeUrl = resolvedUrl + '/' + emailRequest.getSharedId();
templateModel.put(FTL_SHARED_NODE_URL, sharedNodeUrl);
templateModel.put(FTL_SHARED_NODE_NAME, emailRequest.getSharedNodeName());
templateModel.put(FTL_SENDER_MESSAGE, emailRequest.getSenderMessage());
final String templateAssetsUrl = getUrl(clientApp.getTemplateAssetsUrl(), ClientAppConfig.PROP_TEMPLATE_ASSETS_URL);
final String templateAssetsUrl = getUrl(clientApp.getResolvedTemplateAssetsUrl(sysAdminParams), ClientAppConfig.PROP_TEMPLATE_ASSETS_URL);
templateModel.put(FTL_TEMPLATE_ASSETS_URL, templateAssetsUrl);
// Set the email details
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.
actionParams.put(MailActionExecuter.PARAM_FROM, this.defaultEmailSender);
actionParams.put(MailActionExecuter.PARAM_FROM_PERSONAL_NAME, senderFullName);
@@ -981,21 +934,21 @@ public class QuickShareServiceImpl implements QuickShareService,
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
String siteName = getSiteName(nodeRef);
boolean isSharedByCurrentUser = currentUser.equals(sharedByUserId);
boolean isSharedByCurrentUser = Objects.equals(currentUser, sharedByUserId);
if (siteName != null)
{
// 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);
if (isSharedByCurrentUser || (role != null && (role.equals(SiteModel.SITE_MANAGER) || role.equals(SiteModel.SITE_COLLABORATOR)))
|| (authorityService.isAdminAuthority(currentUser)))
|| (authorityService.isAdminAuthority(currentUser)))
{
canDeleteSharedLink = true;
}
}
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;
}
@@ -1015,11 +968,7 @@ public class QuickShareServiceImpl implements QuickShareService,
while (parent != null && !nodeService.getType(parent).equals(SiteModel.TYPE_SITE))
{
// check that we can read parent name
if (permissionService.hasReadPermission(parent) == AccessStatus.ALLOWED)
{
String parentName = (String) nodeService.getProperty(parent,ContentModel.PROP_NAME);
}
else
if (!(permissionService.hasReadPermission(parent) == AccessStatus.ALLOWED))
{
return null;
}
@@ -1048,8 +997,7 @@ public class QuickShareServiceImpl implements QuickShareService,
{
url = url.substring(0, url.length() - 1);
}
// Replace '${shareUrl} placeholder if it does exist.
return UrlUtil.replaceShareUrlPlaceholder(url, sysAdminParams);
return url;
}
private <T> T getDefaultIfNull(T defaultValue, T newValue)
@@ -1134,7 +1082,7 @@ public class QuickShareServiceImpl implements QuickShareService,
{
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);
// 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)
{
deleteQuickShareLinkExpiryAction(expiryActionNodeRef);
@@ -1247,11 +1195,11 @@ public class QuickShareServiceImpl implements QuickShareService,
// Create the expiry action
final QuickShareLinkExpiryAction expiryAction = new QuickShareLinkExpiryActionImpl(java.util.UUID.randomUUID().toString(), sharedId,
"QuickShare link expiry action");
"QuickShare link expiry action");
// Create the persisted schedule
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.setScheduleStart(expiryDate);
@@ -1273,7 +1221,7 @@ public class QuickShareServiceImpl implements QuickShareService,
if (logger.isDebugEnabled())
{
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
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.
if (sharedNodeRef != null && nodeService.getProperty(sharedNodeRef, QuickShareModel.PROP_QSHARE_EXPIRY_DATE) != null)
{
@@ -1342,7 +1290,7 @@ public class QuickShareServiceImpl implements QuickShareService,
quickShareLinkExpiryActionPersister.deleteQuickShareLinkExpiryAction(linkExpiryAction);
}
private QuickShareLinkExpiryAction attachSchedule(QuickShareLinkExpiryAction quickShareLinkExpiryAction)
private void attachSchedule(QuickShareLinkExpiryAction quickShareLinkExpiryAction)
{
if (quickShareLinkExpiryAction.getSchedule() == null)
{
@@ -1350,7 +1298,6 @@ public class QuickShareServiceImpl implements QuickShareService,
quickShareLinkExpiryAction.setSchedule(schedule);
}
return quickShareLinkExpiryAction;
}
private NodeRef getQuickShareLinkExpiryActionNode(String sharedId)
@@ -1381,54 +1328,53 @@ public class QuickShareServiceImpl implements QuickShareService,
private enum ExpiryDatePeriod
{
DAYS
{
@Override
int getDuration(DateTime now, DateTime expiryDate)
{
Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.days()).getDays();
}
{
@Override
int getDuration(DateTime now, DateTime expiryDate)
{
Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.days()).getDays();
}
@Override
String getMessage()
{
return "day (24 hours)";
}
},
@Override
String getMessage()
{
return "day (24 hours)";
}
},
HOURS
{
@Override
int getDuration(DateTime now, DateTime expiryDate)
{
Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.hours()).getHours();
}
{
@Override
int getDuration(DateTime now, DateTime expiryDate)
{
Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.hours()).getHours();
}
@Override
String getMessage()
{
return "hour";
}
},
@Override
String getMessage()
{
return "hour";
}
},
MINUTES
{
@Override
public int getDuration(DateTime now, DateTime expiryDate)
{
Interval interval = new Interval(now.withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.minutes()).getMinutes();
}
{
@Override
public int getDuration(DateTime now, DateTime expiryDate)
{
Interval interval = new Interval(now.withMillisOfSecond(0), expiryDate);
return interval.toPeriod(PeriodType.minutes()).getMinutes();
}
@Override
String getMessage()
{
return "minute";
}
};
@Override
String getMessage()
{
return "minute";
}
};
abstract int getDuration(DateTime now, DateTime expiryDate);
abstract String getMessage();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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