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.encoding;
18  
19  import java.io.OutputStream;
20  import java.io.OutputStreamWriter;
21  import java.io.UnsupportedEncodingException;
22  import java.io.Writer;
23  
24  import org.apache.velocity.VelocityContext;
25  import org.apache.velocity.app.VelocityEngine;
26  import org.opensaml.common.SAMLObject;
27  import org.opensaml.common.binding.SAMLMessageContext;
28  import org.opensaml.common.xml.SAMLConstants;
29  import org.opensaml.saml1.core.ResponseAbstractType;
30  import org.opensaml.ws.message.MessageContext;
31  import org.opensaml.ws.message.encoder.MessageEncodingException;
32  import org.opensaml.ws.transport.http.HTTPOutTransport;
33  import org.opensaml.ws.transport.http.HTTPTransportUtils;
34  import org.opensaml.xml.util.Base64;
35  import org.opensaml.xml.util.XMLHelper;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  
39  /**
40   * SAML 1.X HTTP POST message encoder.
41   */
42  public class HTTPPostEncoder extends BaseSAML1MessageEncoder {
43  
44      /** Class logger. */
45      private final Logger log = LoggerFactory.getLogger(HTTPPostEncoder.class);
46  
47      /** Velocity engine used to evaluate the template when performing POST encoding. */
48      private VelocityEngine velocityEngine;
49  
50      /** ID of the velocity template used when performing POST encoding. */
51      private String velocityTemplateId;
52  
53      /**
54       * Constructor.
55       * 
56       * @param engine velocity engine instance used to create POST body
57       * @param templateId ID of the template used to create POST body
58       */
59      public HTTPPostEncoder(VelocityEngine engine, String templateId) {
60          super();
61          velocityEngine = engine;
62          velocityTemplateId = templateId;
63      }
64  
65      /** {@inheritDoc} */
66      public String getBindingURI() {
67          return SAMLConstants.SAML1_POST_BINDING_URI;
68      }
69  
70      /** {@inheritDoc} */
71      public boolean providesMessageConfidentiality(MessageContext messageContext) throws MessageEncodingException {
72          return false;
73      }
74  
75      /** {@inheritDoc} */
76      public boolean providesMessageIntegrity(MessageContext messageContext) throws MessageEncodingException {
77          return false;
78      }
79  
80      /** {@inheritDoc} */
81      protected void doEncode(MessageContext messageContext) throws MessageEncodingException {
82          if (!(messageContext instanceof SAMLMessageContext)) {
83              log.error("Invalid message context type, this encoder only support SAMLMessageContext");
84              throw new MessageEncodingException(
85                      "Invalid message context type, this encoder only support SAMLMessageContext");
86          }
87  
88          if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) {
89              log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport");
90              throw new MessageEncodingException(
91                      "Invalid outbound message transport type, this encoder only support HTTPOutTransport");
92          }
93  
94          SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext;
95  
96          SAMLObject outboundMessage = samlMsgCtx.getOutboundSAMLMessage();
97          if (outboundMessage == null) {
98              throw new MessageEncodingException("No outbound SAML message contained in message context");
99          }
100         String endpointURL = getEndpointURL(samlMsgCtx).buildURL();
101 
102         if (samlMsgCtx.getOutboundSAMLMessage() instanceof ResponseAbstractType) {
103             ((ResponseAbstractType) samlMsgCtx.getOutboundSAMLMessage()).setRecipient(endpointURL);
104         }
105 
106         signMessage(samlMsgCtx);
107         samlMsgCtx.setOutboundMessage(outboundMessage);
108 
109         postEncode(samlMsgCtx, endpointURL);
110     }
111 
112     /**
113      * Base64 and POST encodes the outbound message and writes it to the outbound transport.
114      * 
115      * @param messageContext current message context
116      * @param endpointURL endpoint URL to encode message to
117      * 
118      * @throws MessageEncodingException thrown if there is a problem encoding the message
119      */
120     protected void postEncode(SAMLMessageContext messageContext, String endpointURL) throws MessageEncodingException {
121         log.debug("Invoking velocity template to create POST body");
122 
123         try {
124             VelocityContext context = new VelocityContext();
125 
126             log.debug("Encoding action url of: {}", endpointURL);
127             context.put("action", endpointURL);
128 
129             log.debug("Marshalling and Base64 encoding SAML message");
130             String messageXML = XMLHelper.nodeToString(marshallMessage(messageContext.getOutboundSAMLMessage()));
131             String encodedMessage = Base64.encodeBytes(messageXML.getBytes("UTF-8"), Base64.DONT_BREAK_LINES);
132             context.put("SAMLResponse", encodedMessage);
133 
134             if (messageContext.getRelayState() != null) {
135                 log.debug("Setting TARGET parameter to: {}", messageContext.getRelayState());
136                 context.put("TARGET", messageContext.getRelayState());
137             }
138 
139             HTTPOutTransport outTransport = (HTTPOutTransport) messageContext.getOutboundMessageTransport();
140             HTTPTransportUtils.addNoCacheHeaders(outTransport);
141             HTTPTransportUtils.setUTF8Encoding(outTransport);
142             HTTPTransportUtils.setContentType(outTransport, "text/html");
143 
144             OutputStream transportOutStream = outTransport.getOutgoingStream();
145             Writer out = new OutputStreamWriter(transportOutStream, "UTF-8");
146             velocityEngine.mergeTemplate(velocityTemplateId, "UTF-8", context, out);
147             out.flush();
148         }catch(UnsupportedEncodingException e){
149             log.error("UTF-8 encoding is not supported, this VM is not Java compliant.");
150             throw new MessageEncodingException("Unable to encode message, UTF-8 encoding is not supported");
151         } catch (Exception e) {
152             log.error("Error invoking velocity template", e);
153             throw new MessageEncodingException("Error creating output document", e);
154         }
155     }
156 }