View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.saml1.binding.decoding;
18  
19  import java.util.List;
20  
21  import javax.xml.namespace.QName;
22  
23  import org.opensaml.common.SAMLObject;
24  import org.opensaml.common.binding.SAMLMessageContext;
25  import org.opensaml.common.binding.artifact.SAMLArtifactMap;
26  import org.opensaml.common.xml.SAMLConstants;
27  import org.opensaml.ws.message.MessageContext;
28  import org.opensaml.ws.message.decoder.MessageDecodingException;
29  import org.opensaml.ws.soap.soap11.Envelope;
30  import org.opensaml.ws.soap.soap11.Header;
31  import org.opensaml.ws.transport.http.HTTPInTransport;
32  import org.opensaml.xml.AttributeExtensibleXMLObject;
33  import org.opensaml.xml.XMLObject;
34  import org.opensaml.xml.parse.ParserPool;
35  import org.opensaml.xml.util.DatatypeHelper;
36  import org.opensaml.xml.util.LazyList;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  /**
41   * SAML 1.1 HTTP SOAP 1.1 binding decoder.
42   */
43  public class HTTPSOAP11Decoder extends BaseSAML1MessageDecoder {
44  
45      /** Class logger. */
46      private final Logger log = LoggerFactory.getLogger(HTTPSOAP11Decoder.class);
47  
48      /** QNames of understood SOAP headers. */
49      private List<QName> understoodHeaders;
50  
51      /** QName of SOAP mustUnderstand header attribute. */
52      private final QName soapMustUnderstand = new QName(SAMLConstants.SOAP11ENV_NS, "mustUnderstand");
53  
54      /** Constructor. */
55      public HTTPSOAP11Decoder() {
56          super();
57          understoodHeaders = new LazyList<QName>();
58      }
59  
60      /**
61       * Constructor.
62       * 
63       * @param pool parser pool used to deserialize messages
64       */
65      public HTTPSOAP11Decoder(ParserPool pool) {
66          super(pool);
67          understoodHeaders = new LazyList<QName>();
68      }
69  
70      /**
71       * Constructor.
72       * 
73       * @param map Artifact to SAML map
74       * 
75       * @deprecated
76       */
77      public HTTPSOAP11Decoder(SAMLArtifactMap map) {
78          super(map);
79          understoodHeaders = new LazyList<QName>();
80      }
81  
82      /**
83       * Constructor.
84       * 
85       * @param map used to map artifacts to SAML
86       * @param pool parser pool used to deserialize messages
87       * 
88       * @deprecated
89       */
90      public HTTPSOAP11Decoder(SAMLArtifactMap map, ParserPool pool) {
91          super(map, pool);
92          understoodHeaders = new LazyList<QName>();
93      }
94  
95      /** {@inheritDoc} */
96      public String getBindingURI() {
97          return SAMLConstants.SAML1_SOAP11_BINDING_URI;
98      }
99  
100     /**
101      * Gets the SOAP header names that are understood by the application.
102      * 
103      * @return SOAP header names that are understood by the application
104      */
105     public List<QName> getUnderstoodHeaders() {
106         return understoodHeaders;
107     }
108 
109     /**
110      * Sets the SOAP header names that are understood by the application.
111      * 
112      * @param headerNames SOAP header names that are understood by the application
113      */
114     public void setUnderstoodHeaders(List<QName> headerNames) {
115         understoodHeaders.clear();
116         if (headerNames != null) {
117             understoodHeaders.addAll(headerNames);
118         }
119     }
120 
121     /** {@inheritDoc} */
122     protected void doDecode(MessageContext messageContext) throws MessageDecodingException {
123         if (!(messageContext instanceof SAMLMessageContext)) {
124             log.error("Invalid message context type, this decoder only support SAMLMessageContext");
125             throw new MessageDecodingException(
126                     "Invalid message context type, this decoder only support SAMLMessageContext");
127         }
128 
129         if (!(messageContext.getInboundMessageTransport() instanceof HTTPInTransport)) {
130             log.error("Invalid inbound message transport type, this decoder only support HTTPInTransport");
131             throw new MessageDecodingException(
132                     "Invalid inbound message transport type, this decoder only support HTTPInTransport");
133         }
134 
135         SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext;
136 
137         HTTPInTransport inTransport = (HTTPInTransport) samlMsgCtx.getInboundMessageTransport();
138         if (!inTransport.getHTTPMethod().equalsIgnoreCase("POST")) {
139             throw new MessageDecodingException("This message deocoder only supports the HTTP POST method");
140         }
141 
142         log.debug("Unmarshalling SOAP message");
143         Envelope soapMessage = (Envelope) unmarshallMessage(inTransport.getIncomingStream());
144         samlMsgCtx.setInboundMessage(soapMessage);
145 
146         Header messageHeader = soapMessage.getHeader();
147         if (messageHeader != null) {
148             checkUnderstoodSOAPHeaders(soapMessage.getHeader().getUnknownXMLObjects());
149         }
150 
151         List<XMLObject> soapBodyChildren = soapMessage.getBody().getUnknownXMLObjects();
152         if (soapBodyChildren.size() < 1 || soapBodyChildren.size() > 1) {
153             log.error("Unexpected number of children in the SOAP body, " + soapBodyChildren.size()
154                     + ".  Unable to extract SAML message");
155             throw new MessageDecodingException(
156                     "Unexpected number of children in the SOAP body, unable to extract SAML message");
157         }
158 
159         XMLObject incommingMessage = soapBodyChildren.get(0);
160         if (!(incommingMessage instanceof SAMLObject)) {
161             log.error("Unexpected SOAP body content.  Expected a SAML request but recieved {}", incommingMessage
162                     .getElementQName());
163             throw new MessageDecodingException("Unexpected SOAP body content.  Expected a SAML request but recieved "
164                     + incommingMessage.getElementQName());
165         }
166 
167         SAMLObject samlMessage = (SAMLObject) incommingMessage;
168         log.debug("Decoded SOAP messaged which included SAML message of type {}", samlMessage.getElementQName());
169         samlMsgCtx.setInboundSAMLMessage(samlMessage);
170 
171         populateMessageContext(samlMsgCtx);
172     }
173 
174     /**
175      * Checks that, if any SOAP headers, require understand that they are in the understood header list.
176      * 
177      * @param headers SOAP headers to check
178      * 
179      * @throws MessageDecodingException thrown if a SOAP header requires understanding but is not understood by the
180      *             decoder
181      */
182     protected void checkUnderstoodSOAPHeaders(List<XMLObject> headers) throws MessageDecodingException {
183         if (headers == null || headers.isEmpty()) {
184             return;
185         }
186 
187         AttributeExtensibleXMLObject attribExtensObject;
188         for (XMLObject header : headers) {
189             if (header instanceof AttributeExtensibleXMLObject) {
190                 attribExtensObject = (AttributeExtensibleXMLObject) header;
191                 if (DatatypeHelper.safeEquals("1", attribExtensObject.getUnknownAttributes().get(soapMustUnderstand))) {
192                     if (!understoodHeaders.contains(header.getElementQName())) {
193                         throw new MessageDecodingException("SOAP decoder encountered a  header, "
194                                 + header.getElementQName()
195                                 + ", that requires undestanding however this decoder does not understand that header");
196                     }
197                 }
198             }
199         }
200     }
201 
202     /** {@inheritDoc} */
203     protected boolean isIntendedDestinationEndpointURIRequired(SAMLMessageContext samlMsgCtx) {
204         return false;
205     }
206 }