HTTP Basic-Auth support for direct access to GitBlit plugin.

Allow to access directly to GitBlit without going through
the standard Gerrit login process interactively: username
and password are retrieved using HTTP Basic-Auth from the
Http Request.

This is useful when using GitBlit RSS feeds that cannot
authenticate using the manual Gerrit form-based
authentication.

Cherry-picked from 6984f3e035a663414056c21823d18007c07465b8
and adapted for Gerrit 2.5

Change-Id: Iabf3a690f67c96207390070a7d380539a03c8459
Signed-off-by: Luca Milanesio <luca.milanesio@gmail.com>
diff --git a/pom.xml b/pom.xml
index 5926b67..51b2af5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,11 @@
       <scope>provided</scope>
       <version>3.1</version>
     </dependency>
+      <dependency>
+        <groupId>commons-codec</groupId>
+        <artifactId>commons-codec</artifactId>
+        <version>1.4</version>
+      </dependency>
     <dependency>
       <groupId>org.codehaus.groovy</groupId>
       <artifactId>groovy-all</artifactId>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlit.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlit.java
index 6e1fe55..8da4164 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlit.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/app/GerritGitBlit.java
@@ -23,6 +23,7 @@
 import com.gitblit.models.UserModel;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
+import com.googlesource.gerrit.plugins.gitblit.auth.GerritToGitBlitUserModel;
 import com.googlesource.gerrit.plugins.gitblit.auth.GerritToGitBlitUserService;
 
 @Singleton
@@ -36,12 +37,17 @@
   public UserModel authenticate(HttpServletRequest request) {
     String user = (String) request.getAttribute("gerrit-username");
     String token = (String) request.getAttribute("gerrit-token");
-    if (token == null) {
-      return null;
+    String password = (String) request.getAttribute("gerrit-password");
+    if (token != null) {
+      return GitBlit.self().authenticate(user,
+          (GerritToGitBlitUserService.SESSIONAUTH + token).toCharArray());
+    } else if(user != null && password != null){
+      return GitBlit.self().authenticate(user, password.toCharArray());
+    } else {
+      return GitBlit.self().authenticate(
+          GerritToGitBlitUserModel.ANONYMOUS_USER,
+          GerritToGitBlitUserModel.ANONYMOUS_PASSWORD);
     }
-
-    return GitBlit.self().authenticate(user,
-        (GerritToGitBlitUserService.SESSIONAUTH + token).toCharArray());
   }
 
   @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritAuthFilter.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritAuthFilter.java
index 81ff438..e2872d7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritAuthFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritAuthFilter.java
@@ -13,8 +13,10 @@
 // limitations under the License.
 package com.googlesource.gerrit.plugins.gitblit.auth;
 
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+
 import java.io.IOException;
-import java.net.HttpURLConnection;
+import java.io.UnsupportedEncodingException;
 
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
@@ -23,12 +25,18 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.codec.binary.Base64;
+
 import com.gitblit.GitBlit;
 import com.gitblit.models.UserModel;
+import com.google.common.base.Objects;
 import com.google.gerrit.httpd.WebSession;
 import com.google.inject.Provider;
+import com.google.inject.Singleton;
 
+@Singleton
 public class GerritAuthFilter {
+  private static final String LIT_BASIC = "Basic ";
 
   /**
    * Returns the user making the request, if the user has authenticated.
@@ -58,20 +66,52 @@
       ServletRequest request, ServletResponse response, FilterChain chain)
       throws IOException, ServletException {
     HttpServletRequest httpRequest = (HttpServletRequest) request;
-    HttpServletResponse httpResponse = (HttpServletResponse) response;
 
-    if (webSession.get().isSignedIn()
-        || httpRequest.getHeader("Authorization") != null) {
-      request.setAttribute("gerrit-username", webSession.get().getCurrentUser()
-          .getUserName());
-      request.setAttribute("gerrit-token", webSession.get().getToken());
-      return true;
+    String hdr = httpRequest.getHeader("Authorization");
+    if (hdr != null) {
+      return filterBasicAuth((HttpServletRequest) request,
+          (HttpServletResponse) response, hdr);
+    } else if (webSession.get().isSignedIn()) {
+      return filterSessionAuth(webSession, (HttpServletRequest) request);
     } else {
-      httpResponse.setStatus(HttpURLConnection.HTTP_UNAUTHORIZED);
-      httpResponse.setHeader("WWW-Authenticate",
-          "Basic realm=\"Gerrit Code Review\"");
+      return true;
+    }
+  }
+
+  public boolean filterSessionAuth(final Provider<WebSession> webSession,
+      HttpServletRequest request) {
+    request.setAttribute("gerrit-username", webSession.get().getCurrentUser()
+        .getUserName());
+    request.setAttribute("gerrit-token", webSession.get().getToken());
+    return true;
+  }
+
+  public boolean filterBasicAuth(HttpServletRequest request,
+      HttpServletResponse response, String hdr) throws IOException,
+      UnsupportedEncodingException {
+    if (!hdr.startsWith(LIT_BASIC)) {
+      response.setHeader("WWW-Authenticate", "Basic realm=\"Gerrit Code Review\"");
+      response.sendError(SC_UNAUTHORIZED);
       return false;
     }
+
+    final byte[] decoded =
+        new Base64().decode(hdr.substring(LIT_BASIC.length()).getBytes());
+    String usernamePassword =
+        new String(decoded, Objects.firstNonNull(
+            request.getCharacterEncoding(), "UTF-8"));
+    int splitPos = usernamePassword.indexOf(':');
+    if (splitPos < 1) {
+      response.setHeader("WWW-Authenticate", "Basic realm=\"Gerrit Code Review\"");
+      response.sendError(SC_UNAUTHORIZED);
+      return false;
+    }
+    request.setAttribute("gerrit-username",
+        usernamePassword.substring(0, splitPos));
+    request.setAttribute("gerrit-password",
+        usernamePassword.substring(splitPos + 1));
+
+    return true;
   }
 
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserModel.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserModel.java
index fa5e377..30e3baa 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserModel.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserModel.java
@@ -25,9 +25,12 @@
 import com.google.gerrit.reviewdb.client.Project.NameKey;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectControl.Factory;
 import com.google.gerrit.server.project.RefControl;
 
 public class GerritToGitBlitUserModel extends UserModel {
+  public static final String ANONYMOUS_USER = "$anonymous";
+  public static final char[] ANONYMOUS_PASSWORD = ANONYMOUS_USER.toCharArray();
 
   private static final long serialVersionUID = 1L;
 
@@ -59,6 +62,11 @@
     this.projectControlFactory = projectControlFactory;
   }
 
+  public GerritToGitBlitUserModel(final ProjectControl.Factory projectControlFactory) {
+    super(ANONYMOUS_USER);
+    this.projectControlFactory = projectControlFactory;
+  }
+
   @Deprecated
   public boolean canAccessRepository(String repositoryName) {
     boolean result = false;
@@ -212,4 +220,8 @@
   public int compareTo(UserModel o) {
     return username.compareTo(o.username);
   }
+
+  public static UserModel getAnonymous(Factory projectControl) {
+    return new GerritToGitBlitUserModel(ANONYMOUS_USER, projectControl);
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserService.java b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserService.java
index 2a3e774..8b8183e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/gitblit/auth/GerritToGitBlitUserService.java
@@ -59,7 +59,10 @@
   public UserModel authenticate(String username, char[] password) {
     String passwordString = new String(password);
 
-    if (passwordString.startsWith(GerritToGitBlitUserService.SESSIONAUTH)) {
+    if (username.equals(GerritToGitBlitUserModel.ANONYMOUS_USER)) {
+      return GerritToGitBlitUserModel.getAnonymous(projectControl);
+    } else if (passwordString
+        .startsWith(GerritToGitBlitUserService.SESSIONAUTH)) {
       return authenticateSSO(username,
           passwordString.substring(GerritToGitBlitUserService.SESSIONAUTH
               .length()));