AEM Cloud Service - Dynamic Media folder path length validation, prevent deep nesting

Goal

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

When the Scene7 sync path (including account name) is more than 255 chars, asset upload to AEM works fine, however sync to Scene7 fails with error in s7 job console Failed to insert to DB. This post is on providing folder path validation (240 chars) so users do not upload assets in a deep nested structure and corrupt the repo with DM sync failures...

Demo | Package Install | Github


Upload Validation in Browser


Upload Validation in Desktop App


S7 Job Console Error for Path > 255 chars


Solution

1) Create a filter apps.experienceaem.assets.core.filters.FolderNestingCheck, to provide server side validation of folder path lengths...

package apps.experienceaem.assets.core.filters;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import java.io.IOException;

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

    private static int S7_FOLDER_LENGTH_MAX = 240;
    private static String CONTENT_DAM_PATH = "/content/dam";
    private static String INITIATE_UPLOAD_JSON = ".initiateUpload.json";
    private static String CREATE_ASSET_HTML = ".createasset.html";

    @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;
            }

            if(uri.contains(CONTENT_DAM_PATH)){
                String folderPath = uri.substring(uri.indexOf(CONTENT_DAM_PATH) + CONTENT_DAM_PATH.length());

                if(folderPath.endsWith(INITIATE_UPLOAD_JSON)){
                    folderPath = folderPath.substring(0, folderPath.lastIndexOf(INITIATE_UPLOAD_JSON));
                }else if(folderPath.endsWith(CREATE_ASSET_HTML)){
                    folderPath = folderPath.substring(0, folderPath.lastIndexOf(CREATE_ASSET_HTML));
                }

                if(folderPath.length() > S7_FOLDER_LENGTH_MAX){
                    log.info("Uploading to deep nested folders not allowed : " + uri);
                    slingResponse.sendError(SlingHttpServletResponse.SC_FORBIDDEN, "Uploading to deep nested folders not allowed: " + uri);
                    return;
                }
            }

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

    @Override
    public void destroy() {
    }
}

2) For showing the validation alert in browser, create a clientlib /apps/eaem-cs-no-deep-nesting/clientlibs/no-deep-nesting with categories dam.gui.coral.fileupload

3) Add JS logic for validation in /apps/eaem-cs-no-deep-nesting/clientlibs/no-deep-nesting/deep-nesting-upload.js

(function ($, $document) {
    "use strict";

    var _ = window._,
        S7_FOLDER_LENGTH_MAX = 240,
        CONTENT_DAM_PATH = "/content/dam";

    var _origConfirmUpload = window.DamFileUpload.prototype._confirmUpload,
        _origOnInputChange = window.Dam.ChunkFileUpload.prototype._onInputChange;

    window.Dam.ChunkFileUpload.prototype._onInputChange = function(event){
        var files = event.target.files;

        if(!files && event.dataTransfer && event.dataTransfer.files){
            files = event.dataTransfer.files;
        }

        if(!files || (files.length == 0)){
            _origOnInputChange.call(this, event);
            return;
        }

        if(!isS7RelativeFolderPathWithinLimit()){
            showAlert("Uploading to deep nested folders not allowed");
            return;
        }

        _origOnInputChange.call(this, event);
    };

    window.DamFileUpload.prototype._confirmUpload = function (event) {
        if(!isS7RelativeFolderPathWithinLimit()){
            showAlert("Uploading to deep nested folders not allowed");
            return;
        }

        _origConfirmUpload.call(this, event);
    };

    function isS7RelativeFolderPathWithinLimit(){
        var folderPath = window.location.pathname;

        if(folderPath.includes("/content/dam")){
            folderPath = folderPath.substring(folderPath.indexOf(CONTENT_DAM_PATH) + CONTENT_DAM_PATH.length);
        }

        return (folderPath.length <= S7_FOLDER_LENGTH_MAX);
    }

    function showAlert(message, title, callback){
        var fui = $(window).adaptTo("foundation-ui"),
            options = [{
                id: "ok",
                text: "OK",
                primary: true
            }];

        message = message || "Unknown Error";
        title = title || "Error";

        fui.prompt(title, message, "warning", options, callback);
    }
}(jQuery, jQuery(document)));


1 comment:

  1. Thank you for the great post.
    Prancer is a pre-deployment and post-deployment multi-cloud validation framework for your Infrastructure as Code (IaC) pipeline and continuous compliance in the cloud.

    ReplyDelete