Initial attribute query test, along with bugfixes, where Issuer is not authenticated
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 30 May 2007 12:33:02 +0000 (12:33 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 30 May 2007 12:33:02 +0000 (12:33 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2219 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractAttributeQuery.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractSAML2ProfileHandler.java
tests/data/conf1/internal.xml
tests/data/conf1/protocol.xml
tests/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML2AttributeQueryTestCase.java

index eed1dce..20ad003 100644 (file)
@@ -29,7 +29,10 @@ import org.opensaml.saml2.core.Assertion;
 import org.opensaml.saml2.core.AttributeQuery;
 import org.opensaml.saml2.core.AttributeStatement;
 import org.opensaml.saml2.core.Response;
+import org.opensaml.saml2.core.Status;
+import org.opensaml.saml2.core.StatusCode;
 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.ws.security.SecurityPolicyException;
 
 import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
 import edu.internet2.middleware.shibboleth.common.attribute.SAML2AttributeAuthority;
@@ -63,10 +66,15 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
         AttributeQueryRequestContext requestContext = new AttributeQueryRequestContext(request, response);
 
         getMessageDecoder(requestContext);
-        
-        decodeRequest(requestContext);
 
-        buildResponse(requestContext);
+        try {
+            decodeRequest(requestContext);
+            buildResponse(requestContext);
+        } catch (SecurityPolicyException e) {
+            buildErrorResponse(requestContext, e);
+        }catch (AttributeRequestException e){
+            buildErrorResponse(requestContext, e);
+        }
 
         getMessageEncoder(requestContext);
 
@@ -104,19 +112,22 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
      * @param requestContext request context contianing the request to decode
      * 
      * @throws ProfileException throw if there is a problem decoding the request
+     * @throws SecurityPolicyException thrown if the message was decoded properly but did not meet the necessary
+     *             security policy requirements
      */
-    protected void decodeRequest(AttributeQueryRequestContext requestContext)
-            throws ProfileException {
+    protected void decodeRequest(AttributeQueryRequestContext requestContext) throws ProfileException,
+            SecurityPolicyException {
 
         try {
             requestContext.getMessageDecoder().decode();
             if (log.isDebugEnabled()) {
                 log.debug("decoded http servlet request");
             }
-            requestContext.setAttributeQuery((AttributeQuery) requestContext.getMessageDecoder().getSAMLMessage());
         } catch (BindingException e) {
             log.error("Error decoding attribute query message", e);
             throw new ProfileException("Error decoding attribute query message");
+        } finally{
+            requestContext.setAttributeQuery((AttributeQuery) requestContext.getMessageDecoder().getSAMLMessage());
         }
     }
 
@@ -126,8 +137,10 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
      * @param requestContext current request context
      * 
      * @throws ProfileException thrown if there is a problem creating the SAML response
+     * @throws AttributeRequestException thrown if there is a problem resolving attributes
      */
-    protected void buildResponse(AttributeQueryRequestContext requestContext) throws ProfileException {
+    protected void buildResponse(AttributeQueryRequestContext requestContext) throws ProfileException,
+            AttributeRequestException {
         DateTime issueInstant = new DateTime();
 
         // create the attribute statement
@@ -142,7 +155,7 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
         Response samlResponse = getResponseBuilder().buildObject();
         populateStatusResponse(samlResponse, issueInstant, requestContext.getAttributeQuery(), requestContext
                 .getRelyingPartyConfiguration());
-        
+
         // TODO handle subject
         samlResponse.getAssertions().add(assertion);
 
@@ -150,6 +163,9 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
         signAssertion(assertion, requestContext.getRelyingPartyConfiguration(), requestContext
                 .getProfileConfiguration());
 
+        Status status = buildStatus(StatusCode.SUCCESS_URI, null, null);
+        samlResponse.setStatus(status);
+
         requestContext.setAttributeQueryResponse(samlResponse);
     }
 
@@ -161,9 +177,10 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
      * @return attribute statement resulting from the query
      * 
      * @throws ProfileException thrown if there is a problem making the query
+     * @throws AttributeRequestException thrown if there is a problem resolving attributes
      */
     protected AttributeStatement buildAttributeStatement(AttributeQueryRequestContext requestContext)
-            throws ProfileException {
+            throws ProfileException, AttributeRequestException {
         ShibbolethAttributeRequestContext attributeRequestContext = buildAttributeRequestContext(requestContext
                 .getRelyingPartyId(), requestContext.getUserSession(), requestContext.getProfileRequest());
 
@@ -173,7 +190,7 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
             return attributeAuthority.performAttributeQuery(attributeRequestContext);
         } catch (AttributeRequestException e) {
             log.error("Error resolving attributes", e);
-            throw new ProfileException("Error resolving attributes", e);
+            throw e;
         }
     }
 
@@ -190,14 +207,16 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
      */
     protected ShibbolethAttributeRequestContext buildAttributeRequestContext(String spEntityId, Session userSession,
             ProfileRequest<ServletRequest> request) throws ProfileException {
-        ServiceInformation spInformation = userSession.getServiceInformation(spEntityId);
         ShibbolethAttributeRequestContext requestContext = null;
         try {
             requestContext = new ShibbolethAttributeRequestContext(getMetadataProvider(),
                     getRelyingPartyConfiguration(spEntityId));
             requestContext.setPrincipalName(userSession.getPrincipalID());
-            requestContext.setPrincipalAuthenticationMethod(spInformation.getAuthenticationMethod()
-                    .getAuthenticationMethod());
+            if (userSession != null) {
+                ServiceInformation spInformation = userSession.getServiceInformation(spEntityId);
+                requestContext.setPrincipalAuthenticationMethod(spInformation.getAuthenticationMethod()
+                        .getAuthenticationMethod());
+            }
             requestContext.setRequest(request.getRawRequest());
             return requestContext;
         } catch (MetadataProviderException e) {
@@ -207,6 +226,25 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
     }
 
     /**
+     * Constructs an SAML response message carrying a request error.
+     * 
+     * @param requestContext current request context
+     * @param error the encountered error
+     */
+    protected void buildErrorResponse(AttributeQueryRequestContext requestContext, Exception error) {
+        DateTime issueInstant = new DateTime();
+        Response samlResponse = getResponseBuilder().buildObject();
+        populateStatusResponse(samlResponse, issueInstant, requestContext.getAttributeQuery(), requestContext
+                .getRelyingPartyConfiguration());
+
+        Status status = buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI, error
+                .getLocalizedMessage());
+        samlResponse.setStatus(status);
+
+        requestContext.setAttributeQueryResponse(samlResponse);
+    }
+
+    /**
      * Writes an aduit log entry indicating the successful response to the attribute request.
      * 
      * @param requestContext current request context
@@ -214,9 +252,13 @@ public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler
     protected void writeAuditLogEntry(AttributeQueryRequestContext requestContext) {
         AuditLogEntry auditLogEntry = new AuditLogEntry();
         auditLogEntry.setMessageProfile(getProfileId());
-        auditLogEntry.setPrincipalAuthenticationMethod(requestContext.getUserSession().getServiceInformation(
-                requestContext.getRelyingPartyId()).getAuthenticationMethod().getAuthenticationMethod());
-        auditLogEntry.setPrincipalId(requestContext.getUserSession().getPrincipalID());
+        
+        if(requestContext.getUserSession() != null){
+            auditLogEntry.setPrincipalAuthenticationMethod(requestContext.getUserSession().getServiceInformation(
+                    requestContext.getRelyingPartyId()).getAuthenticationMethod().getAuthenticationMethod());
+            auditLogEntry.setPrincipalId(requestContext.getUserSession().getPrincipalID());
+        }
+
         auditLogEntry.setProviderId(requestContext.getRelyingPartyConfiguration().getProviderId());
         auditLogEntry.setRelyingPartyId(requestContext.getRelyingPartyId());
         auditLogEntry.setRequestBinding(requestContext.getMessageDecoder().getBindingURI());
index 414f42f..846b85b 100644 (file)
@@ -275,9 +275,11 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
 
         Status status = statusBuilder.buildObject();
+        
         StatusCode statusCode = statusCodeBuilder.buildObject();
-
         statusCode.setValue(DatatypeHelper.safeTrimOrNullString(topLevelCode));
+        status.setStatusCode(statusCode);
+        
         if (secondLevelCode != null) {
             StatusCode secondLevelStatusCode = statusCodeBuilder.buildObject();
             secondLevelStatusCode.setValue(DatatypeHelper.safeTrimOrNullString(secondLevelCode));
index b2ef7ac..b7a6537 100644 (file)
@@ -1,8 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
-    default-autowire="byType">
+    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
     
     <!-- Spring configuration file that boostraps OpenSAML -->
     <bean id="shibboleth.OpensamlConfig" class="edu.internet2.middleware.shibboleth.common.config.OpensamlConfigBean" lazy-init="false">
@@ -78,7 +77,8 @@
         <property name="namespaceAware" value="true" />
     </bean>
 
-    <bean id="shibboleth.SAML2AttributeQueryMessageSecurityPolicyFactory" class="org.opensaml.common.binding.security.SAMLSecurityPolicyFactory">
+    <bean id="shibboleth.SAML2AttributeQueryMessageSecurityPolicyFactoryNoIssuerAuth"
+          class="org.opensaml.common.binding.security.SAMLSecurityPolicyFactory">
         <property name="issuerRole">
             <bean id="shibboleth.SAML2AttributeQueryRole" class="javax.xml.namespace.QName">
                 <constructor-arg value="urn:oasis:names:tc:SAML:2.0:metadata" />
                 <ref bean="shibboleth.MessageIssueInstantRuleFactory" />
             </list>
         </property>
+        <property name="requiredAuthenticatedIssuer" value="false" />
+    </bean>
+    
+    <bean id="shibboleth.SAML2AttributeQueryMessageSecurityPolicyFactoryIssuerAuth"
+          class="org.opensaml.common.binding.security.SAMLSecurityPolicyFactory"
+          parent="shibboleth.SAML2AttributeQueryMessageSecurityPolicyFactoryNoIssuerAuth">
+        <property name="requiredAuthenticatedIssuer" value="true" />
     </bean>
     
     <bean id="shibboleth.SAML2ProtocolMessageRuleFactory" class="org.opensaml.saml2.binding.security.SAML2ProtocolMessageRuleFactory" />
                     <key>
                         <value>urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST</value>
                     </key>
-                    <bean id="shibboleth.SAML2HttpPostDecoderBuilder" class="org.opensaml.saml2.binding.decoding.HTTPPostDecoderBuilder" />
+                    <bean id="shibboleth.SAML2HttpPostDecoderBuilder" class="org.opensaml.saml2.binding.decoding.HTTPPostDecoderBuilder">
+                        <property name="parser" ref="shibboleth.ParserPool" />
+                    </bean>
                 </entry>
                 <entry>
                     <key>
                         <value>urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect</value>
                     </key>
-                    <bean id="shibboleth.SAML2HttpRedirectDecoderBuilder" class="org.opensaml.saml2.binding.decoding.HTTPRedirectDeflateDecoderBuilder" />
+                    <bean id="shibboleth.SAML2HttpRedirectDecoderBuilder" class="org.opensaml.saml2.binding.decoding.HTTPRedirectDeflateDecoderBuilder" >
+                        <property name="parser" ref="shibboleth.ParserPool" />
+                    </bean>
                 </entry>
                 <entry>
                     <key>
                         <value>urn:oasis:names:tc:SAML:2.0:bindings:SOAP</value>
                     </key>
-                    <bean id="shibboleth.SAML2HttpSoap11DecoderBuilder" class="org.opensaml.saml2.binding.decoding.HTTPSOAP11DecoderBuilder" />
+                    <bean id="shibboleth.SAML2HttpSoap11DecoderBuilder" class="org.opensaml.saml2.binding.decoding.HTTPSOAP11DecoderBuilder" >
+                        <property name="parser" ref="shibboleth.ParserPool" />
+                    </bean>
                 </entry>
                 <entry>
                     <key>
                         <value>urn:oasis:names:tc:SAML:1.0:profiles:browser-post</value>
                     </key>
-                    <bean id="shibboleth.SAML1HttpPostDecoderBuilder" class="org.opensaml.saml1.binding.decoding.HTTPPostDecoderBuilder" />
+                    <bean id="shibboleth.SAML1HttpPostDecoderBuilder" class="org.opensaml.saml1.binding.decoding.HTTPPostDecoderBuilder" >
+                        <property name="parser" ref="shibboleth.ParserPool" />
+                    </bean>
                 </entry>
                 <entry>
                     <key>
                         <value>urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding</value>
                     </key>
-                    <bean id="shibboleth.SAML1HttpSoap11DecoderBuilder" class="org.opensaml.saml1.binding.decoding.HTTPSOAP11DecoderBuilder" />
+                    <bean id="shibboleth.SAML1HttpSoap11DecoderBuilder" class="org.opensaml.saml1.binding.decoding.HTTPSOAP11DecoderBuilder" >
+                        <property name="parser" ref="shibboleth.ParserPool" />
+                    </bean>
                 </entry>
             </map>
         </property>
index 4a5680b..0aa0a65 100644 (file)
@@ -1,9 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <ProfileHandlerGroup xmlns="urn:mace:shibboleth:2.0:profile-handler"
-                 xmlns:idpProfile="urn:mace:shibboleth:2.0:idp:profiles"
-                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-                 xsi:schemaLocation="urn:mace:shibboleth:2.0:profile-handler classpath:/schema/shibboleth-2.0-profile-handler.xsd
+    xmlns:idpProfile="urn:mace:shibboleth:2.0:idp:profiles" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="urn:mace:shibboleth:2.0:profile-handler classpath:/schema/shibboleth-2.0-profile-handler.xsd
                                      urn:mace:shibboleth:2.0:idp:profiles classpath:/schema/shibboleth-2.0-idp-profile.xsd">
 
     <ErrorHandler xsi:type="JSPErrorHandler" jspPagePath="/error.jsp" />
     <ProfileHandler xsi:type="idpProfile:Status">
         <RequestPath>/shibboleth/IdP/status</RequestPath>
     </ProfileHandler>
-    
+
     <ProfileHandler xsi:type="idpProfile:SAML2AttributeQuery"
-                    securityPolicyFactoryId="shibboleth.SAML2AttributeQueryMessageSecurityPolicyFactory">
-        <RequestPath>/IdP/saml2/SOAP/AttributeQuery</RequestPath>
+        securityPolicyFactoryId="shibboleth.SAML2AttributeQueryMessageSecurityPolicyFactoryNoIssuerAuth">
+        <RequestPath>/IdP/saml2/SOAP/AttributeQueryNoAuth</RequestPath>
     </ProfileHandler>
 
-<!--
-    <ProfileHandler xsi:type="idpProfile:SAML2SSO">
-        <RequestPath>/IdP/saml2/HTTP/SSO</RequestPath>
+    <ProfileHandler xsi:type="idpProfile:SAML2AttributeQuery"
+        securityPolicyFactoryId="shibboleth.SAML2AttributeQueryMessageSecurityPolicyFactoryIssuerAuth">
+        <RequestPath>/IdP/saml2/SOAP/AttributeQuery</RequestPath>
     </ProfileHandler>
 
-    <ProfileHandler xsi:type="idpProfile:SAML2AttributeQuery">
+    <!--
+        <ProfileHandler xsi:type="idpProfile:SAML2SSO">
+        <RequestPath>/IdP/saml2/HTTP/SSO</RequestPath>
+        </ProfileHandler>
+        
+        <ProfileHandler xsi:type="idpProfile:SAML2AttributeQuery">
         <RequestPath>/IdP/saml2/SOAP/attribute</RequestPath>
-    </ProfileHandler>
--->
+        </ProfileHandler>
+    -->
 
 </ProfileHandlerGroup>
\ No newline at end of file
index a43bd06..0e01ffb 100644 (file)
@@ -16,6 +16,8 @@
 
 package edu.internet2.middleware.shibboleth.idp.system.conf1;
 
+import java.io.StringWriter;
+
 import org.joda.time.DateTime;
 import org.opensaml.common.SAMLObjectBuilder;
 import org.opensaml.common.SAMLVersion;
@@ -45,7 +47,8 @@ import edu.internet2.middleware.shibboleth.idp.profile.ShibbolethProfileResponse
  */
 public class SAML2AttributeQueryTestCase extends BaseConf1TestCase {
 
-    public void testAttributeQuery() throws Exception {
+    /** Tests a request where the Issuer can not be authenticated. */
+    public void testUnathenticatedIssuerAttributeQuery() throws Exception {
         AttributeQuery query = buildAttributeQuery();
         String soapMessage = getSOAPMessage(query);
 
@@ -58,13 +61,16 @@ public class SAML2AttributeQueryTestCase extends BaseConf1TestCase {
         ShibbolethProfileHandlerManager handlerManager = (ShibbolethProfileHandlerManager) getApplicationContext()
                 .getBean("shibboleth.ProfileHandler");
         ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
-        
-        return;
+        assertNotNull(handler);
 
-//        ProfileRequest profileRequest = new ShibbolethProfileRequest(servletRequest);
-//        ProfileResponse profileResponse = new ShibbolethProfileResponse(servletResponse);
-//        handler.processRequest(profileRequest, profileResponse);
+        // Process request
+        ProfileRequest profileRequest = new ShibbolethProfileRequest(servletRequest);
+        ProfileResponse profileResponse = new ShibbolethProfileResponse(servletResponse);
+        handler.processRequest(profileRequest, profileResponse);
 
+        String response = servletResponse.getContentAsString();
+        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:Responder"));
+        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:RequestDenied"));
     }
 
     /**
@@ -125,6 +131,8 @@ public class SAML2AttributeQueryTestCase extends BaseConf1TestCase {
         Marshaller marshaller = marshallerFactory.getMarshaller(envelope);
         Element envelopeElem = marshaller.marshall(envelope);
 
-        return XMLHelper.nodeToString(envelopeElem);
+        StringWriter writer = new StringWriter();
+        XMLHelper.writeNode(envelopeElem, writer);
+        return writer.toString();
     }
 }
\ No newline at end of file