Quantcast
Channel: Experiencing Adobe Experience Manager (AEM, CQ)
Viewing all articles
Browse latest Browse all 525

AEM Cloud Service - Assets server side check for Duplicate file names

$
0
0

Goal

AEM Cloud Version : 2021.2.4944.20210221T230729Z-210225 (Feb 21, 2021)

The client side duplicate check post provides a helpful error message when user tries to upload duplicate files using a browser, however it does not protect when user uploads files using other means, say AEM Desktop app. This post provides a server side way of handling duplicate file names

Demo | Package Install | Github


Error uploading duplicate in Browser



Error uploading duplicate in Desktop App


Solution

1) Add a service user eaem-service-user in repo init script ui.config\src\main\content\jcr_root\apps\eaem-cs-server-duplicate-check\osgiconfig\config.author\org.apache.sling.jcr.repoinit.RepositoryInitializer-eaem.config

scripts=[
"
create service user eaem-service-user with path system/cq:services/experience-aem
set principal ACL for eaem-service-user
allow jcr:read on /apps
allow jcr:read on /conf
allow jcr:read on /content/dam
end
"
]


2) Provide the service user to bundle mapping in ui.config\src\main\content\jcr_root\apps\eaem-cs-server-duplicate-check\osgiconfig\config.author\org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-ea.xml

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root
xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="sling:OsgiConfig"
user.mapping="[eaem-cs-server-duplicate-check.core:eaem-service-user=[eaem-service-user]]"/>


3) Add a filter apps.experienceaem.assets.core.filters.DuplicateAssetNameCheck executing for .initiateUpload.json and .createasset.html requests to check for duplicate file names across the repo

package apps.experienceaem.assets.core.filters;

import com.day.cq.search.PredicateGroup;
import com.day.cq.search.Query;
import com.day.cq.search.QueryBuilder;
import com.day.cq.search.result.Hit;
import com.day.cq.search.result.SearchResult;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.request.RequestParameter;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.*;

@Component(
service = Filter.class,
immediate = true,
name = "Experience AEM DAM server side duplicate file name check",
property = {
Constants.SERVICE_RANKING + ":Integer=-99",
"sling.filter.scope=COMPONENT",
"sling.filter.pattern=((.*.initiateUpload.json)|(.*.createasset.html))",
}
)
public class DuplicateAssetNameCheck implements Filter {
private static Logger log = LoggerFactory.getLogger(DuplicateAssetNameCheck.class);

private static String INITIATE_UPLOAD_JSON = ".initiateUpload.json";
private static String CREATE_ASSET_HTML = ".createasset.html";
private static final String EAEM_SERVICE_USER = "eaem-service-user";

@Reference
private ResourceResolverFactory factory;

@Reference
private QueryBuilder builder;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
try{
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest)request;
SlingHttpServletResponse slingResponse = (SlingHttpServletResponse)response;

String uri = slingRequest.getRequestURI();

if(!uri.endsWith(INITIATE_UPLOAD_JSON) && !uri.endsWith(CREATE_ASSET_HTML)){
chain.doFilter(request, response);
return;
}

/*String userAgent = slingRequest.getHeader("User-Agent");

if(isBrowser(userAgent)){
//duplicate filename check in browsers is already done on client side
log.info("A Browser User agent : " + userAgent);
chain.doFilter(request, response);
return;
}*/

String fileNames[] = request.getParameterValues("fileName");

if(fileNames == null){
RequestParameter params[] = slingRequest.getRequestParameters("file");

if(ArrayUtils.isEmpty(params)){
log.warn("Skipping duplicate check, 'fileName' and 'file' params, both are empty");
chain.doFilter(request, response);
return;
}

for (final RequestParameter param : params) {
if (param.getFileName() == null) {
continue;
}

fileNames = new String[1];
fileNames[0] = param.getFileName();

break;
}
}

if(ArrayUtils.isEmpty(fileNames)){
log.warn("Skipping duplicate check, 'fileName' and 'file' params, both are empty");
chain.doFilter(request, response);
return;
}

List<String> duplicatePaths = getDuplicateFilePaths(factory, builder, fileNames);

log.info("duplicatePaths : " + duplicatePaths + ", for file : " + String.join(",", fileNames));

if(!CollectionUtils.isEmpty(duplicatePaths)){
log.info("Duplicate file names detected while upload : " + duplicatePaths);
slingResponse.sendError(SlingHttpServletResponse.SC_FORBIDDEN, "Duplicates found: " + String.join(",", duplicatePaths));
return;
}

chain.doFilter(request, response);
}catch(Exception e){
log.error("Error checking for duplicates", e);
}
}

public static List<String> getDuplicateFilePaths(ResourceResolverFactory resourceResolverFactory,
QueryBuilder builder, String fileNames[]) throws RepositoryException {
ResourceResolver resourceResolver = getServiceResourceResolver(resourceResolverFactory);
List<String> duplicates = new ArrayList<String>();

Query query = builder.createQuery(PredicateGroup.create(getQueryPredicateMap(fileNames)), resourceResolver.adaptTo(Session.class));

SearchResult result = query.getResult();

for (Hit hit : result.getHits()) {
duplicates.add(hit.getPath());
}

return duplicates;
}

private static Map<String, String> getQueryPredicateMap(String[] fileNames) {
Map<String, String> map = new HashMap<>();
map.put("path", "/content/dam");
map.put("type", "dam:Asset");
map.put("group.p.or", "true");

for(int index = 0; index < fileNames.length; index++){
map.put("group." + index + "_nodename", fileNames[index]);
}

return map;
}

public static ResourceResolver getServiceResourceResolver(ResourceResolverFactory resourceResolverFactory) {
Map<String, Object> subServiceUser = new HashMap<>();
subServiceUser.put(ResourceResolverFactory.SUBSERVICE, EAEM_SERVICE_USER);
try {
return resourceResolverFactory.getServiceResourceResolver(subServiceUser);
} catch (LoginException ex) {
log.error("Could not login as SubService user {}, exiting SearchService service.", EAEM_SERVICE_USER, ex);
return null;
}
}

private boolean isBrowser(String userAgent){
return (userAgent.contains("Mozilla") || userAgent.contains("Chrome") || userAgent.contains("Safari"));
}

@Override
public void destroy() {
}
}



Viewing all articles
Browse latest Browse all 525

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>