2 * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package edu.internet2.middleware.shibboleth.idp.profile;
19 import java.util.List;
22 import javax.servlet.http.HttpServletRequest;
24 import org.opensaml.common.IdentifierGenerator;
25 import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
26 import org.opensaml.common.binding.encoding.SAMLMessageEncoder;
27 import org.opensaml.saml2.metadata.Endpoint;
28 import org.opensaml.saml2.metadata.provider.MetadataProvider;
29 import org.opensaml.ws.message.encoder.MessageEncodingException;
30 import org.opensaml.ws.security.SecurityPolicyResolver;
31 import org.opensaml.ws.transport.InTransport;
32 import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
33 import org.opensaml.xml.security.credential.Credential;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 import edu.internet2.middleware.shibboleth.common.log.AuditLogEntry;
38 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
39 import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractShibbolethProfileHandler;
40 import edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext;
41 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartySecurityPolicyResolver;
42 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.CryptoOperationRequirementLevel;
43 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.SAMLMDRelyingPartyConfigurationManager;
44 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.AbstractSAML1ProfileConfiguration;
45 import edu.internet2.middleware.shibboleth.idp.session.Session;
48 * Base class for SAML profile handlers.
50 public abstract class AbstractSAMLProfileHandler extends
51 AbstractShibbolethProfileHandler<SAMLMDRelyingPartyConfigurationManager, Session> {
53 /** SAML message audit log. */
54 private final Logger auditLog = LoggerFactory.getLogger(AuditLogEntry.AUDIT_LOGGER_NAME);
57 private final Logger log = LoggerFactory.getLogger(AbstractSAMLProfileHandler.class);
59 /** Generator of IDs which may be used for SAML assertions, requests, etc. */
60 private IdentifierGenerator idGenerator;
62 /** All the SAML message decoders configured for the IdP. */
63 private Map<String, SAMLMessageDecoder> messageDecoders;
65 /** All the SAML message encoders configured for the IdP. */
66 private Map<String, SAMLMessageEncoder> messageEncoders;
68 /** SAML message binding used by inbound messages. */
69 private String inboundBinding;
71 /** SAML message bindings that may be used by outbound messages. */
72 private List<String> supportedOutboundBindings;
74 /** Resolver used to determine active security policy for an incoming request. */
75 private SecurityPolicyResolver securityPolicyResolver;
78 protected AbstractSAMLProfileHandler() {
83 * Gets the resolver used to determine active security policy for an incoming request.
85 * @return resolver used to determine active security policy for an incoming request
87 public SecurityPolicyResolver getSecurityPolicyResolver() {
88 if (securityPolicyResolver == null) {
89 setSecurityPolicyResolver(new RelyingPartySecurityPolicyResolver(getRelyingPartyConfigurationManager()));
92 return securityPolicyResolver;
96 * Sets the resolver used to determine active security policy for an incoming request.
98 * @param resolver resolver used to determine active security policy for an incoming request
100 public void setSecurityPolicyResolver(SecurityPolicyResolver resolver) {
101 securityPolicyResolver = resolver;
105 * Gets the audit log for this handler.
107 * @return audit log for this handler
109 protected Logger getAduitLog() {
114 * Gets an ID generator which may be used for SAML assertions, requests, etc.
116 * @return ID generator
118 public IdentifierGenerator getIdGenerator() {
123 * Gets the SAML message binding used by inbound messages.
125 * @return SAML message binding used by inbound messages
127 public String getInboundBinding() {
128 return inboundBinding;
132 * Gets all the SAML message decoders configured for the IdP indexed by SAML binding URI.
134 * @return SAML message decoders configured for the IdP indexed by SAML binding URI
136 public Map<String, SAMLMessageDecoder> getMessageDecoders() {
137 return messageDecoders;
141 * Gets all the SAML message encoders configured for the IdP indexed by SAML binding URI.
143 * @return SAML message encoders configured for the IdP indexed by SAML binding URI
145 public Map<String, SAMLMessageEncoder> getMessageEncoders() {
146 return messageEncoders;
150 * A convenience method for retrieving the SAML metadata provider from the relying party manager.
152 * @return the metadata provider or null
154 public MetadataProvider getMetadataProvider() {
155 SAMLMDRelyingPartyConfigurationManager rpcManager = getRelyingPartyConfigurationManager();
156 if (rpcManager != null) {
157 return rpcManager.getMetadataProvider();
164 * Gets the SAML message bindings that may be used by outbound messages.
166 * @return SAML message bindings that may be used by outbound messages
168 public List<String> getSupportedOutboundBindings() {
169 return supportedOutboundBindings;
173 * Gets the user's session, if there is one.
175 * @param inTransport current inbound transport
177 * @return user's session
179 protected Session getUserSession(InTransport inTransport) {
180 String sessionId = getUserSessionId(inTransport);
181 return getSessionManager().getSession(sessionId);
185 * Gets the user's session ID from the current request.
187 * @param inTransport current inbound transport
189 * @return user's session ID
191 protected String getUserSessionId(InTransport inTransport) {
192 HttpServletRequest rawRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
194 if (rawRequest != null) {
195 return (String) rawRequest.getSession().getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
202 * Gets an ID generator which may be used for SAML assertions, requests, etc.
204 * @param generator an ID generator which may be used for SAML assertions, requests, etc
206 public void setIdGenerator(IdentifierGenerator generator) {
207 idGenerator = generator;
211 * Sets the SAML message binding used by inbound messages.
213 * @param binding SAML message binding used by inbound messages
215 public void setInboundBinding(String binding) {
216 inboundBinding = binding;
220 * Sets all the SAML message decoders configured for the IdP indexed by SAML binding URI.
222 * @param decoders SAML message decoders configured for the IdP indexed by SAML binding URI
224 public void setMessageDecoders(Map<String, SAMLMessageDecoder> decoders) {
225 messageDecoders = decoders;
229 * Sets all the SAML message encoders configured for the IdP indexed by SAML binding URI.
231 * @param encoders SAML message encoders configured for the IdP indexed by SAML binding URI
233 public void setMessageEncoders(Map<String, SAMLMessageEncoder> encoders) {
234 messageEncoders = encoders;
238 * Sets the SAML message bindings that may be used by outbound messages.
240 * @param bindings SAML message bindings that may be used by outbound messages
242 public void setSupportedOutboundBindings(List<String> bindings) {
243 supportedOutboundBindings = bindings;
247 * Encodes the request's SAML response and writes it to the servlet response.
249 * @param requestContext current request context
251 * @throws ProfileException thrown if no message encoder is registered for this profiles binding
253 protected void encodeResponse(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
256 Endpoint peerEndpoint = requestContext.getPeerEntityEndpoint();
257 if (peerEndpoint == null) {
259 .error("No return endpoint available for relying party {}", requestContext
260 .getInboundMessageIssuer());
261 throw new ProfileException("No peer endpoint available to which to send SAML response");
264 SAMLMessageEncoder encoder = getMessageEncoders().get(requestContext.getPeerEntityEndpoint().getBinding());
265 if (encoder == null) {
266 log.error("No outbound message encoder configured for binding {}", requestContext
267 .getPeerEntityEndpoint().getBinding());
268 throw new ProfileException("No outbound message encoder configured for binding "
269 + requestContext.getPeerEntityEndpoint().getBinding());
272 AbstractSAML1ProfileConfiguration profileConfig = (AbstractSAML1ProfileConfiguration) requestContext
273 .getProfileConfiguration();
274 if (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.always
275 || (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.conditional && !encoder
276 .providesMessageIntegrity(requestContext))) {
277 Credential signingCredential = null;
278 if (profileConfig.getSigningCredential() != null) {
279 signingCredential = profileConfig.getSigningCredential();
280 } else if (requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential() != null) {
281 signingCredential = requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential();
284 if (signingCredential == null) {
285 throw new ProfileException(
286 "Signing of responses is required but no signing credential is available");
289 requestContext.setOutboundSAMLMessageSigningCredential(signingCredential);
292 log.debug("Encoding response to SAML request {} from relying party {}", requestContext
293 .getInboundSAMLMessageId(), requestContext.getInboundMessageIssuer());
295 requestContext.setMessageEncoder(encoder);
296 encoder.encode(requestContext);
297 } catch (MessageEncodingException e) {
298 throw new ProfileException("Unable to encode response to relying party: "
299 + requestContext.getInboundMessageIssuer(), e);
304 * Writes an aduit log entry indicating the successful response to the attribute request.
306 * @param context current request context
308 protected void writeAuditLogEntry(BaseSAMLProfileRequestContext context) {
309 AuditLogEntry auditLogEntry = new AuditLogEntry();
310 auditLogEntry.setMessageProfile(getProfileId());
311 auditLogEntry.setPrincipalAuthenticationMethod(context.getPrincipalAuthenticationMethod());
312 auditLogEntry.setPrincipalName(context.getPrincipalName());
313 auditLogEntry.setAssertingPartyId(context.getLocalEntityId());
314 auditLogEntry.setRelyingPartyId(context.getInboundMessageIssuer());
315 auditLogEntry.setRequestBinding(context.getMessageDecoder().getBindingURI());
316 auditLogEntry.setRequestId(context.getInboundSAMLMessageId());
317 auditLogEntry.setResponseBinding(context.getMessageEncoder().getBindingURI());
318 auditLogEntry.setResponseId(context.getOutboundSAMLMessageId());
319 if (context.getReleasedAttributes() != null) {
320 auditLogEntry.getReleasedAttributes().addAll(context.getReleasedAttributes());
323 getAduitLog().info(auditLogEntry.toString());