View Javadoc
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  package org.apache.wss4j.stax.impl.processor.output;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import javax.xml.namespace.QName;
25  import javax.xml.stream.XMLStreamConstants;
26  import javax.xml.stream.XMLStreamException;
27  
28  import org.apache.wss4j.common.ext.WSSecurityException;
29  import org.apache.wss4j.stax.ext.WSSConstants;
30  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
31  import org.apache.wss4j.stax.utils.WSSUtils;
32  import org.apache.xml.security.exceptions.XMLSecurityException;
33  import org.apache.xml.security.stax.ext.AbstractOutputProcessor;
34  import org.apache.xml.security.stax.ext.OutputProcessorChain;
35  import org.apache.xml.security.stax.ext.SecurePart;
36  import org.apache.xml.security.stax.ext.stax.XMLSecAttribute;
37  import org.apache.xml.security.stax.ext.stax.XMLSecEndElement;
38  import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
39  import org.apache.xml.security.stax.ext.stax.XMLSecStartElement;
40  
41  /**
42   * Processor to build the Security Header structure
43   */
44  public class SecurityHeaderOutputProcessor extends AbstractOutputProcessor {
45  
46      public SecurityHeaderOutputProcessor() throws XMLSecurityException {
47          super();
48          setPhase(WSSConstants.Phase.PREPROCESSING);
49      }
50  
51      @Override
52      public void processEvent(XMLSecEvent xmlSecEvent, OutputProcessorChain outputProcessorChain)
53          throws XMLStreamException, XMLSecurityException {
54  
55          boolean eventHandled = false;
56  
57          switch (xmlSecEvent.getEventType()) {   //NOPMD
58              case XMLStreamConstants.START_ELEMENT:
59                  XMLSecStartElement xmlSecStartElement = xmlSecEvent.asStartElement();
60                  final String soapMessageVersion = WSSUtils.getSOAPMessageVersionNamespace(xmlSecStartElement);
61                  int level = xmlSecStartElement.getDocumentLevel();
62  
63                  if (level == 1 && soapMessageVersion == null) {
64                      throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "notASOAPMessage");
65                  } else if (level == 1) {
66                      //set correct namespace on secure parts
67                      List<SecurePart> encryptionParts = securityProperties.getEncryptionSecureParts();
68                      if (!encryptionParts.isEmpty()) {
69                          for (SecurePart securePart : encryptionParts) {
70                              // Check to see if the wrong SOAP NS was used
71                              changeSOAPNamespace(securePart, soapMessageVersion);
72  
73                              if (securePart.getIdToSecure() == null) {
74                                  outputProcessorChain.getSecurityContext().putAsMap(
75                                          WSSConstants.ENCRYPTION_PARTS,
76                                          securePart.getName(),
77                                          securePart
78                                  );
79                              } else {
80                                  outputProcessorChain.getSecurityContext().putAsMap(
81                                          WSSConstants.ENCRYPTION_PARTS,
82                                          securePart.getIdToSecure(),
83                                          securePart
84                                  );
85                              }
86                          }
87                      }
88                      List<SecurePart> signatureParts = securityProperties.getSignatureSecureParts();
89                      if (!signatureParts.isEmpty()) {
90                          for (SecurePart securePart : signatureParts) {
91                              // Check to see if the wrong SOAP NS was used
92                              changeSOAPNamespace(securePart, soapMessageVersion);
93  
94                              if (securePart.getIdToSecure() == null) {
95                                  outputProcessorChain.getSecurityContext().putAsMap(
96                                          WSSConstants.SIGNATURE_PARTS,
97                                          securePart.getName(),
98                                          securePart
99                                  );
100                             } else {
101                                 outputProcessorChain.getSecurityContext().putAsMap(
102                                         WSSConstants.SIGNATURE_PARTS,
103                                         securePart.getIdToSecure(),
104                                         securePart
105                                 );
106                             }
107                         }
108                     }
109                 } else if (WSSUtils.isSecurityHeaderElement(xmlSecEvent, ((WSSSecurityProperties) getSecurityProperties()).getActor())) {
110                         //remove this processor. its no longer needed.
111                         outputProcessorChain.removeProcessor(this);
112                 } else if (level == 2
113                         && WSSConstants.TAG_SOAP_BODY_LN.equals(xmlSecStartElement.getName().getLocalPart())
114                         && xmlSecStartElement.getName().getNamespaceURI().equals(soapMessageVersion)) {
115                     //hmm it seems we don't have a soap header in the current document
116                     //so output one and add securityHeader
117 
118                     //create subchain and output soap-header and securityHeader
119                     OutputProcessorChain subOutputProcessorChain =
120                         outputProcessorChain.createSubChain(this, xmlSecStartElement.getParentXMLSecStartElement());
121                     createStartElementAndOutputAsEvent(subOutputProcessorChain,
122                             new QName(soapMessageVersion, WSSConstants.TAG_SOAP_HEADER_LN, WSSConstants.PREFIX_SOAPENV), true, null);
123                     boolean mustUnderstand = ((WSSSecurityProperties) getSecurityProperties()).isMustUnderstand();
124                     buildSecurityHeader(soapMessageVersion, subOutputProcessorChain, mustUnderstand);
125                     createEndElementAndOutputAsEvent(subOutputProcessorChain,
126                             new QName(soapMessageVersion, WSSConstants.TAG_SOAP_HEADER_LN, WSSConstants.PREFIX_SOAPENV));
127 
128                     //output current soap-header event
129                     outputProcessorChain.processEvent(xmlSecEvent);
130                     //remove this processor. its no longer needed.
131                     outputProcessorChain.removeProcessor(this);
132 
133                     eventHandled = true;
134                 }
135                 break;
136             case XMLStreamConstants.END_ELEMENT:
137                 XMLSecEndElement xmlSecEndElement = xmlSecEvent.asEndElement();
138                 int documentLevel = xmlSecEndElement.getDocumentLevel();
139                 String soapMessageVersionNS =
140                     WSSUtils.getSOAPMessageVersionNamespace(xmlSecEndElement.getParentXMLSecStartElement());
141                 if (documentLevel == 2 && WSSConstants.TAG_SOAP_HEADER_LN.equals(xmlSecEndElement.getName().getLocalPart())
142                         && xmlSecEndElement.getName().getNamespaceURI().equals(soapMessageVersionNS)) {
143                     OutputProcessorChain subOutputProcessorChain = outputProcessorChain.createSubChain(this);
144                     boolean mustUnderstand = ((WSSSecurityProperties) getSecurityProperties()).isMustUnderstand();
145                     buildSecurityHeader(xmlSecEndElement.getName().getNamespaceURI(), subOutputProcessorChain, mustUnderstand);
146                     //output current soap-header event
147                     outputProcessorChain.processEvent(xmlSecEvent);
148                     //remove this processor. its no longer needed.
149                     outputProcessorChain.removeProcessor(this);
150 
151                     eventHandled = true;
152                 }
153                 break;
154         }
155 
156         if (!eventHandled) {
157             outputProcessorChain.processEvent(xmlSecEvent);
158         }
159     }
160 
161     private void changeSOAPNamespace(SecurePart securePart, String soapVersion) {
162         final QName secureName = securePart.getName();
163         if (secureName != null) {
164             QName newName = secureName;
165 
166             if (WSSConstants.NS_SOAP11.equals(secureName.getNamespaceURI())
167                 && WSSConstants.NS_SOAP12.equals(soapVersion)) {
168                 newName = new QName(soapVersion, secureName.getLocalPart(), secureName.getPrefix());
169             } else if (WSSConstants.NS_SOAP12.equals(secureName.getNamespaceURI())
170                 && WSSConstants.NS_SOAP11.equals(soapVersion)) {
171                 newName = new QName(soapVersion, secureName.getLocalPart(), secureName.getPrefix());
172             }
173 
174             if (!secureName.equals(newName)) {
175                 securePart.setName(newName);
176             }
177         }
178     }
179 
180     private void buildSecurityHeader(
181         String soapMessageVersion,
182         OutputProcessorChain subOutputProcessorChain,
183         boolean mustUnderstand
184     ) throws XMLStreamException, XMLSecurityException {
185         List<XMLSecAttribute> attributes = new ArrayList<>(1);
186         final String actor = ((WSSSecurityProperties) getSecurityProperties()).getActor();
187         if (actor != null && !actor.isEmpty()) {
188             if (WSSConstants.NS_SOAP11.equals(soapMessageVersion)) {
189                 attributes.add(createAttribute(WSSConstants.ATT_SOAP11_ACTOR, actor));
190             } else {
191                 attributes.add(createAttribute(WSSConstants.ATT_SOAP12_ROLE, actor));
192             }
193         }
194         if (mustUnderstand) {
195             if (WSSConstants.NS_SOAP11.equals(soapMessageVersion)) {
196                 attributes.add(createAttribute(WSSConstants.ATT_SOAP11_MUST_UNDERSTAND, "1"));
197             } else {
198                 attributes.add(createAttribute(WSSConstants.ATT_SOAP12_MUST_UNDERSTAND, "true"));
199             }
200         }
201         createStartElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_SECURITY, true, attributes);
202         createEndElementAndOutputAsEvent(subOutputProcessorChain, WSSConstants.TAG_WSSE_SECURITY);
203     }
204 }