DispatcherAccessHealthCheck.java 9.32 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Modified
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.PropertyUnbounded
 *  org.apache.felix.scr.annotations.Service
 *  org.apache.http.HttpEntity
 *  org.apache.http.StatusLine
 *  org.apache.http.client.methods.CloseableHttpResponse
 *  org.apache.http.client.methods.HttpGet
 *  org.apache.http.client.methods.HttpUriRequest
 *  org.apache.http.impl.client.CloseableHttpClient
 *  org.apache.http.impl.client.HttpClients
 *  org.apache.sling.commons.osgi.PropertiesUtil
 *  org.apache.sling.hc.api.HealthCheck
 *  org.apache.sling.hc.api.Result
 *  org.apache.sling.hc.api.ResultLog
 *  org.apache.sling.hc.util.FormattingResultLog
 *  org.osgi.service.component.ComponentContext
 */
package com.adobe.cq.security.hc.dispatcher.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.List;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Service;
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.hc.api.HealthCheck;
import org.apache.sling.hc.api.Result;
import org.apache.sling.hc.api.ResultLog;
import org.apache.sling.hc.util.FormattingResultLog;
import org.osgi.service.component.ComponentContext;

@Component(metatype=1, label="Adobe CQ Dispatcher Configuration Health Check", description="This checks the basic configuration of the Dispatcher component.")
@Properties(value={@Property(name="hc.name", value={"CQ Dispatcher Configuration"}, label="Name", description="Name of the health check."), @Property(name="hc.tags", unbounded=PropertyUnbounded.ARRAY, value={"dispatcher", "production", "security"}, label="Tags", description="Tags for the health check."), @Property(name="hc.mbean.name", value={"dispatcherConfig"}, label="MBean Name", description="Name of the JMX mbean to register for this check.")})
@Service(value={HealthCheck.class})
public class DispatcherAccessHealthCheck
implements HealthCheck {
    @Property(value={""}, label="Dispatcher Address", description="The address where the dispatcher is installed.")
    private static final String DISPATCHER_URL = "dispatcher.address";
    @Property(value={"/content", "/libs/cq/personalization/", "/etc/designs/", "/etc/clientlibs/", "/etc/segmentation.segment.js", "/libs/cq/personalization/components/clickstreamcloud/content/config.json", "/libs/wcm/stats/tracker.js", "/libs/cq/personalization/", "/libs/cq/security/userinfo.json", "/libs/cq/i18n/"}, label="Unrestricted paths", unbounded=PropertyUnbounded.ARRAY, description="The paths which should not be restricted by the dispatcher.")
    private static final String ALLOWED_PATHS = "dispatcher.filter.allowed";
    @Property(value={"/", "/content/", "/etc/", "/libs/", "/etc/replication.xml", "/etc/replication.infinity.xml", "/content.inifinity.json", "/content.tidy.json", "/content.sysview.xml", "/content.docview.json", "/content.docview.xml", "/content.0.json", "/content.1.json", "/content.2.json", "/content.feed.xml"}, label="Restricted paths", unbounded=PropertyUnbounded.ARRAY, description="The paths which should be restricted by the dispatcher.")
    private static final String NOT_ALLOWED_PATHS = "dispatcher.filter.blocked";
    private String dispatcherAddress;
    private List<String> allowedPaths;
    private List<String> blockedPaths;

    @Activate
    protected void activate(ComponentContext ctx) {
        this.dispatcherAddress = PropertiesUtil.toString(ctx.getProperties().get("dispatcher.address"), (String)"");
        this.allowedPaths = Arrays.asList(PropertiesUtil.toStringArray(ctx.getProperties().get("dispatcher.filter.allowed"), (String[])new String[0]));
        this.blockedPaths = Arrays.asList(PropertiesUtil.toStringArray(ctx.getProperties().get("dispatcher.filter.blocked"), (String[])new String[0]));
    }

    @Modified
    protected void update(ComponentContext ctx) {
        this.dispatcherAddress = PropertiesUtil.toString(ctx.getProperties().get("dispatcher.address"), (String)"");
        this.allowedPaths = Arrays.asList(PropertiesUtil.toStringArray(ctx.getProperties().get("dispatcher.filter.allowed"), (String[])new String[0]));
        this.blockedPaths = Arrays.asList(PropertiesUtil.toStringArray(ctx.getProperties().get("dispatcher.filter.blocked"), (String[])new String[0]));
    }

    public Result execute() {
        FormattingResultLog resultLog = new FormattingResultLog();
        int successes = 0;
        int failures = 0;
        if ("".equals(this.dispatcherAddress.trim())) {
            resultLog.warn("Unable to check the dispatcher's basic configuration because its address is not specified.", new Object[0]);
            resultLog.debug("[The address can be specified via the 'Dispatcher Address' property of this health check.]( )", new Object[0]);
            return new Result((ResultLog)resultLog);
        }
        CloseableHttpClient client = HttpClients.createDefault();
        for (String path2 : this.allowedPaths) {
            if (this.checkPath(client, path2, true, resultLog)) {
                ++successes;
                continue;
            }
            ++failures;
        }
        for (String path2 : this.blockedPaths) {
            if (this.checkPath(client, path2, false, resultLog)) {
                ++successes;
                continue;
            }
            ++failures;
        }
        if (failures != 0) {
            String errorMsg = failures > 1 ? "{} path access configurations failed during testing. " : "{} path access configuration failed during testing. ";
            resultLog.warn("[" + errorMsg + "When configuring the dispatcher, you should restrict external access as much as possible.]( )", new Object[]{failures});
            resultLog.debug("[Check the 'Restrict Access via the Dispatcher' section in the security guidelines](https://www.adobe.com/go/aem6_2_docs_security_access_en)", new Object[0]);
            resultLog.debug("[Check the 'Configuring the Dispatcher to prevent DoS' section in the security guidelines](https://www.adobe.com/go/aem6_2_docs_security_dos_en)", new Object[0]);
        } else {
            resultLog.info("[All {} paths checked meet the configuration access guidelines.]( )", new Object[]{successes});
        }
        if (client != null) {
            try {
                client.close();
            }
            catch (IOException e) {
                resultLog.warn("Could not close HTTP client due to an IOException.", new Object[0]);
            }
        }
        return new Result((ResultLog)resultLog);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private boolean checkPath(CloseableHttpClient client, String path, boolean shouldWork, FormattingResultLog resultLog) {
        boolean success;
        block17 : {
            success = true;
            HttpGet httpget = new HttpGet(this.dispatcherAddress + path);
            CloseableHttpResponse httpResponse = null;
            try {
                boolean validRequest;
                httpResponse = client.execute((HttpUriRequest)httpget);
                int code = httpResponse.getStatusLine().getStatusCode();
                boolean bl = validRequest = code == 200 || code == 401 || code == 403;
                if (shouldWork == validRequest) {
                    if (shouldWork) {
                        resultLog.debug("Request to [{}] worked, as expected.", new Object[]{path});
                    } else {
                        resultLog.debug("Request to [{}] did not work, as expected.", new Object[]{path});
                    }
                    break block17;
                }
                if (shouldWork) {
                    resultLog.warn("Request to [{}] did not work. It was expected to work.", new Object[]{path});
                } else {
                    resultLog.warn("Request to [{}] worked. It was expected to fail.", new Object[]{path});
                }
                success = false;
            }
            catch (Exception e) {
                resultLog.warn("Could not check path [{}].", new Object[]{path});
                success = false;
            }
            finally {
                if (httpResponse != null && httpResponse.getEntity() != null) {
                    try {
                        httpResponse.getEntity().getContent().close();
                    }
                    catch (Exception e) {
                        resultLog.warn("Could not close the HTTP response due to an IOException.", new Object[0]);
                    }
                }
            }
        }
        return success;
    }
}