/** * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2006-2016 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.italiangrid.voms.aa.x509.impl; import java.io.StringWriter; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.glite.security.voms.admin.error.VOMSException; import org.italiangrid.voms.aa.VOMSErrorMessage; import org.italiangrid.voms.aa.VOMSWarningMessage; import org.italiangrid.voms.aa.x509.VOMSResponseBuilder; import org.opensaml.xml.util.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Element; public enum VOMSResponseBuilderImpl implements VOMSResponseBuilder { INSTANCE; private final Logger log = LoggerFactory .getLogger(VOMSResponseBuilderImpl.class); protected DocumentBuilder docBuilder; private TransformerFactory transformerFactory = TransformerFactory .newInstance(); private VOMSResponseBuilderImpl() { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setIgnoringComments(true); factory.setNamespaceAware(false); factory.setValidating(false); try { docBuilder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { log.error("Error configuring DOM document builder: " + e.getMessage(), e); throw new VOMSException(e); } } protected String xmlDocAsString(Document doc) { Transformer transformer; try { transformer = transformerFactory.newTransformer(); } catch (TransformerConfigurationException e) { log.error("Error creating XML transformer:" + e.getMessage(), e); throw new VOMSException(e); } StringWriter writer = new StringWriter(); DOMSource source = new DOMSource(doc); StreamResult res = new StreamResult(writer); try { transformer.transform(source, res); writer.flush(); } catch (TransformerException e) { log.error("Error caught serializing XML :" + e.getMessage()); if (log.isDebugEnabled()) log.error(e.getMessage(), e); throw new VOMSException("Error caugh serializing XML :", e); } String output = writer.toString(); log.debug("Serialized: {}", output); return output; } @Override public String createResponse(byte[] acBytes, List<VOMSWarningMessage> warnings) { Document response = docBuilder.newDocument(); VOMSResponseFragment frag = new VOMSResponseFragment(response); frag.buildACElement(Base64.encodeBytes(acBytes), warnings); response.appendChild(frag.getFragment()); return xmlDocAsString(response); } @Override public String createErrorResponse(VOMSErrorMessage errorMessage) { Document response = docBuilder.newDocument(); VOMSResponseFragment frag = new VOMSResponseFragment(response); frag.buildErrorElement(errorMessage); response.appendChild(frag.getFragment()); return xmlDocAsString(response); } @Override public String createLegacyErrorResponse(VOMSErrorMessage errorMessage) { Document response = docBuilder.newDocument(); VOMSResponseFragment frag = new VOMSResponseFragment(response); frag.buildLegacyErrorElement(errorMessage); response.appendChild(frag.getFragment()); return xmlDocAsString(response); } } class VOMSResponseFragment { private Document doc; DocumentFragment fragment; VOMSResponseFragment(Document document) { this.doc = document; fragment = doc.createDocumentFragment(); } void buildACElement(String base64EncodedACString, List<VOMSWarningMessage> warnings) { Element root = doc.createElement("voms"); fragment.appendChild(root); Element ac = doc.createElement("ac"); appendTextChild(ac, base64EncodedACString); root.appendChild(ac); for (VOMSWarningMessage w : warnings) { Element warningElement = doc.createElement("warning"); String warningMessage = String.format("WARNING: %s : %s", w.getVo(), w.getMessage()); appendTextChild(warningElement, warningMessage); root.appendChild(warningElement); } } void buildLegacyErrorElement(VOMSErrorMessage m) { Element root = doc.createElement("vomsans"); Element error = doc.createElement("error"); Element errorItem = doc.createElement("item"); Element errorItemNumber = doc.createElement("number"); Element errorItemMessage = doc.createElement("message"); Element ac = doc.createElement("ac"); Element version = doc.createElement("version"); appendTextChild(errorItemNumber, Integer.toString(m.getError().getLegacyErrorCode())); appendTextChild(errorItemMessage, m.getMessage()); appendTextChild(version, "3"); // This nonsense is needed so that legacy voms-clients correctly parse the // generated response and report errors as expected. appendTextChild(ac, "QQ==\n"); root.appendChild(version); root.appendChild(error); root.appendChild(ac); error.appendChild(errorItem); errorItem.appendChild(errorItemNumber); errorItem.appendChild(errorItemMessage); fragment.appendChild(root); } void buildErrorElement(VOMSErrorMessage m) { Element root = doc.createElement("voms"); Element error = doc.createElement("error"); Element errorCodeElement = doc.createElement("code"); Element errorMessageElement = doc.createElement("message"); appendTextChild(errorMessageElement, m.getMessage()); appendTextChild(errorCodeElement, Integer.toString(m.getError().getLegacyErrorCode())); error.appendChild(errorCodeElement); error.appendChild(errorMessageElement); root.appendChild(error); fragment.appendChild(root); } DocumentFragment getFragment() { return fragment; } private void appendTextChild(Element e, String text) { e.appendChild(doc.createTextNode(text)); } }