// Copyright 2014 Google Inc. All Rights Reserved. // // 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 com.google.enterprise.adaptor.sharepoint; import static org.junit.Assert.assertEquals; import com.google.enterprise.adaptor.sharepoint.FormsAuthenticationHandlerTest.MockScheduledExecutor; import com.google.enterprise.adaptor.sharepoint.SamlAuthenticationHandler.HttpPostClient; import com.google.enterprise.adaptor.sharepoint.SamlAuthenticationHandler.PostResponseInfo; import java.io.IOException; import java.net.URL; import java.net.URLEncoder; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class AdfsHandshakeManagerTest { @Rule public ExpectedException thrown = ExpectedException.none(); private static class UnsupportedHttpPostClient implements HttpPostClient { @Override public SamlAuthenticationHandler.PostResponseInfo issuePostRequest( URL url, Map<String, String> connectionProperties, String requestBody) throws IOException { throw new UnsupportedOperationException(); } } private static class MockHttpPostClient implements HttpPostClient { private Map<URL, PostResponseInfo> responseMap; private Map<URL, String> receivedRequestBodyMap; public MockHttpPostClient() { responseMap = new HashMap<URL, PostResponseInfo>(); receivedRequestBodyMap = new HashMap<URL, String>(); } @Override public SamlAuthenticationHandler.PostResponseInfo issuePostRequest( URL url, Map<String, String> connectionProperties, String requestBody) throws IOException { if (!responseMap.containsKey(url)) { throw new UnsupportedOperationException( "Unexpected Http Post for URL " + url); } // log incoming request body receivedRequestBodyMap.put(url, requestBody); return responseMap.get(url); } } @Test public void testConstructor() { new AdfsHandshakeManager.Builder( "http://endpoint", "username", "password", "https://sts", "realm") .build(); } @Test public void testNullUsername() { thrown.expect(NullPointerException.class); new AdfsHandshakeManager.Builder( "http://endpoint", null, "password", "https://sts", "realm").build(); } @Test public void testNullPassword() { thrown.expect(NullPointerException.class); new AdfsHandshakeManager.Builder( "http://endpoint", "username", null, "https://sts", "realm").build(); } @Test public void testNullEndpoint() { thrown.expect(NullPointerException.class); new AdfsHandshakeManager.Builder( null, "username", "password","https://sts", "realm").build(); } @Test public void testNullSts() { thrown.expect(NullPointerException.class); new AdfsHandshakeManager.Builder( "http://endpoint", "username", "password", null, "realm").build(); } @Test public void testNullRealm() { thrown.expect(NullPointerException.class); new AdfsHandshakeManager.Builder( "http://endpoint", "username", "password", "http://sts", null).build(); } @Test public void testRequestToken() throws IOException{ MockHttpPostClient postClient = new MockHttpPostClient(); AdfsHandshakeManager manager = new AdfsHandshakeManager.Builder( "https://sharepoint.intranet.com", "username@domain", "pass]]>word&123", "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed", "urn:realm:sharepoint", postClient).build(); URL tokenRequest = new URL( "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed"); String tokenResponse = "<s:Envelope " + "xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\">" + "<s:Header>Some header</s:Header>" + "<t:RequestSecurityTokenResponse " + "xmlns:t=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">" + "This is requested token" + "</t:RequestSecurityTokenResponse>" + "</s:Envelope>"; postClient.responseMap.put(tokenRequest, new PostResponseInfo(tokenResponse, null)); assertEquals("<t:RequestSecurityTokenResponse " + "xmlns:t=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">" + "This is requested token" + "</t:RequestSecurityTokenResponse>", manager.requestToken()); String expectedRequestBody = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" + "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" " + "xmlns:a=\"http://www.w3.org/2005/08/addressing\" " + "xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-" + "wssecurity-utility-1.0.xsd\"><s:Header><a:Action " + "s:mustUnderstand=\"1\">" + "http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>" + "<a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous" + "</a:Address></a:ReplyTo><a:To s:mustUnderstand=\"1\"><![CDATA[" + "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed]]>" + "</a:To><o:Security s:mustUnderstand=\"1\" " + "xmlns:o=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-" + "wssecurity-secext-1.0.xsd\"><o:UsernameToken>" + "<o:Username><![CDATA[username@domain]]></o:Username>" + "<o:Password><![CDATA[pass]]]]><![CDATA[>word&123]]>" + "</o:Password></o:UsernameToken></o:Security></s:Header>" + "<s:Body><t:RequestSecurityToken " + "xmlns:t=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">" + "<wsp:AppliesTo " + "xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\">" + "<a:EndpointReference><a:Address>" + "<![CDATA[urn:realm:sharepoint]]></a:Address>" + "</a:EndpointReference></wsp:AppliesTo><t:KeyType>" + "http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey" + "</t:KeyType><t:RequestType>" + "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" + "</t:RequestType><t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion" + "</t:TokenType></t:RequestSecurityToken></s:Body></s:Envelope>"; assertEquals(expectedRequestBody, postClient.receivedRequestBodyMap.get(tokenRequest)); } @Test public void testNullRequestToken() throws IOException{ MockHttpPostClient postClient = new MockHttpPostClient(); AdfsHandshakeManager manager = new AdfsHandshakeManager.Builder( "https://sharepoint.intranet.com", "username@domain", "password&123", "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed", "urn:realm:sharepoint", postClient).build(); URL tokenRequest = new URL( "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed"); postClient.responseMap.put(tokenRequest, new PostResponseInfo("<data>some invalid content</data>", null)); thrown.expect(IOException.class); String token = manager.requestToken(); } @Test public void testGetAuthenticationCookie() throws IOException{ MockHttpPostClient postClient = new MockHttpPostClient(); AdfsHandshakeManager manager = new AdfsHandshakeManager.Builder( "https://sharepoint.intranet.com", "username@domain", "password&123", "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed", "urn:realm:sharepoint", postClient).build(); URL submitToken = new URL("https://sharepoint.intranet.com/_trust"); Map<String, List<String>> responseHeaders = new HashMap<String, List<String>>(); responseHeaders.put("some-header", Arrays.asList("some value")); responseHeaders.put("Set-Cookie", Arrays.asList("FedAuth=AutheCookie")); postClient.responseMap.put(submitToken, new PostResponseInfo("submit token response", responseHeaders)); String cookie = manager.getAuthenticationCookie( "<t:RequestSecurityTokenResponse " + "xmlns:t=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">" + "This is requested token" + "</t:RequestSecurityTokenResponse>"); assertEquals("FedAuth=AutheCookie;", cookie); String expectedSubmitTokenRequest = "wa=wsignin1.0&wctx=" + URLEncoder.encode("https://sharepoint.intranet.com/_layouts/" + "Authenticate.aspx","UTF-8") + "&wresult=" + URLEncoder.encode("<t:RequestSecurityTokenResponse " + "xmlns:t=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">" + "This is requested token" + "</t:RequestSecurityTokenResponse>", "UTF-8"); assertEquals(expectedSubmitTokenRequest, postClient.receivedRequestBodyMap.get(submitToken)); } @Test public void testAuthenticateInSamlHandlerWithADFS() throws IOException{ MockHttpPostClient postClient = new MockHttpPostClient(); String username = "username@domain"; String password = "password&123"; AdfsHandshakeManager manager = new AdfsHandshakeManager.Builder( "https://sharepoint.intranet.com", username, password, "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed", "urn:realm:sharepoint", postClient).build(); URL tokenRequest = new URL( "https://sts.dmain.com/adfs/services/trust/2005/usernamemixed"); String tokenResponse = "<s:Envelope " + "xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\">" + "<s:Header>Some header</s:Header>" + "<t:RequestSecurityTokenResponse " + "xmlns:t=\"http://schemas.xmlsoap.org/ws/2005/02/trust\">" + "This is requested token" + "</t:RequestSecurityTokenResponse>" + "</s:Envelope>"; postClient.responseMap.put(tokenRequest, new PostResponseInfo(tokenResponse, null)); URL submitToken = new URL("https://sharepoint.intranet.com/_trust"); Map<String, List<String>> responseHeaders = new HashMap<String, List<String>>(); responseHeaders.put("some-header", Arrays.asList("some value")); responseHeaders.put("Set-Cookie", Arrays.asList("FedAuth=AutheCookie")); postClient.responseMap.put(submitToken, new PostResponseInfo(null, responseHeaders)); SamlAuthenticationHandler authenticationHandler = new SamlAuthenticationHandler.Builder(username, password, new MockScheduledExecutor(), manager).build(); AuthenticationResult result = authenticationHandler.authenticate(); assertEquals("FedAuth=AutheCookie;", result.getCookie()); assertEquals("NO_ERROR", result.getErrorCode()); assertEquals(600, result.getCookieTimeOut()); } @Test public void testEscapeCdata() { AdfsHandshakeManager manager = new AdfsHandshakeManager.Builder( "http://endpoint", "username", "password", "https://sts", "realm") .build(); assertEquals("<![CDATA[This is simple]]>", manager.escapeCdata("This is simple")); assertEquals( "<![CDATA[This is simple]]]]><![CDATA[>with additional text]]>", manager.escapeCdata("This is simple]]>with additional text")); assertEquals( "<![CDATA[This is > & simple]]]]><![CDATA[>]]>", manager.escapeCdata("This is > & simple]]>")); assertEquals( "<![CDATA[]]]]><![CDATA[>]]>", manager.escapeCdata("]]>")); assertEquals("<![CDATA[<![CDATA[This is simple]]]]><![CDATA[>]]>", manager.escapeCdata("<![CDATA[This is simple]]>")); assertEquals("<![CDATA[This is simple]]]]>" + "<![CDATA[>with multiple]]]]><![CDATA[>]]>", manager.escapeCdata("This is simple]]>with multiple]]>")); } }