Merge branch 'stable-2.16' into stable-3.0

* stable-2.16:
  Create HTTP session only for login requests

Change-Id: I432b5917224f4e56c6167384ba069d7976718aee
diff --git a/README.md b/README.md
index bb9c949..0425eaf 100644
--- a/README.md
+++ b/README.md
@@ -179,7 +179,14 @@
 
 Default is `UserName`
 
-**saml.serviceProviderEntityId**: Saml service provider entity id
+**saml.serviceProviderEntityId**: SAML service provider entity id.
+
+Default is not set.
+
+**saml.identityProviderEntityId**: SAML identity provider entity id.  When present
+a `IDPSSODescriptor` is expected in the SAML metadata document.  When absent a
+saml service provider with its `SPSSODescriptor` is assumed.
+This value takes precedence over the value in **saml.serviceProviderEntityId**.
 
 Default is not set.
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/saml/SamlConfig.java b/src/main/java/com/googlesource/gerrit/plugins/saml/SamlConfig.java
index d5aae19..98f09ea 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/saml/SamlConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/saml/SamlConfig.java
@@ -23,7 +23,7 @@
 @Singleton
 public class SamlConfig {
   private static final String SAML_SECTION = "saml";
-
+  private final String identityProviderEntityId;
   private final String serviceProviderEntityId;
   private final String metadataPath;
   private final String keystorePath;
@@ -42,6 +42,7 @@
   @Inject
   SamlConfig(@GerritServerConfig Config cfg) {
     serviceProviderEntityId = getString(cfg, "serviceProviderEntityId");
+    identityProviderEntityId = getString(cfg, "identityProviderEntityId");
     metadataPath = getString(cfg, "metadataPath");
     keystorePath = getString(cfg, "keystorePath");
     privateKeyPassword = getString(cfg, "privateKeyPassword");
@@ -119,4 +120,8 @@
   public boolean useNameQualifier() {
     return useNameQualifier;
   }
+
+  public String getIdentityProviderEntityId() {
+    return identityProviderEntityId;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java b/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java
index 7307910..2c6549f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/saml/SamlWebFilter.java
@@ -25,6 +25,8 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
@@ -81,14 +83,23 @@
         new SAML2Configuration(
             samlConfig.getKeystorePath(), samlConfig.getKeystorePassword(),
             samlConfig.getPrivateKeyPassword(), samlConfig.getMetadataPath());
-    samlClientConfig.setMaximumAuthenticationLifetime(samlConfig.getMaxAuthLifetimeAttr());
-    samlClientConfig.setServiceProviderMetadataPath(
-        ensureExists(sitePaths.data_dir).resolve("sp-metadata.xml").toString());
-    if (!Strings.isNullOrEmpty(samlConfig.getServiceProviderEntityId())) {
-      samlClientConfig.setServiceProviderEntityId(samlConfig.getServiceProviderEntityId());
+
+    if (!Strings.isNullOrEmpty(samlConfig.getIdentityProviderEntityId())) {
+      if (!Strings.isNullOrEmpty(samlConfig.getServiceProviderEntityId())) {
+        log.warn(
+            "Both identityProviderEntityId as serviceProviderEntityId are set, ignoring serviceProviderEntityId.");
+      }
+      samlClientConfig.setIdentityProviderEntityId(samlConfig.getIdentityProviderEntityId());
+    } else {
+      samlClientConfig.setServiceProviderMetadataPath(
+          ensureExists(sitePaths.data_dir).resolve("sp-metadata.xml").toString());
+      if (!Strings.isNullOrEmpty(samlConfig.getServiceProviderEntityId())) {
+        samlClientConfig.setServiceProviderEntityId(samlConfig.getServiceProviderEntityId());
+      }
     }
 
     samlClientConfig.setUseNameQualifier(samlConfig.useNameQualifier());
+    samlClientConfig.setMaximumAuthenticationLifetime(samlConfig.getMaxAuthLifetimeAttr());
 
     saml2Client = new SAML2Client(samlClientConfig);
     String callbackUrl = gerritConfig.getString("gerrit", null, "canonicalWebUrl") + SAML_CALLBACK;
@@ -223,13 +234,28 @@
   }
 
   private static String getAttribute(SAML2Profile user, String attrName) {
-    List<?> names = (List<?>) user.getAttribute(attrName);
+    // TODO(davido): Replace with the invocation from upstream method.
+    List<String> names = extractAttributeValues(user, attrName);
     if (names != null && !names.isEmpty()) {
-      return (String) names.get(0);
+      return names.get(0);
     }
     return null;
   }
 
+  // TODO(davido): Remove if getAttribute() uses the upstream method.
+  private static List<String> extractAttributeValues(SAML2Profile user, String attrName) {
+    final Object value = user.getAttribute(attrName);
+    if (value instanceof String) {
+      return Collections.singletonList((String) value);
+    } else if (value instanceof String[]) {
+      return Arrays.asList((String[]) value);
+    } else if (value instanceof List) {
+      return (List<String>) value;
+    } else {
+      return null;
+    }
+  }
+
   private static String getAttributeOrElseId(SAML2Profile user, String attrName) {
     String value = getAttribute(user, attrName);
     if (value != null) {