Retrieve display name from UAA userinfo endpoint

The JSON Web Tokens returned by UAA contain only
user name and email address, but not the user's display name.
For that an additional request to the UAA /userinfo endpoint
is necessary. Note that the UAA client configured for
Gerrit must have the scope "openid" for this to work
as described in the configuration documentation.

Change-Id: I5d1169da88cb32c8eb4133e9710df7a200d7d74a
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 dbc9e20..fd4b536 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/cfoauth/CFOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/cfoauth/CFOAuthService.java
@@ -65,7 +65,9 @@
 
   @Override
   public OAuthUserInfo getUserInfo(OAuthToken token) throws IOException {
-    return getAsOAuthUserInfo(uaaClient.toAccessToken(token.getToken()));
+    AccessToken accessToken = uaaClient.toAccessToken(token.getToken());
+    String displayName = uaaClient.getDisplayName(token.getToken());
+    return getAsOAuthUserInfo(accessToken, displayName);
   }
 
   @Override
@@ -82,9 +84,10 @@
     return new OAuthToken(accessToken.getValue(), null, null);
   }
 
-  private OAuthUserInfo getAsOAuthUserInfo(AccessToken accessToken) {
+  private OAuthUserInfo getAsOAuthUserInfo(AccessToken accessToken,
+      String displayName) {
     return new OAuthUserInfo(accessToken.getExternalId(),
         accessToken.getUserName(), accessToken.getEmailAddress(),
-        null, null);
+        displayName, null);
   }
 }
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 cb9153b..611f317 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/cfoauth/UAAClient.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/cfoauth/UAAClient.java
@@ -44,6 +44,7 @@
       + "authorize?response_type=code&client_id=%s&redirect_uri=%s";
   private static final String TOKEN_ENDPOINT = OAUTH_ENDPOINT + "token";
   private static final String TOKEN_KEY_ENDPOINT = "%s/token_key";
+  private static final String USERINFO_ENDPOINT = "%s/userinfo";
 
   private static final String GRANT_TYPE = "grant_type";
   private static final String BY_AUTHORIZATION_CODE = "authorization_code";
@@ -56,9 +57,11 @@
   private static final String EXP_ATTRIBUTE = "exp";
   private static final String USER_NAME_ATTRIBUTE = "user_name";
   private static final String EMAIL_ATTRIBUTE = "email";
+  private static final String NAME_ATTRIBUTE = "name";
 
   private static final String AUTHORIZATION_HEADER = "Authorization";
   private static final String BASIC_AUTHENTICATION = "Basic";
+  private static final String BEARER_AUTHENTICATION = "Bearer";
 
   private final String clientCredentials;
   private final String redirectUrl;
@@ -66,6 +69,7 @@
   private final String authorizationEndpoint;
   private final String accessTokenEndpoint;
   private final String tokenKeyEndpoint;
+  private final String userInfoEndpoint;
 
   private final boolean verifySignatures;
 
@@ -88,6 +92,7 @@
         uaaServerUrl, encode(clientId), encode(redirectUrl));
     this.accessTokenEndpoint = String.format(TOKEN_ENDPOINT, uaaServerUrl);
     this.tokenKeyEndpoint = String.format(TOKEN_KEY_ENDPOINT, uaaServerUrl);
+    this.userInfoEndpoint = String.format(USERINFO_ENDPOINT, uaaServerUrl);
   }
 
   /**
@@ -150,6 +155,29 @@
     return new AccessToken(accessToken, username, emailAddress, expiresAt);
   }
 
+  /**
+   * Retrieves the display name of the access token owner.
+   * This method queries the <tt>/userinfo</tt> endpoint of the
+   * UAA server and requires the scope <tt>openid</tt>.
+   *
+   * @param accessToken the access token.
+   * @return the display name of the access token owner.
+   *
+   * @throws UAAClientException if the UAA request failed.
+   */
+  public String getDisplayName(String accessToken) {
+    OAuthRequest request = new OAuthRequest(GET, userInfoEndpoint);
+    request.addHeader(AUTHORIZATION_HEADER,
+        BEARER_AUTHENTICATION + " " + accessToken);
+    Response response = request.send();
+    if (response.getCode() != HTTP_OK) {
+      throw new UAAClientException(MessageFormat.format(
+          "GET /userinfo failed with status {0}", response.getCode()));
+    }
+    JsonObject userInfoResponse = getAsJsonObject(response.getBody());
+    return getAttribute(userInfoResponse, NAME_ATTRIBUTE);
+  }
+
   @VisibleForTesting
   AccessToken parseAccessTokenResponse(String tokenResponse)
       throws UAAClientException {