Allow ignoring SSL cert when connecting to gerrit

Introduce a new boolean flag '--ignore-ssl-cert' (false by default),
to ignore validating SSL certificate. This is useful when connecting
to gerrit servers installed with self-signed certificate.

Feature: Issue 10097
Change-Id: I3467800c46123d42e8f7e37fb51a0eebf6e357d1
diff --git a/README.md b/README.md
index db7bd8a..eee18aa 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@
     If not specified events will be ignored
 - --writeNotProcessedEventsTo location where to write a TSV file containing the events we couldn't process
     with a description fo the reason why
+- -k --ignore-ssl-cert allows to proceed even for server connections otherwise considered insecure.
 
 
   CSVs with 3 columns are expected in input.
diff --git a/common/src/main/scala/com/gerritforge/analytics/common/api/TrustAll.scala b/common/src/main/scala/com/gerritforge/analytics/common/api/TrustAll.scala
new file mode 100644
index 0000000..eb9394b
--- /dev/null
+++ b/common/src/main/scala/com/gerritforge/analytics/common/api/TrustAll.scala
@@ -0,0 +1,15 @@
+package com.gerritforge.analytics.common.api
+
+import java.security.cert.X509Certificate
+
+import javax.net.ssl._
+
+object TrustAll extends X509TrustManager {
+  override val getAcceptedIssuers: Array[X509Certificate] = Array.empty[X509Certificate]
+  override def checkClientTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = ()
+  override def checkServerTrusted(x509Certificates: Array[X509Certificate], s: String): Unit = ()
+}
+
+object VerifiesAllHostNames extends HostnameVerifier {
+  override def verify(s: String, sslSession: SSLSession) = true
+}
\ No newline at end of file
diff --git a/common/src/main/scala/com/gerritforge/analytics/common/api/gerritApiConnectivity.scala b/common/src/main/scala/com/gerritforge/analytics/common/api/gerritApiConnectivity.scala
index b3214e2..91fa6aa 100644
--- a/common/src/main/scala/com/gerritforge/analytics/common/api/gerritApiConnectivity.scala
+++ b/common/src/main/scala/com/gerritforge/analytics/common/api/gerritApiConnectivity.scala
@@ -17,6 +17,7 @@
 import java.net.URL
 
 import com.typesafe.scalalogging.LazyLogging
+import javax.net.ssl.{HttpsURLConnection, SSLContext}
 import org.apache.commons.codec.binary.Base64
 
 import scala.io.{BufferedSource, Codec, Source}
@@ -34,9 +35,11 @@
     BASIC + " " + encodeCredentials(username, password)
 }
 
-class GerritConnectivity(maybeUsername: Option[String], maybePassword: Option[String]) extends HttpBasicAuthentication with Serializable with LazyLogging {
+class GerritConnectivity(maybeUsername: Option[String], maybePassword: Option[String], ignoreSSLCert: Boolean = false) extends HttpBasicAuthentication with Serializable with LazyLogging {
   private def createBasicSecuredConnection(url: String, username: String, password: String): BufferedSource = {
     try {
+      if(ignoreSSLCert) trustAllSSLCerts()
+
       val unsecureURL = new URL(url)
       val endPointPath = unsecureURL.getFile
       val basicAuthURL = unsecureURL.toString.replace(endPointPath, s"/a$endPointPath")
@@ -53,6 +56,8 @@
   }
 
   private def createNonSecuredConnection(url: String): BufferedSource = {
+    if(ignoreSSLCert) trustAllSSLCerts()
+
     logger.info(s"Connecting to API $url")
     Source.fromURL(url, Codec.UTF8.name)
   }
@@ -65,4 +70,12 @@
       } yield (createBasicSecuredConnection(url, username, password))
       ).getOrElse(createNonSecuredConnection(url))
   }
+
+  private def trustAllSSLCerts(): Unit = {
+    logger.warn("Trusting all SSL certificates")
+    val sslContext = SSLContext.getInstance("SSL")
+    sslContext.init(null, Array(TrustAll), new java.security.SecureRandom())
+    HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory)
+    HttpsURLConnection.setDefaultHostnameVerifier(VerifiesAllHostNames)
+  }
 }
diff --git a/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/job/Main.scala b/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/job/Main.scala
index f99a4d1..de9ae01 100644
--- a/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/job/Main.scala
+++ b/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/job/Main.scala
@@ -105,6 +105,10 @@
         c.copy(password = Some(input))
       } text "Gerrit API Password"
 
+      opt[Boolean]('k', "ignore-ssl-cert") optional () action { (input, c) =>
+        c.copy(ignoreSSLCert = Some(input))
+      } text "Ignore SSL certificate validation"
+
       opt[Boolean]('r', "extract-branches") optional () action { (input, c) =>
         c.copy(extractBranches = Some(input))
       } text "enables branches extraction for each commit"
diff --git a/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/model/GerritEndpointConfig.scala b/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/model/GerritEndpointConfig.scala
index e12ad91..b949fa9 100644
--- a/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/model/GerritEndpointConfig.scala
+++ b/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/model/GerritEndpointConfig.scala
@@ -34,9 +34,10 @@
     eventsFailureOutputPath: Option[String] = None,
     username: Option[String] = None,
     password: Option[String] = None,
+    ignoreSSLCert: Option[Boolean] = None,
     extractBranches: Option[Boolean] = None) {
 
-  val gerritApiConnection: GerritConnectivity = new GerritConnectivity(username, password)
+  val gerritApiConnection: GerritConnectivity = new GerritConnectivity(username, password, ignoreSSLCert.getOrElse(false))
 
   val gerritProjectsUrl: Option[String] = baseUrl.map { url =>
     s"${url}/projects/" + prefix.fold("")("?p=" + _)
diff --git a/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/plugin/ProcessGitCommitsCommand.scala b/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/plugin/ProcessGitCommitsCommand.scala
index 41cce5e..bf42ee2 100644
--- a/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/plugin/ProcessGitCommitsCommand.scala
+++ b/gitcommits/src/main/scala/com/gerritforge/analytics/gitcommits/plugin/ProcessGitCommitsCommand.scala
@@ -49,6 +49,11 @@
              usage = "\"emails to author alias\" input data path")
   var emailAlias: String = null
 
+  @ArgOption(name = "--ignore-ssl-cert",
+    aliases = Array("-k"),
+    usage = "Ignore SSL certificate validation")
+  var ignoreSSLCert: Boolean = false
+
   @ArgOption(name = "--extract-branches",
              aliases = Array("-r"),
              usage = "enables branches extraction for each commit")
@@ -63,7 +68,9 @@
                                                beginDate,
                                                endDate,
                                                aggregate,
-                                               emailAlias)
+                                               emailAlias,
+                                               ignoreSSLCert=Some(ignoreSSLCert)
+    )
 
     implicit val spark: SparkSession = SparkSession
       .builder()