Add support for computed display name user property

If identity provider doesn't support multivalue user property in SAML
mapping, allow to compute DisplayName by adding first name and last
name user properties. To achieve that, the following config must be
set:

[saml]
    computedDisplayName = true
    firstNameAttr = firstName
    lastNameAttr = lastName

When identity provider includes in the mapping for the first and last
names:

  firstName=John
  lastName=Doe

then the DisplayName is computed as "John Doe".

Change-Id: I9fe0a4ce1678623dbeae69806fe158d0a29781fd
diff --git a/README.md b/README.md
index be8b051..9266e27 100644
--- a/README.md
+++ b/README.md
@@ -120,6 +120,25 @@
 
 Default is `DisplayName`
 
+**saml.computedDisplayName**: Set to compute display name attribute from first
+and last names.
+
+Default is false.
+
+**saml.firstNameAttr**: Gerrit will look for an attribute with this name in
+the assertion to find the first name of the user. Only used, when `computedDisplayName`
+is set to true. If the attribute is not found, the NameId from the SAML assertion
+is used instead.
+
+Default is `FirstName`
+
+**saml.lastNameAttr**: Gerrit will look for an attribute with this name in
+the assertion to find the last name of the user. Only used, when `computedDisplayName`
+is set to true. If the attribute is not found, the NameId from the SAML assertion
+is used instead.
+
+Default is `LastName`
+
 **saml.emailAddressAttr**: Gerrit will look for an attribute with this name in
 the assertion to find a the email address of the user. If the attribute is not
 found, the NameId from the SAML assertion is used instead.
diff --git a/src/main/java/com/thesamet/gerrit/plugins/saml/SamlConfig.java b/src/main/java/com/thesamet/gerrit/plugins/saml/SamlConfig.java
index 80f7d9e..ef01d02 100644
--- a/src/main/java/com/thesamet/gerrit/plugins/saml/SamlConfig.java
+++ b/src/main/java/com/thesamet/gerrit/plugins/saml/SamlConfig.java
@@ -22,6 +22,7 @@
 /** SAML 2.0 related settings from {@code gerrit.config}. */
 @Singleton
 public class SamlConfig {
+  private static final String SAML_SECTION = "saml";
 
   private final String metadataPath;
   private final String keystorePath;
@@ -31,6 +32,9 @@
   private final String userNameAttr;
   private final String emailAddressAttr;
   private final int maxAuthLifetimeAttr;
+  private final boolean computedDisplayName;
+  private final String firstNameAttr;
+  private final String lastNameAttr;
   private final int maxAuthLifetimeDefault = 24 * 60 * 60; // 24h;
 
   @Inject
@@ -43,6 +47,9 @@
     userNameAttr = getGetStringWithDefault(cfg, "userNameAttr", "UserName");
     emailAddressAttr = getGetStringWithDefault(cfg, "emailAddressAttr", "EmailAddress");
     maxAuthLifetimeAttr = cfg.getInt("saml", "maxAuthLifetime", maxAuthLifetimeDefault);
+    computedDisplayName = cfg.getBoolean(SAML_SECTION, "computedDisplayName", false);
+    firstNameAttr = getGetStringWithDefault(cfg, "firstNameAttr", "FirstName");
+    lastNameAttr = getGetStringWithDefault(cfg, "lastNameAttr", "LastName");
   }
 
   public String getMetadataPath() {
@@ -78,7 +85,7 @@
   }
 
   private static String getString(Config cfg, String name) {
-    return cfg.getString("saml", null, name);
+    return cfg.getString(SAML_SECTION, null, name);
   }
 
   private static String getGetStringWithDefault(Config cfg, String name, String defaultValue) {
@@ -88,4 +95,16 @@
     }
     return defaultValue;
   }
+
+  public String getFirstNameAttr() {
+    return firstNameAttr;
+  }
+
+  public String getLastNameAttr() {
+    return lastNameAttr;
+  }
+
+  public boolean isComputedDisplayName() {
+    return computedDisplayName;
+  }
 }
diff --git a/src/main/java/com/thesamet/gerrit/plugins/saml/SamlWebFilter.java b/src/main/java/com/thesamet/gerrit/plugins/saml/SamlWebFilter.java
index 30ab256..b00a190 100644
--- a/src/main/java/com/thesamet/gerrit/plugins/saml/SamlWebFilter.java
+++ b/src/main/java/com/thesamet/gerrit/plugins/saml/SamlWebFilter.java
@@ -223,6 +223,12 @@
   }
 
   private String getDisplayName(SAML2Profile user) {
+    if (samlConfig.isComputedDisplayName()) {
+      return String.format(
+          "%s %s",
+          getAttributeOrElseId(user, samlConfig.getFirstNameAttr()),
+          getAttributeOrElseId(user, samlConfig.getLastNameAttr()));
+    }
     return getAttributeOrElseId(user, samlConfig.getDisplayNameAttr());
   }