import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
import org.opensaml.common.binding.encoding.SAMLMessageEncoder;
import org.opensaml.saml2.metadata.Endpoint;
+import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
+import org.opensaml.saml2.metadata.provider.MetadataProviderException;
import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.ws.security.SecurityPolicyResolver;
import org.opensaml.ws.transport.InTransport;
import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractShibbolethProfileHandler;
import edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext;
+import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartySecurityPolicyResolver;
import edu.internet2.middleware.shibboleth.common.relyingparty.provider.AbstractSAMLProfileConfiguration;
import edu.internet2.middleware.shibboleth.common.relyingparty.provider.CryptoOperationRequirementLevel;
}
/**
+ * Gets the user's session based on their principal name.
+ *
+ * @param principalName user's principal name
+ *
+ * @return the user's session
+ */
+ protected Session getUserSession(String principalName) {
+ return getSessionManager().getSession(principalName);
+ }
+
+ /**
* Gets an ID generator which may be used for SAML assertions, requests, etc.
*
* @param generator an ID generator which may be used for SAML assertions, requests, etc
supportedOutboundBindings = bindings;
}
+ /** {@inheritDoc} */
+ public RelyingPartyConfiguration getRelyingPartyConfiguration(String relyingPartyId) {
+ try {
+ if (getMetadataProvider().getEntityDescriptor(relyingPartyId) == null) {
+ log.warn("No metadata for relying party {}, treating party as anonymous", relyingPartyId);
+ return getRelyingPartyConfigurationManager().getAnonymousRelyingConfiguration();
+ }
+ } catch (MetadataProviderException e) {
+ log.error("Unable to look up relying party metadata", e);
+ return null;
+ }
+
+ return super.getRelyingPartyConfiguration(relyingPartyId);
+ }
+
/**
- * Encodes the request's SAML response and writes it to the servlet response.
+ * Populates the request context with information.
+ *
+ * This method requires the the following request context properties to be populated: inbound message transport,
+ * peer entity ID, metadata provider
+ *
+ * This methods populates the following request context properties: user's session, user's principal name, service
+ * authentication method, peer entity metadata, relying party configuration, local entity ID, outbound message
+ * issuer, local entity metadata
*
* @param requestContext current request context
+ * @throws ProfileException thrown if there is a problem looking up the relying party's metadata
+ */
+ protected void populateRequestContext(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
+ populateRelyingPartyInformation(requestContext);
+ populateAssertingPartyInformation(requestContext);
+ populateSAMLMessageInformation(requestContext);
+ populateProfileInformation(requestContext);
+ populateUserInformation(requestContext);
+ }
+
+ /**
+ * Populates the request context with information about the relying party.
*
- * @throws ProfileException thrown if no message encoder is registered for this profiles binding
+ * This method requires the the following request context properties to be populated: peer entity ID
+ *
+ * This methods populates the following request context properties: peer entity metadata, relying party
+ * configuration
+ *
+ * @param requestContext current request context
+ * @throws ProfileException thrown if there is a problem looking up the relying party's metadata
*/
- protected void encodeResponse(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
+ protected void populateRelyingPartyInformation(BaseSAMLProfileRequestContext requestContext)
+ throws ProfileException {
+ MetadataProvider metadataProvider = requestContext.getMetadataProvider();
+ String relyingPartyId = requestContext.getInboundMessageIssuer();
+
+ EntityDescriptor relyingPartyMetadata;
try {
+ relyingPartyMetadata = metadataProvider.getEntityDescriptor(relyingPartyId);
+ requestContext.setPeerEntityMetadata(relyingPartyMetadata);
+ } catch (MetadataProviderException e) {
+ log.error("Error looking up metadata for relying party " + relyingPartyId, e);
+ throw new ProfileException("Error looking up metadata for relying party " + relyingPartyId);
+ }
+
+ RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
+ if (rpConfig == null) {
+ log.error("Unable to retrieve relying party configuration data for entity with ID {}", relyingPartyId);
+ throw new ProfileException("Unable to retrieve relying party configuration data for entity with ID "
+ + relyingPartyId);
+ }
+ requestContext.setRelyingPartyConfiguration(rpConfig);
+ }
+
+ /**
+ * Populates the request context with information about the asserting party. Unless overridden,
+ * {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
+ * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)} has already been invoked and the
+ * properties it provides are available in the request context.
+ *
+ * This method requires the the following request context properties to be populated: metadata provider, relying
+ * party configuration
+ *
+ * This methods populates the following request context properties: local entity ID, outbound message issuer, local
+ * entity metadata
+ *
+ * @param requestContext current request context
+ * @throws ProfileException thrown if there is a problem looking up the asserting party's metadata
+ */
+ protected void populateAssertingPartyInformation(BaseSAMLProfileRequestContext requestContext)
+ throws ProfileException {
+ String assertingPartyId = requestContext.getRelyingPartyConfiguration().getProviderId();
+ requestContext.setLocalEntityId(assertingPartyId);
+ requestContext.setOutboundMessageIssuer(assertingPartyId);
- Endpoint peerEndpoint = requestContext.getPeerEntityEndpoint();
- if (peerEndpoint == null) {
- log
- .error("No return endpoint available for relying party {}", requestContext
- .getInboundMessageIssuer());
- throw new ProfileException("No peer endpoint available to which to send SAML response");
+ try {
+ EntityDescriptor localEntityDescriptor = requestContext.getMetadataProvider().getEntityDescriptor(
+ assertingPartyId);
+ if (localEntityDescriptor != null) {
+ requestContext.setLocalEntityMetadata(localEntityDescriptor);
}
+ } catch (MetadataProviderException e) {
+ log.error("Error looking up metadata for asserting party " + assertingPartyId, e);
+ throw new ProfileException("Error looking up metadata for asserting party " + assertingPartyId);
+ }
+ }
+ /**
+ * Populates the request context with information from the inbound SAML message. Unless overridden,
+ * {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
+ * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)},and
+ * {@link #populateAssertingPartyInformation(BaseSAMLProfileRequestContext)} have already been invoked and the
+ * properties they provide are available in the request context.
+ *
+ *
+ * @param requestContext current request context
+ *
+ * @throws ProfileException thrown if there is a problem populating the request context with information
+ */
+ protected abstract void populateSAMLMessageInformation(BaseSAMLProfileRequestContext requestContext)
+ throws ProfileException;
+
+ /**
+ * Populates the request context with the information about the profile. Unless overridden,
+ * {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
+ * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)},
+ * {@link #populateAssertingPartyInformation(BaseSAMLProfileRequestContext)}, and
+ * {@link #populateSAMLMessageInformation(BaseSAMLProfileRequestContext)} have already been invoked and the
+ * properties they provide are available in the request context.
+ *
+ * This method requires the the following request context properties to be populated: relying party configuration
+ *
+ * This methods populates the following request context properties: communication profile ID, profile configuration,
+ * outbound message artifact type, peer entity endpoint
+ *
+ * @param requestContext current request context
+ *
+ * @throws ProfileException thrown if there is a problem populating the profile information
+ */
+ protected void populateProfileInformation(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
+ AbstractSAMLProfileConfiguration profileConfig = (AbstractSAMLProfileConfiguration) requestContext
+ .getRelyingPartyConfiguration().getProfileConfiguration(getProfileId());
+ if (profileConfig != null) {
+ requestContext.setProfileConfiguration(profileConfig);
+ requestContext.setOutboundMessageArtifactType(profileConfig.getOutboundArtifactType());
+ }
+
+ Endpoint endpoint = selectEndpoint(requestContext);
+ if (endpoint == null) {
+ log.error("No return endpoint available for relying party {}", requestContext.getInboundMessageIssuer());
+ throw new ProfileException("No peer endpoint available to which to send SAML response");
+ }
+ requestContext.setPeerEntityEndpoint(endpoint);
+ }
+
+ /**
+ * Populates the request context with the information about the user if they have an existing session. Unless
+ * overridden, {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
+ * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)},
+ * {@link #populateAssertingPartyInformation(BaseSAMLProfileRequestContext)},
+ * {@link #populateProfileInformation(BaseSAMLProfileRequestContext)}, and
+ * {@link #populateSAMLMessageInformation(BaseSAMLProfileRequestContext)} have already been invoked and the
+ * properties they provide are available in the request context.
+ *
+ * This method should populate: user's session, user's principal name, and service authentication method
+ *
+ * @param requestContext current request context
+ *
+ * @throws ProfileException thrown if there is a problem populating the user's information
+ */
+ protected abstract void populateUserInformation(BaseSAMLProfileRequestContext requestContext)
+ throws ProfileException;
+
+ /**
+ * Selects the appropriate endpoint for the relying party and stores it in the request context.
+ *
+ * @param requestContext current request context
+ *
+ * @return Endpoint selected from the information provided in the request context
+ *
+ * @throws ProfileException thrown if there is a problem selecting a response endpoint
+ */
+ protected abstract Endpoint selectEndpoint(BaseSAMLProfileRequestContext requestContext) throws ProfileException;
+
+ /**
+ * Encodes the request's SAML response and writes it to the servlet response.
+ *
+ * @param requestContext current request context
+ *
+ * @throws ProfileException thrown if no message encoder is registered for this profiles binding
+ */
+ protected void encodeResponse(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
+ try {
SAMLMessageEncoder encoder = getMessageEncoders().get(requestContext.getPeerEntityEndpoint().getBinding());
if (encoder == null) {
log.error("No outbound message encoder configured for binding {}", requestContext