// Copyright 2006 Google 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 com.google.enterprise.connector.servlet; import com.google.common.collect.ImmutableList; import com.google.enterprise.connector.common.StringUtils; import com.google.enterprise.connector.manager.MockManager; import com.google.enterprise.connector.spi.AuthenticationIdentity; import com.google.enterprise.connector.spi.AuthenticationResponse; import com.google.enterprise.connector.spi.Principal; import com.google.enterprise.connector.spi.SimpleAuthenticationIdentity; import com.google.enterprise.connector.spi.SpiConstants; import junit.framework.TestCase; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Collection; import java.util.logging.Logger; /** * Tests the AuthenticateTest servlet class * */ public class AuthenticateTest extends TestCase { private static final Logger LOGGER = Logger.getLogger(AuthenticateTest.class.getName()); public void testAuthenticate() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " <Success ConnectorName=\"connector2\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, null, "fooUser", "fooPassword", null); } public void testEmptyBody() { String xmlBody = ""; String expectedResult = "<CmResponse>\n <StatusId>" + ConnectorMessageCode.ERROR_PARSING_XML_REQUEST + "</StatusId>\n</CmResponse>\n"; doTest(xmlBody, expectedResult, null, "fooUser", "fooPassword", null); } public void testNoCredentials() { String xmlBody = "<AuthnRequest>\n</AuthnRequest>"; doTestInvalidAuthentication(xmlBody, ConnectorMessageCode.RESPONSE_EMPTY_NODE); } public void testBadUsername() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>badUser</Username>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; doTestFailedAuthentication(xmlBody); } public void testEmptyUsername() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username></Username>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; doTestInvalidAuthentication(xmlBody, ConnectorMessageCode.RESPONSE_NULL_IDENTITY); } public void testNoUsername() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username></Username>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; doTestInvalidAuthentication(xmlBody, ConnectorMessageCode.RESPONSE_NULL_IDENTITY); } public void testBadPassword() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Password>badPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; doTestFailedAuthentication(xmlBody); } public void testEmptyPassword() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Password></Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; doTestInvalidAuthentication(xmlBody, ConnectorMessageCode.RESPONSE_NULL_IDENTITY); } private void doTestInvalidAuthentication(String xmlBody, int messageCode) { String expectedResult = "<CmResponse>\n <StatusId>" + messageCode + "</StatusId>\n</CmResponse>\n"; doTest(xmlBody, expectedResult, null, "fooUser", "fooPassword", null); } private void doTestFailedAuthentication(String xmlBody) { String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Failure ConnectorName=\"connector1\"/>\n" + " <Failure ConnectorName=\"connector2\"/>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, null, "fooUser", "fooPassword", null); } public void testWithDomain() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>fooDomain</Domain>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " <Success ConnectorName=\"connector2\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, "fooDomain", "fooUser", "fooPassword", null); } public void testPassAndFail() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>connector1</Domain>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " <Failure ConnectorName=\"connector2\"/>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, "connector1", "fooUser", "fooPassword", null); } public void testFailAndPass() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>connector2</Domain>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Failure ConnectorName=\"connector1\"/>\n" + " <Success ConnectorName=\"connector2\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, "connector2", "fooUser", "fooPassword", null); } public void testWithTwoConnectorNameElements() { String xmlBody = "<AuthnRequest>\n" + " <Connectors>\n" + " <ConnectorName>connector1</ConnectorName>\n" + " <ConnectorName>connector2</ConnectorName>\n" + " </Connectors>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>fooDomain</Domain>" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " <Success ConnectorName=\"connector2\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, "fooDomain", "fooUser", "fooPassword", null); } public void testWithOneConnectorNameElement() { String xmlBody = "<AuthnRequest>\n" + " <Connectors>\n" + " <ConnectorName>connector1</ConnectorName>\n" + " </Connectors>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>fooDomain</Domain>" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + " </Success>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, "fooDomain", "fooUser", "fooPassword", null); } public void testGetGroupsNoPassword() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " </Credentials>\n" + "</AuthnRequest>"; doTestGroups(xmlBody); } public void testAuthenticateWithGroups() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Password>fooPassword</Password>\n" + " </Credentials>\n" + "</AuthnRequest>"; doTestGroups(xmlBody); } /** * Confirms that a failing group lookup is turned into a Success in * the XML response. */ public void testFailGroup() { String xmlBody = "<AuthnRequest>\n" + " <Connectors>\n" + " <ConnectorName>connector1</ConnectorName>\n" + " </Connectors>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>timbuktu</Domain>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\"/>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; MockManager manager = MockManager.getInstance(); manager.setShouldVerifyIdentity(true); manager.setExpectedIdentity("connector1", "fooUser", "fooPassword", makeGroups("staff", "wheel")); AuthenticationIdentity identity = new SimpleAuthenticationIdentity( "fooUser", "fooPassword", "timbuktu"); AuthenticationResponse response = manager.authenticate("connector1", identity); assertFalse(response.isValid()); doTest(xmlBody, expectedResult, "connector1", "fooUser", "fooPassword", makeGroups("staff", "wheel")); } public void testPassAndFailGroups() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>connector1</Domain>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + " <Group>staff</Group>\n" + " <Group>wheel</Group>\n" + " </Success>\n" + " <Success ConnectorName=\"connector2\"/>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, "connector1", "fooUser", "fooPassword", makeGroups("staff", "wheel")); } public void testGroupsWithNamespaces() { String xmlBody = "<AuthnRequest>\n" + " <Connectors>\n" + " <ConnectorName>connector1</ConnectorName>\n" + " </Connectors>\n" + " <Credentials>\n" + " <Username>fooUser</Username>\n" + " <Domain>connector1</Domain>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + /* TODO: Re-enable once the GSA supports principal-type attribute. " <Group principaltype=\"dn\" namespace=\"global\">staff</Group>\n" + " <Group principaltype=\"dn\" namespace=\"global\">wheel</Group>\n" + * else */ " <Group namespace=\"global\">staff</Group>\n" + " <Group namespace=\"global\">wheel</Group>\n" + /* end TODO */ " <Group principal-type=\"unqualified\" namespace=\"local\">wheel" + "</Group>\n" + " <Group principal-type=\"unqualified\" namespace=\"local\">slo" + "</Group>\n" + " </Success>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; Collection<Principal> groups = ImmutableList.of( new Principal(SpiConstants.PrincipalType.UNKNOWN, "global", "staff"), new Principal(SpiConstants.PrincipalType.UNKNOWN, "global", "wheel"), new Principal(SpiConstants.PrincipalType.UNQUALIFIED, "local", "wheel"), new Principal(SpiConstants.PrincipalType.UNQUALIFIED, "local", "slo")); doTest(xmlBody, expectedResult, "connector1", "fooUser", "fooPassword", groups); } private void doTestGroups(String xmlBody) { String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>fooUser</Identity>\n" + " <Group>staff</Group>\n" + " <Group>wheel</Group>\n" + " </Success>\n" + " <Success ConnectorName=\"connector2\">\n" + " <Identity>fooUser</Identity>\n" + " <Group>staff</Group>\n" + " <Group>wheel</Group>\n" + " </Success>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, null, "fooUser", "fooPassword", makeGroups("staff", "wheel")); } /** Tests unsafe XML characters in user and group names. */ public void testEvilUserAndGroupNames() { String xmlBody = "<AuthnRequest>\n" + " <Credentials>\n" + " <Username>foo<bar>baz&</Username>\n" + " <Domain>connector1</Domain>\n" + " </Credentials>\n" + "</AuthnRequest>"; String expectedResult = "<CmResponse>\n" + " <AuthnResponse>\n" + " <Success ConnectorName=\"connector1\">\n" + " <Identity>foo<bar>baz&</Identity>\n" + " <Group>st&ff</Group>\n" + " <Group>we'll</Group>\n" + " </Success>\n" + " <Success ConnectorName=\"connector2\"/>\n" + " </AuthnResponse>\n" + "</CmResponse>\n"; doTest(xmlBody, expectedResult, "connector1", "foo<bar>baz&", "fooPassword", makeGroups("st&ff", "we'll")); } private Collection<String> makeGroups(String... groups) { return ImmutableList.copyOf(groups); } private void doTest(String xmlBody, String expectedResult, String domain, String username, String password, Collection<?> groups) { LOGGER.info("============== " + getName() + " ===================="); LOGGER.info("xmlBody:\n" + xmlBody); MockManager manager = MockManager.getInstance(); manager.setShouldVerifyIdentity(true); manager.setExpectedIdentity(domain, username, password, groups); StringWriter writer = new StringWriter(); PrintWriter out = new PrintWriter(writer); Authenticate.handleDoPost(xmlBody, manager, out); out.flush(); String result = writer.toString(); out.close(); LOGGER.info("expected result:\n" + expectedResult); LOGGER.info("actual result:\n" + result); assertEquals(StringUtils.normalizeNewlines(expectedResult), StringUtils.normalizeNewlines(result)); } }