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.util.List;
21
22 import org.apache.velocity.VelocityContext;
23 import org.apache.velocity.app.VelocityEngine;
24 import org.opensaml.Configuration;
25 import org.opensaml.common.binding.SAMLMessageContext;
26 import org.opensaml.common.binding.artifact.AbstractSAMLArtifact;
27 import org.opensaml.common.binding.artifact.SAMLArtifactMap;
28 import org.opensaml.common.xml.SAMLConstants;
29 import org.opensaml.saml2.binding.artifact.AbstractSAML2Artifact;
30 import org.opensaml.saml2.binding.artifact.SAML2ArtifactBuilder;
31 import org.opensaml.saml2.binding.artifact.SAML2ArtifactType0004;
32 import org.opensaml.util.URLBuilder;
33 import org.opensaml.ws.message.MessageContext;
34 import org.opensaml.ws.message.encoder.MessageEncodingException;
35 import org.opensaml.ws.transport.http.HTTPOutTransport;
36 import org.opensaml.ws.transport.http.HTTPTransportUtils;
37 import org.opensaml.xml.io.MarshallingException;
38 import org.opensaml.xml.util.Pair;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42
43
44
45 public class HTTPArtifactEncoder extends BaseSAML2MessageEncoder {
46
47
48 private final Logger log = LoggerFactory.getLogger(HTTPArtifactEncoder.class);
49
50
51 private boolean postEncoding;
52
53
54 private VelocityEngine velocityEngine;
55
56
57 private String velocityTemplateId;
58
59
60 private SAMLArtifactMap artifactMap;
61
62
63 private byte[] defaultArtifactType;
64
65
66
67
68
69
70
71
72 public HTTPArtifactEncoder(VelocityEngine engine, String template, SAMLArtifactMap map) {
73 super();
74 postEncoding = false;
75 velocityEngine = engine;
76 velocityTemplateId = template;
77 artifactMap = map;
78 defaultArtifactType = SAML2ArtifactType0004.TYPE_CODE;
79 }
80
81
82 public String getBindingURI() {
83 return SAMLConstants.SAML2_ARTIFACT_BINDING_URI;
84 }
85
86
87
88
89
90
91 public boolean isPostEncoding() {
92 return postEncoding;
93 }
94
95
96
97
98
99
100 public void setPostEncoding(boolean post) {
101 postEncoding = post;
102 }
103
104
105 public boolean providesMessageConfidentiality(MessageContext messageContext) throws MessageEncodingException {
106 return false;
107 }
108
109
110 public boolean providesMessageIntegrity(MessageContext messageContext) throws MessageEncodingException {
111 return false;
112 }
113
114
115 protected void doEncode(MessageContext messageContext) throws MessageEncodingException {
116 if (!(messageContext instanceof SAMLMessageContext)) {
117 log.error("Invalid message context type, this encoder only support SAMLMessageContext");
118 throw new MessageEncodingException(
119 "Invalid message context type, this encoder only support SAMLMessageContext");
120 }
121
122 if (!(messageContext.getOutboundMessageTransport() instanceof HTTPOutTransport)) {
123 log.error("Invalid outbound message transport type, this encoder only support HTTPOutTransport");
124 throw new MessageEncodingException(
125 "Invalid outbound message transport type, this encoder only support HTTPOutTransport");
126 }
127
128 SAMLMessageContext artifactContext = (SAMLMessageContext) messageContext;
129 HTTPOutTransport outTransport = (HTTPOutTransport) artifactContext.getOutboundMessageTransport();
130 outTransport.setCharacterEncoding("UTF-8");
131
132 if (postEncoding) {
133 postEncode(artifactContext, outTransport);
134 } else {
135 getEncode(artifactContext, outTransport);
136 }
137 }
138
139
140
141
142
143
144
145
146
147 protected void postEncode(SAMLMessageContext artifactContext, HTTPOutTransport outTransport)
148 throws MessageEncodingException {
149 log.debug("Performing HTTP POST SAML 2 artifact encoding");
150
151 log.debug("Creating velocity context");
152 VelocityContext context = new VelocityContext();
153 context.put("action", getEndpointURL(artifactContext));
154 context.put("SAMLArt", buildArtifact(artifactContext).base64Encode());
155
156 if (checkRelayState(artifactContext.getRelayState())) {
157 context.put("RelayState", HTTPTransportUtils.urlEncode(artifactContext.getRelayState()));
158 }
159
160 try {
161 log.debug("Invoking velocity template");
162 OutputStreamWriter outWriter = new OutputStreamWriter(outTransport.getOutgoingStream());
163 velocityEngine.mergeTemplate(velocityTemplateId, "UTF-8", context, outWriter);
164 } catch (Exception e) {
165 log.error("Error invoking velocity template to create POST form", e);
166 throw new MessageEncodingException("Error creating output document", e);
167 }
168 }
169
170
171
172
173
174
175
176
177
178 protected void getEncode(SAMLMessageContext artifactContext, HTTPOutTransport outTransport)
179 throws MessageEncodingException {
180 log.debug("Performing HTTP GET SAML 2 artifact encoding");
181
182 URLBuilder urlBuilder = getEndpointURL(artifactContext);
183
184 List<Pair<String, String>> params = urlBuilder.getQueryParams();
185
186 AbstractSAMLArtifact artifact = buildArtifact(artifactContext);
187 if(artifact == null){
188 log.error("Unable to build artifact for message to relying party");
189 throw new MessageEncodingException("Unable to builder artifact for message to relying party");
190 }
191 params.add(new Pair<String, String>("SAMLart", artifact.base64Encode()));
192
193 if (checkRelayState(artifactContext.getRelayState())) {
194 params.add(new Pair<String, String>("RelayState", artifactContext.getRelayState()));
195 }
196
197 outTransport.sendRedirect(urlBuilder.buildURL());
198 }
199
200
201
202
203
204
205
206
207
208
209 protected AbstractSAML2Artifact buildArtifact(SAMLMessageContext artifactContext) throws MessageEncodingException {
210
211 SAML2ArtifactBuilder artifactBuilder;
212 if (artifactContext.getOutboundMessageArtifactType() != null) {
213 artifactBuilder = Configuration.getSAML2ArtifactBuilderFactory().getArtifactBuilder(
214 artifactContext.getOutboundMessageArtifactType());
215 } else {
216 artifactBuilder = Configuration.getSAML2ArtifactBuilderFactory().getArtifactBuilder(defaultArtifactType);
217 artifactContext.setOutboundMessageArtifactType(defaultArtifactType);
218 }
219
220 AbstractSAML2Artifact artifact = artifactBuilder.buildArtifact(artifactContext);
221 if(artifact == null){
222 log.error("Unable to build artifact for message to relying party");
223 throw new MessageEncodingException("Unable to builder artifact for message to relying party");
224 }
225 String encodedArtifact = artifact.base64Encode();
226 try {
227 artifactMap.put(encodedArtifact, artifactContext.getInboundMessageIssuer(), artifactContext
228 .getOutboundMessageIssuer(), artifactContext.getOutboundSAMLMessage());
229 } catch (MarshallingException e) {
230 log.error("Unable to marshall assertion to be represented as an artifact", e);
231 throw new MessageEncodingException("Unable to marshall assertion to be represented as an artifact", e);
232 }
233
234 return artifact;
235 }
236 }