1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.saml2.binding.encoding;
18
19 import java.io.OutputStreamWriter;
20 import java.io.UnsupportedEncodingException;
21 import java.io.Writer;
22
23 import org.apache.velocity.VelocityContext;
24 import org.apache.velocity.app.VelocityEngine;
25 import org.opensaml.common.SAMLObject;
26 import org.opensaml.common.binding.SAMLMessageContext;
27 import org.opensaml.common.xml.SAMLConstants;
28 import org.opensaml.saml2.core.RequestAbstractType;
29 import org.opensaml.saml2.core.StatusResponseType;
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
41
42 public class HTTPPostEncoder extends BaseSAML2MessageEncoder {
43
44
45 private final Logger log = LoggerFactory.getLogger(HTTPPostEncoder.class);
46
47
48 private VelocityEngine velocityEngine;
49
50
51 private String velocityTemplateId;
52
53
54
55
56
57
58
59 public HTTPPostEncoder(VelocityEngine engine, String templateId) {
60 super();
61 velocityEngine = engine;
62 velocityTemplateId = templateId;
63 }
64
65
66 public String getBindingURI() {
67 return SAMLConstants.SAML2_POST_BINDING_URI;
68 }
69
70
71 public boolean providesMessageConfidentiality(MessageContext messageContext) throws MessageEncodingException {
72 return false;
73 }
74
75
76 public boolean providesMessageIntegrity(MessageContext messageContext) throws MessageEncodingException {
77 return false;
78 }
79
80
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 StatusResponseType) {
103 ((StatusResponseType) samlMsgCtx.getOutboundSAMLMessage()).setDestination(endpointURL);
104 }
105
106 signMessage(samlMsgCtx);
107 samlMsgCtx.setOutboundMessage(outboundMessage);
108
109 postEncode(samlMsgCtx, endpointURL);
110 }
111
112
113
114
115
116
117
118
119
120 protected void postEncode(SAMLMessageContext messageContext, String endpointURL) throws MessageEncodingException {
121 log.debug("Invoking Velocity template to create POST body");
122 try {
123 VelocityContext context = new VelocityContext();
124
125 populateVelocityContext(context, messageContext, endpointURL);
126
127 HTTPOutTransport outTransport = (HTTPOutTransport) messageContext.getOutboundMessageTransport();
128 HTTPTransportUtils.addNoCacheHeaders(outTransport);
129 HTTPTransportUtils.setUTF8Encoding(outTransport);
130 HTTPTransportUtils.setContentType(outTransport, "text/html");
131
132 Writer out = new OutputStreamWriter(outTransport.getOutgoingStream(), "UTF-8");
133 velocityEngine.mergeTemplate(velocityTemplateId, "UTF-8", context, out);
134 out.flush();
135 } catch (Exception e) {
136 log.error("Error invoking Velocity template", e);
137 throw new MessageEncodingException("Error creating output document", e);
138 }
139 }
140
141
142
143
144
145
146
147
148
149 protected void populateVelocityContext(VelocityContext velocityContext, SAMLMessageContext messageContext,
150 String endpointURL) throws MessageEncodingException {
151
152 log.debug("Encoding action url of: {}", endpointURL);
153 velocityContext.put("action", endpointURL);
154
155 log.debug("Marshalling and Base64 encoding SAML message");
156 if (messageContext.getOutboundSAMLMessage().getDOM() == null) {
157 marshallMessage(messageContext.getOutboundSAMLMessage());
158 }
159 try {
160 String messageXML = XMLHelper.nodeToString(messageContext.getOutboundSAMLMessage().getDOM());
161 String encodedMessage = Base64.encodeBytes(messageXML.getBytes("UTF-8"), Base64.DONT_BREAK_LINES);
162 if (messageContext.getOutboundSAMLMessage() instanceof RequestAbstractType) {
163 velocityContext.put("SAMLRequest", encodedMessage);
164 } else if (messageContext.getOutboundSAMLMessage() instanceof StatusResponseType) {
165 velocityContext.put("SAMLResponse", encodedMessage);
166 } else {
167 throw new MessageEncodingException(
168 "SAML message is neither a SAML RequestAbstractType or StatusResponseType");
169 }
170 } catch (UnsupportedEncodingException e) {
171 log.error("UTF-8 encoding is not supported, this VM is not Java compliant.");
172 throw new MessageEncodingException("Unable to encode message, UTF-8 encoding is not supported");
173 }
174
175 String relayState = messageContext.getRelayState();
176 if (checkRelayState(relayState)) {
177 log.debug("Encoding relay state of: {}", relayState);
178 velocityContext.put("RelayState", relayState);
179 }
180 }
181 }