Some refactoring in profile handlers to expose explicit methods for the determination
authorputmanb <putmanb@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 21 Jul 2009 21:39:35 +0000 (21:39 +0000)
committerputmanb <putmanb@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 21 Jul 2009 21:39:35 +0000 (21:39 +0000)
of whether to sign assertions and responses and to encrypt assertions and name IDs.
Makes code nicer style-wise, and also allows overriding, as in the delegation extension.

git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/branches/REL_2@2874 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/main/java/edu/internet2/middleware/shibboleth/idp/profile/AbstractSAMLProfileHandler.java
src/main/java/edu/internet2/middleware/shibboleth/idp/profile/saml1/AbstractSAML1ProfileHandler.java
src/main/java/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractSAML2ProfileHandler.java

index 6e37287..3010232 100644 (file)
@@ -501,9 +501,7 @@ public abstract class AbstractSAMLProfileHandler extends
             AbstractSAMLProfileConfiguration profileConfig = (AbstractSAMLProfileConfiguration) requestContext
                     .getProfileConfiguration();
             if (profileConfig != null) {
-                if (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.always
-                        || (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.conditional && !encoder
-                                .providesMessageIntegrity(requestContext))) {
+                if (isSignResponse(requestContext)) {
                     Credential signingCredential = profileConfig.getSigningCredential();
                     if (signingCredential == null) {
                         signingCredential = requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential();
@@ -535,6 +533,36 @@ public abstract class AbstractSAMLProfileHandler extends
     }
 
     /**
+     * Determine whether responses should be signed.
+     * 
+     * @param requestContext the current request context
+     * @return true if responses should be signed, false otherwise
+     * @throws ProfileException if there is a problem determining whether responses should be signed
+     */
+    protected boolean isSignResponse(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
+        
+        SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
+        
+        AbstractSAMLProfileConfiguration profileConfig = 
+            (AbstractSAMLProfileConfiguration) requestContext.getProfileConfiguration();
+        
+        if (profileConfig != null) {
+            try {
+                return profileConfig.getSignResponses() == CryptoOperationRequirementLevel.always
+                    || (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.conditional 
+                        && !encoder.providesMessageIntegrity(requestContext));
+            } catch (MessageEncodingException e) {
+                log.error("Unable to determine if outbound encoding '{}' provides message integrity protection",
+                        encoder.getBindingURI());
+                throw new ProfileException("Unable to determine if outbound response should be signed");
+            }
+        } else {
+            return false;
+        }
+        
+    }
+
+    /**
      * Get the outbound message encoder to use.
      * 
      * <p>The default implementation uses the binding URI from the 
index 41758ad..d2920cc 100644 (file)
@@ -46,7 +46,6 @@ import org.opensaml.saml1.core.StatusMessage;
 import org.opensaml.saml1.core.Subject;
 import org.opensaml.saml1.core.SubjectConfirmation;
 import org.opensaml.saml1.core.SubjectStatement;
-import org.opensaml.saml2.metadata.RoleDescriptor;
 import org.opensaml.saml2.metadata.SPSSODescriptor;
 import org.opensaml.ws.message.encoder.MessageEncodingException;
 import org.opensaml.xml.XMLObjectBuilder;
@@ -603,41 +602,14 @@ public abstract class AbstractSAML1ProfileHandler extends AbstractSAMLProfileHan
         log.debug("Determining if SAML assertion to relying party '{}' should be signed", requestContext
                 .getInboundMessageIssuer());
 
-        boolean signAssertion = false;
-
-        RoleDescriptor relyingPartyRole = requestContext.getPeerEntityRoleMetadata();
-        SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
-        AbstractSAML1ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
-
-        try {
-            if (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.always
-                    || (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.conditional && !encoder
-                            .providesMessageIntegrity(requestContext))) {
-                signAssertion = true;
-                log.debug("IdP relying party configuration '{}' indicates to sign assertions: {}", requestContext
-                        .getRelyingPartyConfiguration().getRelyingPartyId(), signAssertion);
-            }
-        } catch (MessageEncodingException e) {
-            String msg = MessageFormatter.format(
-                    "Unable to determine if outbound encoding '{}' provides message integrity protection", encoder
-                            .getBindingURI());
-            log.error(msg);
-            throw new ProfileException(msg);
-        }
-
-        if (!signAssertion && relyingPartyRole instanceof SPSSODescriptor) {
-            SPSSODescriptor ssoDescriptor = (SPSSODescriptor) relyingPartyRole;
-            if (ssoDescriptor.getWantAssertionsSigned() != null) {
-                signAssertion = ssoDescriptor.getWantAssertionsSigned().booleanValue();
-                log.debug("Entity metadata for relying party '{}' indicates to sign assertions: {}", requestContext
-                        .getInboundMessageIssuer(), signAssertion);
-            }
-        }
+        boolean signAssertion = isSignAssertion(requestContext);
 
         if (!signAssertion) {
             return;
         }
 
+        AbstractSAML1ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
+
         log.debug("Determining credential to use to sign assertion to relying party '{}'", requestContext
                 .getInboundMessageIssuer());
         Credential signatureCredential = profileConfig.getSigningCredential();
@@ -683,6 +655,43 @@ public abstract class AbstractSAML1ProfileHandler extends AbstractSAMLProfileHan
             throw new ProfileException(msg, e);
         }
     }
+    
+    /**
+     * Determine whether issued assertions should be signed.
+     * 
+     * @param requestContext the current request context
+     * @return true if assertions should be signed, false otherwise
+     * @throws ProfileException if there is a problem determining whether assertions should be signed
+     */
+    protected boolean isSignAssertion(BaseSAML1ProfileRequestContext<?, ?, ?> requestContext) throws ProfileException {
+        
+        SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
+        AbstractSAML1ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
+        
+        try {
+            boolean signAssertion = profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.always
+                || (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.conditional 
+                    && !encoder.providesMessageIntegrity(requestContext));
+            
+            log.debug("IdP relying party configuration '{}' indicates to sign assertions: {}", requestContext
+                    .getRelyingPartyConfiguration().getRelyingPartyId(), signAssertion);
+            
+            if (!signAssertion && requestContext.getPeerEntityRoleMetadata() instanceof SPSSODescriptor) {
+                SPSSODescriptor ssoDescriptor = (SPSSODescriptor) requestContext.getPeerEntityRoleMetadata();
+                if (ssoDescriptor.getWantAssertionsSigned() != null) {
+                    signAssertion = ssoDescriptor.getWantAssertionsSigned().booleanValue();
+                    log.debug("Entity metadata for relying party '{} 'indicates to sign assertions: {}", requestContext
+                            .getInboundMessageIssuer(), signAssertion);
+                }
+            }
+            
+            return signAssertion;
+        } catch (MessageEncodingException e) {
+            log.error("Unable to determine if outbound encoding '{}' provides message integrity protection",
+                    encoder.getBindingURI());
+            throw new ProfileException("Unable to determine if outbound assertion should be signed");
+        }
+    }
 
     /**
      * Writes an audit log entry indicating the successful response to the attribute request.
index d987f0f..6bfb0bd 100644 (file)
@@ -266,34 +266,25 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
 
             signAssertion(requestContext, assertion);
 
-            SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
-            try {
-                if (requestContext.getProfileConfiguration().getEncryptAssertion() == CryptoOperationRequirementLevel.always
-                        || (requestContext.getProfileConfiguration().getEncryptAssertion() == CryptoOperationRequirementLevel.conditional && !encoder
-                                .providesMessageConfidentiality(requestContext))) {
-                    log.debug("Attempting to encrypt assertion to relying party '{}'", requestContext
-                            .getInboundMessageIssuer());
-                    try {
-                        Encrypter encrypter = getEncrypter(requestContext.getInboundMessageIssuer());
-                        samlResponse.getEncryptedAssertions().add(encrypter.encrypt(assertion));
-                    } catch (SecurityException e) {
-                        log.error("Unable to construct encrypter", e);
-                        requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
-                                "Unable to encrypt assertion"));
-                        throw new ProfileException("Unable to construct encrypter", e);
-                    } catch (EncryptionException e) {
-                        log.error("Unable to encrypt assertion", e);
-                        requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
-                                "Unable to encrypt assertion"));
-                        throw new ProfileException("Unable to encrypt assertion", e);
-                    }
-                } else {
-                    samlResponse.getAssertions().add(assertion);
+            if (isEncryptAssertion(requestContext)) {
+                log.debug("Attempting to encrypt assertion to relying party '{}'", requestContext
+                        .getInboundMessageIssuer());
+                try {
+                    Encrypter encrypter = getEncrypter(requestContext.getInboundMessageIssuer());
+                    samlResponse.getEncryptedAssertions().add(encrypter.encrypt(assertion));
+                } catch (SecurityException e) {
+                    log.error("Unable to construct encrypter", e);
+                    requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
+                    "Unable to encrypt assertion"));
+                    throw new ProfileException("Unable to construct encrypter", e);
+                } catch (EncryptionException e) {
+                    log.error("Unable to encrypt assertion", e);
+                    requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
+                    "Unable to encrypt assertion"));
+                    throw new ProfileException("Unable to encrypt assertion", e);
                 }
-            } catch (MessageEncodingException e) {
-                log.error("Unable to determine if outbound encoding '{}' can provide confidentiality", encoder
-                        .getBindingURI());
-                throw new ProfileException("Unable to determine if assertions should be encrypted");
+            } else {
+                samlResponse.getAssertions().add(assertion);
             }
         }
 
@@ -306,6 +297,28 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     }
 
     /**
+     * Determine whether issued assertions should be encrypted.
+     * 
+     * @param requestContext the current request context
+     * @return true if assertions should be encrypted, false otherwise
+     * @throws ProfileException if there is a problem determining whether assertions should be encrypted
+     */
+    protected boolean isEncryptAssertion(BaseSAML2ProfileRequestContext<?, ?, ?> requestContext) 
+            throws ProfileException {
+        
+        SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
+        try {
+            return requestContext.getProfileConfiguration().getEncryptAssertion() == CryptoOperationRequirementLevel.always
+                || (requestContext.getProfileConfiguration().getEncryptAssertion() == CryptoOperationRequirementLevel.conditional
+                    && !encoder.providesMessageConfidentiality(requestContext));
+        } catch (MessageEncodingException e) {
+            log.error("Unable to determine if outbound encoding '{}' can provide confidentiality", encoder
+                    .getBindingURI());
+            throw new ProfileException("Unable to determine if assertions should be encrypted"); 
+        }
+    }
+
+    /**
      * Extension point for for subclasses to post-process the Response before it is signed and encoded.
      * 
      * @param requestContext the current request context
@@ -541,36 +554,13 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         log.debug("Determining if SAML assertion to relying party '{}' should be signed", requestContext
                 .getInboundMessageIssuer());
 
-        boolean signAssertion = false;
-
-        SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
-        AbstractSAML2ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
-        try {
-            if (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.always
-                    || (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.conditional && !encoder
-                            .providesMessageIntegrity(requestContext))) {
-                signAssertion = true;
-                log.debug("IdP relying party configuration '{}' indicates to sign assertions: {}", requestContext
-                        .getRelyingPartyConfiguration().getRelyingPartyId(), signAssertion);
-            }
-        } catch (MessageEncodingException e) {
-            log.error("Unable to determine if outbound encoding '{}' provides message integrity protection", encoder
-                    .getBindingURI());
-            throw new ProfileException("Unable to determine if outbound message should be signed");
-        }
-
-        if (!signAssertion && requestContext.getPeerEntityRoleMetadata() instanceof SPSSODescriptor) {
-            SPSSODescriptor ssoDescriptor = (SPSSODescriptor) requestContext.getPeerEntityRoleMetadata();
-            if (ssoDescriptor.getWantAssertionsSigned() != null) {
-                signAssertion = ssoDescriptor.getWantAssertionsSigned().booleanValue();
-                log.debug("Entity metadata for relying party '{} 'indicates to sign assertions: {}", requestContext
-                        .getInboundMessageIssuer(), signAssertion);
-            }
-        }
+        boolean signAssertion = isSignAssertion(requestContext);
 
         if (!signAssertion) {
             return;
         }
+        
+        AbstractSAML2ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
 
         log.debug("Determining signing credntial for assertion to relying party '{}'", requestContext
                 .getInboundMessageIssuer());
@@ -619,6 +609,43 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     }
 
     /**
+     * Determine whether issued assertions should be signed.
+     * 
+     * @param requestContext the current request context
+     * @return true if assertions should be signed, false otherwise
+     * @throws ProfileException if there is a problem determining whether assertions should be signed
+     */
+    protected boolean isSignAssertion(BaseSAML2ProfileRequestContext<?, ?, ?> requestContext) throws ProfileException {
+        
+        SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
+        AbstractSAML2ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
+        
+        try {
+            boolean signAssertion = profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.always
+                || (profileConfig.getSignAssertions() == CryptoOperationRequirementLevel.conditional 
+                    && !encoder.providesMessageIntegrity(requestContext));
+            
+            log.debug("IdP relying party configuration '{}' indicates to sign assertions: {}", requestContext
+                    .getRelyingPartyConfiguration().getRelyingPartyId(), signAssertion);
+            
+            if (!signAssertion && requestContext.getPeerEntityRoleMetadata() instanceof SPSSODescriptor) {
+                SPSSODescriptor ssoDescriptor = (SPSSODescriptor) requestContext.getPeerEntityRoleMetadata();
+                if (ssoDescriptor.getWantAssertionsSigned() != null) {
+                    signAssertion = ssoDescriptor.getWantAssertionsSigned().booleanValue();
+                    log.debug("Entity metadata for relying party '{} 'indicates to sign assertions: {}", requestContext
+                            .getInboundMessageIssuer(), signAssertion);
+                }
+            }
+            
+            return signAssertion;
+        } catch (MessageEncodingException e) {
+            log.error("Unable to determine if outbound encoding '{}' provides message integrity protection", encoder
+                    .getBindingURI());
+            throw new ProfileException("Unable to determine if outbound assertion should be signed");
+        }
+    }
+
+    /**
      * Build a status message, with an optional second-level failure message.
      * 
      * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
@@ -674,41 +701,52 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         }
 
         requestContext.setSubjectNameIdentifier(nameID);
-
-        boolean nameIdEncRequiredByAuthnRequest = false;
-        if (requestContext.getInboundSAMLMessage() instanceof AuthnRequest) {
-            AuthnRequest authnRequest = (AuthnRequest) requestContext.getInboundSAMLMessage();
-            NameIDPolicy policy = authnRequest.getNameIDPolicy();
-            if (policy != null && DatatypeHelper.safeEquals(policy.getFormat(), NameID.ENCRYPTED)) {
-                nameIdEncRequiredByAuthnRequest = true;
+        
+        if (isEncryptNameID(requestContext)) {
+            log.debug("Attempting to encrypt NameID to relying party '{}'", requestContext
+                    .getInboundMessageIssuer());
+            try {
+                Encrypter encrypter = getEncrypter(requestContext.getInboundMessageIssuer());
+                subject.setEncryptedID(encrypter.encrypt(nameID));
+            } catch (SecurityException e) {
+                log.error("Unable to construct encrypter", e);
+                requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
+                "Unable to encrypt NameID"));
+                throw new ProfileException("Unable to construct encrypter", e);
+            } catch (EncryptionException e) {
+                log.error("Unable to encrypt NameID", e);
+                requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
+                "Unable to encrypt NameID"));
+                throw new ProfileException("Unable to encrypt NameID", e);
             }
+        } else {
+            subject.setNameID(nameID);
         }
 
+
+        return subject;
+    }
+    
+
+    /**
+     * Determine whether NameID's should be encrypted.
+     * 
+     * @param requestContext the current request context
+     * @return true if NameID's should be encrypted, false otherwise
+     * @throws ProfileException if there is a problem determining whether NameID's should be encrypted
+     */
+    protected boolean isEncryptNameID(BaseSAML2ProfileRequestContext<?, ?, ?> requestContext) 
+            throws ProfileException {
+        
+        boolean nameIdEncRequiredByAuthnRequest = isRequestRequiresEncryptNameID(requestContext);
+        
         SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
+        boolean nameIdEncRequiredByConfig = false;
         try {
-            if (nameIdEncRequiredByAuthnRequest
-                    || requestContext.getProfileConfiguration().getEncryptNameID() == CryptoOperationRequirementLevel.always
-                    || (requestContext.getProfileConfiguration().getEncryptNameID() == CryptoOperationRequirementLevel.conditional && !encoder
-                            .providesMessageConfidentiality(requestContext))) {
-                log.debug("Attempting to encrypt NameID to relying party '{}'", requestContext
-                        .getInboundMessageIssuer());
-                try {
-                    Encrypter encrypter = getEncrypter(requestContext.getInboundMessageIssuer());
-                    subject.setEncryptedID(encrypter.encrypt(nameID));
-                } catch (SecurityException e) {
-                    log.error("Unable to construct encrypter", e);
-                    requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
-                            "Unable to encrypt NameID"));
-                    throw new ProfileException("Unable to construct encrypter", e);
-                } catch (EncryptionException e) {
-                    log.error("Unable to encrypt NameID", e);
-                    requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
-                            "Unable to encrypt NameID"));
-                    throw new ProfileException("Unable to encrypt NameID", e);
-                }
-            } else {
-                subject.setNameID(nameID);
-            }
+            nameIdEncRequiredByConfig = 
+                requestContext.getProfileConfiguration().getEncryptNameID() == CryptoOperationRequirementLevel.always
+                    || (requestContext.getProfileConfiguration().getEncryptNameID() == CryptoOperationRequirementLevel.conditional
+                            && !encoder.providesMessageConfidentiality(requestContext));
         } catch (MessageEncodingException e) {
             String msg = MessageFormatter.format(
                     "Unable to determine if outbound encoding '{}' provides message confidentiality protection",
@@ -716,8 +754,27 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
             log.error(msg);
             throw new ProfileException(msg);
         }
+        
+        return nameIdEncRequiredByAuthnRequest || nameIdEncRequiredByConfig;
+    }
 
-        return subject;
+    /**
+     * Determine whether information in the SAML request requires the issued NameID to 
+     * be encrypted.
+     * 
+     * @param requestContext the current request context
+     * @return true if the request indicates NameID encryption is required, false otherwise
+     */
+    protected boolean isRequestRequiresEncryptNameID(BaseSAML2ProfileRequestContext<?, ?, ?> requestContext) {
+        boolean nameIdEncRequiredByAuthnRequest = false;
+        if (requestContext.getInboundSAMLMessage() instanceof AuthnRequest) {
+            AuthnRequest authnRequest = (AuthnRequest) requestContext.getInboundSAMLMessage();
+            NameIDPolicy policy = authnRequest.getNameIDPolicy();
+            if (policy != null && DatatypeHelper.safeEquals(policy.getFormat(), NameID.ENCRYPTED)) {
+                nameIdEncRequiredByAuthnRequest = true;
+            }
+        }
+        return nameIdEncRequiredByAuthnRequest;
     }
 
     /**