content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.calcite.avatica;
  
  import org.apache.calcite.avatica.jdbc.JdbcMeta;
  import org.apache.calcite.avatica.remote.LocalService;
  import org.apache.calcite.avatica.server.HttpServer;
  import org.apache.calcite.avatica.util.DateTimeUtils;
  
  import org.apache.kerby.kerberos.kerb.KrbException;
  
  import org.bouncycastle.asn1.x500.X500Name;
  import org.bouncycastle.asn1.x500.style.IETFUtils;
  import org.bouncycastle.asn1.x500.style.RFC4519Style;
  import org.bouncycastle.asn1.x509.BasicConstraints;
  import org.bouncycastle.asn1.x509.Extension;
  import org.bouncycastle.asn1.x509.KeyUsage;
  import org.bouncycastle.cert.X509CertificateHolder;
  import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
  import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
  import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
  import org.bouncycastle.jce.provider.BouncyCastleProvider;
  import org.bouncycastle.operator.OperatorCreationException;
  import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
  import org.junit.AfterClass;
  import org.junit.Before;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.math.BigInteger;
  import java.security.KeyPair;
  import java.security.KeyPairGenerator;
  import java.security.KeyStore;
  import java.security.NoSuchAlgorithmException;
  import java.security.PrivateKey;
  import java.security.PublicKey;
  import java.security.Security;
  import java.security.cert.Certificate;
  import java.security.cert.CertificateException;
  import java.security.cert.X509Certificate;
  import java.sql.SQLException;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.List;
  
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assume.assumeNotNull;
  
  /**
   * Common Base for HTTP End2End Tests
   */
  public abstract class HttpBaseTest {
    protected static final Logger LOG = LoggerFactory.getLogger(HttpBaseTest.class);
  
    protected static final String KEYSTORE_PASSWORD = "avaticasecret";
    protected static final String KEYSTORE_EMPTY_PASSWORD = "";
    protected static final ConnectionSpec CONNECTION_SPEC = ConnectionSpec.HSQLDB;
    protected static final List<HttpServer> SERVERS_TO_STOP = new ArrayList<>();
  
    protected static final String TARGET_DIR_NAME = System.getProperty("target.dir", "target");
    protected static final File TARGET_DIR =
            new File(System.getProperty("user.dir"), TARGET_DIR_NAME);
    protected static final File KEYSTORE = new File(TARGET_DIR, "avatica-test.jks");
    protected static final File EMPTY_PW_KEYSTORE = new File(TARGET_DIR, "avatica-test-emptypw.jks");
  
    protected static LocalService localService;
  
    protected final String jdbcUrl;
  
    public static void setupClass() throws SQLException {
      // Create a self-signed cert
      if (KEYSTORE.isFile()) {
        assertTrue("Failed to delete keystore: " + KEYSTORE, KEYSTORE.delete());
      }
      new CertTool().createSelfSignedCert(KEYSTORE, "avatica", KEYSTORE_PASSWORD);
  
      if (EMPTY_PW_KEYSTORE.isFile()) {
        assertTrue("Failed to delete keystore: " + EMPTY_PW_KEYSTORE, EMPTY_PW_KEYSTORE.delete());
      }
      new CertTool().createSelfSignedCert(EMPTY_PW_KEYSTORE, "avatica", KEYSTORE_EMPTY_PASSWORD);
  
      // Create a LocalService around HSQLDB
      JdbcMeta jdbcMeta;
      jdbcMeta = new JdbcMeta(CONNECTION_SPEC.url,
          CONNECTION_SPEC.username, CONNECTION_SPEC.password);
      localService = new LocalService(jdbcMeta);
    }
  
    @AfterClass public static void stopServers() throws KrbException {
      for (HttpServer server : SERVERS_TO_STOP) {
        server.stop();
      }
      SERVERS_TO_STOP.clear();
    }
  
    @Before public void checkUrl() {
      //We signal that we skip the test because of the IBM Java issue by specifying a Null URL
      assumeNotNull(jdbcUrl);
    }
  
    public HttpBaseTest(String jdbcUrl) {
      this.jdbcUrl = jdbcUrl;
    }
  
    /**
     * Utility class for creating certificates for testing.
     */
    private static class CertTool {
      private static final String SIGNING_ALGORITHM = "SHA256WITHRSA";
      private static final String ENC_ALGORITHM = "RSA";
  
      static {
        Security.addProvider(new BouncyCastleProvider());
      }
  
      private void createSelfSignedCert(File targetKeystore, String keyName,
          String keystorePassword) {
        if (targetKeystore.exists()) {
          throw new RuntimeException("Keystore already exists: " + targetKeystore);
        }
  
        try {
          KeyPair kp = generateKeyPair();
  
          X509Certificate cert = generateCert(keyName, kp, true, kp.getPublic(),
              kp.getPrivate());
  
          char[] password = keystorePassword.toCharArray();
          KeyStore keystore = KeyStore.getInstance("JKS");
          keystore.load(null, null);
          keystore.setCertificateEntry(keyName + "Cert", cert);
          keystore.setKeyEntry(keyName + "Key", kp.getPrivate(), password, new Certificate[] {cert});
          try (FileOutputStream fos = new FileOutputStream(targetKeystore)) {
            keystore.store(fos, password);
          }
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      }
  
      private KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator gen = KeyPairGenerator.getInstance(ENC_ALGORITHM);
        gen.initialize(2048);
        return gen.generateKeyPair();
      }
  
      private X509Certificate generateCert(String keyName, KeyPair kp, boolean isCertAuthority,
                                           PublicKey signerPublicKey, PrivateKey signerPrivateKey)
          throws IOException, OperatorCreationException, CertificateException,
          NoSuchAlgorithmException {
        Calendar startDate = DateTimeUtils.calendar();
        Calendar endDate = DateTimeUtils.calendar();
        endDate.add(Calendar.YEAR, 100);
  
        BigInteger serialNumber = BigInteger.valueOf(startDate.getTimeInMillis());
        X500Name issuer = new X500Name(
            IETFUtils.rDNsFromString("cn=localhost", RFC4519Style.INSTANCE));
        JcaX509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(issuer,
            serialNumber, startDate.getTime(), endDate.getTime(), issuer, kp.getPublic());
        JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();
        certGen.addExtension(Extension.subjectKeyIdentifier, false,
            extensionUtils.createSubjectKeyIdentifier(kp.getPublic()));
        certGen.addExtension(Extension.basicConstraints, false,
            new BasicConstraints(isCertAuthority));
        certGen.addExtension(Extension.authorityKeyIdentifier, false,
            extensionUtils.createAuthorityKeyIdentifier(signerPublicKey));
        if (isCertAuthority) {
          certGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign));
        }
        X509CertificateHolder certificateHolder = certGen.build(
            new JcaContentSignerBuilder(SIGNING_ALGORITHM).build(signerPrivateKey));
        return new JcaX509CertificateConverter().getCertificate(certificateHolder);
      }
    }
  }
  
  // End HttpBaseTest.java
  