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.atlas.web.security;
  
  import org.apache.atlas.ApplicationProperties;
  import org.apache.atlas.AtlasException;
  import org.apache.atlas.web.TestUtils;
  import org.apache.commons.configuration.ConfigurationException;
  import org.apache.commons.configuration.PropertiesConfiguration;
  import org.apache.commons.io.FileUtils;
  import org.apache.hadoop.conf.Configuration;
  import org.apache.hadoop.minikdc.MiniKdc;
  import org.apache.hadoop.security.alias.CredentialProviderFactory;
  import org.apache.hadoop.security.ssl.SSLFactory;
  import org.apache.hadoop.security.ssl.SSLHostnameVerifier;
  import org.apache.zookeeper.Environment;
  import org.eclipse.jetty.server.Server;
  import org.eclipse.jetty.webapp.WebAppContext;
  
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.Writer;
  import java.net.URL;
  import java.nio.file.Files;
  import java.util.Locale;
  import java.util.Properties;
  
  import static org.apache.atlas.security.SecurityProperties.CERT_STORES_CREDENTIAL_PROVIDER_PATH;
  import static org.apache.atlas.security.SecurityProperties.CLIENT_AUTH_KEY;
  import static org.apache.atlas.security.SecurityProperties.KEYSTORE_FILE_KEY;
  import static org.apache.atlas.security.SecurityProperties.SSL_CLIENT_PROPERTIES;
  import static org.apache.atlas.security.SecurityProperties.TLS_ENABLED;
  import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_FILE_KEY;
  import static org.testng.Assert.assertNotNull;
  
  /**
   *
   */
  public class BaseSecurityTest {
      private static final String  JAAS_ENTRY = "%s { \n" + " %s required\n"
              // kerberos module
              + " keyTab=\"%s\"\n" + " debug=true\n" + " principal=\"%s\"\n" + " useKeyTab=true\n"
              + " useTicketCache=false\n" + " doNotPrompt=true\n" + " storeKey=true;\n" + "}; \n";
      protected            MiniKdc kdc;
  
      public static String writeConfiguration(final PropertiesConfiguration configuration) throws Exception {
          String confLocation = System.getProperty("atlas.conf");
          URL    url;
          if (confLocation == null) {
              url = BaseSecurityTest.class.getResource("/" + ApplicationProperties.APPLICATION_PROPERTIES);
          } else {
              url = new File(confLocation, ApplicationProperties.APPLICATION_PROPERTIES).toURI().toURL();
          }
          PropertiesConfiguration configuredProperties = new PropertiesConfiguration();
          configuredProperties.load(url);
  
          configuredProperties.copy(configuration);
  
          String persistDir = TestUtils.getTempDirectory();
          configuredProperties.setProperty("atlas.authentication.method.file", "true");
          configuredProperties.setProperty("atlas.authentication.method.file.filename", persistDir
                  + "/users-credentials");
          configuredProperties.setProperty("atlas.auth.policy.file", persistDir
                  + "/policy-store.txt");
          TestUtils.writeConfiguration(configuredProperties, persistDir + File.separator +
                  ApplicationProperties.APPLICATION_PROPERTIES);
          setupUserCredential(persistDir);
          setUpPolicyStore(persistDir);
          ApplicationProperties.forceReload();
          return persistDir;
      }
  
      public static void setupUserCredential(String tmpDir) throws Exception {
          String credentialFileStr = "admin=ADMIN::8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918\n" +
                  "michael=DATA_SCIENTIST::95bfb24de17d285d734b9eaa9109bfe922adc85f20d2e5e66a78bddb4a4ebddb\n" +
                  "paul=DATA_STEWARD::e7c0dcf5f8a93e93791e9bac1ae454a691c1d2a902fc4256d489e96c1b9ac68c\n" +
                  "testuser=DATA_STEWARD::e7c0dcf5f8a93e93791e9bac1ae454a691c1d2a902fc4256d489e96c1b9ac68c\n";
  
          File credentialFile = new File(tmpDir, "users-credentials");
  
          FileUtils.write(credentialFile, credentialFileStr);
      }
  
      public static void setUpPolicyStore(String tmpDir) throws Exception {
          String policyStr = "adminPolicy;;admin:rwud;;ROLE_ADMIN:rwud;;type:*,entity:*,operation:*\n" +
                  "dataStewardPolicy;;testuser:rwud;;DATA_STEWARD:rwu;;type:*,entity:*\n";
  
          File policyFile = new File(tmpDir, "policy-store.txt");
          FileUtils.write(policyFile, policyStr);
      }
  
      public static void persistSSLClientConfiguration(org.apache.commons.configuration.Configuration clientConfig)
              throws AtlasException, IOException {
          //trust settings
          Configuration configuration = new Configuration(false);
          File          sslClientFile = getSSLClientFile();
          if (!sslClientFile.exists()) {
              configuration.set("ssl.client.truststore.type", "jks");
              configuration.set("ssl.client.truststore.location", clientConfig.getString(TRUSTSTORE_FILE_KEY));
              if (clientConfig.getBoolean(CLIENT_AUTH_KEY, false)) {
                  // need to get client key properties
                  configuration.set("ssl.client.keystore.location", clientConfig.getString(KEYSTORE_FILE_KEY));
                  configuration.set("ssl.client.keystore.type", "jks");
              }
              // add the configured credential provider
              configuration.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
                      clientConfig.getString(CERT_STORES_CREDENTIAL_PROVIDER_PATH));
              String hostnameVerifier = clientConfig.getString(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY);
              if (hostnameVerifier != null) {
                  configuration.set(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, hostnameVerifier);
              }
  
              configuration.writeXml(new FileWriter(sslClientFile));
          }
      }
  
      public String createJAASEntry(String context, String principal, File keytab) {
          String keytabpath = keytab.getAbsolutePath();
          // fix up for windows; no-op on unix
          keytabpath = keytabpath.replace('\\', '/');
          return String.format(Locale.ENGLISH, JAAS_ENTRY, context, getKerberosAuthModuleForJVM(), keytabpath, principal);
      }
  
      protected void generateTestProperties(Properties props) throws ConfigurationException, IOException {
          PropertiesConfiguration config =
                  new PropertiesConfiguration(System.getProperty("user.dir") +
                          "/../src/conf/" + ApplicationProperties.APPLICATION_PROPERTIES);
          for (String propName : props.stringPropertyNames()) {
              config.setProperty(propName, props.getProperty(propName));
          }
          File file = new File(System.getProperty("user.dir"), ApplicationProperties.APPLICATION_PROPERTIES);
          file.deleteOnExit();
          Writer fileWriter = new FileWriter(file);
          config.save(fileWriter);
      }
  
      protected void startEmbeddedServer(Server server) throws Exception {
          WebAppContext webapp = new WebAppContext();
          webapp.setContextPath("/");
          webapp.setWar(getWarPath());
          server.setHandler(webapp);
  
          server.start();
      }
  
      protected File startKDC() throws Exception {
          File       target     = Files.createTempDirectory("sectest").toFile();
          File       kdcWorkDir = new File(target, "kdc");
          Properties kdcConf    = MiniKdc.createConf();
          kdcConf.setProperty(MiniKdc.DEBUG, "true");
          kdc = new MiniKdc(kdcConf, kdcWorkDir);
          kdc.start();
  
          assertNotNull(kdc.getRealm());
          return kdcWorkDir;
      }
  
      protected String getKerberosAuthModuleForJVM() {
          if (System.getProperty("java.vendor").contains("IBM")) {
              return "com.ibm.security.auth.module.Krb5LoginModule";
          } else {
              return "com.sun.security.auth.module.Krb5LoginModule";
          }
      }
  
      protected void bindJVMtoJAASFile(File jaasFile) {
          String path = jaasFile.getAbsolutePath();
          System.setProperty(Environment.JAAS_CONF_KEY, path);
          disableZookeeperSecurity();
      }
  
      protected File createKeytab(MiniKdc kdc, File kdcWorkDir, String principal, String filename) throws Exception {
          File keytab = new File(kdcWorkDir, filename);
          kdc.createPrincipal(keytab, principal, principal + "/localhost", principal + "/127.0.0.1");
          return keytab;
      }
  
      protected String getWarPath() {
          return TestUtils.getWarPath();
      }
  
      protected PropertiesConfiguration getSSLConfiguration(String providerUrl) {
          String                        projectBaseDirectory = System.getProperty("projectBaseDir");
          final PropertiesConfiguration configuration        = new PropertiesConfiguration();
          configuration.setProperty("atlas.services.enabled", false);
          configuration.setProperty(TLS_ENABLED, true);
          configuration.setProperty(TRUSTSTORE_FILE_KEY, projectBaseDirectory + "/webapp/target/atlas.keystore");
          configuration.setProperty(KEYSTORE_FILE_KEY, projectBaseDirectory + "/webapp/target/atlas.keystore");
          configuration.setProperty(CERT_STORES_CREDENTIAL_PROVIDER_PATH, providerUrl);
          configuration.setProperty(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY,
                  SSLHostnameVerifier.DEFAULT_AND_LOCALHOST.toString());
          return configuration;
      }
  
      /* We only want Atlas to work in secure mode for the tests
       * for otherwise a lot more configuration is required to
       * make other components like Kafka run in secure mode.
       */
      private void disableZookeeperSecurity() {
          System.setProperty("zookeeper.sasl.client", "false");
          System.setProperty("zookeeper.sasl.clientconfig", "");
      }
  
      private static File getSSLClientFile() throws AtlasException {
          File sslDir;
  
          try {
              String persistDir = null;
              URL    resource   = BaseSecurityTest.class.getResource("/");
  
              if (resource != null) {
                  persistDir = resource.toURI().getPath();
              }
  
              sslDir = new File(persistDir);
  
              // LOG.info("ssl-client.xml will be created in {}", sslDir);
          } catch (Exception e) {
              throw new AtlasException("Failed to find client configuration directory", e);
          }
  
          return new File(sslDir, SSL_CLIENT_PROPERTIES);
      }
  }
  