DefaultLoginsHealthCheck.java 11.7 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  javax.jcr.Credentials
 *  javax.jcr.RepositoryException
 *  javax.jcr.Session
 *  javax.jcr.SimpleCredentials
 *  org.apache.felix.scr.annotations.Activate
 *  org.apache.felix.scr.annotations.Component
 *  org.apache.felix.scr.annotations.Properties
 *  org.apache.felix.scr.annotations.Property
 *  org.apache.felix.scr.annotations.PropertyUnbounded
 *  org.apache.felix.scr.annotations.Reference
 *  org.apache.felix.scr.annotations.Service
 *  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.apache.sling.jcr.api.SlingRepository
 *  org.osgi.framework.InvalidSyntaxException
 *  org.osgi.service.cm.Configuration
 *  org.osgi.service.cm.ConfigurationAdmin
 *  org.osgi.service.component.ComponentContext
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.granite.repository.hc.impl;

import java.io.IOException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.jcr.Credentials;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
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.Reference;
import org.apache.felix.scr.annotations.Service;
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.apache.sling.jcr.api.SlingRepository;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * This class specifies class file version 49.0 but uses Java 6 signatures.  Assumed Java 6.
 */
@Component(metatype=1, label="Adobe Granite Repository Default Login Health Check", description="This checks if the default account logins have been disabled.")
@Properties(value={@Property(name="hc.name", value={"Default Login Accounts"}, label="Name", description="Name of the health check."), @Property(name="hc.tags", unbounded=PropertyUnbounded.ARRAY, value={"login", "security", "production"}, label="Tags", description="Tags for the health check."), @Property(name="hc.mbean.name", value={"defaultLogins"}, label="MBean Name", description="Name of the JMX mbean to register for this check.")})
@Service(value={HealthCheck.class})
public class DefaultLoginsHealthCheck
implements HealthCheck {
    private final Logger log;
    private static final String DEFAULT_ADMIN_LOGIN = "admin:admin";
    private static final String DEFAULT_AUTHOR_LOGIN = "author:author";
    private static final String OSGI_MANAGER_SERVICE_PID = "org.apache.felix.webconsole.internal.servlet.OsgiManager";
    static final String DEFAULT_HASHED_PASSWORD = "{sha-256}jGl25bVBBBW96Qi9Te4V37Fnqchz/Eu4qB9vKrRIqRg=";
    private List<String> accountLogins;
    private List<String> consoleLogins;
    @Property(value={"admin:admin", "author:author"}, unbounded=PropertyUnbounded.ARRAY, label="Logins", description="The logins to check")
    private static final String PROP_LOGINS = "accountLogins";
    private static final String DEFAULT_OSGI_CONSOLE_LOGIN = "admin:admin";
    @Property(value={"admin:admin"}, unbounded=PropertyUnbounded.ARRAY, label="Console Logins", description="The logins for the console to check")
    private static final String OSGI_LOGINS = "consoleLogins";
    @Reference
    private SlingRepository repository;
    @Reference
    private ConfigurationAdmin configurationAdmin;

    public DefaultLoginsHealthCheck() {
        this.log = LoggerFactory.getLogger(this.getClass());
    }

    @Activate
    public void activate(ComponentContext ctx) {
        this.accountLogins = Arrays.asList(PropertiesUtil.toStringArray(ctx.getProperties().get("accountLogins"), (String[])new String[0]));
        this.consoleLogins = Arrays.asList(PropertiesUtil.toStringArray(ctx.getProperties().get("consoleLogins"), (String[])new String[0]));
        this.log.info("Activated, accountLogins={}, consoleLogins={}", this.accountLogins, this.consoleLogins);
    }

    public Result execute() {
        FormattingResultLog resultLog = new FormattingResultLog();
        this.checkSystemLoginAccounts(resultLog);
        this.checkConsoleAdminPassword(resultLog);
        return new Result((ResultLog)resultLog);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private boolean checkSystemLoginAccounts(FormattingResultLog resultLog) {
        int checked = 0;
        int failures = 0;
        ArrayList<Map.Entry<String, String>> logins = this.getLoginEntries(this.accountLogins, resultLog);
        Iterator<Map.Entry<String, String>> loginsIt = logins.iterator();
        while (loginsIt.hasNext()) {
            ++checked;
            Map.Entry<String, String> login = loginsIt.next();
            String testUser = login.getKey();
            String testPass = login.getValue();
            SimpleCredentials cred = new SimpleCredentials(testUser, testPass.toCharArray());
            Session s = null;
            try {
                s = this.repository.login((Credentials)cred);
                if (s != null) {
                    resultLog.warn("Login as [{}: {}] succeeded, was expected to fail.", new Object[]{testUser, testPass});
                    ++failures;
                    continue;
                }
                resultLog.debug("Login as [{}: {}] didn't throw an Exception but returned null Session.", new Object[]{testUser, testPass});
                continue;
            }
            catch (RepositoryException re) {
                resultLog.debug("Login as [{}: {}] failed, as expected.", new Object[]{testUser, testPass});
                continue;
            }
            finally {
                if (s == null) continue;
                s.logout();
                continue;
            }
        }
        if (checked == 0) {
            resultLog.warn("No login checks were performed. Configured account logins: {}.", new Object[]{this.accountLogins});
        } else if (failures != 0) {
            resultLog.debug("It is strongly recommended to change the default admin accounts for CQ.", new Object[0]);
            resultLog.debug("[You can change the admin account passwords via the User Admin.](/libs/granite/security/content/useradmin.html)", new Object[0]);
            resultLog.debug("[Check the 'Changing the CQ Admin Password' section in the security guidelines.](https://www.adobe.com/go/aem6_2_docs_security_adminpass_en)", new Object[0]);
        }
        return checked != 0 && failures == 0;
    }

    private boolean checkConsoleAdminPassword(FormattingResultLog resultLog) {
        boolean matched = false;
        try {
            String filter = "(service.pid=org.apache.felix.webconsole.internal.servlet.OsgiManager)";
            Configuration[] osgiConsoleCfgs = this.configurationAdmin.listConfigurations(filter);
            if (osgiConsoleCfgs == null || osgiConsoleCfgs.length == 0) {
                resultLog.warn("The default OSGI console credentials were not changed. It is strongly recommended to change them.", new Object[0]);
                resultLog.warn("[You can change the OSGI admin password via the configuration of the Apache Felix OSGI Management Console.]({})", new Object[]{"/system/console/configMgr/org.apache.felix.webconsole.internal.servlet.OsgiManager"});
                resultLog.debug("[Check the 'Changing the OSGI Web Console Admin Password' section in the security guideline.](https://www.adobe.com/go/aem6_2_docs_security_osgipass_en)", new Object[0]);
                return false;
            }
            ArrayList<Map.Entry<String, String>> logins = this.getLoginEntries(this.consoleLogins, resultLog);
            int checked = logins.size();
            for (Configuration config : osgiConsoleCfgs) {
                Dictionary properties = config.getProperties();
                String username = PropertiesUtil.toString(properties.get("username"), (String)"");
                String password = PropertiesUtil.toString(properties.get("password"), (String)"");
                for (Map.Entry<String, String> login : logins) {
                    String testUser = login.getKey();
                    String testPass = login.getValue();
                    if (username.equals(testUser) && password.equals("{sha-256}jGl25bVBBBW96Qi9Te4V37Fnqchz/Eu4qB9vKrRIqRg=")) {
                        resultLog.warn("The default admin password for the OSGI console was not changed. It is strongly recommended to change the default admin credentials for the OSGI console.", new Object[0]);
                        matched = true;
                        continue;
                    }
                    resultLog.debug("The the OSGI console admin password was changed, as expected.", new Object[]{testUser, testPass});
                }
            }
            if (checked == 0) {
                resultLog.debug("No login checks were performed for the OSGI console.", new Object[0]);
            } else if (matched) {
                resultLog.warn("[You can change the OSGI admin password via the configuration of the Apache Felix OSGI Management Console.]({})", new Object[]{"/system/console/configMgr/org.apache.felix.webconsole.internal.servlet.OsgiManager"});
                resultLog.debug("[Check the 'Changing the OSGI Web Console Admin Password' section in the security guideline.](https://www.adobe.com/go/aem6_2_docs_security_osgipass_en)", new Object[0]);
            }
            return checked != 0 && !matched;
        }
        catch (InvalidSyntaxException e) {
            resultLog.warn("Could not get OSGI Management Bundle configuration, couldn't test OSGI console logins.", new Object[0]);
            return false;
        }
        catch (IOException e) {
            resultLog.warn("Could not get OSGI Management Bundle configuration, couldn't test OSGI console logins.", new Object[0]);
            return false;
        }
    }

    private ArrayList<Map.Entry<String, String>> getLoginEntries(List<String> entries, FormattingResultLog resultLog) {
        ArrayList<Map.Entry<String, String>> logins = new ArrayList<Map.Entry<String, String>>();
        for (String entry : entries) {
            String[] parts = entry.split(":");
            if (parts.length != 2) {
                resultLog.warn("Expected login in the form username:password, got {}.", new Object[]{entry});
                continue;
            }
            logins.add(new AbstractMap.SimpleEntry<String, String>(parts[0].trim(), parts[1].trim()));
        }
        return logins;
    }

    protected void bindRepository(SlingRepository slingRepository) {
        this.repository = slingRepository;
    }

    protected void unbindRepository(SlingRepository slingRepository) {
        if (this.repository == slingRepository) {
            this.repository = null;
        }
    }

    protected void bindConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
        this.configurationAdmin = configurationAdmin;
    }

    protected void unbindConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
        if (this.configurationAdmin == configurationAdmin) {
            this.configurationAdmin = null;
        }
    }
}