Basic unit test and related fixes for Shibboleth SSO profile
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Fri, 7 Sep 2007 17:47:19 +0000 (17:47 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Fri, 7 Sep 2007 17:47:19 +0000 (17:47 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2373 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/idp/profile/saml1/ShibbolethSSODecoder.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml1/ShibbolethSSOProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/SSOProfileHandler.java
test/data/conf1/relying-party.xml
test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML2SSOTestCase.java
test/edu/internet2/middleware/shibboleth/idp/system/conf1/ShibbolethSSOTestCase.java [new file with mode: 0644]

index 16144c2..e09fe39 100644 (file)
@@ -17,6 +17,8 @@
 package edu.internet2.middleware.shibboleth.idp.profile.saml1;
 
 import org.apache.log4j.Logger;
+import org.joda.time.DateTime;
+import org.joda.time.chrono.ISOChronology;
 import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
 import org.opensaml.saml1.binding.decoding.BaseSAML1MessageDecoder;
 import org.opensaml.ws.message.MessageContext;
@@ -62,7 +64,7 @@ public class ShibbolethSSODecoder extends BaseSAML1MessageDecoder implements SAM
             throw new MessageDecodingException(
                     "No providerId parameter given in Shibboleth SSO authentication request.");
         }
-        requestContext.setPeerEntityId(providerId);
+        requestContext.setInboundMessageIssuer(providerId);
 
         String shire = DatatypeHelper.safeTrimOrNullString(transport.getParameterValue("shire"));
         if (shire == null) {
@@ -81,9 +83,9 @@ public class ShibbolethSSODecoder extends BaseSAML1MessageDecoder implements SAM
         String timeStr = DatatypeHelper.safeTrimOrNullString(transport.getParameterValue("time"));
         if (timeStr != null) {
             long time = Long.parseLong(timeStr);
-            requestContext.setTime(time);
+            requestContext.setInboundSAMLMessageIssueInstant(new DateTime(time, ISOChronology.getInstanceUTC()));
         }
         
-        populateMessageContext(requestContext);
+        populateRelyingPartyMetadata(requestContext);
     }
 }
\ No newline at end of file
index 8c9160f..4657631 100644 (file)
@@ -55,7 +55,6 @@ import edu.internet2.middleware.shibboleth.common.ShibbolethConstants;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.ShibbolethSSOConfiguration;
-import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SSOConfiguration;
 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.ShibbolethSSOLoginContext;
@@ -143,7 +142,9 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
         ShibbolethSSORequestContext requestContext = decodeRequest(inTransport, outTransport);
         ShibbolethSSOLoginContext loginContext = requestContext.getLoginContext();
 
-        if (getRelyingPartyConfiguration(loginContext.getRelyingPartyId()) == null) {
+        RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(loginContext.getRelyingPartyId());
+        ShibbolethSSOConfiguration ssoConfig = (ShibbolethSSOConfiguration) rpConfig.getProfileConfigurations().get(ShibbolethSSOConfiguration.PROFILE_ID);
+        if (ssoConfig == null) {
             log.error("Shibboleth SSO profile is not configured for relying party " + loginContext.getRelyingPartyId());
             throw new ProfileException("Shibboleth SSO profile is not configured for relying party "
                     + loginContext.getRelyingPartyId());
@@ -201,7 +202,7 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
         }
 
         ShibbolethSSOLoginContext loginContext = new ShibbolethSSOLoginContext();
-        loginContext.setRelyingParty(requestContext.getPeerEntityId());
+        loginContext.setRelyingParty(requestContext.getInboundMessageIssuer());
         loginContext.setSpAssertionConsumerService(requestContext.getSpAssertionConsumerService());
         loginContext.setSpTarget(requestContext.getRelayState());
         loginContext.setAuthenticationEngineURL(authenticationManagerPath);
@@ -275,6 +276,8 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
         ShibbolethSSORequestContext requestContext = new ShibbolethSSORequestContext();
 
         try {
+            requestContext.setMessageDecoder(getMessageDecoders().get(getInboundBinding()));
+            
             requestContext.setLoginContext(loginContext);
             requestContext.setPrincipalName(loginContext.getPrincipalName());
             requestContext.setPrincipalAuthenticationMethod(loginContext.getAuthenticationMethod());
@@ -288,7 +291,7 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
             requestContext.setMetadataProvider(metadataProvider);
 
             String relyingPartyId = loginContext.getRelyingPartyId();
-            requestContext.setPeerEntityId(relyingPartyId);
+            requestContext.setInboundMessageIssuer(relyingPartyId);
             EntityDescriptor relyingPartyMetadata = metadataProvider.getEntityDescriptor(relyingPartyId);
             requestContext.setPeerEntityMetadata(relyingPartyMetadata);
             requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
@@ -393,9 +396,6 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
     public class ShibbolethSSORequestContext extends
             BaseSAML1ProfileRequestContext<Request, Response, ShibbolethSSOConfiguration> {
 
-        /** Time since the epoch. */
-        private long time;
-
         /** SP-provide assertion consumer service URL. */
         private String spAssertionConsumerService;
 
@@ -437,23 +437,5 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
         public void setSpAssertionConsumerService(String acs) {
             spAssertionConsumerService = acs;
         }
-
-        /**
-         * Sets the time since the epoch.
-         * 
-         * @return time since the epoch
-         */
-        public long getTime() {
-            return time;
-        }
-
-        /**
-         * Sets the time since the epoch.
-         * 
-         * @param time time since the epoch
-         */
-        public void setTime(long time) {
-            this.time = time;
-        }
     }
 }
\ No newline at end of file
index 2651ceb..794aa5a 100644 (file)
@@ -146,7 +146,7 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
         try {
             SSORequestContext requestContext = decodeRequest(inTransport, outTransport);
 
-            String relyingPartyId = requestContext.getPeerEntityId();
+            String relyingPartyId = requestContext.getInboundMessageIssuer();
             RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
             if (rpConfig == null) {
                 log.error("No relying party configuration for " + relyingPartyId);
@@ -300,7 +300,7 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
             requestContext.setMetadataProvider(metadataProvider);
 
             String relyingPartyId = loginContext.getRelyingPartyId();
-            requestContext.setPeerEntityId(relyingPartyId);
+            requestContext.setInboundMessageIssuer(relyingPartyId);
             EntityDescriptor relyingPartyMetadata = metadataProvider.getEntityDescriptor(relyingPartyId);
             requestContext.setPeerEntityMetadata(relyingPartyMetadata);
             requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
index 3a4f505..ca6e16a 100644 (file)
     <!-- ========================================== -->
     <!--      Relying Party Configurations          -->
     <!-- ========================================== -->
-    <!-- 
     <AnonymousRelyingParty provider="http://example.org/IdP" />
     
     <DefaultRelyingParty provider="http://example.org/IdP" />
-    -->
         
-    <RelyingParty id="urn:example.org:myFederation"
-                  provider="urn:example.org:myFederation:idp1">
+    <RelyingParty id="urn:example.org"
+                  provider="urn:example.org:idp1">
         <ProfileConfiguration xsi:type="saml:ShibbolethSSOProfile" />
-        <!-- 
         <ProfileConfiguration xsi:type="saml:SAML1AttributeQueryProfile" />
         <ProfileConfiguration xsi:type="saml:SAML2SSOProfile" />
         <ProfileConfiguration xsi:type="saml:SAML2AttributeQueryProfile" />
-        -->
     </RelyingParty>
     
+    <RelyingParty id="urn:example.org:BogusSP"
+                  provider="urn:example.org:idp1" />
+    
     
     <!-- ========================================== -->
     <!--      Metadata Configuration                -->
     <!-- ========================================== -->
-
-    <!-- MetadataProvider reading metadata from a URL. -->
-    <!-- Fill in metadataURL and backingFile attributes with deployment specific information -->
-    <!--
-    <MetadataProvider id="URLMD" xsi:type="FileBackedHTTPMetadataProvider" xmlns="urn:mace:shibboleth:2.0:metadata"
-                      metadataURL="http://example.org/my/metadata/file.xml" backingFile="$IDP_HOME$/temp/metadata/somefile.xml" />
-    -->
-                  
-    <!-- MetadataProvider reading metadata from the filesystem -->
-    <!-- Fill in metadataFile attribute with deployment specific information -->
-    <!--
-    <MetadataProvider id="FSMD" xsi:type="FilesystemMetadataProvider" xmlns="urn:mace:shibboleth:2.0:metadata"
-                      metadataFile="$IDP_HOME$/metadata/somefile.xml" />
-    -->
-    
-    <!-- MetadataProvider defining metadata inline -->
-    <!--
     <MetadataProvider id="InlineMD" xsi:type="InlineMetadataProvider" xmlns="urn:mace:shibboleth:2.0:metadata">
-        <EntitiesDescriptor Name="urn:example.org:myFederation" xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
-            <EntityDescriptor entityID="urn:example.org:myFederation:idp1">
-                <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+    
+        <EntitiesDescriptor Name="urn:example.org" xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
+        
+            <EntityDescriptor entityID="urn:example.org:idp1">
+                <IDPSSODescriptor protocolSupportEnumeration="urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
+                    <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
+                    <SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://example.org/myIdP"/>
                     <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.org/myIdP" />
                 </IDPSSODescriptor>
             </EntityDescriptor>
-            <EntityDescriptor entityID="urn:example.org:myFederation:sp1">
-                <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+            
+            <EntityDescriptor entityID="urn:example.org:sp1">
+                <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
+                    <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
+                    <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" Location="https://example.org/mySP" index="0"/>
                     <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.org/mySP" index="0" />
                     <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.org/mySP" index="0" />
                 </SPSSODescriptor>
             </EntityDescriptor>
+            
         </EntitiesDescriptor>
+        
     </MetadataProvider>
-    -->
-    
-    <!-- MetadataProvider the combining other MetadataProviders -->
-    <!--
-    <MetadataProvider id="ExampleMD" xsi:type="ChainingMetadataProvider" xmlns="urn:mace:shibboleth:2.0:metadata">
-        <MetadataProvider id="URLMD" xsi:type="FileBackedHTTPMetadataProvider"
-                      metadataURL="http://example.org/my/metadata" backingFile="/path/to/temp/location" />
-        <MetadataProvider id="FSMD" xsi:type="FilesystemMetadataProvider" metadataFile="/path/to/metadata/file.xml" />
-    </MetadataProvider>
-    -->
     
     <!-- ========================================== -->
     <!--     Security Configurations                -->
     <!-- ========================================== -->
     <security:SecurityPolicy id="shibboleth.DefaultSecurityPolicy" xsi:type="security:SecurityPolicyType">
-        <security:Rule xsi:type="samlsec:SAML1Protocol"/>
-        <security:Rule xsi:type="samlsec:SAML2Protocol"/>
         <security:Rule xsi:type="samlsec:Replay"/>
         <security:Rule xsi:type="samlsec:IssueInstant"/>
         <security:Rule xsi:type="samlsec:MandatoryIssuer"/>
index 771732f..a067b55 100644 (file)
@@ -50,7 +50,7 @@ public class SAML2SSOTestCase extends BaseConf1TestCase {
         String authnRequestString = getSamlRequestString(authnRequest);
 
         MockHttpServletRequest servletRequest = new MockHttpServletRequest();
-        servletRequest.setPathInfo("/IdP/saml2/SSONoAuth");
+        servletRequest.setPathInfo("/saml2/POST/SSO");
         servletRequest.setParameter("SAMLRequest", Base64.encodeBytes(authnRequestString.getBytes()));
 
         MockHttpServletResponse servletResponse = new MockHttpServletResponse();
@@ -77,7 +77,7 @@ public class SAML2SSOTestCase extends BaseConf1TestCase {
         String authnRequestString = getSamlRequestString(authnRequest);
 
         MockHttpServletRequest servletRequest = new MockHttpServletRequest();
-        servletRequest.setPathInfo("/IdP/saml2/SSONoAuth");
+        servletRequest.setPathInfo("/saml2/POST/SSO");
         servletRequest.setParameter("SAMLRequest", Base64.encodeBytes(authnRequestString.getBytes()));
 
         MockHttpServletResponse servletResponse = new MockHttpServletResponse();
@@ -107,7 +107,7 @@ public class SAML2SSOTestCase extends BaseConf1TestCase {
         loginContext.setPrincipalName("testUser");
 
         MockHttpServletRequest servletRequest = new MockHttpServletRequest();
-        servletRequest.setPathInfo("/IdP/saml2/SSONoAuth");
+        servletRequest.setPathInfo("/saml2/POST/SSO");
 
         HttpSession session = servletRequest.getSession();
         session.setAttribute(Saml2LoginContext.LOGIN_CONTEXT_KEY, loginContext);
diff --git a/test/edu/internet2/middleware/shibboleth/idp/system/conf1/ShibbolethSSOTestCase.java b/test/edu/internet2/middleware/shibboleth/idp/system/conf1/ShibbolethSSOTestCase.java
new file mode 100644 (file)
index 0000000..9b39f14
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.system.conf1;
+
+import javax.servlet.http.HttpSession;
+
+import org.joda.time.DateTime;
+import org.opensaml.ws.transport.http.HTTPInTransport;
+import org.opensaml.ws.transport.http.HTTPOutTransport;
+import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
+import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
+import edu.internet2.middleware.shibboleth.idp.authn.ShibbolethSSOLoginContext;
+
+/**
+ * Unit test for Shibboleth SSO requests.
+ */
+public class ShibbolethSSOTestCase extends BaseConf1TestCase {
+
+    /** Tests initial leg of the SSO request where request is decoded and sent to the authentication engine. */
+    public void testFirstAuthenticationLeg() throws Exception {
+        MockHttpServletRequest servletRequest = buildServletRequest();
+        MockHttpServletResponse servletResponse = new MockHttpServletResponse();
+
+        ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
+                "shibboleth.HandlerManager");
+        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        assertNotNull(handler);
+
+        // Process request
+        HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
+        HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse);
+        handler.processRequest(profileRequest, profileResponse);
+
+        HttpSession session = servletRequest.getSession();
+        ShibbolethSSOLoginContext loginContext = (ShibbolethSSOLoginContext) session
+                .getAttribute(ShibbolethSSOLoginContext.LOGIN_CONTEXT_KEY);
+
+        assertNotNull(loginContext);
+        assertEquals(false, loginContext.getAuthenticationAttempted());
+        assertEquals(false, loginContext.getForceAuth());
+        assertEquals(false, loginContext.getPassiveAuth());
+        assertEquals("/AuthnEngine", loginContext.getAuthenticationEngineURL());
+        assertEquals("/shibboleth/SSO", loginContext.getProfileHandlerURL());
+        assertEquals("urn:example.org:sp", loginContext.getRelyingPartyId());
+        assertEquals(0, loginContext.getRequestedAuthenticationMethods().size());
+        assertEquals("https://example.org/mySP", loginContext.getSpAssertionConsumerService());
+        assertEquals("https://example.org/mySP", loginContext.getSpTarget());
+
+        assertEquals("/AuthnEngine", servletResponse.getForwardedUrl());
+    }
+
+    /** Tests second leg of the SSO request where request returns to SSO handler and AuthN statement is generated. */
+    public void testSecondAuthenticationLeg() throws Exception {
+        MockHttpServletRequest servletRequest = buildServletRequest();
+        MockHttpServletResponse servletResponse = new MockHttpServletResponse();
+
+        HttpSession httpSession = servletRequest.getSession(true);
+        httpSession.setAttribute(ShibbolethSSOLoginContext.LOGIN_CONTEXT_KEY, buildLoginContext());
+
+        ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
+                "shibboleth.HandlerManager");
+        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        assertNotNull(handler);
+
+        // Process request
+        HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
+        HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse);
+        handler.processRequest(profileRequest, profileResponse);
+
+        String response = servletResponse.getContentAsString();
+        assertTrue(response.contains("action=\"https://example.org/mySP\" method=\"post\""));
+        assertTrue(response.contains("name=\"TARGET\" value=\"https://example.org/mySP\""));
+        assertTrue(response.contains("SAMLResponse"));
+    }
+
+    /** Tests that the SSO handler correctly fails out if the Shib SSO profile is not configured. */
+    public void testAuthenticationWithoutConfiguredSSO() {
+        MockHttpServletRequest servletRequest = buildServletRequest();
+        servletRequest.setParameter("providerId", "urn:example.org:BogusSP");
+        
+        MockHttpServletResponse servletResponse = new MockHttpServletResponse();
+
+        ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
+                "shibboleth.HandlerManager");
+        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        assertNotNull(handler);
+
+        // Process request
+        HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
+        HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse);
+        try {
+            handler.processRequest(profileRequest, profileResponse);
+            fail("Request processing expected to due to lack of configured Shib SSO profile");
+        } catch (ProfileException e) {
+
+        }
+    }
+
+    protected MockHttpServletRequest buildServletRequest() {
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setPathInfo("/shibboleth/SSO");
+        request.setParameter("providerId", "urn:example.org:sp1");
+        request.setParameter("shire", "https://example.org/mySP");
+        request.setParameter("target", "https://example.org/mySP");
+
+        return request;
+    }
+
+    protected ShibbolethSSOLoginContext buildLoginContext() {
+        ShibbolethSSOLoginContext loginContext = new ShibbolethSSOLoginContext();
+        loginContext.setAuthenticationInstant(new DateTime());
+        loginContext.setAuthenticationMethod("urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified");
+        loginContext.setPrincipalAuthenticated(true);
+        loginContext.setPrincipalName("testUser");
+        loginContext.setRelyingParty("urn:example.org:sp1");
+        loginContext.setSpAssertionConsumerService("https://example.org/mySP");
+        loginContext.setSpTarget("https://example.org/mySP");
+
+        return loginContext;
+    }
+}
\ No newline at end of file