/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.http.servlet.configmanager;
import com.novell.ldapchai.exception.ChaiUnavailableException;
import password.pwm.PwmConstants;
import password.pwm.config.ActionConfiguration;
import password.pwm.config.Configuration;
import password.pwm.config.PwmSetting;
import password.pwm.config.PwmSettingSyntax;
import password.pwm.config.StoredValue;
import password.pwm.config.stored.StoredConfigReference;
import password.pwm.config.stored.StoredConfigurationImpl;
import password.pwm.config.stored.StoredConfigurationUtil;
import password.pwm.error.PwmUnrecoverableException;
import password.pwm.http.HttpMethod;
import password.pwm.http.JspUrl;
import password.pwm.http.PwmRequest;
import password.pwm.http.PwmRequestAttribute;
import password.pwm.http.servlet.AbstractPwmServlet;
import password.pwm.util.secure.X509Utils;
import password.pwm.util.logging.PwmLogger;
import password.pwm.ws.server.RestResultBean;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.Serializable;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@WebServlet(
name = "ConfigManagerCertificateServlet",
urlPatterns = {
PwmConstants.URL_PREFIX_PRIVATE + "/config/manager/certificates",
}
)
public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
private static final PwmLogger LOGGER = PwmLogger.forClass(ConfigManagerCertificatesServlet.class);
public enum ConfigManagerCertificateAction implements AbstractPwmServlet.ProcessAction {
certificateData(HttpMethod.GET),
;
private final HttpMethod method;
ConfigManagerCertificateAction(final HttpMethod method) {
this.method = method;
}
public Collection<HttpMethod> permittedMethods() {
return Collections.singletonList(method);
}
}
protected ConfigManagerCertificateAction readProcessAction(final PwmRequest request)
throws PwmUnrecoverableException {
try {
return ConfigManagerCertificateAction.valueOf(request.readParameterAsString(PwmConstants.PARAM_ACTION_REQUEST));
} catch (IllegalArgumentException e) {
return null;
}
}
protected void processAction(final PwmRequest pwmRequest)
throws ServletException, IOException, ChaiUnavailableException, PwmUnrecoverableException
{
final ConfigManagerCertificateAction action = readProcessAction(pwmRequest);
final ArrayList<CertificateDebugDataItem> certificateDebugDataItems = new ArrayList<>(makeCertificateDebugData(pwmRequest.getConfig()));
if (action != null && action == ConfigManagerCertificateAction.certificateData) {
final RestResultBean restResultBean = new RestResultBean(certificateDebugDataItems);
pwmRequest.outputJsonResult(restResultBean);
return;
}
pwmRequest.setAttribute(PwmRequestAttribute.ConfigHasCertificates, !certificateDebugDataItems.isEmpty());
pwmRequest.forwardToJsp(JspUrl.CONFIG_MANAGER_CERTIFICATES);
}
List<CertificateDebugDataItem> makeCertificateDebugData(final Configuration configuration) throws PwmUnrecoverableException {
final StoredConfigurationImpl storedConfiguration = configuration.getStoredConfiguration();
final List<StoredConfigReference> modifiedSettings = StoredConfigurationUtil.modifiedSettings(storedConfiguration);
final List<CertificateDebugDataItem> certificateDebugDataItems = new ArrayList<>();
for (final StoredConfigReference ref : modifiedSettings) {
if (ref.getRecordType() == StoredConfigReference.RecordType.SETTING) {
final PwmSetting pwmSetting = PwmSetting.forKey(ref.getRecordID());
if (pwmSetting.getSyntax() == PwmSettingSyntax.X509CERT) {
final StoredValue storedValue;
if (pwmSetting.getCategory().hasProfiles()) {
storedValue = storedConfiguration.readSetting(pwmSetting, ref.getProfileID());
} else {
storedValue = storedConfiguration.readSetting(pwmSetting);
}
final X509Certificate[] certificates = (X509Certificate[])storedValue.toNativeObject();
certificateDebugDataItems.addAll(makeItems(pwmSetting, ref.getProfileID(), certificates));
} else if (pwmSetting.getSyntax() == PwmSettingSyntax.ACTION) {
final StoredValue storedValue;
if (pwmSetting.getCategory().hasProfiles()) {
storedValue = storedConfiguration.readSetting(pwmSetting, ref.getProfileID());
} else {
storedValue = storedConfiguration.readSetting(pwmSetting);
}
final List<ActionConfiguration> actionConfigurations = (List)storedValue.toNativeObject();
for (final ActionConfiguration actionConfiguration : actionConfigurations) {
final X509Certificate[] certificates = actionConfiguration.getCertificates();
certificateDebugDataItems.addAll(makeItems(pwmSetting, ref.getProfileID(), certificates));
}
}
}
}
Collections.sort(certificateDebugDataItems);
return Collections.unmodifiableList(certificateDebugDataItems);
}
Collection<CertificateDebugDataItem> makeItems(
final PwmSetting setting,
final String profileId,
final X509Certificate[] certificates
) throws PwmUnrecoverableException {
if (certificates == null) {
return Collections.emptyList();
}
final List<CertificateDebugDataItem> certificateDebugDataItems = new ArrayList<>();
for (final X509Certificate certificate : certificates) {
final CertificateDebugDataItem certificateDebugDataItem = makeItem(setting, profileId, certificate);
certificateDebugDataItems.add(certificateDebugDataItem);
}
return certificateDebugDataItems;
}
CertificateDebugDataItem makeItem(
final PwmSetting setting,
final String profileId,
final X509Certificate certificate
)
throws PwmUnrecoverableException
{
final CertificateDebugDataItem item = new CertificateDebugDataItem();
item.setMenuLocation(setting.toMenuLocationDebug(profileId, PwmConstants.DEFAULT_LOCALE));
item.setSubject(certificate.getSubjectDN().toString());
item.setSerial(certificate.getSerialNumber().toString());
item.setAlgorithm(certificate.getSigAlgName());
item.setIssueDate(certificate.getNotBefore().toInstant());
item.setExpirationDate(certificate.getNotAfter().toInstant());
try {
item.setDetail(X509Utils.makeDetailText(certificate));
} catch (CertificateEncodingException e) {
LOGGER.error("unexpected error parsing certificate detail text: " + e.getMessage());
}
return item;
}
public static class CertificateDebugDataItem implements Serializable, Comparable {
private String menuLocation;
private String subject;
private String serial;
private String algorithm;
private Instant expirationDate;
private Instant issueDate;
private String detail;
public String getMenuLocation() {
return menuLocation;
}
public void setMenuLocation(final String menuLocation) {
this.menuLocation = menuLocation;
}
public String getSubject() {
return subject;
}
public void setSubject(final String subject) {
this.subject = subject;
}
public String getSerial() {
return serial;
}
public void setSerial(final String serial) {
this.serial = serial;
}
public String getAlgorithm() {
return algorithm;
}
public void setAlgorithm(final String algorithm) {
this.algorithm = algorithm;
}
public Instant getExpirationDate() {
return expirationDate;
}
public void setExpirationDate(final Instant expirationDate) {
this.expirationDate = expirationDate;
}
public Instant getIssueDate() {
return issueDate;
}
public void setIssueDate(final Instant issueDate) {
this.issueDate = issueDate;
}
public String getDetail() {
return detail;
}
public void setDetail(final String detail) {
this.detail = detail;
}
@Override
public int compareTo(final Object o) {
return expirationDate.compareTo(((CertificateDebugDataItem)o).getExpirationDate());
}
}
}