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.util;
  
  import org.apache.atlas.web.dao.UserDao;
  import org.apache.commons.cli.CommandLine;
  import org.apache.commons.cli.DefaultParser;
  import org.apache.commons.cli.Options;
  import org.apache.commons.lang.StringUtils;
  import org.apache.hadoop.conf.Configuration;
  import org.apache.hadoop.security.alias.CredentialProvider;
  import org.apache.hadoop.security.alias.CredentialProviderFactory;
  
  import java.io.Console;
  import java.io.IOException;
  import java.util.Arrays;
  
  import static org.apache.atlas.security.SecurityProperties.KEYSTORE_PASSWORD_KEY;
  import static org.apache.atlas.security.SecurityProperties.SERVER_CERT_PASSWORD_KEY;
  import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_PASSWORD_KEY;
  
  /**
   * A utility class for generating a credential provider containing the entries required for supporting the SSL
   * implementation
   * of the DGC server.
   */
  public class CredentialProviderUtility {
      private static final String[]   KEYS                = new String[] {KEYSTORE_PASSWORD_KEY, TRUSTSTORE_PASSWORD_KEY, SERVER_CERT_PASSWORD_KEY};
      private static final TextDevice DEFAULT_TEXT_DEVICE = new TextDevice() {
          final Console console = System.console();
  
          @Override
          public void printf(String fmt, Object... params) {
              console.printf(fmt, params);
          }
  
          @Override
          public String readLine(String fmt, Object... args) {
              return console.readLine(fmt, args);
          }
  
          @Override
          public char[] readPassword(String fmt, Object... args) {
              return console.readPassword(fmt, args);
          }
      };
  
      public static TextDevice textDevice = DEFAULT_TEXT_DEVICE;
  
      private CredentialProviderUtility() {
          // to block instantiation
      }
  
      public static void main(String[] args) throws IOException {
          try {
              CommandLine cmd                    = new DefaultParser().parse(createOptions(), args);
              boolean     generatePasswordOption = cmd.hasOption("g");
              String      key                    = cmd.getOptionValue("k");
              char[]      cred                   = null;
              String      providerPath           = cmd.getOptionValue("f");
  
              if (cmd.hasOption("p")) {
                  cred = cmd.getOptionValue("p").toCharArray();
              }
  
              if (generatePasswordOption) {
                  String userName = cmd.getOptionValue("u");
                  String password = cmd.getOptionValue("p");
  
                  if (userName != null && password != null) {
                      String  encryptedPassword = UserDao.encrypt(password);
                      boolean silentOption      = cmd.hasOption("s");
  
                      if (silentOption) {
                          System.out.println(encryptedPassword);
                      } else {
                          System.out.println("Your encrypted password is  : " + encryptedPassword);
                      }
                  } else {
                      System.out.println("Please provide username and password as input. Usage: cputil.py -g -u <username> -p <password>");
                  }
  
                  return;
              }
  
              if (key != null && cred != null && providerPath != null) {
                  if (!StringUtils.isEmpty(String.valueOf(cred))) {
                      Configuration conf = new Configuration(false);
  
                      conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, providerPath);
  
                      CredentialProvider provider = CredentialProviderFactory.getProviders(conf).get(0);
  
                      provider.createCredentialEntry(key, cred);
                      provider.flush();
  
                      System.out.println("Password is stored in Credential Provider");
                  } else {
                      System.out.println("Please enter a valid password");
                  }
                  return;
              }
          } catch (Exception e) {
              System.out.println("Exception while generatePassword  " + e.getMessage());
              return;
          }
  
          // prompt for the provider name
          CredentialProvider provider = getCredentialProvider(textDevice);
  
          if (provider != null) {
              for (String key : KEYS) {
                  char[] cred = getPassword(textDevice, key);
  
                  // create a credential entry and store it
                  if (provider.getCredentialEntry(key) != null) {
                      String  choice    = textDevice.readLine("Entry for %s already exists.  Overwrite? (y/n) [y]:", key);
                      boolean overwrite = StringUtils.isEmpty(choice) || choice.equalsIgnoreCase("y");
  
                      if (overwrite) {
                          provider.deleteCredentialEntry(key);
                          provider.flush();
                          provider.createCredentialEntry(key, cred);
                          provider.flush();
  
                          textDevice.printf("Entry for %s was overwritten with the new value.\n", key);
                      } else {
                          textDevice.printf("Entry for %s was not overwritten.\n", key);
                      }
                  } else {
                      provider.createCredentialEntry(key, cred);
  
                      provider.flush();
                  }
              }
          }
      }
  
      private static Options createOptions() {
          Options options = new Options();
  
          options.addOption("k", "ldapkey", true, "key");
          options.addOption("f", "ldapPath", true, "path");
          options.addOption("g", "generatePassword", false, "Generate Password");
          options.addOption("s", "silent", false, "Silent");
          options.addOption("u", "username", true, "UserName");
          options.addOption("p", "password", true, "Password");
  
          return options;
      }
  
      /**
       * Retrieves a password from the command line.
       *
       * @param textDevice the system console.
       * @param key the password key/alias.
       * @return the password.
       */
      private static char[] getPassword(TextDevice textDevice, String key) {
          char[] ret;
  
          while (true) {
              char[]  passwd1 = textDevice.readPassword("Please enter the password value for %s:", key);
              char[]  passwd2 = textDevice.readPassword("Please enter the password value for %s again:", key);
              boolean isMatch = Arrays.equals(passwd1, passwd2);
  
              if (!isMatch) {
                  textDevice.printf("Password entries don't match. Please try again.\n");
              } else {
                  if (passwd1 == null || passwd1.length == 0) {
                      textDevice.printf("An empty password is not valid.  Please try again.\n");
                  } else {
                      ret = passwd1;
  
                      if (passwd2 != null) {
                          Arrays.fill(passwd2, ' ');
                      }
  
                      break;
                  }
              }
  
              if (passwd1 != null) {
                  Arrays.fill(passwd1, ' ');
              }
  
              if (passwd2 != null) {
                  Arrays.fill(passwd2, ' ');
              }
          }
  
          return ret;
      }
  
      /**
       * \
       * Returns a credential provider for the entered JKS path.
       *
       * @param textDevice the system console.
       * @return the Credential provider
       * @throws IOException
       */
      private static CredentialProvider getCredentialProvider(TextDevice textDevice) throws IOException {
          String providerPath = textDevice.readLine("Please enter the full path to the credential provider:");
  
          if (providerPath != null) {
              Configuration conf = new Configuration(false);
  
              conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, providerPath);
  
              return CredentialProviderFactory.getProviders(conf).get(0);
          }
  
          return null;
      }
  
      public abstract static class TextDevice {
          public abstract void printf(String fmt, Object... params);
  
          public abstract String readLine(String fmt, Object... args);
  
          public abstract char[] readPassword(String fmt, Object... args);
      }
  }
  