1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.ws.security.message;
21  
22  import java.util.Iterator;
23  import java.util.LinkedList;
24  import java.util.List;
25  
26  import javax.xml.namespace.QName;
27  
28  import org.w3c.dom.Document;
29  import org.w3c.dom.Element;
30  import org.w3c.dom.Node;
31  import org.w3c.dom.NodeList;
32  
33  /**
34   * Applies message transformations to the tests in
35   * org.apache.ws.security.message.RequireSignedEncryptedDataElementsTest
36   * 
37   * @author <a href="mailto:alessio.soldano@jboss.com">Alessio Soldano</a>
38   */
39  public class TestMessageTransformer extends org.junit.Assert {
40      private static final org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
41          .getLog(TestMessageTransformer.class);
42  
43      public static Element duplicateEncryptedDataInWsseHeader(Element saaj, boolean moveReferenceList) {
44          if (moveReferenceList) {
45              moveReferenceList(saaj);
46          }
47          Element body = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
48                                                              "Body"), true);
49          Element encData = getFirstChildElement(body, new QName("http://www.w3.org/2001/04/xmlenc#",
50                                                                 "EncryptedData"), true);
51          Element newEncData = createNewEncryptedData(encData);
52          Element sh = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
53                                                            "Header"), true);
54          Element wsseHeader = getFirstChildElement(sh,
55                                                    new QName(
56                                                              "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
57                                                              "Security"), true);
58  
59          Node newWsseHeader = wsseHeader.cloneNode(false);
60          Node cur = wsseHeader.getFirstChild();
61  
62          String newId = newEncData.getAttributeNS(null, "Id");
63          while (cur != null) {
64              cur = copyHeadersAndUpdateRefList(cur, newWsseHeader, newId);
65          }
66          newWsseHeader.appendChild(newEncData);
67  
68          if (!moveReferenceList) {
69              updateEncryptedKeyRefList(newWsseHeader, newId);
70          }
71  
72          Node parent = wsseHeader.getParentNode();
73          parent.removeChild(wsseHeader);
74          parent.appendChild(newWsseHeader);
75          print(saaj.getOwnerDocument());
76          return newEncData;
77      }
78  
79      public static Element duplicateEncryptedDataInWsseWrapperHeader(Element saaj, boolean moveReferenceList) {
80          if (moveReferenceList) {
81              moveReferenceList(saaj);
82          }
83          Element body = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
84                                                              "Body"), true);
85          Element encData = getFirstChildElement(body, new QName("http://www.w3.org/2001/04/xmlenc#",
86                                                                 "EncryptedData"), true);
87          Element newEncData = createNewEncryptedData(encData);
88          Element sh = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
89                                                            "Header"), true);
90          Element signature = getFirstChildElement(sh, new QName("http://www.w3.org/2000/09/xmldsig#",
91                                                                 "Signature"), true);
92  
93          Node wsseHeader = signature.getParentNode();
94          Node newWsseHeader = wsseHeader.cloneNode(false);
95          Node cur = wsseHeader.getFirstChild();
96          String newId = newEncData.getAttributeNS(null, "Id");
97          while (!cur.isSameNode(signature)) {
98              cur = copyHeadersAndUpdateRefList(cur, newWsseHeader, newId);
99          }
100         Element wrapper = encData.getOwnerDocument().createElementNS(null, "a");
101         wrapper.appendChild(newEncData);
102         newWsseHeader.appendChild(wrapper);
103         while (cur != null) {
104             cur = copyHeadersAndUpdateRefList(cur, newWsseHeader, newId);
105         }
106 
107         if (!moveReferenceList) {
108             updateEncryptedKeyRefList(newWsseHeader, newId);
109         }
110 
111         Node parent = wsseHeader.getParentNode();
112         parent.removeChild(wsseHeader);
113         parent.appendChild(newWsseHeader);
114         print(saaj.getOwnerDocument());
115         return newEncData;
116     }
117 
118     public static Element duplicateEncryptedDataInExternalWrapperElement(Element saaj,
119                                                                          boolean moveReferenceList) {
120         if (moveReferenceList) {
121             moveReferenceList(saaj);
122         }
123         Element body = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
124                                                             "Body"), true);
125         Element encData = getFirstChildElement(body, new QName("http://www.w3.org/2001/04/xmlenc#",
126                                                                "EncryptedData"), true);
127         Element newEncData = createNewEncryptedData(encData);
128         Element sh = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
129                                                           "Header"), true);
130         Element wsseHeader = getFirstChildElement(sh,
131                                                   new QName(
132                                                             "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
133                                                             "Security"), true);
134 
135         Node newWsseHeader = wsseHeader.cloneNode(false);
136         Node cur = wsseHeader.getFirstChild();
137         String newId = newEncData.getAttributeNS(null, "Id");
138         while (cur != null) {
139             cur = copyHeadersAndUpdateRefList(cur, newWsseHeader, newId);
140         }
141         sh.removeChild(wsseHeader);
142         sh.appendChild(newWsseHeader);
143 
144         if (!moveReferenceList) {
145             updateEncryptedKeyRefList(newWsseHeader, newId);
146         }
147 
148         Element wrapper = encData.getOwnerDocument().createElementNS(null, "a");
149         wrapper.setAttributeNS("http://schemas.xmlsoap.org/soap/envelope/", "mustUnderstand", "0");
150         wrapper.setAttributeNS("http://schemas.xmlsoap.org/soap/envelope/", "actor", "foo");
151         wrapper.appendChild(newEncData);
152         sh.appendChild(wrapper);
153         print(saaj.getOwnerDocument());
154         return newEncData;
155     }
156     
157     public static Element addEncryptedDataWithEmbeddedEncryptedKeyInWsseHeader(Element saaj) {
158         moveReferenceList(saaj);
159         Element body = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
160                                                             "Body"), true);
161         Element encData = getFirstChildElement(body, new QName("http://www.w3.org/2001/04/xmlenc#",
162                                                                "EncryptedData"), true);
163 
164         Element newEncData = (Element)encData.cloneNode(true);
165         String newId = newEncData.getAttributeNS(null, "Id") + "b";
166         newEncData.setAttributeNS(null, "Id", newId);
167 
168         Element encKey = getFirstChildElement(saaj, new QName("http://www.w3.org/2001/04/xmlenc#",
169                                                               "EncryptedKey"), true);
170         Element newEncKey = (Element)encKey.cloneNode(true);
171         String newEcId = newEncKey.getAttributeNS(null, "Id") + "b";
172         newEncKey.setAttributeNS(null, "Id", newEcId);
173 
174         Element keyInfo = getFirstChildElement(newEncData, new QName("http://www.w3.org/2000/09/xmldsig#",
175                                                                      "KeyInfo"), true);
176         Element str = getFirstChildElement(newEncData,
177                                            new QName(
178                                                      "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
179                                                      "SecurityTokenReference"), true);
180         keyInfo.replaceChild(newEncKey, str);
181 
182         Element wsseHeader = getFirstChildElement(saaj,
183                                                   new QName(
184                                                             "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
185                                                             "Security"), true);
186 
187         Node newWsseHeader = wsseHeader.cloneNode(false);
188         Node cur = wsseHeader.getFirstChild();
189 
190         while (cur != null) {
191             cur = copyHeadersAndUpdateRefList(cur, newWsseHeader, newId);
192         }
193         newWsseHeader.appendChild(newEncData);
194 
195         Node parent = wsseHeader.getParentNode();
196         parent.removeChild(wsseHeader);
197         parent.appendChild(newWsseHeader);
198         print(saaj.getOwnerDocument());
199         return newEncData;
200 
201     }
202 
203     private static void moveReferenceList(Element saaj) {
204         Element sh = getFirstChildElement(saaj, new QName("http://schemas.xmlsoap.org/soap/envelope/",
205                                                           "Header"), true);
206         Element encKey = getFirstChildElement(sh, new QName("http://www.w3.org/2001/04/xmlenc#",
207                                                             "EncryptedKey"), true);
208         Element refList = getFirstChildElement(encKey, new QName("http://www.w3.org/2001/04/xmlenc#",
209                                                                  "ReferenceList"), true);
210 
211         Node wsseHeader = encKey.getParentNode();
212         encKey.removeChild(refList);
213         wsseHeader.appendChild(refList);
214     }
215 
216     private static void updateEncryptedKeyRefList(Node wsseHeader, String newId) {
217         Element encryptedKey = getFirstChildElement(wsseHeader,
218                                                     new QName("http://www.w3.org/2001/04/xmlenc#",
219                                                               "EncryptedKey"), true);
220         Element ref = getFirstChildElement(encryptedKey, new QName("http://www.w3.org/2001/04/xmlenc#",
221                                                                    "DataReference"), true);
222         Element newRef = (Element)ref.cloneNode(true);
223         newRef.setAttributeNS(null, "URI", "#" + newId);
224         ref.getParentNode().appendChild(newRef);
225     }
226 
227     private static void print(Document doc) {
228         if (LOG.isDebugEnabled()) {
229             LOG.debug("After transformation....");
230             String outputString = org.apache.ws.security.util.XMLUtils.PrettyDocumentToString(doc);
231             LOG.debug(outputString);
232         }
233     }
234 
235     private static Element createNewEncryptedData(Element encData) {
236         Element newEncData = (Element)encData.cloneNode(true);
237         String id = newEncData.getAttributeNS(null, "Id");
238         String newId = id + "b";
239         newEncData.setAttributeNS(null, "Id", newId);
240         return newEncData;
241     }
242 
243     private static Node copyHeadersAndUpdateRefList(Node cur, Node dest, String newId) {
244         Node temp = cur.cloneNode(true);
245         dest.appendChild(temp);
246         if (newId != null && temp.getNodeType() == Node.ELEMENT_NODE) {
247             Element t = (Element)temp;
248             if (t.getLocalName().equals("ReferenceList")) {
249                 Element ref = getFirstChildElement(t, new QName("http://www.w3.org/2001/04/xmlenc#",
250                                                                 "DataReference"), true);
251                 Element newRef = (Element)ref.cloneNode(true);
252                 newRef.setAttributeNS(null, "URI", "#" + newId);
253                 t.appendChild(newRef);
254             }
255         }
256         return cur.getNextSibling();
257     }
258 
259     private static Element getFirstChildElement(Node node, QName nodeName, boolean recursive) {
260         Element childElement = null;
261         Iterator<Element> it = getChildElements(node, nodeName, recursive).iterator();
262         if (it.hasNext()) {
263             childElement = (Element)it.next();
264         }
265         return childElement;
266     }
267 
268     private static List<Element> getChildElements(Node node, QName nodeName, boolean recursive) {
269         List<Element> list = new LinkedList<Element>();
270 
271         NodeList nlist = node.getChildNodes();
272         int len = nlist.getLength();
273         for (int i = 0; i < len; i++) {
274             Node child = nlist.item(i);
275             if (child.getNodeType() == Node.ELEMENT_NODE) {
276                 search(list, (Element)child, nodeName, recursive);
277             }
278         }
279         return list;
280     }
281 
282     private static void search(List<Element> list, Element baseElement, QName nodeName, boolean recursive) {
283         if (nodeName == null) {
284             list.add(baseElement);
285         } else {
286             QName qname;
287             if (nodeName.getNamespaceURI().length() > 0) {
288                 qname = new QName(baseElement.getNamespaceURI(), baseElement.getLocalName());
289             } else {
290                 qname = new QName(baseElement.getLocalName());
291             }
292             if (qname.equals(nodeName)) {
293                 list.add(baseElement);
294             }
295         }
296         if (recursive) {
297             NodeList nlist = baseElement.getChildNodes();
298             int len = nlist.getLength();
299             for (int i = 0; i < len; i++) {
300                 Node child = nlist.item(i);
301                 if (child.getNodeType() == Node.ELEMENT_NODE) {
302                     search(list, (Element)child, nodeName, recursive);
303                 }
304             }
305         }
306     }
307 
308 }