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.test;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.nio.charset.StandardCharsets;
25  import java.util.ArrayList;
26  import java.util.Enumeration;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.logging.Level;
33  import java.util.logging.LogManager;
34  import java.util.logging.Logger;
35  
36  import javax.security.auth.callback.CallbackHandler;
37  import javax.xml.namespace.NamespaceContext;
38  import javax.xml.parsers.DocumentBuilderFactory;
39  import javax.xml.parsers.ParserConfigurationException;
40  import javax.xml.stream.XMLInputFactory;
41  import javax.xml.stream.XMLStreamException;
42  import javax.xml.stream.XMLStreamReader;
43  import javax.xml.stream.XMLStreamWriter;
44  import javax.xml.transform.TransformerException;
45  import javax.xml.transform.TransformerFactory;
46  import javax.xml.xpath.XPath;
47  import javax.xml.xpath.XPathExpression;
48  import javax.xml.xpath.XPathExpressionException;
49  import javax.xml.xpath.XPathFactory;
50  
51  import org.apache.wss4j.common.bsp.BSPRule;
52  import org.apache.wss4j.common.crypto.Crypto;
53  import org.apache.wss4j.common.crypto.CryptoFactory;
54  import org.apache.wss4j.common.ext.WSSecurityException;
55  import org.apache.wss4j.common.util.SOAPUtil;
56  import org.apache.wss4j.common.util.XMLUtils;
57  import org.apache.wss4j.dom.WSConstants;
58  
59  import org.apache.wss4j.dom.engine.WSSConfig;
60  import org.apache.wss4j.dom.engine.WSSecurityEngine;
61  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
62  import org.apache.wss4j.dom.handler.HandlerAction;
63  import org.apache.wss4j.dom.handler.RequestData;
64  import org.apache.wss4j.dom.handler.WSHandler;
65  import org.apache.wss4j.dom.handler.WSHandlerConstants;
66  import org.apache.wss4j.dom.handler.WSHandlerResult;
67  import org.apache.wss4j.dom.util.WSSecurityUtil;
68  import org.apache.wss4j.stax.ext.WSSConstants;
69  import org.apache.wss4j.stax.ext.WSSSecurityProperties;
70  import org.apache.wss4j.stax.setup.ConfigurationConverter;
71  import org.apache.wss4j.stax.setup.InboundWSSec;
72  import org.apache.wss4j.stax.setup.OutboundWSSec;
73  import org.apache.wss4j.stax.setup.WSSec;
74  import org.apache.wss4j.stax.test.utils.StAX2DOM;
75  import org.apache.wss4j.stax.test.utils.XmlReaderToWriter;
76  import org.apache.xml.security.exceptions.XMLSecurityException;
77  import org.apache.xml.security.stax.securityEvent.SecurityEvent;
78  import org.apache.xml.security.stax.securityEvent.SecurityEventConstants;
79  import org.apache.xml.security.stax.securityEvent.SecurityEventListener;
80  
81  import org.w3c.dom.Document;
82  import org.w3c.dom.Element;
83  
84  import static org.junit.jupiter.api.Assertions.assertNotEquals;
85  import static org.junit.jupiter.api.Assertions.assertNotNull;
86  import static org.junit.jupiter.api.Assertions.fail;
87  
88  public abstract class AbstractTestBase {
89  
90      //javax.xml.transform.Transformer transformer = TransformerFactory.newInstance().newTransformer();
91      //transformer.transform(new StreamSource(new ByteArrayInputStream(baos.toByteArray())), new StreamResult(System.out));
92  
93      protected static final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
94      protected static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
95      protected DocumentBuilderFactory documentBuilderFactory;
96  
97      protected static final String SECURED_DOCUMENT = "securedDocument";
98  
99      static {
100         LogManager.getLogManager().addLogger(Logger.getLogger("org.jcp.xml.dsig.internal.dom"));
101         LogManager.getLogManager().getLogger("org.jcp.xml.dsig.internal.dom").setLevel(Level.FINE);
102         WSSConfig.init();
103     }
104 
105     public AbstractTestBase() {
106         documentBuilderFactory = DocumentBuilderFactory.newInstance();
107         documentBuilderFactory.setNamespaceAware(true);
108         documentBuilderFactory.setIgnoringComments(false);
109         documentBuilderFactory.setCoalescing(false);
110         documentBuilderFactory.setIgnoringElementContentWhitespace(false);
111         xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, false);
112         xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
113         //xmlInputFactory.setProperty(WstxInputProperties.P_MIN_TEXT_SEGMENT, new Integer(5 * 8192));
114     }
115 
116     public Document doInboundSecurity(WSSSecurityProperties securityProperties, InputStream inputStream)
117             throws XMLStreamException, ParserConfigurationException, XMLSecurityException {
118         return doInboundSecurity(securityProperties, xmlInputFactory.createXMLStreamReader(inputStream), null);
119     }
120 
121     public Document doInboundSecurity(WSSSecurityProperties securityProperties, InputStream inputStream,
122                                       SecurityEventListener securityEventListener)
123             throws XMLStreamException, ParserConfigurationException, XMLSecurityException {
124         return doInboundSecurity(securityProperties, xmlInputFactory.createXMLStreamReader(inputStream), securityEventListener);
125     }
126 
127     public Document doInboundSecurity(WSSSecurityProperties securityProperties, InputStream inputStream,
128                                       List<SecurityEvent> securityEventList, SecurityEventListener securityEventListener)
129             throws XMLStreamException, ParserConfigurationException, XMLSecurityException {
130         return doInboundSecurity(securityProperties, xmlInputFactory.createXMLStreamReader(inputStream), securityEventList, securityEventListener);
131     }
132 
133     public Document doInboundSecurity(WSSSecurityProperties securityProperties, XMLStreamReader xmlStreamReader)
134             throws XMLStreamException, ParserConfigurationException, XMLSecurityException {
135         return doInboundSecurity(securityProperties, xmlStreamReader, null);
136     }
137 
138     public Document doInboundSecurity(WSSSecurityProperties securityProperties, XMLStreamReader xmlStreamReader,
139                                       SecurityEventListener securityEventListener)
140             throws XMLStreamException, ParserConfigurationException, XMLSecurityException {
141         return doInboundSecurity(securityProperties, xmlStreamReader, new ArrayList<SecurityEvent>(), securityEventListener);
142     }
143 
144     public Document doInboundSecurity(WSSSecurityProperties securityProperties, XMLStreamReader xmlStreamReader,
145                                       List<SecurityEvent> securityEventList, SecurityEventListener securityEventListener)
146             throws XMLStreamException, ParserConfigurationException, XMLSecurityException {
147         InboundWSSec wsSecIn = WSSec.getInboundWSSec(securityProperties, false, true);
148         XMLStreamReader outXmlStreamReader = wsSecIn.processInMessage(xmlStreamReader, securityEventList, securityEventListener);
149         return StAX2DOM.readDoc(documentBuilderFactory.newDocumentBuilder(), outXmlStreamReader);
150     }
151 
152     protected ByteArrayOutputStream doOutboundSecurity(WSSSecurityProperties securityProperties, InputStream sourceDocument)
153             throws Exception {
154         ByteArrayOutputStream baos = new ByteArrayOutputStream();
155         OutboundWSSec wsSecOut = WSSec.getOutboundWSSec(securityProperties);
156         XMLStreamWriter xmlStreamWriter = wsSecOut.processOutMessage(baos, StandardCharsets.UTF_8.name(), new ArrayList<SecurityEvent>());
157         XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(sourceDocument);
158         XmlReaderToWriter.writeAll(xmlStreamReader, xmlStreamWriter);
159         xmlStreamWriter.close();
160         return baos;
161     }
162 
163     protected ByteArrayOutputStream doOutboundSecurity(Map<String, Object> config, InputStream sourceDocument)
164         throws Exception {
165         ByteArrayOutputStream baos = new ByteArrayOutputStream();
166         WSSSecurityProperties securityProperties = ConfigurationConverter.convert(config);
167         OutboundWSSec wsSecOut = WSSec.getOutboundWSSec(securityProperties);
168         XMLStreamWriter xmlStreamWriter = wsSecOut.processOutMessage(baos, StandardCharsets.UTF_8.name(), new ArrayList<SecurityEvent>());
169         XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(sourceDocument);
170         XmlReaderToWriter.writeAll(xmlStreamReader, xmlStreamWriter);
171         xmlStreamWriter.close();
172         return baos;
173     }
174 
175     protected Document doOutboundSecurityWithWSS4J(InputStream sourceDocument, String action, Properties properties)
176             throws WSSecurityException, TransformerException, IOException {
177         Map<String, Object> context = doOutboundSecurityWithWSS4J_1(sourceDocument, action, properties);
178         return (Document) context.get(SECURED_DOCUMENT);
179     }
180 
181     protected Map<String, Object> doOutboundSecurityWithWSS4J_1(
182             InputStream sourceDocument, String action, final Properties properties
183     ) throws WSSecurityException, TransformerException, IOException {
184         CustomWSS4JHandler wss4JHandler = new CustomWSS4JHandler();
185         final Map<String, Object> messageContext = getMessageContext(sourceDocument);
186         messageContext.put(WSHandlerConstants.ACTION, action);
187         messageContext.put(WSHandlerConstants.USER, "transmitter");
188 
189         Properties sigProperties = new Properties();
190         sigProperties.setProperty("org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin");
191         sigProperties.setProperty("org.apache.wss4j.crypto.merlin.keystore.file", "transmitter.jks");
192         sigProperties.setProperty("org.apache.wss4j.crypto.merlin.keystore.password", "default");
193         //sigProperties.setProperty("org.apache.wss4j.crypto.merlin.keystore.alias", "transmitter");
194         wss4JHandler.setPassword(messageContext, "default");
195         messageContext.put(WSHandlerConstants.SIG_PROP_REF_ID, "" + sigProperties.hashCode());
196         messageContext.put("" + sigProperties.hashCode(), sigProperties);
197 
198         Properties encProperties = new Properties();
199         encProperties.setProperty("org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin");
200         encProperties.setProperty("org.apache.wss4j.crypto.merlin.keystore.file", "transmitter.jks");
201         encProperties.setProperty("org.apache.wss4j.crypto.merlin.keystore.password", "default");
202         //sigProperties.setProperty("org.apache.wss4j.crypto.merlin.keystore.alias", "transmitter");
203         wss4JHandler.setPassword(messageContext, "default");
204         messageContext.put(WSHandlerConstants.ENCRYPTION_USER, "receiver");
205         messageContext.put(WSHandlerConstants.ENC_PROP_REF_ID, "" + encProperties.hashCode());
206         messageContext.put("" + encProperties.hashCode(), encProperties);
207 
208         Enumeration<?> enumeration = properties.propertyNames();
209         while (enumeration.hasMoreElements()) {
210             String s = (String) enumeration.nextElement();
211             messageContext.put(s, properties.get(s));
212         }
213 
214         RequestData requestData = new RequestData();
215         requestData.setMsgContext(messageContext);
216         if (messageContext.get(WSHandlerConstants.PW_CALLBACK_REF) == null) {
217             requestData.setCallbackHandler(new WSS4JCallbackHandlerImpl());
218         }
219         requestData.setWssConfig(WSSConfig.getNewInstance());
220 
221         wss4JHandler.doSender(messageContext, requestData, true);
222 
223         return messageContext;
224     }
225 
226     protected Document doInboundSecurityWithWSS4J(Document document, String action) throws Exception {
227         Map<String, Object> messageContext = doInboundSecurityWithWSS4J_1(document, action);
228         return (Document) messageContext.get(SECURED_DOCUMENT);
229     }
230 
231     protected Map<String, Object> doInboundSecurityWithWSS4J_1(Document document, String action) throws Exception {
232         return doInboundSecurityWithWSS4J_1(document, action, new Properties(), false);
233     }
234 
235     protected Map<String, Object> doInboundSecurityWithWSS4J_1(
236             Document document, String action, Properties properties, boolean client
237     ) throws Exception {
238         CustomWSS4JHandler wss4JHandler = new CustomWSS4JHandler();
239         Map<String, Object> messageContext = getMessageContext(document);
240         messageContext.put(WSHandlerConstants.ACTION, action);
241         if (client) {
242             messageContext.put(WSHandlerConstants.USER, "transmitter");
243         } else {
244             messageContext.put(WSHandlerConstants.USER, "receiver");
245         }
246 
247         if (properties.get(WSHandlerConstants.PW_CALLBACK_REF) != null) {
248             messageContext.put(WSHandlerConstants.PW_CALLBACK_REF, properties.get(WSHandlerConstants.PW_CALLBACK_REF));
249         } else {
250             messageContext.put(WSHandlerConstants.PW_CALLBACK_REF, new WSS4JCallbackHandlerImpl());
251         }
252 
253         messageContext.put(WSHandlerConstants.VALIDATE_SAML_SUBJECT_CONFIRMATION, "false");
254         Enumeration<?> enumeration = properties.propertyNames();
255         while (enumeration.hasMoreElements()) {
256             String s = (String) enumeration.nextElement();
257             messageContext.put(s, properties.get(s));
258         }
259 
260         RequestData requestData = new RequestData();
261         requestData.setMsgContext(messageContext);
262         if (client) {
263             final Crypto crypto = CryptoFactory.getInstance("transmitter-crypto.properties");
264             requestData.setDecCrypto(crypto);
265             requestData.setSigVerCrypto(crypto);
266         } else {
267             final Crypto crypto = CryptoFactory.getInstance("receiver-crypto.properties");
268             requestData.setDecCrypto(crypto);
269             requestData.setSigVerCrypto(crypto);
270         }
271 
272         if (properties.get(WSHandlerConstants.ALLOW_USERNAMETOKEN_NOPASSWORD) != null) {
273             messageContext.put(WSHandlerConstants.ALLOW_USERNAMETOKEN_NOPASSWORD,
274                                properties.get(WSHandlerConstants.ALLOW_USERNAMETOKEN_NOPASSWORD));
275         } else if (WSHandlerConstants.USERNAME_TOKEN_SIGNATURE.equals(action)) {
276             messageContext.put(WSHandlerConstants.ALLOW_USERNAMETOKEN_NOPASSWORD, "true");
277         }
278 
279         // Disable PrefixList checking as the stax code doesn't support this yet
280         //todo
281         List<BSPRule> ignoredRules = new ArrayList<>();
282         ignoredRules.add(BSPRule.R5404);
283         ignoredRules.add(BSPRule.R5406);
284         ignoredRules.add(BSPRule.R5407);
285         ignoredRules.add(BSPRule.R5417);
286         ignoredRules.add(BSPRule.R3063);
287         ignoredRules.add(BSPRule.R5620);
288         ignoredRules.add(BSPRule.R5621);
289         //ignoredRules.add(BSPRule.R5215);
290         requestData.setIgnoredBSPRules(ignoredRules);
291 
292         wss4JHandler.doReceiver(messageContext, requestData, false);
293 
294         return messageContext;
295     }
296 
297     protected Map<String, Object> getMessageContext(InputStream inputStream) {
298         Map<String, Object> context = new HashMap<>();
299         try {
300             context.put(SECURED_DOCUMENT, SOAPUtil.toSOAPPart(inputStream));
301         } catch (Exception e) {
302             throw new RuntimeException(e);
303         }
304         return context;
305     }
306 
307     private Map<String, Object> getMessageContext(Document document) {
308         Map<String, Object> context = new HashMap<>();
309         context.put(SECURED_DOCUMENT, document);
310         return context;
311     }
312 
313     protected XPathExpression getXPath(String expression) throws XPathExpressionException {
314         XPathFactory xPathFactory = XPathFactory.newInstance();
315         XPath xPath = xPathFactory.newXPath();
316         xPath.setNamespaceContext(
317                 new NamespaceContext() {
318                     @Override
319                     public String getNamespaceURI(String prefix) {
320                         if (WSSConstants.PREFIX_DSIG.equals(prefix)) {
321                             return WSSConstants.NS_DSIG;
322                         } else if (WSSConstants.PREFIX_SOAPENV.equals(prefix)) {
323                             return WSSConstants.NS_SOAP11;
324                         } else if (WSSConstants.PREFIX_WSSE.equals(prefix)) {
325                             return WSSConstants.NS_WSSE10;
326                         } else if (WSSConstants.PREFIX_WSU.equals(prefix)) {
327                             return WSSConstants.NS_WSU10;
328                         } else if (WSSConstants.PREFIX_XENC.equals(prefix)) {
329                             return WSSConstants.NS_XMLENC;
330                         } else if (WSSConstants.PREFIX_XENC11.equals(prefix)) {
331                             return WSSConstants.NS_XMLENC11;
332                         } else {
333                             return null;
334                         }
335                     }
336 
337                     @Override
338                     public String getPrefix(String namespaceURI) {
339                         if (WSSConstants.NS_DSIG.equals(namespaceURI)) {
340                             return WSSConstants.PREFIX_DSIG;
341                         } else if (WSSConstants.NS_SOAP11.equals(namespaceURI)) {
342                             return WSSConstants.PREFIX_SOAPENV;
343                         } else if (WSSConstants.NS_WSSE10.equals(namespaceURI)) {
344                             return WSSConstants.PREFIX_WSSE;
345                         } else if (WSSConstants.NS_WSU10.equals(namespaceURI)) {
346                             return WSSConstants.PREFIX_WSU;
347                         } else if (WSSConstants.NS_XMLENC.equals(namespaceURI)) {
348                             return WSSConstants.PREFIX_XENC;
349                         } else if (WSSConstants.NS_XMLENC11.equals(namespaceURI)) {
350                             return WSSConstants.PREFIX_XENC11;
351                         } else {
352                             return null;
353                         }
354                     }
355 
356                     @Override
357                     public Iterator<String> getPrefixes(String namespaceURI) {
358                         return null;
359                     }
360                 }
361         );
362         return xPath.compile(expression);
363     }
364 
365     class CustomWSS4JHandler extends WSHandler {
366 
367         private final org.slf4j.Logger LOG =
368             org.slf4j.LoggerFactory.getLogger(CustomWSS4JHandler.class.getName());
369         private final boolean doDebug = LOG.isDebugEnabled();
370 
371         /**
372          * Handles incoming web service requests and outgoing responses
373          *
374          * @throws TransformerException
375          * @throws IOException
376          */
377         public boolean doSender(Map<String, Object> mc, RequestData reqData, boolean isRequest)
378                 throws WSSecurityException, TransformerException, IOException {
379 
380             /*
381              * Get the action first.
382              */
383             String action = (String) mc.get(WSHandlerConstants.ACTION);
384             if (action == null) {
385                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
386                                               new Object[] {"WSS4JHandler: No action defined"});
387             }
388             List<HandlerAction> actions = WSSecurityUtil.decodeHandlerAction(action, null);
389             if (actions.isEmpty()) {
390                 return true;
391             }
392 
393             /*
394             * For every action we need a username, so get this now. The username
395             * defined in the deployment descriptor takes precedence.
396             */
397             reqData.setUsername((String) getOption(WSHandlerConstants.USER));
398             if (reqData.getUsername() == null || reqData.getUsername().isEmpty()) {
399                 reqData.setUsername((String) mc.get(WSHandlerConstants.USER));
400             }
401 
402             /*
403             * Now we perform some set-up for UsernameToken and Signature
404             * functions. No need to do it for encryption only. Check if username
405             * is available and then get a password.
406             */
407             boolean usernameRequired = false;
408             for (HandlerAction handlerAction : actions) {
409                 if (handlerAction.getAction() == WSConstants.SIGN
410                     || handlerAction.getAction() == WSConstants.UT
411                     || handlerAction.getAction() == WSConstants.UT_SIGN) {
412                     usernameRequired = true;
413                     break;
414                 }
415             }
416             if (usernameRequired && (reqData.getUsername() == null || reqData.getUsername().isEmpty())) {
417                 /*
418                  * We need a username - if none throw a WSSecurityException. For encryption
419                  * there is a specific parameter to get a username.
420                  */
421                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
422                                               new Object[] {"WSS4JHandler: Empty username for specified action"}
423                 );
424             }
425             if (doDebug) {
426                 LOG.debug("Actor: " + reqData.getActor());
427             }
428             /*
429             * Now get the SOAP part from the request message and convert it into a
430             * Document.
431             *
432             * Now we can perform our security operations on this request.
433             */
434             Document doc = (Document) mc.get(SECURED_DOCUMENT);
435             if (doc == null) {
436                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
437                                               new Object[] {"WSS4JHandler: cannot get SOAP envlope from message"}
438                 );
439             }
440             if (doDebug) {
441                 LOG.debug("WSS4JHandler: orginal SOAP request: ");
442                 LOG.debug(XMLUtils.prettyDocumentToString(doc));
443             }
444             doSenderAction(doc, reqData, actions, isRequest);
445 
446             mc.put(SECURED_DOCUMENT, doc);
447 
448             return true;
449         }
450 
451         @SuppressWarnings("unchecked")
452         public boolean doReceiver(Map<String, Object> mc, RequestData reqData, boolean isRequest)
453                 throws WSSecurityException {
454             String action = (String) mc.get(WSHandlerConstants.ACTION);
455             if (action == null) {
456                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
457                                               new Object[] {"WSS4JHandler: No action defined"});
458             }
459             List<Integer> actions = WSSecurityUtil.decodeAction(action);
460 
461             String actor = (String) mc.get(WSHandlerConstants.ACTOR);
462 
463             Document doc = (Document) mc.get(SECURED_DOCUMENT);
464 
465             /*
466              * Check if it's a fault. Don't process faults.
467              */
468             org.apache.wss4j.dom.SOAPConstants soapConstants =
469                     WSSecurityUtil.getSOAPConstants(doc.getDocumentElement());
470             if (XMLUtils.findElement(
471                 doc.getDocumentElement(), "Fault", soapConstants.getEnvelopeURI()) != null
472             ) {
473                 return false;
474             }
475 
476             /*
477              * To check a UsernameToken or to decrypt an encrypted message we need
478              * a password.
479              */
480             CallbackHandler cbHandler = getPasswordCallbackHandler(reqData);
481             reqData.setCallbackHandler(cbHandler);
482 
483             /*
484              * Get and check the Signature specific parameters first because they
485              * may be used for encryption too.
486              */
487             WSSConfig wssConfig = WSSConfig.getNewInstance();
488             reqData.setWssConfig(wssConfig);
489             reqData.setActor(actor);
490             doReceiverAction(actions, reqData);
491 
492             Element elem = WSSecurityUtil.getSecurityHeader(doc, actor);
493 
494             WSSecurityEngine secEngine = new WSSecurityEngine();
495             secEngine.setWssConfig(wssConfig);
496             WSHandlerResult wsResult = null;
497             try {
498                 wsResult = secEngine.processSecurityHeader(elem, reqData);
499             } catch (WSSecurityException ex) {
500                 if (doDebug) {
501                     LOG.debug(ex.getMessage(), ex);
502                 }
503                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "empty",
504                                               new Object[] {"WSS4JHandler: security processing failed"}
505                 );
506             }
507             if (wsResult.getResults() == null || wsResult.getResults().isEmpty()) {
508                 // no security header found
509                 if (actions.isEmpty()) {
510                     return true;
511                 } else {
512                     throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "empty",
513                                                   new Object[] {"WSS4JHandler: Request does not contain required Security header"}
514                     );
515                 }
516             }
517             if (reqData.isEnableSignatureConfirmation() && !isRequest) {
518                 checkSignatureConfirmation(reqData, wsResult);
519             }
520 
521             if (doDebug) {
522                 LOG.debug("Processed received SOAP request");
523             }
524 
525             /*
526              * now check the security actions: do they match, in right order?
527              */
528             if (!checkReceiverResults(wsResult.getResults(), actions)) {
529                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "empty",
530                                               new Object[] {"WSS4JHandler: security processing failed (actions mismatch)"}
531                 );
532             }
533 
534             /*
535              * All ok up to this point. Now construct and setup the
536              * security result structure. The service may fetch this
537              * and check it.
538              */
539             List<WSHandlerResult> results = null;
540             if ((results = (List<WSHandlerResult>) mc.get(WSHandlerConstants.RECV_RESULTS)) == null) {
541                 results = new ArrayList<>();
542                 mc.put(WSHandlerConstants.RECV_RESULTS, results);
543             }
544             results.add(0, wsResult);
545             if (doDebug) {
546                 LOG.debug("WSS4JHandler: exit invoke()");
547             }
548 
549             return true;
550         }
551 
552         @Override
553         protected boolean checkReceiverResults(
554             List<WSSecurityEngineResult> wsResult, List<Integer> actions
555         ) {
556             List<WSSecurityEngineResult> wsSecurityEngineResults = new ArrayList<>();
557             for (WSSecurityEngineResult result : wsResult) {
558                 boolean found = false;
559                 for (WSSecurityEngineResult res : wsSecurityEngineResults) {
560                     if (result.get(WSSecurityEngineResult.TAG_ACTION).equals(res.get(WSSecurityEngineResult.TAG_ACTION))) {
561                         found = true;
562                         break;
563                     }
564                 }
565                 if (!found) {
566                     wsSecurityEngineResults.add(result);
567                 }
568             }
569             int size = actions.size();
570             int ai = 0;
571             for (WSSecurityEngineResult result : wsSecurityEngineResults) {
572                 final Integer act = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
573                 if (act != null) {
574                     if (act.intValue() == WSConstants.SC || act.intValue() == WSConstants.BST
575                         || act.intValue() == WSConstants.DKT || act.intValue() == WSConstants.SCT
576                         || act.intValue() == WSConstants.UT_NOPASSWORD) {
577                         continue;
578                     }
579 
580                     if (ai >= size || actions.get(ai++).intValue() != act) {
581                         return false;
582                     }
583                 }
584             }
585             /*
586         if (ai != size) {
587             return false;
588         }
589              */
590             return true;
591         }
592 
593         @Override
594         public Object getOption(String key) {
595             return null;
596         }
597 
598         @SuppressWarnings("unchecked")
599         @Override
600         public Object getProperty(Object msgContext, String key) {
601             return ((Map<String, Object>) msgContext).get(key);
602         }
603 
604         @SuppressWarnings("unchecked")
605         @Override
606         public void setProperty(Object msgContext, String key, Object value) {
607             ((Map<String, Object>) msgContext).put(key, value);
608         }
609 
610         @SuppressWarnings("unchecked")
611         @Override
612         public String getPassword(Object msgContext) {
613             return (String) ((Map<String, Object>) msgContext).get("password");
614         }
615 
616         @SuppressWarnings("unchecked")
617         @Override
618         public void setPassword(Object msgContext, String password) {
619             ((Map<String, Object>) msgContext).put("password", password);
620         }
621     }
622 
623     protected class TestSecurityEventListener implements SecurityEventListener {
624         private SecurityEventConstants.Event[] expectedEvents;
625         private List<SecurityEvent> receivedSecurityEvents = new ArrayList<>();
626 
627         public TestSecurityEventListener(SecurityEventConstants.Event[] expectedEvents) {
628             this.expectedEvents = expectedEvents;
629         }
630 
631         public List<SecurityEvent> getReceivedSecurityEvents() {
632             return receivedSecurityEvents;
633         }
634 
635         @SuppressWarnings("unchecked")
636         public <T> T getSecurityEvent(SecurityEventConstants.Event securityEvent) {
637             for (SecurityEvent event : receivedSecurityEvents) {
638                 if (event.getSecurityEventType() == securityEvent) {
639                     return (T) event;
640                 }
641             }
642             return null;
643         }
644 
645         @SuppressWarnings("unchecked")
646         public <T> List<T> getSecurityEvents(SecurityEventConstants.Event securityEvent) {
647             List<T> foundEvents = new ArrayList<>();
648             for (SecurityEvent event : receivedSecurityEvents) {
649                 if (event.getSecurityEventType() == securityEvent) {
650                     foundEvents.add((T) event);
651                 }
652             }
653             return foundEvents;
654         }
655 
656         @Override
657         public void registerSecurityEvent(SecurityEvent securityEvent) throws WSSecurityException {
658             assertNotNull(securityEvent.getCorrelationID());
659             assertNotEquals("", securityEvent.getCorrelationID());
660             receivedSecurityEvents.add(securityEvent);
661         }
662 
663         public void compare() {
664             if (expectedEvents.length != receivedSecurityEvents.size()) {
665                 printEvents();
666                 fail("event count mismatch");
667             }
668             boolean asserted = true;
669             for (int i = 0; i < expectedEvents.length; i++) {
670                 if (!expectedEvents[i].equals(receivedSecurityEvents.get(i).getSecurityEventType())) {
671                     asserted = false;
672                     break;
673                 }
674             }
675             if (!asserted) {
676                 printEvents();
677                 fail("event mismatch");
678             }
679         }
680 
681         private void printEvents() {
682             System.out.println("expected events:");
683             for (int i = 0; i < expectedEvents.length; i++) {
684                 SecurityEventConstants.Event expectedEvent = expectedEvents[i];
685                 System.out.println("WSSecurityEventConstants." + expectedEvent + ",");
686             }
687             System.out.println("received events:");
688             for (int i = 0; i < receivedSecurityEvents.size(); i++) {
689                 SecurityEvent securityEvent = receivedSecurityEvents.get(i);
690                 System.out.println("WSSecurityEventConstants." + securityEvent.getSecurityEventType() + ",");
691             }
692         }
693     }
694 
695     public static Double getJavaSpecificationVersion() {
696         String jsv = System.getProperty("java.specification.version");
697         if (jsv != null) {
698             return Double.parseDouble(jsv);
699         }
700         return 0.0d;
701     }
702 }