package org.ngbw.utils; import java.io.Console; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Paths; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import java.util.List; import java.util.regex.Pattern; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.ngbw.sdk.Workbench; import org.ngbw.sdk.WorkbenchException; import org.ngbw.sdk.common.util.StringUtils; import org.ngbw.sdk.database.Country; import org.ngbw.sdk.database.User; import org.ngbw.sdk.phoenix.CipresPhoenixUserAccountManager; /** * This is the new version 2 utilizing Apache Commons CLI library. By utilizing the library, * it effectively eliminates "Array out of bound" exception when parsing command line options and parameters. * * @author Tony Chen * * @version 2.0.0 */ public class UserCountryIs { private static final Logger logger = Logger.getLogger(UserCountryIs.class); private static final String VERSION = "2.2.0"; private static String log4jFile = null; private static CommandLine cmdln = null; private static List argsList = null; private User targetUser; private User authenticatedUser = null; private UserCountryIs ( User user ) { this.targetUser = user; } private static synchronized void usage ( final Options options ) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("UserCountryIs", options, Boolean.TRUE); } private static synchronized void printHelp ( final Options options ) { final String baseCommand = "> java -cp [classpath-to-this-jar] " + UserCountryIs.class.getName() + " "; System.out.println(); System.out.println("Run as:"); System.out.println(baseCommand + "-u [function] [option-values ...]"); System.out.println(); usage(options); System.out.println(); System.out.println(); System.out.println("Functions"); System.out.println("---------"); System.out.println("Supported functions are:"); System.out.println(" -list List user's info."); System.out.println(" -c or -country COUNTRY Change the user's country."); System.out.println(" -sync Sync user's information with Phoenix Bioinformatics."); System.out.println(); System.out.println("Examples:"); System.out.println(" UserCountryIs -u johndoe -list"); System.out.println(" UserCountryIs -u johndoe -sync"); System.out.println(" UserCountryIs -u johndoe -c CA"); System.out.println(" UserCountryIs -u johndoe -c CA -sync"); System.out.println(); System.out.println("Additional Options:"); System.out.println("-----------------"); System.out.println("Additional (optional) options are:"); System.out.println(" -help Show this help information."); System.out.println(" -version Show this application version numbers."); System.out.println(" -log LOG4J A Log4j config file (in XML) to output logs."); System.out.println(); System.out.println("Examples:"); System.out.println(" UserCountryIs -log /path/to/log4j.xml -u johndoe -list"); System.out.println(" UserCountryIs -log /path/to/log4j.xml -u johndoe -c CA"); System.out.println(); } private static synchronized void printVersion () { System.out.println("CIPRES " + UserCountryIs.class.getSimpleName() + " " + VERSION); } private static synchronized void loadLog4jConfigs ( final String log4jConfigsFile ) throws IOException { try { if (null == log4jConfigsFile || log4jConfigsFile.trim().isEmpty()) { throw new IllegalArgumentException("Log4j configs filename cannot be empty or null. "); } else if (!Files.exists(Paths.get(log4jConfigsFile))) { throw new FileNotFoundException(String.format("Log4j properties file [%s] not found.", log4jConfigsFile)); } org.apache.log4j.xml.DOMConfigurator.configureAndWatch(log4jConfigsFile, (1000 * 15)); // 15 sec } catch ( IOException e ) { throw e; } } private static synchronized Options buildOptions () { final Options options = new Options(); options.addOption(Option.builder("u").hasArg().argName("user").desc("The user's ID, username, or identifier.").build()); options.addOption(Option.builder("c").hasArg().argName("country").desc("Set the user's country to this.").build()); options.addOption(Option.builder("list").hasArg(false).desc("List details of the user.").build()); options.addOption(Option.builder("query").hasArg().argName("ip-address").desc("Query user's country from the IP address.").build()); options.addOption(Option.builder("sync").hasArg(false).desc("Sync user's info with Phoenix Bioinformatics.").build()); options.addOption(Option.builder("help").hasArg(false).desc("Print help information.").build()); options.addOption(Option.builder("version").hasArg(false).desc("Show version and description.").build()); options.addOption(Option.builder("log").hasArg().argName("log4j").desc("Configure Log4j with this Log4j XML file (optional).").build()); return options; } private static synchronized CommandLine getCommandLine ( final String[] args ) throws ParseException { CommandLineParser parser = new DefaultParser(); return parser.parse(buildOptions(), args, Boolean.FALSE); } private static synchronized boolean processOptions ( final CommandLine cmdln ) throws Throwable { boolean shouldQuit = false; if (null == cmdln.getOptions() || cmdln.getOptions().length == 0) { printHelp(buildOptions()); shouldQuit = true; } else if (cmdln.hasOption("help")) { printHelp(buildOptions()); shouldQuit = true; } else if (cmdln.hasOption("version")) { printVersion(); shouldQuit = true; } else { if (cmdln.hasOption("log")) { log4jFile = cmdln.getOptionValue("log"); } argsList = cmdln.getArgList(); } // Surpress uncessary warning messages from Log4j lib. surpressLogsToStdOutput(); return shouldQuit; } private static synchronized void configOptions () { if (null != log4jFile && !log4jFile.trim().isEmpty()) { try { loadLog4jConfigs(log4jFile); } catch ( Throwable t ) { System.err.println(t.getMessage()); Logger.getRootLogger().setLevel(Level.OFF); } } } private static synchronized void surpressLogsToStdOutput () { if (log4jFile == null || log4jFile.trim().isEmpty()) { Logger.getRootLogger().setLevel(Level.OFF); } } private synchronized void list () throws Throwable { if (null != targetUser){ final Country country = (targetUser.getCountry() == null) ? null : Country.getCountry(targetUser.getCountry()); System.out.println(); System.out.println("UserID: " + targetUser.getUserId()); System.out.println("Username: " + targetUser.getUsername()); System.out.println("Identifier: " + targetUser.getUUID()); System.out.println("Email: " + targetUser.getEmail()); System.out.println("Name: " + targetUser.getFirstName() + " " + targetUser.getLastName()); System.out.println("Country: " + (country == null? "?" : String.format("%s (%s)", country.getName(), country.getAbbreviation()))); } } private synchronized void syncWithPhoenix () throws Throwable { if (null != targetUser) { if (null == CipresPhoenixUserAccountManager.checkUserExistence(targetUser)) { throw new RuntimeException(String.format( "The user, %s, has never been registered with Phoenix Bioinformatics. " + "Registering with Phoenix Bioinformatics requires the user's password.", targetUser.getUsername())); } else if (StringUtils.isNullOrEmpty(targetUser.getCountry(), true)) { throw new RuntimeException(String.format( "The country of the user, %s, is not populated correctly. Please update " + "the user's country before syncing with Phoenix's system.", targetUser.getUsername())); } else { try { System.out.println(); System.out.println("Syncing user's information with Phoenix Bioinformatics ..."); CipresPhoenixUserAccountManager.update(targetUser, null); // not updating the password. targetUser.setPhoenixSyncReq("N"); targetUser.save(); System.out.println("User's information synced successfully with " + "Phoenix Bioinformatics."); } catch ( Throwable t ) { System.err.println("Error when syncing user's info with Phoenix " + "Bioinformatics:\n" + t.getMessage()); } } System.out.flush(); System.err.flush(); } } private synchronized void setCountry ( final String countryCode ) throws Throwable { if (null != targetUser) { final Country newCountry = Country.getCountry(countryCode); final Country currCountry = (targetUser.getCountry() == null? null : Country.getCountry(targetUser.getCountry())); System.out.println(); if (null == newCountry) { System.out.println("Unknown country: " + countryCode); } else if (newCountry.equals(currCountry)) { System.out.println("The new country, '" + newCountry.getName() + "' is the same as current country."); } else { if (currCountry == null) { System.out.print(String.format("Changing user's country to '%s' ", newCountry.getName())); } else { System.out.print(String.format( "Changing user's country from '%s' to '%s' ", currCountry.getName(), newCountry.getName())); } System.out.flush(); for (int i = 0; i < 2; i++) { Thread.sleep(1000); System.out.print("."); System.out.flush(); } targetUser.setCountry(newCountry.getAbbreviation()); targetUser.setPhoenixSyncReq("Y"); targetUser.save(); for (int i = 0; i < 3; i++) { Thread.sleep(1000); System.out.print("."); System.out.flush(); } Thread.sleep(1000); System.out.println(" done."); System.out.flush(); } } } /** * Queries user's country from the IP address. * *

* The IP is in IPv4 format. * * @param ipAddress the user's IP address * * @throws Throwable */ private synchronized void query ( final String ipAddress ) throws Throwable { try { final String countryCode = Workbench.getInstance().getCountryCodeFromIP(ipAddress); if (null != countryCode) { System.out.println("The country code of the IP " + ipAddress + " is " + countryCode + "."); } else { System.err.println("Unable to find country code of the IP: " + ipAddress); } } catch ( Throwable t ) { logger.error(t.getMessage(), t); throw t; } } public static void main ( String[] args ) { logger.debug("BEGIN: main(String[])::void"); int status = 0; UserCountryIs userCountryIs = null; try { cmdln = getCommandLine(args); if (!processOptions(cmdln)) { configOptions(); if (cmdln.hasOption("query")) { userCountryIs.query(cmdln.getOptionValue("query")); } else { if (!cmdln.hasOption("u")) { throw new RuntimeException("You must specify a username with the option 'u'."); } Workbench.getInstance(); Long userId = null; String userIdentifier = null; final User user; final String value = cmdln.getOptionValue("u"); // userId | userUUID | username if (StringUtils.hasPattern(value.trim(), Pattern.compile("^[0-9]+$"))) { userId = Long.parseLong(value.trim()); } else { userIdentifier = value.trim(); } // User findUser(userId, userName, email, uuid, activatedUserOnly, enforceUniqueness) user = User.findUser(userId, userIdentifier, userIdentifier, userIdentifier, false, true); if (null == user) { throw new RuntimeException("User '" + value.trim() + "' not found."); } userCountryIs = new UserCountryIs(user); final boolean hasAction = (cmdln.hasOption("c")) || (cmdln.hasOption("sync")); if (!hasAction) { userCountryIs.list(); } else { userCountryIs.authenticateUser(); if (cmdln.hasOption("c")) { userCountryIs.setCountry(cmdln.getOptionValue("c")); } if (cmdln.hasOption("sync")) { userCountryIs.syncWithPhoenix(); } } System.out.println(); } } } catch ( Throwable t ) { status = 1; surpressLogsToStdOutput(); System.err.println(t.getMessage()); logger.error(t.getMessage(), t); } userCountryIs = null; logger.trace("BEGIN: main(String[])::void"); System.exit(status); } private void authenticateUser () throws IOException, SQLException, InterruptedException { if (authenticatedUser == null) { System.out.println(); System.out.println("Authentication is required:"); final int maxTries = 3; int tries = 0; Console console = null; // Try 3 times to get the Console or throw an Exception. do { tries += 1; console = System.console(); if (console != null) { break; } Thread.sleep(300L); } while (tries < maxTries); if (console == null) { throw new RuntimeException("Unable to get System Console."); } User tempUser = null; // Try 3 times to authenticate the user. tries = 0; do { System.out.println(); System.out.print("Username or Email: "); String username = console.readLine(); tempUser = (username == null || username.trim().isEmpty()) ? null : (username.trim().contains("@") ? User.findUserByEmail(username.trim()) : User.findAllUser(username.trim())); if (tempUser == null) { System.err.println(String.format("Unable to find user info of '%s'.", username)); } else { if (tempUser.getRole() == null) { tempUser = null; System.err.println(String.format("The user '%s' does not have " + "proper role to make modification.", username)); } else if (!tempUser.getRole().equalsIgnoreCase("ADMIN")) { tempUser = null; System.err.println(String.format("The user '%s' does not have " + "proper role to make modification.", username)); } else { break; } } tries += 1; } while (tries < maxTries); if (tempUser != null) { tries = 0; do { System.out.print("Password: "); char[] passwordChars = console.readPassword(); try { final String password = new String(passwordChars); final String hashedPassword = Workbench.getInstance() .secureUserPassword(password, null); if (tempUser.getPassword().equals(hashedPassword)) { authenticatedUser = tempUser; break; } else { System.err.println("Incorrect password. Please try again."); } } catch ( UnsupportedEncodingException | NoSuchAlgorithmException | WorkbenchException e ) { System.err.println(e.getMessage()); System.err.println("Please try again."); } tries += 1; } while (tries < maxTries); } if (authenticatedUser == null) { throw new RuntimeException("Authentication failed."); } } } }