/** * Copyright 2010 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 org.waveprotocol.box.server.rpc; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import junit.framework.TestCase; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.waveprotocol.box.server.account.HumanAccountData; import org.waveprotocol.box.server.account.HumanAccountDataImpl; import org.waveprotocol.box.server.authentication.AccountStoreHolder; import org.waveprotocol.box.server.authentication.AuthTestUtil; import org.waveprotocol.box.server.authentication.PasswordDigest; import org.waveprotocol.box.server.authentication.SessionManager; import org.waveprotocol.box.server.persistence.AccountStore; import org.waveprotocol.box.server.persistence.memory.MemoryStore; import org.waveprotocol.wave.model.wave.ParticipantId; import org.waveprotocol.wave.util.escapers.PercentEscaper; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.StringReader; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * @author josephg@gmail.com (Joseph Gentle) */ public class AuthenticationServletTest extends TestCase { private static final ParticipantId USER = ParticipantId.ofUnsafe("frodo@example.com"); private AuthenticationServlet servlet; @Mock private HttpServletRequest req; @Mock private HttpServletResponse resp; @Mock private HttpSession session; @Mock private SessionManager manager; @Override protected void setUp() throws Exception { MockitoAnnotations.initMocks(this); AccountStore store = new MemoryStore(); HumanAccountData account = new HumanAccountDataImpl(USER, new PasswordDigest("password".toCharArray())); store.putAccount(account); servlet = new AuthenticationServlet(AuthTestUtil.makeConfiguration(), manager, "examPLe.com"); AccountStoreHolder.init(store, "eXaMple.com"); } @Override protected void tearDown() throws Exception { AccountStoreHolder.resetForTesting(); } public void testGetReturnsSomething() throws IOException { when(req.getSession(false)).thenReturn(null); PrintWriter writer = mock(PrintWriter.class); when(resp.getWriter()).thenReturn(writer); when(req.getLocale()).thenReturn(Locale.ENGLISH); servlet.doGet(req, resp); verify(resp).setStatus(HttpServletResponse.SC_OK); } public void testGetRedirects() throws IOException { String location = "/abc123?nested=query&string"; when(req.getSession(false)).thenReturn(session); when(manager.getLoggedInUser(session)).thenReturn(USER); configureRedirectString(location); servlet.doGet(req, resp); verify(resp).sendRedirect(location); } public void testValidLoginWorks() throws IOException { attemptLogin("frodo@example.com", "password", true); verify(resp).sendRedirect("/"); } public void testUserWithNoDomainGetsDomainAutomaticallyAdded() throws Exception { attemptLogin("frodo", "password", true); verify(resp).sendRedirect("/"); } public void testLoginRedirects() throws IOException { String redirect = "/abc123?nested=query&string"; configureRedirectString(redirect); attemptLogin("frodo@example.com", "password", true); verify(resp).sendRedirect(redirect); } public void testLoginDoesNotRedirectToRemoteSite() throws IOException { configureRedirectString("http://example.com/other/site"); attemptLogin("frodo@example.com", "password", true); verify(resp, never()).sendRedirect(anyString()); } public void testIncorrectPasswordReturns403() throws IOException { attemptLogin("frodo@example.com", "incorrect", false); verify(resp).setStatus(HttpServletResponse.SC_FORBIDDEN); verify(session, never()).setAttribute(eq("user"), anyString()); } public void testInvalidUsernameReturns403() throws IOException { attemptLogin("madeup@example.com", "incorrect", false); verify(resp).setStatus(HttpServletResponse.SC_FORBIDDEN); verify(session, never()).setAttribute(eq("address"), anyString()); } // *** Utility methods private void configureRedirectString(String location) { PercentEscaper escaper = new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER, false); String queryStr = "r=" + escaper.escape(location); when(req.getQueryString()).thenReturn(queryStr); } private void attemptLogin(String address, String password, boolean expectSuccess) throws IOException { // The query string is escaped. PercentEscaper escaper = new PercentEscaper(PercentEscaper.SAFECHARS_URLENCODER, true); String data = "address=" + escaper.escape(address) + "&" + "password=" + escaper.escape(password); Reader reader = new StringReader(data); when(req.getReader()).thenReturn(new BufferedReader(reader)); PrintWriter writer = mock(PrintWriter.class); when(resp.getWriter()).thenReturn(writer); when(req.getSession(false)).thenReturn(null); when(req.getSession(true)).thenReturn(session); when(req.getLocale()).thenReturn(Locale.ENGLISH); // Servlet control flow forces us to set these return values first and // verify the logged in user was set afterwards. if (expectSuccess) { when(manager.getLoggedInUser(Mockito.any(HttpSession.class))).thenReturn(USER); when(session.getAttribute("user")).thenReturn(USER); } servlet.doPost(req, resp); if (expectSuccess) { verify(manager).setLoggedInUser(session, USER); } } }