1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.idp.profile;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Map;
22
23 import javax.servlet.http.HttpServletRequest;
24
25 import org.opensaml.common.IdentifierGenerator;
26 import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
27 import org.opensaml.common.binding.encoding.SAMLMessageEncoder;
28 import org.opensaml.saml1.core.NameIdentifier;
29 import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
30 import org.opensaml.saml2.metadata.AuthnAuthorityDescriptor;
31 import org.opensaml.saml2.metadata.Endpoint;
32 import org.opensaml.saml2.metadata.EntityDescriptor;
33 import org.opensaml.saml2.metadata.NameIDFormat;
34 import org.opensaml.saml2.metadata.PDPDescriptor;
35 import org.opensaml.saml2.metadata.RoleDescriptor;
36 import org.opensaml.saml2.metadata.SSODescriptor;
37 import org.opensaml.saml2.metadata.provider.MetadataProvider;
38 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
39 import org.opensaml.ws.message.encoder.MessageEncodingException;
40 import org.opensaml.ws.security.SecurityPolicyResolver;
41 import org.opensaml.ws.transport.InTransport;
42 import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
43 import org.opensaml.xml.security.credential.Credential;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import edu.internet2.middleware.shibboleth.common.log.AuditLogEntry;
48 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
49 import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractShibbolethProfileHandler;
50 import edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext;
51 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
52 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartySecurityPolicyResolver;
53 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.AbstractSAMLProfileConfiguration;
54 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.CryptoOperationRequirementLevel;
55 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.SAMLMDRelyingPartyConfigurationManager;
56 import edu.internet2.middleware.shibboleth.idp.session.Session;
57
58
59
60
61 public abstract class AbstractSAMLProfileHandler extends
62 AbstractShibbolethProfileHandler<SAMLMDRelyingPartyConfigurationManager, Session> {
63
64
65 private final Logger auditLog = LoggerFactory.getLogger(AuditLogEntry.AUDIT_LOGGER_NAME);
66
67
68 private final Logger log = LoggerFactory.getLogger(AbstractSAMLProfileHandler.class);
69
70
71 private IdentifierGenerator idGenerator;
72
73
74 private Map<String, SAMLMessageDecoder> messageDecoders;
75
76
77 private Map<String, SAMLMessageEncoder> messageEncoders;
78
79
80 private String inboundBinding;
81
82
83 private List<String> supportedOutboundBindings;
84
85
86 private SecurityPolicyResolver securityPolicyResolver;
87
88
89 protected AbstractSAMLProfileHandler() {
90 super();
91 }
92
93
94
95
96
97
98 public SecurityPolicyResolver getSecurityPolicyResolver() {
99 if (securityPolicyResolver == null) {
100 setSecurityPolicyResolver(new RelyingPartySecurityPolicyResolver(getRelyingPartyConfigurationManager()));
101 }
102
103 return securityPolicyResolver;
104 }
105
106
107
108
109
110
111 public void setSecurityPolicyResolver(SecurityPolicyResolver resolver) {
112 securityPolicyResolver = resolver;
113 }
114
115
116
117
118
119
120 protected Logger getAduitLog() {
121 return auditLog;
122 }
123
124
125
126
127
128
129 public IdentifierGenerator getIdGenerator() {
130 return idGenerator;
131 }
132
133
134
135
136
137
138 public String getInboundBinding() {
139 return inboundBinding;
140 }
141
142
143
144
145
146
147 public Map<String, SAMLMessageDecoder> getMessageDecoders() {
148 return messageDecoders;
149 }
150
151
152
153
154
155
156 public Map<String, SAMLMessageEncoder> getMessageEncoders() {
157 return messageEncoders;
158 }
159
160
161
162
163
164
165 public MetadataProvider getMetadataProvider() {
166 SAMLMDRelyingPartyConfigurationManager rpcManager = getRelyingPartyConfigurationManager();
167 if (rpcManager != null) {
168 return rpcManager.getMetadataProvider();
169 }
170
171 return null;
172 }
173
174
175
176
177
178
179 public List<String> getSupportedOutboundBindings() {
180 return supportedOutboundBindings;
181 }
182
183
184
185
186
187
188
189
190 protected Session getUserSession(InTransport inTransport) {
191 HttpServletRequest rawRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
192 return (Session) rawRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
193 }
194
195
196
197
198
199
200
201
202 protected Session getUserSession(String principalName) {
203 return getSessionManager().getSession(principalName);
204 }
205
206
207
208
209
210
211 public void setIdGenerator(IdentifierGenerator generator) {
212 idGenerator = generator;
213 }
214
215
216
217
218
219
220 public void setInboundBinding(String binding) {
221 inboundBinding = binding;
222 }
223
224
225
226
227
228
229 public void setMessageDecoders(Map<String, SAMLMessageDecoder> decoders) {
230 messageDecoders = decoders;
231 }
232
233
234
235
236
237
238 public void setMessageEncoders(Map<String, SAMLMessageEncoder> encoders) {
239 messageEncoders = encoders;
240 }
241
242
243
244
245
246
247 public void setSupportedOutboundBindings(List<String> bindings) {
248 supportedOutboundBindings = bindings;
249 }
250
251
252 public RelyingPartyConfiguration getRelyingPartyConfiguration(String relyingPartyId) {
253 try {
254 if (getMetadataProvider().getEntityDescriptor(relyingPartyId) == null) {
255 log.warn("No metadata for relying party {}, treating party as anonymous", relyingPartyId);
256 return getRelyingPartyConfigurationManager().getAnonymousRelyingConfiguration();
257 }
258 } catch (MetadataProviderException e) {
259 log.error("Unable to look up relying party metadata", e);
260 return null;
261 }
262
263 return super.getRelyingPartyConfiguration(relyingPartyId);
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279 protected void populateRequestContext(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
280 populateRelyingPartyInformation(requestContext);
281 populateAssertingPartyInformation(requestContext);
282 populateSAMLMessageInformation(requestContext);
283 populateProfileInformation(requestContext);
284 populateUserInformation(requestContext);
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298 protected void populateRelyingPartyInformation(BaseSAMLProfileRequestContext requestContext)
299 throws ProfileException {
300 MetadataProvider metadataProvider = requestContext.getMetadataProvider();
301 String relyingPartyId = requestContext.getInboundMessageIssuer();
302
303 EntityDescriptor relyingPartyMetadata;
304 try {
305 relyingPartyMetadata = metadataProvider.getEntityDescriptor(relyingPartyId);
306 requestContext.setPeerEntityMetadata(relyingPartyMetadata);
307 } catch (MetadataProviderException e) {
308 log.error("Error looking up metadata for relying party " + relyingPartyId, e);
309 throw new ProfileException("Error looking up metadata for relying party " + relyingPartyId);
310 }
311
312 RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
313 if (rpConfig == null) {
314 log.error("Unable to retrieve relying party configuration data for entity with ID {}", relyingPartyId);
315 throw new ProfileException("Unable to retrieve relying party configuration data for entity with ID "
316 + relyingPartyId);
317 }
318 requestContext.setRelyingPartyConfiguration(rpConfig);
319 }
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336 protected void populateAssertingPartyInformation(BaseSAMLProfileRequestContext requestContext)
337 throws ProfileException {
338 String assertingPartyId = requestContext.getRelyingPartyConfiguration().getProviderId();
339 requestContext.setLocalEntityId(assertingPartyId);
340 requestContext.setOutboundMessageIssuer(assertingPartyId);
341
342 try {
343 EntityDescriptor localEntityDescriptor = requestContext.getMetadataProvider().getEntityDescriptor(
344 assertingPartyId);
345 if (localEntityDescriptor != null) {
346 requestContext.setLocalEntityMetadata(localEntityDescriptor);
347 }
348 } catch (MetadataProviderException e) {
349 log.error("Error looking up metadata for asserting party " + assertingPartyId, e);
350 throw new ProfileException("Error looking up metadata for asserting party " + assertingPartyId);
351 }
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366 protected abstract void populateSAMLMessageInformation(BaseSAMLProfileRequestContext requestContext)
367 throws ProfileException;
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386 protected void populateProfileInformation(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
387 AbstractSAMLProfileConfiguration profileConfig = (AbstractSAMLProfileConfiguration) requestContext
388 .getRelyingPartyConfiguration().getProfileConfiguration(getProfileId());
389 if (profileConfig != null) {
390 requestContext.setProfileConfiguration(profileConfig);
391 requestContext.setOutboundMessageArtifactType(profileConfig.getOutboundArtifactType());
392 }
393
394 Endpoint endpoint = selectEndpoint(requestContext);
395 if (endpoint == null) {
396 log.error("No return endpoint available for relying party {}", requestContext.getInboundMessageIssuer());
397 throw new ProfileException("No peer endpoint available to which to send SAML response");
398 }
399 requestContext.setPeerEntityEndpoint(endpoint);
400 }
401
402
403
404
405
406
407
408
409
410
411 protected List<String> getNameFormats(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
412 ArrayList<String> nameFormats = new ArrayList<String>();
413
414 RoleDescriptor relyingPartyRole = requestContext.getPeerEntityRoleMetadata();
415 if (relyingPartyRole != null) {
416 List<String> relyingPartySupportedFormats = getEntitySupportedFormats(relyingPartyRole);
417 if (relyingPartySupportedFormats != null && !relyingPartySupportedFormats.isEmpty()) {
418 nameFormats.addAll(relyingPartySupportedFormats);
419 }
420 }
421
422
423 if (nameFormats.contains(NameIdentifier.UNSPECIFIED)) {
424 nameFormats.clear();
425 }
426
427 return nameFormats;
428 }
429
430
431
432
433
434
435
436
437 protected List<String> getEntitySupportedFormats(RoleDescriptor role) {
438 List<NameIDFormat> nameIDFormats = null;
439
440 if (role instanceof SSODescriptor) {
441 nameIDFormats = ((SSODescriptor) role).getNameIDFormats();
442 } else if (role instanceof AuthnAuthorityDescriptor) {
443 nameIDFormats = ((AuthnAuthorityDescriptor) role).getNameIDFormats();
444 } else if (role instanceof PDPDescriptor) {
445 nameIDFormats = ((PDPDescriptor) role).getNameIDFormats();
446 } else if (role instanceof AttributeAuthorityDescriptor) {
447 nameIDFormats = ((AttributeAuthorityDescriptor) role).getNameIDFormats();
448 }
449
450 ArrayList<String> supportedFormats = new ArrayList<String>();
451 if (nameIDFormats != null) {
452 for (NameIDFormat format : nameIDFormats) {
453 supportedFormats.add(format.getFormat());
454 }
455 }
456
457 return supportedFormats;
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475 protected abstract void populateUserInformation(BaseSAMLProfileRequestContext requestContext)
476 throws ProfileException;
477
478
479
480
481
482
483
484
485
486
487 protected abstract Endpoint selectEndpoint(BaseSAMLProfileRequestContext requestContext) throws ProfileException;
488
489
490
491
492
493
494
495
496 protected void encodeResponse(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
497 try {
498 SAMLMessageEncoder encoder = getMessageEncoders().get(requestContext.getPeerEntityEndpoint().getBinding());
499 if (encoder == null) {
500 log.error("No outbound message encoder configured for binding {}", requestContext
501 .getPeerEntityEndpoint().getBinding());
502 throw new ProfileException("No outbound message encoder configured for binding "
503 + requestContext.getPeerEntityEndpoint().getBinding());
504 }
505
506 AbstractSAMLProfileConfiguration profileConfig = (AbstractSAMLProfileConfiguration) requestContext
507 .getProfileConfiguration();
508 if (profileConfig != null) {
509 if (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.always
510 || (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.conditional && !encoder
511 .providesMessageIntegrity(requestContext))) {
512 Credential signingCredential = null;
513 if (profileConfig.getSigningCredential() != null) {
514 signingCredential = profileConfig.getSigningCredential();
515 } else if (requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential() != null) {
516 signingCredential = requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential();
517 }
518
519 if (signingCredential == null) {
520 throw new ProfileException(
521 "Signing of responses is required but no signing credential is available");
522 }
523
524 requestContext.setOutboundSAMLMessageSigningCredential(signingCredential);
525 }
526 }
527
528 log.debug("Encoding response to SAML request {} from relying party {}", requestContext
529 .getInboundSAMLMessageId(), requestContext.getInboundMessageIssuer());
530
531 requestContext.setMessageEncoder(encoder);
532 encoder.encode(requestContext);
533 } catch (MessageEncodingException e) {
534 throw new ProfileException("Unable to encode response to relying party: "
535 + requestContext.getInboundMessageIssuer(), e);
536 }
537 }
538
539
540
541
542
543
544 protected void writeAuditLogEntry(BaseSAMLProfileRequestContext context) {
545 AuditLogEntry auditLogEntry = new AuditLogEntry();
546 auditLogEntry.setMessageProfile(getProfileId());
547 auditLogEntry.setPrincipalAuthenticationMethod(context.getPrincipalAuthenticationMethod());
548 auditLogEntry.setPrincipalName(context.getPrincipalName());
549 auditLogEntry.setAssertingPartyId(context.getLocalEntityId());
550 auditLogEntry.setRelyingPartyId(context.getInboundMessageIssuer());
551 auditLogEntry.setRequestBinding(context.getMessageDecoder().getBindingURI());
552 auditLogEntry.setRequestId(context.getInboundSAMLMessageId());
553 auditLogEntry.setResponseBinding(context.getMessageEncoder().getBindingURI());
554 auditLogEntry.setResponseId(context.getOutboundSAMLMessageId());
555 if (context.getReleasedAttributes() != null) {
556 auditLogEntry.getReleasedAttributes().addAll(context.getReleasedAttributes());
557 }
558
559 getAduitLog().info(auditLogEntry.toString());
560 }
561 }