Create a debug mode only method of logging in to Gerrit

Currently the hosted mode debug session is crashing on my 64 bit Ubuntu
desktop anytime I try to use an OpenID login.  As a work around we now
have a servlet that can authenticate the browser as any user identity
desired, provided that the JRE was started with a system property that
permits its usage, and that we were launched from the hosted mode shell
as the servlet is not mapped in the main web.xml.

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/gerrit_debug.launch b/gerrit_debug.launch
index 5c022a4..9f54123 100644
--- a/gerrit_debug.launch
+++ b/gerrit_debug.launch
@@ -23,5 +23,5 @@
 <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-out gwt_www com.google.gerrit.Gerrit/Gerrit.html"/>
 <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
 <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M -DGerritServer=${resource_loc:/gerrit/src/main/java/GerritServer.properties}"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M -DGerritServer=${resource_loc:/gerrit/src/main/java/GerritServer.properties} -Dcom.google.gerrit.server.BecomeAnyAccountLoginServlet=true"/>
 </launchConfiguration>
diff --git a/gerrit_macos.launch b/gerrit_macos.launch
index 3058fcd..e10cb2a 100644
--- a/gerrit_macos.launch
+++ b/gerrit_macos.launch
@@ -25,5 +25,5 @@
 <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-out gwt_www com.google.gerrit.Gerrit/Gerrit.html"/>
 <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
 <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M -XstartOnFirstThread -DGerritServer=${resource_loc:/gerrit/src/main/java/GerritServer.properties}"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M -XstartOnFirstThread -DGerritServer=${resource_loc:/gerrit/src/main/java/GerritServer.properties} -Dcom.google.gerrit.server.BecomeAnyAccountLoginServlet=true"/>
 </launchConfiguration>
diff --git a/src/main/java/com/google/gerrit/Gerrit.gwt.xml b/src/main/java/com/google/gerrit/Gerrit.gwt.xml
index 6075922..d56c42d 100644
--- a/src/main/java/com/google/gerrit/Gerrit.gwt.xml
+++ b/src/main/java/com/google/gerrit/Gerrit.gwt.xml
@@ -57,4 +57,8 @@
            class='com.google.gerrit.server.SuggestServiceSrv'/>
   <servlet path='/rpc/SystemInfoService'
            class='com.google.gerrit.server.SystemInfoServiceSrv'/>
+
+  <!-- Hosted mode debugging ONLY -->
+  <servlet path='/__BecomeAnyAccount'
+           class='com.google.gerrit.server.BecomeAnyAccountLoginServlet'/>
 </module>
diff --git a/src/main/java/com/google/gerrit/client/Gerrit.java b/src/main/java/com/google/gerrit/client/Gerrit.java
index 0f1200c..95307e7 100644
--- a/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -320,19 +320,23 @@
     if (signedIn) {
       whoAmI();
       menuBar.addItem(new LinkMenuItem(C.menuSettings(), Link.SETTINGS));
+      boolean signout = false;
       switch (Common.getGerritConfig().getLoginType()) {
         case HTTP:
           break;
 
         case OPENID:
         default:
-          menuBar.addItem(C.menuSignOut(), new Command() {
-            public void execute() {
-              doSignOut();
-            }
-          });
+          signout = true;
           break;
       }
+      if (signout || (GWT.isClient() && !GWT.isScript())) {
+        menuBar.addItem(C.menuSignOut(), new Command() {
+          public void execute() {
+            doSignOut();
+          }
+        });
+      }
     } else {
       switch (Common.getGerritConfig().getLoginType()) {
         case HTTP:
@@ -347,6 +351,14 @@
           });
           break;
       }
+      if (GWT.isClient() && !GWT.isScript()) {
+        menuBar.addItem("Become", new Command() {
+          public void execute() {
+            final String base = GWT.getModuleBaseURL();
+            Window.Location.assign(base + "__BecomeAnyAccount");
+          }
+        });
+      }
     }
     menuBar.lastInGroup();
 
diff --git a/src/main/java/com/google/gerrit/server/BecomeAnyAccountLoginServlet.java b/src/main/java/com/google/gerrit/server/BecomeAnyAccountLoginServlet.java
new file mode 100644
index 0000000..ac9e69f
--- /dev/null
+++ b/src/main/java/com/google/gerrit/server/BecomeAnyAccountLoginServlet.java
@@ -0,0 +1,169 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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.gerrit.server;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.reviewdb.Account;
+import com.google.gerrit.client.reviewdb.ReviewDb;
+import com.google.gerrit.client.rpc.Common;
+import com.google.gwtjsonrpc.server.XsrfException;
+import com.google.gwtorm.client.OrmException;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class BecomeAnyAccountLoginServlet extends HttpServlet {
+  private boolean allowed;
+  private GerritServer server;
+
+  @Override
+  public void init(final ServletConfig config) throws ServletException {
+    super.init(config);
+    try {
+      allowed = Boolean.getBoolean(getClass().getName());
+    } catch (SecurityException se) {
+      allowed = false;
+    }
+
+    try {
+      server = GerritServer.getInstance();
+    } catch (OrmException e) {
+      throw new ServletException("Cannot load GerritServer", e);
+    } catch (XsrfException e) {
+      throw new ServletException("Cannot load GerritServer", e);
+    }
+  }
+
+  @Override
+  protected void doGet(final HttpServletRequest req,
+      final HttpServletResponse rsp) throws IOException {
+    if (!allowed) {
+      rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+      return;
+    }
+
+    rsp.setContentType("text/html");
+    final ServletOutputStream out = rsp.getOutputStream();
+    out.print("<html>");
+    out.print("<form method=\"POST\"><b>ssh_user_name:</b> "
+        + "<input type=\"text\" size=\"30\" name=\"ssh_user_name\" />"
+        + "<input type=\"submit\" value=\"Become Account\" />" + "</form>");
+    out.print("<form method=\"POST\"><b>preferred_email:</b> "
+        + "<input type=\"text\" size=\"30\" name=\"preferred_email\" />"
+        + "<input type=\"submit\" value=\"Become Account\" />" + "</form>");
+    out.print("<form method=\"POST\"><b>account_id:</b> "
+        + "<input type=\"text\" size=\"12\" name=\"account_id\" />"
+        + "<input type=\"submit\" value=\"Become Account\" />" + "</form>");
+    out.print("</html>");
+  }
+
+  @Override
+  protected void doPost(final HttpServletRequest req,
+      final HttpServletResponse rsp) throws IOException {
+    if (!allowed) {
+      rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+      return;
+    }
+
+    final List<Account> accounts;
+    if (req.getParameter("ssh_user_name") != null) {
+      accounts = bySshUserName(rsp, req.getParameter("ssh_user_name"));
+
+    } else if (req.getParameter("preferred_email") != null) {
+      accounts = byPreferredEmail(rsp, req.getParameter("preferred_email"));
+
+    } else if (req.getParameter("account_id") != null) {
+      accounts = byAccountId(rsp, req.getParameter("account_id"));
+
+    } else {
+      doGet(req, rsp);
+      return;
+    }
+
+    if (accounts.size() == 1) {
+      final Account account = accounts.get(0);
+      final Cookie c = new Cookie(Gerrit.ACCOUNT_COOKIE, "");
+      c.setPath(req.getContextPath() + "/");
+      new AccountCookie(account.getId(), false).set(c, server);
+      rsp.addCookie(c);
+      rsp.sendRedirect("Gerrit.html");
+
+    } else {
+      rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+    }
+  }
+
+  private List<Account> bySshUserName(final HttpServletResponse rsp,
+      final String userName) {
+    try {
+      final ReviewDb db = Common.getSchemaFactory().open();
+      try {
+        return db.accounts().bySshUserName(userName).toList();
+      } finally {
+        db.close();
+      }
+    } catch (OrmException e) {
+      getServletContext().log("cannot query database", e);
+      return Collections.<Account> emptyList();
+    }
+  }
+
+  private List<Account> byPreferredEmail(final HttpServletResponse rsp,
+      final String email) {
+    try {
+      final ReviewDb db = Common.getSchemaFactory().open();
+      try {
+        return db.accounts().byPreferredEmail(email).toList();
+      } finally {
+        db.close();
+      }
+    } catch (OrmException e) {
+      getServletContext().log("cannot query database", e);
+      return Collections.<Account> emptyList();
+    }
+  }
+
+  private List<Account> byAccountId(final HttpServletResponse rsp,
+      final String idStr) {
+    final Account.Id id;
+    try {
+      id = Account.Id.parse(idStr);
+    } catch (NumberFormatException nfe) {
+      return Collections.<Account> emptyList();
+    }
+    try {
+      final ReviewDb db = Common.getSchemaFactory().open();
+      try {
+        final Account account = db.accounts().get(id);
+        return account != null ? Collections.<Account> singletonList(account)
+            : Collections.<Account> emptyList();
+      } finally {
+        db.close();
+      }
+    } catch (OrmException e) {
+      getServletContext().log("cannot query database", e);
+      return Collections.<Account> emptyList();
+    }
+  }
+}