1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.saml2.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.decoding.BaseSAMLMessageDecoder;
26 import org.opensaml.common.xml.SAMLConstants;
27 import org.opensaml.saml2.core.Assertion;
28 import org.opensaml.saml2.core.Issuer;
29 import org.opensaml.saml2.core.NameIDType;
30 import org.opensaml.saml2.core.RequestAbstractType;
31 import org.opensaml.saml2.core.Response;
32 import org.opensaml.saml2.core.StatusResponseType;
33 import org.opensaml.saml2.metadata.EntityDescriptor;
34 import org.opensaml.saml2.metadata.RoleDescriptor;
35 import org.opensaml.saml2.metadata.provider.MetadataProvider;
36 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
37 import org.opensaml.ws.message.MessageContext;
38 import org.opensaml.ws.message.decoder.MessageDecodingException;
39 import org.opensaml.xml.parse.ParserPool;
40 import org.opensaml.xml.security.SecurityException;
41 import org.opensaml.xml.util.DatatypeHelper;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45
46
47
48 public abstract class BaseSAML2MessageDecoder extends BaseSAMLMessageDecoder {
49
50
51 private final Logger log = LoggerFactory.getLogger(BaseSAML2MessageDecoder.class);
52
53
54 public BaseSAML2MessageDecoder() {
55 super();
56 }
57
58
59
60
61
62
63 public BaseSAML2MessageDecoder(ParserPool pool) {
64 super(pool);
65 }
66
67
68 public void decode(MessageContext messageContext) throws MessageDecodingException, SecurityException {
69 super.decode(messageContext);
70
71 checkEndpointURI((SAMLMessageContext) messageContext);
72 }
73
74
75
76
77
78
79
80
81
82
83 protected void populateMessageContext(SAMLMessageContext messageContext) throws MessageDecodingException {
84 populateMessageIdIssueInstantIssuer(messageContext);
85 populateRelyingPartyMetadata(messageContext);
86 }
87
88
89
90
91
92
93
94
95
96 protected void populateMessageIdIssueInstantIssuer(SAMLMessageContext messageContext)
97 throws MessageDecodingException {
98 if (!(messageContext instanceof SAMLMessageContext)) {
99 log.debug("Invalid message context type, this policy rule only support SAMLMessageContext");
100 return;
101 }
102 SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext;
103
104 SAMLObject samlMsg = samlMsgCtx.getInboundSAMLMessage();
105 if (samlMsg == null) {
106 log.error("Message context did not contain inbound SAML message");
107 throw new MessageDecodingException("Message context did not contain inbound SAML message");
108 }
109
110 if (samlMsg instanceof RequestAbstractType) {
111 log.debug("Extracting ID, issuer and issue instant from request");
112 extractRequestInfo(samlMsgCtx, (RequestAbstractType) samlMsg);
113 } else if (samlMsg instanceof StatusResponseType) {
114 log.debug("Extracting ID, issuer and issue instant from status response");
115 extractResponseInfo(samlMsgCtx, (StatusResponseType) samlMsg);
116 } else {
117 throw new MessageDecodingException("SAML 2 message was not a request or a response");
118 }
119
120 if (samlMsgCtx.getInboundMessageIssuer() == null) {
121 log.warn("Issuer could not be extracted from SAML 2 message");
122 }
123
124 }
125
126
127
128
129
130
131
132
133
134
135
136 protected void extractResponseInfo(SAMLMessageContext messageContext, StatusResponseType statusResponse)
137 throws MessageDecodingException {
138
139 messageContext.setInboundSAMLMessageId(statusResponse.getID());
140 messageContext.setInboundSAMLMessageIssueInstant(statusResponse.getIssueInstant());
141
142
143
144 String messageIssuer = null;
145 if (statusResponse.getIssuer() != null) {
146 messageIssuer = extractEntityId(statusResponse.getIssuer());
147 } else if (statusResponse instanceof Response) {
148 List<Assertion> assertions = ((Response) statusResponse).getAssertions();
149 if (assertions != null && assertions.size() > 0) {
150 log.info("Status response message had no issuer, attempting to extract issuer from enclosed Assertion(s)");
151 String assertionIssuer;
152 for (Assertion assertion : assertions) {
153 if (assertion != null && assertion.getIssuer() != null) {
154 assertionIssuer = extractEntityId(assertion.getIssuer());
155 if (messageIssuer != null && !messageIssuer.equals(assertionIssuer)) {
156 throw new MessageDecodingException("SAML 2 assertions, within response "
157 + statusResponse.getID() + " contain different issuer IDs");
158 }
159 messageIssuer = assertionIssuer;
160 }
161 }
162 }
163 }
164
165 messageContext.setInboundMessageIssuer(messageIssuer);
166 }
167
168
169
170
171
172
173
174
175
176 protected void extractRequestInfo(SAMLMessageContext messageContext, RequestAbstractType request)
177 throws MessageDecodingException {
178 messageContext.setInboundSAMLMessageId(request.getID());
179 messageContext.setInboundSAMLMessageIssueInstant(request.getIssueInstant());
180 messageContext.setInboundMessageIssuer(extractEntityId(request.getIssuer()));
181 }
182
183
184
185
186
187
188
189
190
191
192 protected String extractEntityId(Issuer issuer) throws MessageDecodingException {
193 if (issuer != null) {
194 if (issuer.getFormat() == null || issuer.getFormat().equals(NameIDType.ENTITY)) {
195 return issuer.getValue();
196 } else {
197 throw new MessageDecodingException("SAML 2 Issuer is not of ENTITY format type");
198 }
199 }
200
201 return null;
202 }
203
204
205
206
207
208
209
210
211
212
213 protected void populateRelyingPartyMetadata(SAMLMessageContext messageContext) throws MessageDecodingException {
214 MetadataProvider metadataProvider = messageContext.getMetadataProvider();
215 try {
216 if (metadataProvider != null) {
217 EntityDescriptor relyingPartyMD = metadataProvider.getEntityDescriptor(messageContext
218 .getInboundMessageIssuer());
219 messageContext.setPeerEntityMetadata(relyingPartyMD);
220
221 QName relyingPartyRole = messageContext.getPeerEntityRole();
222 if (relyingPartyMD != null && relyingPartyRole != null) {
223 List<RoleDescriptor> roles = relyingPartyMD.getRoleDescriptors(relyingPartyRole,
224 SAMLConstants.SAML11P_NS);
225 if (roles != null && roles.size() > 0) {
226 messageContext.setPeerEntityRoleMetadata(roles.get(0));
227 }
228 }
229 }
230 } catch (MetadataProviderException e) {
231 log.error("Error retrieving metadata for relying party " + messageContext.getInboundMessageIssuer(), e);
232 throw new MessageDecodingException("Error retrieving metadata for relying party "
233 + messageContext.getInboundMessageIssuer(), e);
234 }
235 }
236
237
238
239
240
241
242
243 protected String getIntendedDestinationEndpointURI(SAMLMessageContext samlMsgCtx) throws MessageDecodingException {
244 SAMLObject samlMessage = samlMsgCtx.getInboundSAMLMessage();
245 String messageDestination = null;
246 if (samlMessage instanceof RequestAbstractType) {
247 RequestAbstractType request = (RequestAbstractType) samlMessage;
248 messageDestination = DatatypeHelper.safeTrimOrNullString(request.getDestination());
249 } else if (samlMessage instanceof StatusResponseType) {
250 StatusResponseType response = (StatusResponseType) samlMessage;
251 messageDestination = DatatypeHelper.safeTrimOrNullString(response.getDestination());
252 } else {
253 log.error("Invalid SAML message type encountered: {}", samlMessage.getElementQName().toString());
254 throw new MessageDecodingException("Invalid SAML message type encountered");
255 }
256 return messageDestination;
257 }
258
259 }