Lessons
  Menu

Best Practices for Secure and Efficient Resource Access

When you’re creating a website or an app using Adobe Experience Manager (AEM), you often need to fetch (get) data from the website’s content repository. This is a bit like accessing files and folders on your computer, but in this case, it’s for website content. To do this, you use something called a “ResourceResolver.” Think of the ResourceResolver as a key that helps you open doors to find the data you need.

Now, in the provided code snippet, there’s a method that tries to get articles from the website. It uses the “ResourceResolver” to access the website’s data. However, the way it’s getting this “ResourceResolver” is by asking the “resource” (which represents a specific piece of content) for it.

Here’s where things get tricky:

package com.adobe.aem.guides.wknd.core.services.impl;

import com.adobe.aem.guides.wknd.core.beans.ArticleListDataBean;
import com.adobe.aem.guides.wknd.core.services.ArticleListService;
import com.adobe.aem.guides.wknd.core.services.ResourceResolverService;
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 com.day.cq.wcm.api.Page;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.RepositoryException;
import javax.jcr.Session;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component(service = ArticleListService.class)
public class ArticleListServiceImpl implements ArticleListService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ArticleListServiceImpl.class);

    @Reference
    private QueryBuilder builder;
    

    @Override
    public List<ArticleListDataBean> getArticleListDataBeans(Resource resource, String articleRootPath) {
        List<ArticleListDataBean> articleListDataBeans = new ArrayList<>();
        ResourceResolver resourceResolver = resource.getResourceResolver();

        Map<String, String> predicate = new HashMap<>();
        predicate.put("path", articleRootPath);
        predicate.put("type", "cq:Page");

        try {
            Query query = builder.createQuery(PredicateGroup.create(predicate), resourceResolver.adaptTo(Session.class));
            SearchResult searchResult = query.getResult();

            for (Hit hit : searchResult.getHits()) {
                String path = hit.getPath();
                Resource articleResource = resourceResolver.getResource(path);
                Page articlePage = articleResource != null ? articleResource.adaptTo(Page.class) : null;

                if (articlePage != null) {
                    ArticleListDataBean articleListDataBean = new ArticleListDataBean();
                    articleListDataBean.setPath(path);
                    articleListDataBean.setTitle(articlePage.getTitle());
                    articleListDataBean.setDescription(articlePage.getDescription());

                    articleListDataBeans.add(articleListDataBean);
                }
            }
        } catch (RepositoryException e) {
            LOGGER.error("Error processing search result hit: {}", e.getMessage(), e);
        } catch (Exception e) {
            LOGGER.error("Error executing query for path {}: {}", articleRootPath, e.getMessage(), e);
        }
        return articleListDataBeans;
    }
}

Why Getting ResourceResolver Directly from a Resource is Not Right

Imagine you have a key (ResourceResolver) that you found lying around in a room (the “resource”). You use this key to open various doors within a building (the content repository) to get information. This key is supposed to be personal and controlled; it should only open doors you’re allowed to enter. But since you found it just lying around, you don’t really know if you’re supposed to have access to all these rooms. You might accidentally enter a room you’re not supposed to, which can lead to security issues.

In technical terms, using a ResourceResolver directly from a resource means you’re using a key that’s not necessarily meant for you. It might have restrictions or might not be the right way to access certain information securely and efficiently.

Why We Need to Get It from System Users

Going back to our building and key analogy, a better approach is to get a master key designed specifically for you (or for a specific task). This is what getting a ResourceResolver from system users is like. System users in AEM are special user accounts that are meant for specific tasks, like running services or accessing certain types of data. They are like trusted employees who have been given keys (ResourceResolvers) that open exactly the doors they need to for their work.

So, when you get a ResourceResolver through a system user, you’re essentially getting a key that’s:

  • Securely given to you for a specific purpose, ensuring you only access what you’re supposed to.
  • Optimized for performance, since it’s meant to do exactly what you need, without unnecessary access or overhead.