/*
* DSS - Digital Signature Services
*
* Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
*
* Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* DSS 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>.
*/
package eu.europa.ec.markt.dss.validation102853.report;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import org.apache.commons.lang.StringEscapeUtils;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.jce.X509Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.TSLConstant;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.validation102853.CertificateQualification;
import eu.europa.ec.markt.dss.validation102853.SignatureQualification;
import eu.europa.ec.markt.dss.validation102853.SignatureType;
import eu.europa.ec.markt.dss.validation102853.TLQualification;
import eu.europa.ec.markt.dss.validation102853.policy.ProcessParameters;
import eu.europa.ec.markt.dss.validation102853.policy.ValidationPolicy;
import eu.europa.ec.markt.dss.validation102853.processes.dss.InvolvedServiceInfo;
import eu.europa.ec.markt.dss.validation102853.rules.AttributeName;
import eu.europa.ec.markt.dss.validation102853.rules.AttributeValue;
import eu.europa.ec.markt.dss.validation102853.rules.Indication;
import eu.europa.ec.markt.dss.validation102853.rules.NodeName;
import eu.europa.ec.markt.dss.validation102853.rules.SubIndication;
import eu.europa.ec.markt.dss.validation102853.xml.XmlDom;
import eu.europa.ec.markt.dss.validation102853.xml.XmlNode;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.LABEL_TINTWS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.LABEL_TINVTWS;
/**
* This class builds a SimpleReport XmlDom from the diagnostic data and detailed validation report.
* <p/>
* DISCLAIMER: Project owner DG-MARKT.
*
* @author <a href="mailto:dgmarkt.Project-DSS@arhs-developments.com">ARHS Developments</a>
* @version $Revision: 1016 $ - $Date: 2011-06-17 15:30:45 +0200 (Fri, 17 Jun 2011) $
*/
public class SimpleReportBuilder {
private static final Logger LOG = LoggerFactory.getLogger(SimpleReportBuilder.class);
private final ValidationPolicy constraintData;
private final DiagnosticData diagnosticData;
private int totalSignatureCount = 0;
private int validSignatureCount = 0;
public SimpleReportBuilder(final ValidationPolicy constraintData, final DiagnosticData diagnosticData) {
this.constraintData = constraintData;
this.diagnosticData = diagnosticData;
}
/**
* This method generates the validation simpleReport.
*
* @param params validation process parameters
* @return the object representing {@code SimpleReport}
*/
public SimpleReport build(final ProcessParameters params) {
final XmlNode simpleReport = new XmlNode(NodeName.SIMPLE_REPORT);
simpleReport.setNameSpace(XmlDom.NAMESPACE);
try {
addPolicyNode(simpleReport);
addValidationTime(params, simpleReport);
addDocumentName(simpleReport);
addSignatures(params, simpleReport);
addStatistics(simpleReport);
} catch (Exception e) {
if (!"WAS TREATED".equals(e.getMessage())) {
notifyException(simpleReport, e);
}
}
final Document reportDocument = simpleReport.toDocument();
return new SimpleReport(reportDocument);
}
private void addPolicyNode(final XmlNode report) {
final XmlNode policyNode = report.addChild(NodeName.POLICY);
final String policyName = constraintData.getPolicyName();
final String policyDescription = constraintData.getPolicyDescription();
policyNode.addChild(NodeName.POLICY_NAME, policyName);
policyNode.addChild(NodeName.POLICY_DESCRIPTION, policyDescription);
}
private void addValidationTime(final ProcessParameters params, final XmlNode report) {
final Date validationTime = params.getCurrentTime();
report.addChild(NodeName.VALIDATION_TIME, DSSUtils.formatDate(validationTime));
}
private void addDocumentName(final XmlNode report) {
final String documentName = diagnosticData.getValue("/DiagnosticData/DocumentName/text()");
report.addChild(NodeName.DOCUMENT_NAME, documentName);
}
private void addSignatures(final ProcessParameters params, final XmlNode simpleReport) throws DSSException {
final List<XmlDom> signatures = diagnosticData.getElements("/DiagnosticData/Signature");
validSignatureCount = 0;
totalSignatureCount = 0;
for (final XmlDom signatureXmlDom : signatures) {
addSignature(params, simpleReport, signatureXmlDom);
}
}
private void addStatistics(XmlNode report) {
report.addChild(NodeName.VALID_SIGNATURES_COUNT, Integer.toString(validSignatureCount));
report.addChild(NodeName.SIGNATURES_COUNT, Integer.toString(totalSignatureCount));
}
/**
* @param params validation process parameters
* @param simpleReport
* @param diagnosticSignature the diagnosticSignature element in the diagnostic data
* @throws DSSException
*/
private void addSignature(final ProcessParameters params, final XmlNode simpleReport, final XmlDom diagnosticSignature) throws DSSException {
totalSignatureCount++;
final XmlNode signatureNode = simpleReport.addChild(NodeName.SIGNATURE);
final String signatureId = diagnosticSignature.getValue("./@Id");
signatureNode.setAttribute(AttributeName.ID, signatureId);
final String type = diagnosticSignature.getValue("./@Type");
addCounterSignature(diagnosticSignature, signatureNode, type);
try {
addSigningTime(diagnosticSignature, signatureNode);
addSignatureFormat(diagnosticSignature, signatureNode);
final String signCertId = diagnosticSignature.getValue("./SigningCertificate/@Id");
final XmlDom signCert = params.getCertificate(signCertId);
addSignedBy(signatureNode, signCert);
XmlDom bvData = params.getBvData();
final XmlDom basicValidationConclusion = bvData.getElement("/BasicValidationData/Signature[@Id='%s']/Conclusion", signatureId);
final XmlDom ltvDom = params.getLtvData();
final XmlDom ltvConclusion = ltvDom.getElement("/LongTermValidationData/Signature[@Id='%s']/Conclusion", signatureId);
final String ltvIndication = ltvConclusion.getValue("./Indication/text()");
final String ltvSubIndication = ltvConclusion.getValue("./SubIndication/text()");
final List<XmlDom> ltvInfoList = ltvConclusion.getElements("./Info");
String indication = ltvIndication;
String subIndication = ltvSubIndication;
List<XmlDom> infoList = new ArrayList<XmlDom>();
infoList.addAll(ltvInfoList);
final List<XmlDom> basicValidationInfoList = basicValidationConclusion.getElements("./Info");
final List<XmlDom> basicValidationWarningList = basicValidationConclusion.getElements("./Warning");
final List<XmlDom> basicValidationErrorList = basicValidationConclusion.getElements("./Error");
final boolean noTimestamp = Indication.INDETERMINATE.equals(ltvIndication) && SubIndication.NO_TIMESTAMP.equals(ltvSubIndication);
final boolean noValidTimestamp = Indication.INDETERMINATE.equals(ltvIndication) && SubIndication.NO_VALID_TIMESTAMP.equals(ltvSubIndication);
if (noTimestamp || noValidTimestamp) {
final String basicValidationConclusionIndication = basicValidationConclusion.getValue("./Indication/text()");
final String basicValidationConclusionSubIndication = basicValidationConclusion.getValue("./SubIndication/text()");
indication = basicValidationConclusionIndication;
subIndication = basicValidationConclusionSubIndication;
infoList = basicValidationInfoList;
if (!Indication.VALID.equals(basicValidationConclusionIndication)) {
if (noTimestamp) {
final XmlNode xmlNode = new XmlNode(NodeName.WARNING, LABEL_TINTWS, null);
final XmlDom xmlDom = xmlNode.toXmlDom();
infoList.add(xmlDom);
} else {
final XmlNode xmlNode = new XmlNode(NodeName.WARNING, LABEL_TINVTWS, null);
final XmlDom xmlDom = xmlNode.toXmlDom();
infoList.add(xmlDom);
infoList.addAll(ltvInfoList);
}
}
}
signatureNode.addChild(NodeName.INDICATION, indication);
if (Indication.VALID.equals(indication)) {
validSignatureCount++;
}
if (!subIndication.isEmpty()) {
signatureNode.addChild(NodeName.SUB_INDICATION, subIndication);
}
if (basicValidationConclusion != null) {
final List<XmlDom> errorMessages = diagnosticSignature.getElements("./ErrorMessage");
for (XmlDom errorDom : errorMessages) {
String errorMessage = errorDom.getText();
errorMessage = StringEscapeUtils.escapeXml(errorMessage);
final XmlNode xmlNode = new XmlNode(NodeName.INFO, errorMessage);
final XmlDom xmlDom = xmlNode.toXmlDom();
infoList.add(xmlDom);
}
}
if (!Indication.VALID.equals(ltvIndication)) {
addBasicInfo(signatureNode, basicValidationErrorList);
}
addBasicInfo(signatureNode, basicValidationWarningList);
addBasicInfo(signatureNode, infoList);
addSignatureProfile(signatureNode, signCert);
final XmlDom signatureScopes = diagnosticSignature.getElement("./SignatureScopes");
addSignatureScope(signatureNode, signatureScopes);
} catch (Exception e) {
notifyException(signatureNode, e);
throw new DSSException("WAS TREATED", e);
}
}
private void addCounterSignature(XmlDom diagnosticSignature, XmlNode signatureNode, String type) {
if (AttributeValue.COUNTERSIGNATURE.equals(type)) {
signatureNode.setAttribute(AttributeName.TYPE, AttributeValue.COUNTERSIGNATURE);
final String parentId = diagnosticSignature.getValue("./ParentId/text()");
signatureNode.setAttribute(AttributeName.PARENT_ID, parentId);
}
}
private void addSignatureScope(final XmlNode signatureNode, final XmlDom signatureScopes) {
if (signatureScopes != null) {
signatureNode.addChild(signatureScopes);
}
}
private void addBasicInfo(final XmlNode signatureNode, final List<XmlDom> basicValidationErrorList) {
for (final XmlDom error : basicValidationErrorList) {
signatureNode.addChild(error);
}
}
private void addSigningTime(final XmlDom diagnosticSignature, final XmlNode signatureNode) {
signatureNode.addChild(NodeName.SIGNING_TIME, diagnosticSignature.getValue("./DateTime/text()"));
}
private void addSignatureFormat(final XmlDom diagnosticSignature, final XmlNode signatureNode) {
signatureNode.setAttribute(NodeName.SIGNATURE_FORMAT, diagnosticSignature.getValue("./SignatureFormat/text()"));
}
private void addSignedBy(final XmlNode signatureNode, final XmlDom signCert) {
String signedBy = "?";
if (signCert != null) {
final String dn = signCert.getValue("./SubjectDistinguishedName[@Format='RFC2253']/text()");
final X509Principal principal = new X509Principal(dn);
final Vector<?> values = principal.getValues(new ASN1ObjectIdentifier("2.5.4.3"));
if (values != null && values.size() > 0) {
final String string = (String) values.get(0);
if (DSSUtils.isNotBlank(string)) {
signedBy = DSSUtils.replaceStrStr(string, "&", "&");
}
if (DSSUtils.isEmpty(signedBy)) {
signedBy = DSSUtils.replaceStrStr(dn, "&", "&");
}
}
}
signatureNode.addChild(NodeName.SIGNED_BY, signedBy);
}
private void addSignatureProfile(XmlNode signatureNode, XmlDom signCert) {
/**
* Here we determine the type of the signature.
*/
SignatureType signatureType = SignatureType.NA;
if (signCert != null) {
signatureType = getSignatureType(signCert);
}
signatureNode.addChild(NodeName.SIGNATURE_LEVEL, signatureType.name());
}
/**
* This method returns the type of the qualification of the signature (signing certificate).
*
* @param signCert
* @return
*/
private SignatureType getSignatureType(final XmlDom signCert) {
final CertificateQualification certQualification = new CertificateQualification();
certQualification.setQcp(signCert.getBoolValue("./QCStatement/QCP/text()"));
certQualification.setQcpp(signCert.getBoolValue("./QCStatement/QCPPlus/text()"));
certQualification.setQcc(signCert.getBoolValue("./QCStatement/QCC/text()"));
certQualification.setQcsscd(signCert.getBoolValue("./QCStatement/QCSSCD/text()"));
final TLQualification trustedListQualification = new TLQualification();
final String caqc = InvolvedServiceInfo.getServiceTypeIdentifier(signCert);
final List<String> qualifiers = InvolvedServiceInfo.getQualifiers(signCert);
trustedListQualification.setCaqc(TSLConstant.CA_QC.equals(caqc));
trustedListQualification.setQcCNoSSCD(InvolvedServiceInfo.isQC_NO_SSCD(qualifiers));
trustedListQualification.setQcForLegalPerson(InvolvedServiceInfo.isQC_FOR_LEGAL_PERSON(qualifiers));
trustedListQualification.setQcSSCDAsInCert(InvolvedServiceInfo.isQCSSCD_STATUS_AS_IN_CERT(qualifiers));
trustedListQualification.setQcWithSSCD(qualifiers.contains(TSLConstant.QC_WITH_SSCD) || qualifiers.contains(TSLConstant.QC_WITH_SSCD_119612));
final SignatureType signatureType = SignatureQualification.getSignatureType(certQualification, trustedListQualification);
return signatureType;
}
/**
* @param signatureNode
* @param exception
*/
private static void notifyException(final XmlNode signatureNode, final Exception exception) {
LOG.error(exception.getMessage(), exception);
signatureNode.removeChild(NodeName.INDICATION);
signatureNode.removeChild(NodeName.SUB_INDICATION);
signatureNode.addChild(NodeName.INDICATION, Indication.INDETERMINATE);
signatureNode.addChild(NodeName.SUB_INDICATION, SubIndication.UNEXPECTED_ERROR);
final String message = DSSUtils.getSummaryMessage(exception, SimpleReportBuilder.class);
signatureNode.addChild(NodeName.INFO, message);
}
}