Get rid of HTTP passwords use on GitHub plugin

GitHub plugin cannot use anymore the "trick" of reading a user's
HTTP password for managing Git/HTTP authentication with GitHub OAuth
persistent tokens.

Change-Id: Ib059e065c312c5077fb2e5012a7ffaa6929e952f
diff --git a/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/IdentifiedUserGitHubLoginProvider.java b/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/IdentifiedUserGitHubLoginProvider.java
index ebaae7b..4c92e2e 100644
--- a/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/IdentifiedUserGitHubLoginProvider.java
+++ b/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/IdentifiedUserGitHubLoginProvider.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.ExternalId;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -35,8 +36,8 @@
     UserScopedProvider<GitHubLogin> {
   private static final Logger log = LoggerFactory
       .getLogger(IdentifiedUserGitHubLoginProvider.class);
-  private static final String EXTERNAL_ID_PREFIX = "external:"
-      + OAuthWebFilter.GITHUB_EXT_ID;
+  public static final String EXTERNAL_ID_PREFIX =
+      AccountExternalId.SCHEME_EXTERNAL + OAuthWebFilter.GITHUB_EXT_ID;
 
   private final Provider<IdentifiedUser> userProvider;
   private final GitHubOAuthConfig config;
@@ -80,9 +81,9 @@
 
   private AccessToken newAccessTokenFromUser(String username) {
     AccountState account = accountCache.getByUsername(username);
-    Collection<AccountExternalId> externalIds = account.getExternalIds();
-    for (AccountExternalId accountExternalId : externalIds) {
-      String key = accountExternalId.getKey().get();
+    Collection<ExternalId> externalIds = account.getExternalIds();
+    for (ExternalId accountExternalId : externalIds) {
+      String key = accountExternalId.asAccountExternalId().getKey().get();
       if (key.startsWith(EXTERNAL_ID_PREFIX)) {
         return new AccessToken(key.substring(EXTERNAL_ID_PREFIX.length()));
       }
diff --git a/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/OAuthFilter.java b/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/OAuthFilter.java
index b1426f8..1e816d2 100644
--- a/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/OAuthFilter.java
+++ b/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/OAuthFilter.java
@@ -50,7 +50,6 @@
       .newHashSet("scope.html");
 
   private final GitHubOAuthConfig config;
-  private final OAuthGitFilter gitFilter;
   private final OAuthWebFilter webFilter;
 
   @Inject
@@ -58,13 +57,10 @@
       OAuthWebFilter webFilter, Injector injector) {
     this.config = config;
     this.webFilter = webFilter;
-    Injector childInjector = injector.createChildInjector(OAuthCache.module());
-    this.gitFilter = childInjector.getInstance(OAuthGitFilter.class);
   }
 
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
-    gitFilter.init(filterConfig);
     webFilter.init(filterConfig);
   }
 
@@ -80,7 +76,7 @@
     } else {
 
       if (GIT_HTTP_REQUEST_PATTERN.matcher(requestUrl).matches()) {
-        gitFilter.doFilter(request, response, chain);
+        chain.doFilter(request, response);
       } else {
         System.out.println("Authorising " + requestUrl);
         webFilter.doFilter(request, response, chain);
@@ -127,7 +123,6 @@
   @Override
   public void destroy() {
     log.info("Destroy");
-    gitFilter.destroy();
     webFilter.destroy();
   }
 }
diff --git a/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/OAuthGitFilter.java b/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/OAuthGitFilter.java
deleted file mode 100644
index 02d6020..0000000
--- a/github-oauth/src/main/java/com/googlesource/gerrit/plugins/github/oauth/OAuthGitFilter.java
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright (C) 2013 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.googlesource.gerrit.plugins.github.oauth;
-
-import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
-import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterators;
-import com.google.gerrit.httpd.XGerritAuth;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.googlesource.gerrit.plugins.github.oauth.OAuthProtocol.AccessToken;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.lang.StringUtils;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.concurrent.ExecutionException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-
-@Singleton
-public class OAuthGitFilter implements Filter {
-  private static final String GITHUB_X_OAUTH_BASIC = "x-oauth-basic";
-  private static final org.slf4j.Logger log = LoggerFactory
-      .getLogger(OAuthGitFilter.class);
-  public static final String GIT_REALM_NAME =
-      "GitHub authentication for Gerrit Code Review";
-  private static final String GIT_AUTHORIZATION_HEADER = "Authorization";
-  private static final String GIT_AUTHENTICATION_BASIC = "Basic ";
-
-  private final OAuthCache oauthCache;
-  private final AccountCache accountCache;
-  private final Provider<HttpClient> httpClientProvider;
-  private final GitHubOAuthConfig config;
-  private final XGerritAuth xGerritAuth;
-  private ScopedProvider<GitHubLogin> ghLoginProvider;
-
-  public static class BasicAuthHttpRequest extends HttpServletRequestWrapper {
-    private HashMap<String, String> headers = new HashMap<>();
-
-    public BasicAuthHttpRequest(HttpServletRequest request, String username,
-        String password) {
-      super(request);
-
-      try {
-        headers.put(
-            GIT_AUTHORIZATION_HEADER,
-            GIT_AUTHENTICATION_BASIC
-                + Base64.encodeBase64String((username + ":" + password)
-                    .getBytes(OAuthGitFilter.encoding(request))));
-      } catch (UnsupportedEncodingException e) {
-        // This cannot really happen as we have already used the encoding for
-        // decoding the request
-      }
-    }
-
-    @Override
-    public Enumeration<String> getHeaderNames() {
-      final Enumeration<String> wrappedHeaderNames = super.getHeaderNames();
-      HashSet<String> headerNames = new HashSet<>(headers.keySet());
-      while (wrappedHeaderNames.hasMoreElements()) {
-        headerNames.add(wrappedHeaderNames.nextElement());
-      }
-      return Iterators.asEnumeration(headerNames.iterator());
-    }
-
-    @Override
-    public String getHeader(String name) {
-      String headerValue = headers.get(name);
-      if (headerValue != null) {
-        return headerValue;
-      }
-      return super.getHeader(name);
-    }
-  }
-
-  @Inject
-  public OAuthGitFilter(OAuthCache oauthCache, AccountCache accountCache,
-      PooledHttpClientProvider httpClientProvider, GitHubOAuthConfig config,
-      XGerritAuth xGerritAuth, GitHubLogin.Provider ghLoginProvider) {
-    this.oauthCache = oauthCache;
-    this.accountCache = accountCache;
-    this.httpClientProvider = httpClientProvider;
-    this.config = config;
-    this.xGerritAuth = xGerritAuth;
-    this.ghLoginProvider = ghLoginProvider;
-  }
-
-  @Override
-  public void init(FilterConfig filterConfig) throws ServletException {
-  }
-
-  @Override
-  public void doFilter(ServletRequest request, ServletResponse response,
-      FilterChain chain) throws IOException, ServletException {
-
-    HttpServletRequest httpRequest = (HttpServletRequest) request;
-    HttpServletResponse httpResponse =
-        new OAuthGitWrappedResponse((HttpServletResponse) response);
-    GitHubLogin ghLogin = ghLoginProvider.get(httpRequest);
-    log.debug("OAuthGitFilter(" + httpRequest.getRequestURL() + ") ghLogin="
-        + ghLogin);
-
-    String username =
-        getAuthenticatedUserFromGitRequestUsingOAuthToken(httpRequest,
-            httpResponse);
-    if (username != null) {
-      String gerritPassword =
-          accountCache.getByUsername(username).getPassword(username);
-
-      if (gerritPassword == null) {
-        gerritPassword =
-            generateRandomGerritPassword(username, httpRequest, httpResponse,
-                chain);
-        if (Strings.isNullOrEmpty(gerritPassword)) {
-          httpResponse.sendError(SC_FORBIDDEN,
-              "Unable to generate Gerrit password for Git Basic-Auth");
-        } else {
-          httpResponse.sendRedirect(getRequestPathWithQueryString(httpRequest));
-        }
-        return;
-      }
-
-      httpRequest =
-          new BasicAuthHttpRequest(httpRequest, username, gerritPassword);
-    }
-
-    chain.doFilter(httpRequest, httpResponse);
-  }
-
-  private String getRequestPathWithQueryString(HttpServletRequest httpRequest) {
-    String requestPathWithQueryString =
-        httpRequest.getContextPath() + httpRequest.getServletPath()
-            + Strings.nullToEmpty(httpRequest.getPathInfo()) + "?"
-            + httpRequest.getQueryString();
-    return requestPathWithQueryString;
-  }
-
-  private String generateRandomGerritPassword(String username,
-      HttpServletRequest httpRequest, HttpServletResponse httpResponse,
-      FilterChain chain) throws IOException, ServletException {
-    log.warn("User " + username + " has not a Gerrit HTTP password: "
-        + "generating a random one in order to be able to use Git over HTTP");
-    Cookie gerritCookie =
-        getGerritLoginCookie(username, httpRequest, httpResponse, chain);
-    String xGerritAuthValue = xGerritAuth.getAuthValue(gerritCookie);
-
-    HttpPut putRequest =
-        new HttpPut(getRequestUrlWithAlternatePath(httpRequest,
-            "/accounts/self/password.http"));
-    putRequest.setHeader("Cookie",
-        gerritCookie.getName() + "=" + gerritCookie.getValue());
-    putRequest.setHeader(XGerritAuth.X_GERRIT_AUTH, xGerritAuthValue);
-
-    putRequest.setEntity(new StringEntity("{\"generate\":true}",
-        ContentType.APPLICATION_JSON));
-    HttpResponse putResponse = httpClientProvider.get().execute(putRequest);
-    if (putResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
-      throw new ServletException(
-          "Cannot generate HTTP password for authenticating user " + username);
-    }
-
-    return accountCache.getByUsername(username).getPassword(username);
-  }
-
-  private URI getRequestUrlWithAlternatePath(HttpServletRequest httpRequest,
-      String alternatePath) throws MalformedURLException {
-    URL originalUrl = new URL(httpRequest.getRequestURL().toString());
-    String contextPath = httpRequest.getContextPath();
-    return URI.create(originalUrl.getProtocol() + "://" + originalUrl.getHost()
-        + ":" + getPort(originalUrl) + contextPath + alternatePath);
-  }
-
-  private int getPort(URL originalUrl) {
-    String protocol = originalUrl.getProtocol().toLowerCase();
-    int port = originalUrl.getPort();
-    if (port == -1) {
-      return protocol.equals("https") ? 443 : 80;
-    }
-    return port;
-  }
-
-  private Cookie getGerritLoginCookie(String username,
-      HttpServletRequest httpRequest, HttpServletResponse httpResponse,
-      FilterChain chain) throws IOException, ServletException {
-    AuthenticatedPathHttpRequest loginRequest =
-        new AuthenticatedLoginHttpRequest(httpRequest, config.httpHeader,
-            username);
-    AuthenticatedLoginHttpResponse loginResponse =
-        new AuthenticatedLoginHttpResponse(httpResponse);
-    chain.doFilter(loginRequest, loginResponse);
-    return loginResponse.getGerritCookie();
-  }
-
-  private String getAuthenticatedUserFromGitRequestUsingOAuthToken(
-      HttpServletRequest req, HttpServletResponse rsp) throws IOException {
-    final String httpBasicAuth = getHttpBasicAuthenticationHeader(req);
-    if (httpBasicAuth == null) {
-      return null;
-    }
-
-    if (isInvalidHttpAuthenticationHeader(httpBasicAuth)) {
-      rsp.sendError(SC_UNAUTHORIZED);
-      return null;
-    }
-
-    String oauthToken = StringUtils.substringBefore(httpBasicAuth, ":");
-    String oauthKeyword = StringUtils.substringAfter(httpBasicAuth, ":");
-    if (Strings.isNullOrEmpty(oauthToken)
-        || Strings.isNullOrEmpty(oauthKeyword)) {
-      rsp.sendError(SC_UNAUTHORIZED);
-      return null;
-    }
-
-    if (!oauthKeyword.equalsIgnoreCase(GITHUB_X_OAUTH_BASIC)) {
-      return null;
-    }
-
-    boolean loginSuccessful = false;
-    String oauthLogin = null;
-    try {
-      oauthLogin =
-          oauthCache.getLoginByAccessToken(new AccessToken(oauthToken));
-      loginSuccessful = !Strings.isNullOrEmpty(oauthLogin);
-    } catch (ExecutionException e) {
-      log.warn("Login failed for OAuth token " + oauthToken, e);
-      loginSuccessful = false;
-    }
-
-    if (!loginSuccessful) {
-      rsp.sendError(SC_FORBIDDEN);
-      return null;
-    }
-
-    return oauthLogin;
-  }
-
-
-  private boolean isInvalidHttpAuthenticationHeader(String usernamePassword) {
-    return usernamePassword.indexOf(':') < 1;
-  }
-
-  static String encoding(HttpServletRequest req) {
-    return MoreObjects.firstNonNull(req.getCharacterEncoding(), "UTF-8");
-  }
-
-  private String getHttpBasicAuthenticationHeader(final HttpServletRequest req)
-      throws UnsupportedEncodingException {
-    String hdr = req.getHeader(GIT_AUTHORIZATION_HEADER);
-    if (hdr == null || !hdr.startsWith(GIT_AUTHENTICATION_BASIC)) {
-      return null;
-    }
-    return new String(Base64.decodeBase64(hdr
-        .substring(GIT_AUTHENTICATION_BASIC.length())), encoding(req));
-  }
-
-  @Override
-  public void destroy() {
-    log.info("Destroy");
-  }
-}
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/filters/GitHubOAuthFilter.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/filters/GitHubOAuthFilter.java
index 03010b4..c143cb1 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/filters/GitHubOAuthFilter.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/filters/GitHubOAuthFilter.java
@@ -19,10 +19,12 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.ExternalId;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.github.oauth.GitHubLogin;
+import com.googlesource.gerrit.plugins.github.oauth.IdentifiedUserGitHubLoginProvider;
 import com.googlesource.gerrit.plugins.github.oauth.OAuthFilter;
 import com.googlesource.gerrit.plugins.github.oauth.OAuthProtocol.AccessToken;
 import com.googlesource.gerrit.plugins.github.oauth.OAuthWebFilter;
@@ -84,17 +86,19 @@
   }
 
   private AccountExternalId getGitHubExternalId(CurrentUser user) {
-    Collection<AccountExternalId> accountExtIds =
+    Collection<ExternalId> accountExtIds =
         accountCache.get(((IdentifiedUser) user).getAccountId())
             .getExternalIds();
-    Collection<AccountExternalId> gitHubExtId =
+    Collection<ExternalId> gitHubExtId =
         Collections2.filter(accountExtIds,
-            new Predicate<AccountExternalId>() {
+            new Predicate<ExternalId>() {
               @Override
-              public boolean apply(AccountExternalId externalId) {
-                return externalId.isScheme(AccountExternalId.SCHEME_EXTERNAL)
-                    && externalId.getSchemeRest().startsWith(
-                        OAuthWebFilter.GITHUB_EXT_ID);
+              public boolean apply(ExternalId externalId) {
+            return externalId
+                .key()
+                .get()
+                .startsWith(
+                    IdentifiedUserGitHubLoginProvider.EXTERNAL_ID_PREFIX);
               }
             });
 
@@ -102,7 +106,7 @@
       throw new IllegalStateException("Current Gerrit user "
           + user.getUserName() + " has no GitHub OAuth external ID");
     }
-    return gitHubExtId.iterator().next();
+    return gitHubExtId.iterator().next().asAccountExternalId();
   }
 
   @Override
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/AccountController.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/AccountController.java
index 882ded3..13085c4 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/AccountController.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/AccountController.java
@@ -36,6 +36,7 @@
 import com.googlesource.gerrit.plugins.github.oauth.GitHubLogin;
 
 import org.apache.commons.lang.StringUtils;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.kohsuke.github.GHKey;
 import org.kohsuke.github.GHMyself;
 import org.kohsuke.github.GHVerifiedKey;
@@ -83,13 +84,13 @@
       setAccoutPublicKeys(user, hubLogin, req);
 
       log.info("Created account '" + user.getUserName() + "'");
-    } catch (IOException e) {
+    } catch (IOException | ConfigInvalidException e) {
       log.error("Account '" + user.getUserName() + "' creation failed", e);
-      throw e;
+      throw new IOException(e);
     }
   }
 
-  private void setAccountIdentity(IdentifiedUser user, HttpServletRequest req) throws ServletException {
+  private void setAccountIdentity(IdentifiedUser user, HttpServletRequest req) throws ServletException, ConfigInvalidException {
     String fullName = req.getParameter("fullname");
     String email = req.getParameter("email");
     try {