MainClass.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /*
  2. * Password Management Servlets (PWM)
  3. * http://www.pwm-project.org
  4. *
  5. * Copyright (c) 2006-2009 Novell, Inc.
  6. * Copyright (c) 2009-2018 The PWM Project
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. package password.pwm.util.cli;
  23. import org.apache.log4j.ConsoleAppender;
  24. import org.apache.log4j.EnhancedPatternLayout;
  25. import org.apache.log4j.Layout;
  26. import org.apache.log4j.Logger;
  27. import org.apache.log4j.varia.NullAppender;
  28. import password.pwm.AppProperty;
  29. import password.pwm.PwmApplication;
  30. import password.pwm.PwmApplicationMode;
  31. import password.pwm.PwmConstants;
  32. import password.pwm.PwmEnvironment;
  33. import password.pwm.config.Configuration;
  34. import password.pwm.config.stored.ConfigurationReader;
  35. import password.pwm.error.ErrorInformation;
  36. import password.pwm.error.PwmError;
  37. import password.pwm.error.PwmUnrecoverableException;
  38. import password.pwm.util.cli.commands.ClearResponsesCommand;
  39. import password.pwm.util.cli.commands.CliCommand;
  40. import password.pwm.util.cli.commands.ConfigDeleteCommand;
  41. import password.pwm.util.cli.commands.ConfigLockCommand;
  42. import password.pwm.util.cli.commands.ConfigNewCommand;
  43. import password.pwm.util.cli.commands.ConfigResetHttpsCommand;
  44. import password.pwm.util.cli.commands.ConfigSetPasswordCommand;
  45. import password.pwm.util.cli.commands.ConfigUnlockCommand;
  46. import password.pwm.util.cli.commands.ExportAuditCommand;
  47. import password.pwm.util.cli.commands.ExportHttpsKeyStoreCommand;
  48. import password.pwm.util.cli.commands.ExportHttpsTomcatConfigCommand;
  49. import password.pwm.util.cli.commands.ExportLocalDBCommand;
  50. import password.pwm.util.cli.commands.ExportLogsCommand;
  51. import password.pwm.util.cli.commands.ExportResponsesCommand;
  52. import password.pwm.util.cli.commands.ExportStatsCommand;
  53. import password.pwm.util.cli.commands.HelpCommand;
  54. import password.pwm.util.cli.commands.ImportHttpsKeyStoreCommand;
  55. import password.pwm.util.cli.commands.ImportLocalDBCommand;
  56. import password.pwm.util.cli.commands.ImportResponsesCommand;
  57. import password.pwm.util.cli.commands.LdapSchemaExtendCommand;
  58. import password.pwm.util.cli.commands.LocalDBInfoCommand;
  59. import password.pwm.util.cli.commands.PasswordExpireNotificationCommand;
  60. import password.pwm.util.cli.commands.ResponseStatsCommand;
  61. import password.pwm.util.cli.commands.ShellCommand;
  62. import password.pwm.util.cli.commands.TokenInfoCommand;
  63. import password.pwm.util.cli.commands.UserReportCommand;
  64. import password.pwm.util.cli.commands.VersionCommand;
  65. import password.pwm.util.java.FileSystemUtility;
  66. import password.pwm.util.java.JavaHelper;
  67. import password.pwm.util.localdb.LocalDB;
  68. import password.pwm.util.localdb.LocalDBException;
  69. import password.pwm.util.localdb.LocalDBFactory;
  70. import password.pwm.util.logging.PwmLogLevel;
  71. import password.pwm.util.logging.PwmLogManager;
  72. import password.pwm.util.logging.PwmLogger;
  73. import java.io.File;
  74. import java.io.IOException;
  75. import java.io.OutputStreamWriter;
  76. import java.io.Writer;
  77. import java.util.ArrayList;
  78. import java.util.Arrays;
  79. import java.util.Collection;
  80. import java.util.Collections;
  81. import java.util.HashSet;
  82. import java.util.LinkedHashMap;
  83. import java.util.LinkedList;
  84. import java.util.List;
  85. import java.util.Map;
  86. import java.util.Queue;
  87. import java.util.TreeMap;
  88. public class MainClass
  89. {
  90. private static final PwmLogger LOGGER = PwmLogger.forClass( MainClass.class );
  91. private static final String LOGGING_PATTERN = "%d{yyyy-MM-dd'T'HH:mm:ssX}{GMT}, %-5p, %c{2}, %m%n";
  92. private static MainOptions mainOptions;
  93. public static final Map<String, CliCommand> COMMANDS;
  94. static
  95. {
  96. final List<CliCommand> commandList = new ArrayList<>();
  97. commandList.add( new LocalDBInfoCommand() );
  98. commandList.add( new ExportLogsCommand() );
  99. commandList.add( new UserReportCommand() );
  100. commandList.add( new ExportLocalDBCommand() );
  101. commandList.add( new ImportLocalDBCommand() );
  102. commandList.add( new ExportAuditCommand() );
  103. commandList.add( new ConfigUnlockCommand() );
  104. commandList.add( new ConfigLockCommand() );
  105. commandList.add( new ConfigSetPasswordCommand() );
  106. commandList.add( new ExportStatsCommand() );
  107. commandList.add( new ExportResponsesCommand() );
  108. commandList.add( new ClearResponsesCommand() );
  109. commandList.add( new ImportResponsesCommand() );
  110. commandList.add( new TokenInfoCommand() );
  111. commandList.add( new ConfigNewCommand() );
  112. commandList.add( new VersionCommand() );
  113. commandList.add( new LdapSchemaExtendCommand() );
  114. commandList.add( new ConfigDeleteCommand() );
  115. commandList.add( new ResponseStatsCommand() );
  116. commandList.add( new ImportHttpsKeyStoreCommand() );
  117. commandList.add( new ExportHttpsKeyStoreCommand() );
  118. commandList.add( new ExportHttpsTomcatConfigCommand() );
  119. commandList.add( new ShellCommand() );
  120. commandList.add( new ConfigResetHttpsCommand() );
  121. commandList.add( new HelpCommand() );
  122. commandList.add( new PasswordExpireNotificationCommand() );
  123. final Map<String, CliCommand> sortedMap = new TreeMap<>();
  124. for ( final CliCommand command : commandList )
  125. {
  126. sortedMap.put( command.getCliParameters().commandName, command );
  127. }
  128. COMMANDS = Collections.unmodifiableMap( sortedMap );
  129. }
  130. public static String helpTextFromCommands( final Collection<CliCommand> commands )
  131. {
  132. final StringBuilder output = new StringBuilder();
  133. for ( final CliCommand command : commands )
  134. {
  135. output.append( command.getCliParameters().commandName );
  136. if ( command.getCliParameters().options != null )
  137. {
  138. for ( final CliParameters.Option option : command.getCliParameters().options )
  139. {
  140. output.append( " " );
  141. if ( option.isOptional() )
  142. {
  143. output.append( "<" ).append( option.getName() ).append( ">" );
  144. }
  145. else
  146. {
  147. output.append( "[" ).append( option.getName() ).append( "]" );
  148. }
  149. }
  150. }
  151. output.append( "\n" );
  152. output.append( " " ).append( command.getCliParameters().description );
  153. output.append( "\n" );
  154. }
  155. return output.toString();
  156. }
  157. private static String makeHelpTextOutput( )
  158. {
  159. final StringBuilder output = new StringBuilder();
  160. output.append( helpTextFromCommands( COMMANDS.values() ) );
  161. output.append( "\n" );
  162. output.append( "options:\n" );
  163. output.append( " -force force operations skipping any confirmation\n" );
  164. output.append( " -debugLevel=x set the debug level where x is TRACE, DEBUG, INFO, ERROR, WARN or FATAL\n" );
  165. output.append( " -applicationPath=x set the application path, default is current path\n" );
  166. output.append( "\n" );
  167. output.append( "usage: \n" );
  168. output.append( " command[.bat/.sh] <options> CommandName <command options>" );
  169. return output.toString();
  170. }
  171. private static CliEnvironment createEnv(
  172. final CliParameters parameters,
  173. final List<String> args
  174. )
  175. throws Exception
  176. {
  177. final Map<String, Object> options = parseCommandOptions( parameters, args );
  178. final File applicationPath = figureApplicationPath( mainOptions );
  179. out( "applicationPath=" + applicationPath.getAbsolutePath() );
  180. PwmEnvironment.verifyApplicationPath( applicationPath );
  181. final File configurationFile = locateConfigurationFile( applicationPath );
  182. final ConfigurationReader configReader = loadConfiguration( configurationFile );
  183. final Configuration config = configReader.getConfiguration();
  184. final PwmApplication pwmApplication;
  185. final LocalDB localDB;
  186. if ( parameters.needsPwmApplication )
  187. {
  188. pwmApplication = loadPwmApplication( applicationPath, mainOptions.getApplicationFlags(), config, configurationFile, parameters.readOnly );
  189. localDB = pwmApplication.getLocalDB();
  190. }
  191. else if ( parameters.needsLocalDB )
  192. {
  193. pwmApplication = null;
  194. localDB = loadPwmDB( config, parameters.readOnly, applicationPath );
  195. }
  196. else
  197. {
  198. pwmApplication = null;
  199. localDB = null;
  200. }
  201. out( "environment initialized" );
  202. out( "" );
  203. final Writer outputStream = new OutputStreamWriter( System.out, PwmConstants.DEFAULT_CHARSET );
  204. return CliEnvironment.builder()
  205. .configurationReader( configReader )
  206. .configurationFile( configurationFile )
  207. .config( config )
  208. .applicationPath( applicationPath )
  209. .pwmApplication( pwmApplication )
  210. .localDB( localDB )
  211. .debugWriter( outputStream )
  212. .options( options )
  213. .mainOptions( mainOptions )
  214. .build();
  215. }
  216. public static Map<String, Object> parseCommandOptions(
  217. final CliParameters cliParameters,
  218. final List<String> args
  219. )
  220. throws CliException
  221. {
  222. final Queue<String> argQueue = new LinkedList<>( args );
  223. final Map<String, Object> returnObj = new LinkedHashMap<>();
  224. if ( cliParameters.options != null )
  225. {
  226. for ( final CliParameters.Option option : cliParameters.options )
  227. {
  228. if ( !option.isOptional() && argQueue.isEmpty() )
  229. {
  230. throw new CliException( "missing required option '" + option.getName() + "'" );
  231. }
  232. if ( !argQueue.isEmpty() )
  233. {
  234. final String argument = argQueue.poll();
  235. switch ( option.getType() )
  236. {
  237. case NEW_FILE:
  238. try
  239. {
  240. final File theFile = new File( argument );
  241. if ( theFile.exists() )
  242. {
  243. throw new CliException( "file for option '" + option.getName() + "' at '" + theFile.getAbsolutePath() + "' already exists" );
  244. }
  245. returnObj.put( option.getName(), theFile );
  246. }
  247. catch ( Exception e )
  248. {
  249. if ( e instanceof CliException )
  250. {
  251. throw ( CliException ) e;
  252. }
  253. throw new CliException( "cannot access file for option '" + option.getName() + "', " + e.getMessage() );
  254. }
  255. break;
  256. case EXISTING_FILE:
  257. try
  258. {
  259. final File theFile = new File( argument );
  260. if ( !theFile.exists() )
  261. {
  262. throw new CliException( "file for option '" + option.getName() + "' at '" + theFile.getAbsolutePath() + "' does not exist" );
  263. }
  264. returnObj.put( option.getName(), theFile );
  265. }
  266. catch ( Exception e )
  267. {
  268. if ( e instanceof CliException )
  269. {
  270. throw ( CliException ) e;
  271. }
  272. throw new CliException( "cannot access file for option '" + option.getName() + "', " + e.getMessage() );
  273. }
  274. break;
  275. case STRING:
  276. returnObj.put( option.getName(), argument );
  277. break;
  278. default:
  279. JavaHelper.unhandledSwitchStatement( option.getType() );
  280. }
  281. }
  282. }
  283. }
  284. if ( !argQueue.isEmpty() )
  285. {
  286. throw new CliException( "unknown option '" + argQueue.poll() + "'" );
  287. }
  288. return returnObj;
  289. }
  290. public static void main( final String[] args )
  291. throws Exception
  292. {
  293. out( PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION + " Command Line Utility" );
  294. mainOptions = MainOptions.parseMainCommandLineOptions( args, new OutputStreamWriter( System.out, PwmConstants.DEFAULT_CHARSET ) );
  295. final List<String> workingArgs = mainOptions.getRemainingArguments();
  296. initLog4j( mainOptions.getPwmLogLevel() );
  297. final String commandStr = workingArgs == null || workingArgs.size() < 1 ? null : workingArgs.iterator().next();
  298. boolean commandExceuted = false;
  299. if ( commandStr == null )
  300. {
  301. out( "\n" );
  302. out( makeHelpTextOutput() );
  303. }
  304. else
  305. {
  306. for ( final CliCommand command : COMMANDS.values() )
  307. {
  308. if ( commandStr.equalsIgnoreCase( command.getCliParameters().commandName ) )
  309. {
  310. commandExceuted = true;
  311. executeCommand( command, commandStr, workingArgs.toArray( new String[ workingArgs.size() ] ) );
  312. break;
  313. }
  314. }
  315. if ( !commandExceuted )
  316. {
  317. out( "unknown command '" + workingArgs.iterator().next() + "'" );
  318. }
  319. }
  320. }
  321. private static void executeCommand(
  322. final CliCommand command,
  323. final String commandStr,
  324. final String[] args
  325. )
  326. {
  327. final List<String> argList = new LinkedList<>( Arrays.asList( args ) );
  328. argList.remove( 0 );
  329. final CliEnvironment cliEnvironment;
  330. try
  331. {
  332. cliEnvironment = createEnv( command.getCliParameters(), argList );
  333. }
  334. catch ( Exception e )
  335. {
  336. final String errorMsg = "unable to establish operating environment: " + e.getMessage();
  337. final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_ENVIRONMENT_ERROR, errorMsg );
  338. LOGGER.error( errorInformation.toDebugStr(), e );
  339. out( "unable to establish operating environment: " + e.getMessage() );
  340. System.exit( -1 );
  341. return;
  342. }
  343. try
  344. {
  345. command.execute( commandStr, cliEnvironment );
  346. }
  347. catch ( Exception e )
  348. {
  349. System.out.println( e.getMessage() );
  350. //System.exit(-1);
  351. return;
  352. }
  353. if ( cliEnvironment.getPwmApplication() != null )
  354. {
  355. try
  356. {
  357. cliEnvironment.getPwmApplication().shutdown();
  358. }
  359. catch ( Exception e )
  360. {
  361. out( "error closing operating environment: " + e.getMessage() );
  362. e.printStackTrace();
  363. }
  364. }
  365. if ( cliEnvironment.getLocalDB() != null )
  366. {
  367. try
  368. {
  369. cliEnvironment.getLocalDB().close();
  370. }
  371. catch ( Exception e )
  372. {
  373. out( "error closing LocalDB environment: " + e.getMessage() );
  374. }
  375. }
  376. //System.exit(0);
  377. return;
  378. }
  379. private static void initLog4j( final PwmLogLevel logLevel )
  380. {
  381. if ( logLevel == null )
  382. {
  383. Logger.getRootLogger().removeAllAppenders();
  384. Logger.getRootLogger().addAppender( new NullAppender() );
  385. PwmLogger.markInitialized();
  386. return;
  387. }
  388. final Layout patternLayout = new EnhancedPatternLayout( LOGGING_PATTERN );
  389. final ConsoleAppender consoleAppender = new ConsoleAppender( patternLayout );
  390. for ( final Package logPackage : PwmLogManager.LOGGING_PACKAGES )
  391. {
  392. if ( logPackage != null )
  393. {
  394. final Logger logger = Logger.getLogger( logPackage.getName() );
  395. logger.addAppender( consoleAppender );
  396. logger.setLevel( logLevel.getLog4jLevel() );
  397. }
  398. }
  399. PwmLogger.markInitialized();
  400. }
  401. private static LocalDB loadPwmDB(
  402. final Configuration config,
  403. final boolean readonly,
  404. final File applicationPath
  405. )
  406. throws Exception
  407. {
  408. final File databaseDirectory;
  409. final String pwmDBLocationSetting = config.readAppProperty( AppProperty.LOCALDB_LOCATION );
  410. databaseDirectory = FileSystemUtility.figureFilepath( pwmDBLocationSetting, applicationPath );
  411. return LocalDBFactory.getInstance( databaseDirectory, readonly, null, config );
  412. }
  413. private static ConfigurationReader loadConfiguration( final File configurationFile ) throws Exception
  414. {
  415. final ConfigurationReader reader = new ConfigurationReader( configurationFile );
  416. if ( reader.getConfigMode() == PwmApplicationMode.ERROR )
  417. {
  418. final String errorMsg = reader.getConfigFileError() == null ? "error" : reader.getConfigFileError().toDebugStr();
  419. out( "unable to load configuration: " + errorMsg );
  420. System.exit( -1 );
  421. }
  422. return reader;
  423. }
  424. private static PwmApplication loadPwmApplication(
  425. final File applicationPath,
  426. final Collection<PwmEnvironment.ApplicationFlag> flags,
  427. final Configuration config,
  428. final File configurationFile,
  429. final boolean readonly
  430. )
  431. throws LocalDBException, PwmUnrecoverableException
  432. {
  433. final PwmApplicationMode mode = readonly ? PwmApplicationMode.READ_ONLY : PwmApplicationMode.RUNNING;
  434. final Collection<PwmEnvironment.ApplicationFlag> applicationFlags = new HashSet<>();
  435. if ( flags == null )
  436. {
  437. applicationFlags.addAll( PwmEnvironment.ParseHelper.readApplicationFlagsFromSystem( null ) );
  438. }
  439. else
  440. {
  441. applicationFlags.addAll( flags );
  442. }
  443. applicationFlags.add( PwmEnvironment.ApplicationFlag.CommandLineInstance );
  444. final PwmEnvironment pwmEnvironment = new PwmEnvironment.Builder( config, applicationPath )
  445. .setApplicationMode( mode )
  446. .setConfigurationFile( configurationFile )
  447. .setFlags( applicationFlags )
  448. .createPwmEnvironment();
  449. final PwmApplication pwmApplication = new PwmApplication( pwmEnvironment );
  450. final PwmApplicationMode runningMode = pwmApplication.getApplicationMode();
  451. if ( runningMode != mode )
  452. {
  453. out( "unable to start application in required state '" + mode + "', current state: " + runningMode );
  454. System.exit( -1 );
  455. }
  456. return pwmApplication;
  457. }
  458. private static File locateConfigurationFile( final File applicationPath )
  459. {
  460. return new File( applicationPath + File.separator + PwmConstants.DEFAULT_CONFIG_FILE_FILENAME );
  461. }
  462. private static void out( final CharSequence txt )
  463. {
  464. System.out.println( txt );
  465. }
  466. private static File figureApplicationPath( final MainOptions mainOptions ) throws IOException, PwmUnrecoverableException
  467. {
  468. final File applicationPath;
  469. if ( mainOptions != null && mainOptions.getApplicationPath() != null )
  470. {
  471. applicationPath = mainOptions.getApplicationPath();
  472. }
  473. else
  474. {
  475. final String appPathStr = PwmEnvironment.ParseHelper.readValueFromSystem( PwmEnvironment.EnvironmentParameter.applicationPath, null );
  476. if ( appPathStr != null && !appPathStr.isEmpty() )
  477. {
  478. applicationPath = new File( appPathStr );
  479. }
  480. else
  481. {
  482. final String errorMsg = "unable to locate applicationPath. Specify using -applicationPath option, java option "
  483. + "\"" + PwmEnvironment.EnvironmentParameter.applicationPath.conicalJavaOptionSystemName() + "\""
  484. + ", or system environment setting "
  485. + "\"" + PwmEnvironment.EnvironmentParameter.applicationPath.conicalEnvironmentSystemName() + "\"";
  486. throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_STARTUP_ERROR, errorMsg ) );
  487. }
  488. }
  489. LOGGER.debug( "using applicationPath " + applicationPath.getAbsolutePath() );
  490. return applicationPath;
  491. }
  492. }