Respect auth.userNameToLowerCase

The CloudFoundry UAA in general is preserving the case
of user names, if attached to another user store, e.g.
an LDAP system or database. Therefore, the plugin should
respect the auth.userNameToLowerCase configuration
parameter. If this parameter is set to true, the plugin
will return the username and externalId parameters in
OAuthUserInfo in lowercase, but it will not touch the
access tokens since the latter have a signature.
However, when comparing user names with the "user_name"
or "sub" attributes of access tokens the correct case
conversion will be applied.

Change-Id: I9198809ab3d52d9a05ad96082cf2667981042bd9
Signed-off-by: Michael Ochmann <michael.ochmann@sap.com>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/cfoauth/CFOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/cfoauth/CFOAuthService.java
index 2fde5d9..23968a2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/cfoauth/CFOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/cfoauth/CFOAuthService.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
+import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
@@ -41,6 +42,7 @@
 
   @Inject
   CFOAuthService(PluginConfigFactory cfgFactory,
+      AuthConfig authConfig,
       @PluginName String pluginName,
       @CanonicalWebUrl Provider<String> urlProvider) {
     PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName);
@@ -52,6 +54,7 @@
         cfg.getString(InitOAuthConfig.CLIENT_ID),
         cfg.getString(InitOAuthConfig.CLIENT_SECRET),
         cfg.getBoolean(InitOAuthConfig.VERIFIY_SIGNATURES, true),
+        authConfig.isUserNameToLowerCase(),
         redirectUrl);
   }
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/cfoauth/UAAClient.java b/src/main/java/com/googlesource/gerrit/plugins/cfoauth/UAAClient.java
index c1a5f1f..b800265 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/cfoauth/UAAClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/cfoauth/UAAClient.java
@@ -34,6 +34,7 @@
 import org.scribe.model.Response;
 
 import java.text.MessageFormat;
+import java.util.Locale;
 
 class UAAClient {
 
@@ -79,6 +80,7 @@
   private final String userInfoEndpoint;
 
   private final boolean verifySignatures;
+  private final boolean userNameToLowerCase;
 
   /**
    * Lazily initialized and may be updated from time to time
@@ -90,10 +92,12 @@
       String clientId,
       String clientSecret,
       boolean verifySignatures,
+      boolean userNameToLowerCase,
       String redirectUrl) {
     this.clientCredentials = BASIC_AUTHENTICATION + " "
       + encodeBase64(clientId + ":" + clientSecret);
     this.verifySignatures = verifySignatures;
+    this.userNameToLowerCase = userNameToLowerCase;
     this.redirectUrl = redirectUrl;
     this.authorizationEndpoint = String.format(AUTHORIZE_ENDPOINT,
         uaaServerUrl, encode(clientId), encode(redirectUrl));
@@ -215,7 +219,8 @@
   public boolean isAccessTokenForUser(String username, String accessToken) {
     try {
       JsonObject jsonWebToken = toJsonWebToken(accessToken);
-      return username.equals(getAttribute(jsonWebToken, USER_NAME_ATTRIBUTE));
+      return equalsAdjustCase(username,
+          getAttribute(jsonWebToken, USER_NAME_ATTRIBUTE));
     } catch (UAAClientException e) {
       return false;
     }
@@ -234,7 +239,8 @@
     try {
       JsonObject jsonWebToken = toJsonWebToken(accessToken);
       return getAttribute(jsonWebToken, USER_NAME_ATTRIBUTE) == null &&
-          clientname.equals(getAttribute(jsonWebToken, SUB_ATTRIBUTE));
+          equalsAdjustCase(clientname,
+              getAttribute(jsonWebToken, SUB_ATTRIBUTE));
     } catch (UAAClientException e) {
       return false;
     }
@@ -260,6 +266,9 @@
       throw new UAAClientException(
           "Invalid token: missing or invalid 'user_name' attribute");
     }
+    if (userNameToLowerCase) {
+      username = lowercase(username);
+    }
     String emailAddress = getAttribute(jsonWebToken, EMAIL_ATTRIBUTE);
     if (emailAddress == null) {
       throw new UAAClientException(
@@ -392,6 +401,16 @@
     return accessToken;
   }
 
+  private boolean equalsAdjustCase(String left, String right) {
+    return userNameToLowerCase
+        ? lowercase(left).equals(lowercase(right))
+        : left.equals(right);
+  }
+
+  private static String lowercase(String s) {
+    return s.toLowerCase(Locale.US);
+  }
+
   private String decodeBase64(String s) {
     return new String(Base64.decodeBase64(s), UTF_8);
   }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/cfoauth/UAAClientTest.java b/src/test/java/com/googlesource/gerrit/plugins/cfoauth/UAAClientTest.java
index 244286c..ab22c77 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/cfoauth/UAAClientTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/cfoauth/UAAClientTest.java
@@ -78,7 +78,8 @@
   private static class UAATestClient extends UAAClient {
 
     public UAATestClient() {
-      super(UAA_SERVER_URL, CLIENT_ID, CLIENT_SECRET, true, REDIRECT_URL);
+      super(UAA_SERVER_URL, CLIENT_ID, CLIENT_SECRET, true, false,
+          REDIRECT_URL);
     }
 
     @Override