Browse Source

admin log-viewer enhancements
added csp form-action directive

Jason Rivard 7 years ago
parent
commit
60c21a8ffe
29 changed files with 828 additions and 511 deletions
  1. 3 3
      server/pom.xml
  2. 1 0
      server/src/main/java/password/pwm/AppProperty.java
  3. 1 0
      server/src/main/java/password/pwm/http/HttpContentType.java
  4. 11 3
      server/src/main/java/password/pwm/http/JspUtility.java
  5. 205 2
      server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java
  6. 45 50
      server/src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java
  7. 15 1
      server/src/main/java/password/pwm/i18n/Admin.java
  8. 56 0
      server/src/main/java/password/pwm/svc/sessiontrack/SessionTrackService.java
  9. 21 6
      server/src/main/java/password/pwm/util/localdb/XodusLocalDB.java
  10. 1 1
      server/src/main/java/password/pwm/util/logging/LocalDBLogger.java
  11. 12 52
      server/src/main/java/password/pwm/util/logging/LocalDBSearchQuery.java
  12. 11 4
      server/src/main/java/password/pwm/util/logging/LocalDBSearchResults.java
  13. 28 4
      server/src/main/java/password/pwm/util/logging/PwmLogEvent.java
  14. 1 0
      server/src/main/resources/password/pwm/AppProperty.properties
  15. 1 1
      server/src/main/resources/password/pwm/config/PwmSetting.xml
  16. 8 0
      server/src/main/resources/password/pwm/i18n/Admin.properties
  17. 3 3
      server/src/main/resources/password/pwm/i18n/PwmSetting.properties
  18. 1 1
      server/src/main/webapp/META-INF/context.xml
  19. 9 1
      server/src/main/webapp/WEB-INF/jsp/admin-activity.jsp
  20. 2 3
      server/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp
  21. 4 69
      server/src/main/webapp/WEB-INF/jsp/admin-logview-window.jsp
  22. 74 249
      server/src/main/webapp/WEB-INF/jsp/admin-logview.jsp
  23. 1 1
      server/src/main/webapp/WEB-INF/jsp/admin-tokenlookup.jsp
  24. 43 43
      server/src/main/webapp/WEB-INF/jsp/admin-user-debug.jsp
  25. 1 3
      server/src/main/webapp/WEB-INF/jsp/fragment/header-menu.jsp
  26. 134 0
      server/src/main/webapp/WEB-INF/jsp/fragment/log-viewer.jsp
  27. 118 2
      server/src/main/webapp/public/resources/js/admin.js
  28. 0 9
      server/src/main/webapp/public/resources/js/configmanager.js
  29. 18 0
      server/src/main/webapp/public/resources/style.css

+ 3 - 3
server/pom.xml

@@ -82,12 +82,12 @@
             <plugin>
                 <groupId>com.github.spotbugs</groupId>
                 <artifactId>spotbugs-maven-plugin</artifactId>
-                <version>3.1.2</version>
+                <version>3.1.3</version>
                 <dependencies>
                     <dependency>
                         <groupId>com.github.spotbugs</groupId>
                         <artifactId>spotbugs</artifactId>
-                        <version>3.1.2</version>
+                        <version>3.1.3</version>
                     </dependency>
                 </dependencies>
                 <configuration>
@@ -552,7 +552,7 @@
         <dependency>
             <groupId>com.github.spotbugs</groupId>
             <artifactId>spotbugs-annotations</artifactId>
-            <version>3.1.2</version>
+            <version>3.1.3</version>
             <scope>provided</scope>
         </dependency>
 

+ 1 - 0
server/src/main/java/password/pwm/AppProperty.java

@@ -88,6 +88,7 @@ public enum AppProperty
     DB_INIT_HALT_ON_INDEX_CREATE_ERROR              ( "db.init.haltOnIndexCreateError" ),
     DB_SCHEMA_KEY_LENGTH                            ( "db.schema.keyLength" ),
     DOWNLOAD_FILENAME_STATISTICS_CSV                ( "download.filename.statistics.csv" ),
+    DOWNLOAD_FILENAME_SESSIONS_CSV                  ( "download.filename.sessions.csv" ),
     DOWNLOAD_FILENAME_USER_REPORT_SUMMARY_CSV       ( "download.filename.reportSummary.csv" ),
     DOWNLOAD_FILENAME_USER_REPORT_RECORDS_CSV       ( "download.filename.reportRecords.csv" ),
     DOWNLOAD_FILENAME_AUDIT_RECORDS_CSV             ( "download.filename.auditRecords.csv" ),

+ 1 - 0
server/src/main/java/password/pwm/http/HttpContentType.java

@@ -32,6 +32,7 @@ public enum HttpContentType
 {
     json( "application/json", PwmConstants.DEFAULT_CHARSET ),
     zip( "application/zip" ),
+    gzip( "application/gzip" ),
     xml( "text/xml", PwmConstants.DEFAULT_CHARSET ),
     csv( "text/csv", PwmConstants.DEFAULT_CHARSET ),
     javascript( "text/javascript", PwmConstants.DEFAULT_CHARSET ),

+ 11 - 3
server/src/main/java/password/pwm/http/JspUtility.java

@@ -37,6 +37,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.jsp.PageContext;
 import java.io.Serializable;
+import java.text.NumberFormat;
 import java.time.Instant;
 import java.util.Locale;
 
@@ -167,7 +168,7 @@ public abstract class JspUtility
         return forRequest( pageContext.getRequest() );
     }
 
-    public static String freindlyWrite( final PageContext pageContext, final boolean value )
+    public static String friendlyWrite( final PageContext pageContext, final boolean value )
     {
         final PwmRequest pwmRequest = forRequest( pageContext.getRequest() );
         return value
@@ -175,7 +176,14 @@ public abstract class JspUtility
                 : LocaleHelper.getLocalizedMessage( Display.Value_False, pwmRequest );
     }
 
-    public static String freindlyWrite( final PageContext pageContext, final String input )
+    public static String friendlyWrite( final PageContext pageContext, final long value )
+    {
+        final PwmRequest pwmRequest = forRequest( pageContext.getRequest() );
+        final NumberFormat numberFormat = NumberFormat.getInstance( pwmRequest.getLocale() );
+        return numberFormat.format( value );
+    }
+
+    public static String friendlyWrite( final PageContext pageContext, final String input )
     {
         final PwmRequest pwmRequest = forRequest( pageContext.getRequest() );
         if ( StringUtil.isEmpty( input ) )
@@ -185,7 +193,7 @@ public abstract class JspUtility
         return StringUtil.escapeHtml( input );
     }
 
-    public static String freindlyWrite( final PageContext pageContext, final Instant instant )
+    public static String friendlyWrite( final PageContext pageContext, final Instant instant )
     {
         final PwmRequest pwmRequest = forRequest( pageContext.getRequest() );
         if ( instant == null )

+ 205 - 2
server/src/main/java/password/pwm/http/servlet/admin/AdminServlet.java

@@ -70,6 +70,11 @@ import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDBException;
+import password.pwm.util.logging.LocalDBLogger;
+import password.pwm.util.logging.LocalDBSearchQuery;
+import password.pwm.util.logging.LocalDBSearchResults;
+import password.pwm.util.logging.PwmLogEvent;
+import password.pwm.util.logging.PwmLogLevel;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.reports.ReportUtils;
 import password.pwm.ws.server.RestResultBean;
@@ -79,6 +84,7 @@ import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.Serializable;
 import java.io.Writer;
 import java.lang.management.ManagementFactory;
@@ -93,8 +99,12 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
+import java.util.zip.GZIPOutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 @WebServlet(
         name = "AdminServlet",
@@ -116,6 +126,7 @@ public class AdminServlet extends ControlledPwmServlet
         downloadUserReportCsv( HttpMethod.POST ),
         downloadUserSummaryCsv( HttpMethod.POST ),
         downloadStatisticsLogCsv( HttpMethod.POST ),
+        downloadSessionsCsv( HttpMethod.POST ),
         clearIntruderTable( HttpMethod.POST ),
         reportCommand( HttpMethod.POST ),
         reportStatus( HttpMethod.GET ),
@@ -128,7 +139,9 @@ public class AdminServlet extends ControlledPwmServlet
         statistics( HttpMethod.GET ),
         startPwNotifyJob( HttpMethod.POST ),
         readPwNotifyStatus( HttpMethod.POST ),
-        readPwNotifyLog( HttpMethod.POST ),;
+        readPwNotifyLog( HttpMethod.POST ),
+        readLogData( HttpMethod.POST ),
+        downloadLogData( HttpMethod.POST ),;
 
         private final Collection<HttpMethod> method;
 
@@ -308,6 +321,34 @@ public class AdminServlet extends ControlledPwmServlet
         return ProcessStatus.Halt;
     }
 
+    @ActionHandler( action = "downloadSessionsCsv" )
+    private ProcessStatus downloadSessionsCsv( final PwmRequest pwmRequest )
+            throws PwmUnrecoverableException, IOException, ChaiUnavailableException, ServletException
+    {
+        final PwmApplication pwmApplication = pwmRequest.getPwmApplication();
+
+        pwmRequest.getPwmResponse().markAsDownload(
+                HttpContentType.csv,
+                pwmRequest.getPwmApplication().getConfig().readAppProperty( AppProperty.DOWNLOAD_FILENAME_SESSIONS_CSV )
+        );
+
+        final OutputStream outputStream = pwmRequest.getPwmResponse().getOutputStream();
+        try
+        {
+            pwmApplication.getSessionTrackService().outputToCsv( pwmRequest.getLocale(), pwmRequest.getConfig(), outputStream );
+        }
+        catch ( Exception e )
+        {
+            final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_UNKNOWN, e.getMessage() );
+            pwmRequest.respondWithError( errorInformation );
+        }
+        finally
+        {
+            outputStream.close();
+        }
+        return ProcessStatus.Halt;
+    }
+
     @ActionHandler( action = "clearIntruderTable" )
     private ProcessStatus processClearIntruderTable(
             final PwmRequest pwmRequest
@@ -703,7 +744,7 @@ public class AdminServlet extends ControlledPwmServlet
         {
             final DisplayElement displayElement = new DisplayElement( String.valueOf( key++ ), DisplayElement.Type.string, "Status",
                     "Password Notification Feature is not enabled.  See setting: "
-                    + PwmSetting.PW_EXPY_NOTIFY_ENABLE.toMenuLocationDebug( null, pwmRequest.getLocale() ) );
+                            + PwmSetting.PW_EXPY_NOTIFY_ENABLE.toMenuLocationDebug( null, pwmRequest.getLocale() ) );
             pwmRequest.outputJsonResult( RestResultBean.withData( new PwNotifyStatusBean( Collections.singletonList( displayElement ), false ) ) );
             return ProcessStatus.Halt;
         }
@@ -799,6 +840,168 @@ public class AdminServlet extends ControlledPwmServlet
         return ProcessStatus.Halt;
     }
 
+    public enum LogDisplayType
+    {
+        grid,
+        lines,
+    }
+
+    @ActionHandler( action = "readLogData" )
+    public ProcessStatus readLogData( final PwmRequest pwmRequest ) throws IOException, PwmUnrecoverableException
+    {
+        final LocalDBLogger localDBLogger = pwmRequest.getPwmApplication().getLocalDBLogger();
+
+        final LogDisplayType logDisplayType;
+        final LocalDBSearchQuery searchParameters;
+        {
+            final Map<String, String> inputMap = pwmRequest.readBodyAsJsonStringMap( PwmHttpRequestWrapper.Flag.BypassValidation );
+            final int eventCount = Integer.parseInt( inputMap.getOrDefault( "count", "0" ) );
+            final TimeDuration maxTimeSeconds = new TimeDuration( Integer.parseInt( inputMap.getOrDefault( "maxTime", "5" ) ), TimeUnit.SECONDS );
+            final String username = inputMap.getOrDefault( "username", "" );
+            final String text = inputMap.getOrDefault( "text", "" );
+            final PwmLogLevel logLevel = JavaHelper.readEnumFromString( PwmLogLevel.class, PwmLogLevel.TRACE, inputMap.get( "level" ) );
+            final LocalDBLogger.EventType logType = JavaHelper.readEnumFromString( LocalDBLogger.EventType.class, LocalDBLogger.EventType.Both, inputMap.get( "type" ) );
+            logDisplayType = JavaHelper.readEnumFromString( LogDisplayType.class, LogDisplayType.grid, inputMap.get( "displayType" ) );
+
+            searchParameters = LocalDBSearchQuery.builder()
+                    .minimumLevel( logLevel )
+                    .maxEvents( eventCount )
+                    .username( username )
+                    .text( text )
+                    .maxQueryTime( maxTimeSeconds )
+                    .eventType( logType )
+                    .build();
+        }
+
+        final LocalDBSearchResults searchResults = localDBLogger.readStoredEvents( searchParameters );
+
+        final LinkedHashMap<String, Object> returnData = new LinkedHashMap<>(  );
+        if ( logDisplayType == LogDisplayType.grid )
+        {
+            final ArrayList<PwmLogEvent> pwmLogEvents = new ArrayList<>();
+            while ( searchResults.hasNext() )
+            {
+                pwmLogEvents.add( searchResults.next() );
+            }
+            returnData.put( "records", pwmLogEvents );
+        }
+        else if ( logDisplayType == LogDisplayType.lines )
+        {
+            final ArrayList<String> pwmLogEvents = new ArrayList<>();
+            while ( searchResults.hasNext() )
+            {
+                pwmLogEvents.add( searchResults.next().toLogString() );
+            }
+            returnData.put( "records", pwmLogEvents );
+        }
+        returnData.put( "display", logDisplayType );
+        returnData.put( "size", searchResults.getReturnedEvents() );
+        returnData.put( "duration", searchResults.getSearchTime() );
+        pwmRequest.outputJsonResult( RestResultBean.withData( returnData ) );
+
+        return ProcessStatus.Halt;
+    }
+
+    public enum LogDownloadType
+    {
+        plain,
+        csv,
+    }
+
+    public enum LogDownloadCompression
+    {
+        none,
+        zip,
+        gzip,
+    }
+
+    @ActionHandler( action = "downloadLogData" )
+    public ProcessStatus downloadLogData( final PwmRequest pwmRequest ) throws IOException, PwmUnrecoverableException
+    {
+        final LocalDBLogger localDBLogger = pwmRequest.getPwmApplication().getLocalDBLogger();
+
+        final LogDownloadType logDownloadType = JavaHelper.readEnumFromString( LogDownloadType.class, LogDownloadType.plain, pwmRequest.readParameterAsString( "downloadType" ) );
+
+        final LocalDBSearchQuery searchParameters = LocalDBSearchQuery.builder()
+                .minimumLevel( PwmLogLevel.TRACE )
+                .eventType( LocalDBLogger.EventType.Both )
+                .build();
+
+        final LocalDBSearchResults searchResults = localDBLogger.readStoredEvents( searchParameters );
+
+        final String compressFileNameSuffix;
+        final HttpContentType compressedContentType;
+        final Writer writer;
+        {
+            final LogDownloadCompression logDownloadCompression = JavaHelper.readEnumFromString(
+                    LogDownloadCompression.class,
+                    AdminServlet.LogDownloadCompression.none,
+                    pwmRequest.readParameterAsString( "compressionType" ) );
+
+            final OutputStream compressedStream;
+
+            switch ( logDownloadCompression )
+            {
+                case zip:
+                    final ZipOutputStream zipOutputStream = new ZipOutputStream( pwmRequest.getPwmResponse().getOutputStream() );
+                    zipOutputStream.putNextEntry( new ZipEntry( "debug.txt"  ) );
+                    compressedStream = zipOutputStream;
+                    compressFileNameSuffix = ".zip";
+                    compressedContentType = HttpContentType.zip;
+                    break;
+
+                case gzip:
+                    compressedStream = new GZIPOutputStream( pwmRequest.getPwmResponse().getOutputStream() );
+                    compressFileNameSuffix = ".gz";
+                    compressedContentType = HttpContentType.gzip;
+                    break;
+
+                default:
+                    compressedStream = pwmRequest.getPwmResponse().getOutputStream();
+                    compressFileNameSuffix = "";
+                    compressedContentType = null;
+            }
+            writer = new OutputStreamWriter( compressedStream, PwmConstants.DEFAULT_CHARSET );
+        }
+        
+        switch ( logDownloadType )
+        {
+            case plain:
+            {
+                while ( searchResults.hasNext() )
+                {
+                    writer.write( searchResults.next().toLogString( true ) );
+                    writer.write( "\n" );
+                    pwmRequest.getPwmResponse().markAsDownload(
+                            compressedContentType != null ? compressedContentType : HttpContentType.plain,
+                            "debug.txt" + compressFileNameSuffix );
+                }
+            }
+            break;
+
+            case csv:
+            {
+                pwmRequest.getPwmResponse().markAsDownload(
+                        compressedContentType != null ? compressedContentType : HttpContentType.csv,
+                        "debug.csv" + compressFileNameSuffix );
+                while ( searchResults.hasNext() )
+                {
+                    writer.write( searchResults.next().toCsvLine( ) );
+                    writer.write( "\n" );
+                }
+            }
+            break;
+
+            default:
+                JavaHelper.unhandledSwitchStatement( logDownloadType );
+
+        }
+
+        writer.close();
+
+        return ProcessStatus.Halt;
+    }
+
     @Value
     public static class PwNotifyStatusBean implements Serializable
     {

+ 45 - 50
server/src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java

@@ -28,7 +28,6 @@ import password.pwm.PwmAboutProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.UserIdentity;
-import password.pwm.bean.pub.SessionStateInfoBean;
 import password.pwm.config.Configuration;
 import password.pwm.config.stored.StoredConfigurationImpl;
 import password.pwm.error.PwmUnrecoverableException;
@@ -39,6 +38,7 @@ import password.pwm.http.servlet.admin.UserDebugDataBean;
 import password.pwm.http.servlet.admin.UserDebugDataReader;
 import password.pwm.ldap.LdapDebugDataGenerator;
 import password.pwm.svc.PwmService;
+import password.pwm.svc.cluster.ClusterService;
 import password.pwm.util.LDAPPermissionCalculator;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
@@ -68,7 +68,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -76,6 +75,7 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
@@ -99,7 +99,8 @@ public class DebugItemGenerator
             LDAPPermissionItemGenerator.class,
             LocalDBDebugGenerator.class,
             SessionDataGenerator.class,
-            LdapRecentUserDebugGenerator.class
+            LdapRecentUserDebugGenerator.class,
+            ClusterInfoDebugGenerator.class
     ) );
 
     static void outputZipDebugFile(
@@ -512,14 +513,12 @@ public class DebugItemGenerator
 
             final int maxCount = Integer.parseInt( pwmRequest.getConfig().readAppProperty( AppProperty.CONFIG_MANAGER_ZIPDEBUG_MAXLOGLINES ) );
             final int maxSeconds = Integer.parseInt( pwmRequest.getConfig().readAppProperty( AppProperty.CONFIG_MANAGER_ZIPDEBUG_MAXLOGSECONDS ) );
-            final LocalDBSearchQuery searchParameters = new LocalDBSearchQuery(
-                    PwmLogLevel.TRACE,
-                    maxCount,
-                    null,
-                    null,
-                    ( maxSeconds * 1000 ),
-                    null
-            );
+            final LocalDBSearchQuery searchParameters = LocalDBSearchQuery.builder()
+                    .minimumLevel( PwmLogLevel.TRACE )
+                    .maxEvents( maxCount )
+                    .maxQueryTime( new TimeDuration( maxSeconds, TimeUnit.SECONDS ) )
+                    .build();
+
             final LocalDBSearchResults searchResults = pwmApplication.getLocalDBLogger().readStoredEvents(
                     searchParameters );
             int counter = 0;
@@ -616,46 +615,10 @@ public class DebugItemGenerator
                 final PwmApplication pwmApplication,
                 final PwmRequest pwmRequest,
                 final OutputStream outputStream
-        ) throws Exception
+        )
+                throws Exception
         {
-
-
-            final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter( outputStream );
-            {
-                final List<String> headerRow = new ArrayList<>();
-                headerRow.add( "Label" );
-                headerRow.add( "Create Time" );
-                headerRow.add( "Last Time" );
-                headerRow.add( "Idle" );
-                headerRow.add( "Source Address" );
-                headerRow.add( "Source Host" );
-                headerRow.add( "LDAP Profile" );
-                headerRow.add( "UserID" );
-                headerRow.add( "UserDN" );
-                headerRow.add( "Locale" );
-                headerRow.add( "Last URL" );
-                csvPrinter.printComment( StringUtil.join( headerRow, "," ) );
-            }
-
-            final Iterator<SessionStateInfoBean> debugInfos = pwmApplication.getSessionTrackService().getSessionInfoIterator();
-            while ( debugInfos.hasNext() )
-            {
-                final SessionStateInfoBean info = debugInfos.next();
-                final List<String> dataRow = new ArrayList<>();
-                dataRow.add( info.getLabel() );
-                dataRow.add( JavaHelper.toIsoDate( info.getCreateTime() ) );
-                dataRow.add( JavaHelper.toIsoDate( info.getLastTime() ) );
-                dataRow.add( info.getIdle() );
-                dataRow.add( info.getSrcAddress() );
-                dataRow.add( info.getSrcHost() );
-                dataRow.add( info.getLdapProfile() );
-                dataRow.add( info.getUserID() );
-                dataRow.add( info.getUserDN() );
-                dataRow.add( info.getLocale() != null ? info.getLocale().toLanguageTag() : "" );
-                dataRow.add( info.getLastUrl() );
-                csvPrinter.printRecord( dataRow );
-            }
-            csvPrinter.flush();
+            pwmApplication.getSessionTrackService().outputToCsv( pwmRequest.getLocale(), pwmRequest.getConfig(), outputStream );
         }
     }
 
@@ -693,6 +656,38 @@ public class DebugItemGenerator
         }
     }
 
+    static class ClusterInfoDebugGenerator implements Generator
+    {
+        @Override
+        public String getFilename( )
+        {
+            return "cluster-info.json";
+        }
+
+        @Override
+        public void outputItem(
+                final PwmApplication pwmApplication,
+                final PwmRequest pwmRequest,
+                final OutputStream outputStream
+        )
+                throws Exception
+        {
+            final ClusterService clusterService = pwmApplication.getClusterService();
+
+            final Map<String, Serializable> debugOutput = new LinkedHashMap<>();
+            debugOutput.put( "status", clusterService.status() );
+
+            if ( clusterService.status() == PwmService.STATUS.OPEN )
+            {
+                debugOutput.put( "isMaster", clusterService.isMaster() );
+                debugOutput.put( "nodes", new ArrayList<>( clusterService.nodes() ) );
+            }
+
+            outputStream.write( JsonUtil.serializeMap( debugOutput, JsonUtil.Flag.PrettyPrint ).getBytes( PwmConstants.DEFAULT_CHARSET ) );
+        }
+    }
+
+
     interface Generator
     {
 

+ 15 - 1
server/src/main/java/password/pwm/i18n/Admin.java

@@ -63,7 +63,21 @@ public enum Admin implements PwmDisplayBundle
     EventLog_Narrative_HelpdeskVerifyToken,
     EventLog_Narrative_HelpdeskVerifyTokenIncorrect,
     EventLog_Narrative_HelpdeskVerifyAttributes,
-    EventLog_Narrative_HelpdeskVerifyAttributesIncorrect,;
+    EventLog_Narrative_HelpdeskVerifyAttributesIncorrect,
+
+    Field_Session_UserID,
+    Field_Session_LdapProfile,
+    Field_Session_UserDN,
+    Field_Session_CreateTime,
+    Field_Session_LastTime,
+    Field_Session_Label,
+    Field_Session_Idle,
+    Field_Session_SrcAddress,
+    Field_Session_Locale,
+    Field_Session_SrcHost,
+    Field_Session_LastURL,
+    Field_Session_IntruderAttempts,;
+
 
     public static final String STATISTICS_LABEL_PREFIX = "Statistic_Label.";
     public static final String STATISTICS_DESCRIPTION_PREFIX = "Statistic_Description.";

+ 56 - 0
server/src/main/java/password/pwm/svc/sessiontrack/SessionTrackService.java

@@ -24,19 +24,27 @@ package password.pwm.svc.sessiontrack;
 
 import com.github.benmanes.caffeine.cache.Cache;
 import com.github.benmanes.caffeine.cache.Caffeine;
+import org.apache.commons.csv.CSVPrinter;
 import password.pwm.PwmApplication;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.pub.SessionStateInfoBean;
+import password.pwm.config.Configuration;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.health.HealthRecord;
 import password.pwm.http.PwmSession;
+import password.pwm.i18n.Admin;
 import password.pwm.ldap.UserInfo;
 import password.pwm.svc.PwmService;
+import password.pwm.util.LocaleHelper;
+import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -44,6 +52,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -191,6 +200,53 @@ public class SessionTrackService implements PwmService
         };
     }
 
+    public void outputToCsv(
+            final Locale locale,
+            final Configuration config,
+            final OutputStream outputStream
+            )
+            throws IOException
+    {
+        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter( outputStream );
+        {
+            final List<String> headerRow = new ArrayList<>();
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_Label, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_CreateTime, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_LastTime, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_Idle, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_SrcAddress, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_SrcHost, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_LdapProfile, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_UserID, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_UserDN, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_Locale, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_LastURL, config ) );
+            headerRow.add( LocaleHelper.getLocalizedMessage( locale, Admin.Field_Session_IntruderAttempts, config ) );
+            csvPrinter.printComment( StringUtil.join( headerRow, "," ) );
+        }
+
+        final Iterator<SessionStateInfoBean> debugInfos = getSessionInfoIterator();
+        while ( debugInfos.hasNext() )
+        {
+            final SessionStateInfoBean info = debugInfos.next();
+            final List<String> dataRow = new ArrayList<>();
+            dataRow.add( info.getLabel() );
+            dataRow.add( JavaHelper.toIsoDate( info.getCreateTime() ) );
+            dataRow.add( JavaHelper.toIsoDate( info.getLastTime() ) );
+            dataRow.add( info.getIdle() );
+            dataRow.add( info.getSrcAddress() );
+            dataRow.add( info.getSrcHost() );
+            dataRow.add( info.getLdapProfile() );
+            dataRow.add( info.getUserID() );
+            dataRow.add( info.getUserDN() );
+            dataRow.add( info.getLocale() != null ? info.getLocale().toLanguageTag() : "" );
+            dataRow.add( info.getLastUrl() );
+            dataRow.add( String.valueOf( info.getIntruderAttempts() ) );
+            csvPrinter.printRecord( dataRow );
+        }
+        csvPrinter.flush();
+    }
+
 
     private static SessionStateInfoBean infoBeanFromPwmSession( final PwmSession loopSession )
     {

+ 21 - 6
server/src/main/java/password/pwm/util/localdb/XodusLocalDB.java

@@ -465,17 +465,32 @@ public class XodusLocalDB implements LocalDBProvider
     @Override
     public Map<String, Serializable> debugInfo( )
     {
-        final Statistics statistics = environment.getStatistics();
         final Map<String, Serializable> outputStats = new LinkedHashMap<>();
-        for ( final EnvironmentStatistics.Type type : EnvironmentStatistics.Type.values() )
         {
-            final String name = type.name();
-            final StatisticsItem item = statistics.getStatisticsItem( name );
-            if ( item != null )
+            final Statistics statistics = environment.getStatistics();
+            for ( final EnvironmentStatistics.Type type : EnvironmentStatistics.Type.values() )
             {
-                outputStats.put( name, String.valueOf( item.getTotal() ) );
+                final String name = type.name();
+                final StatisticsItem item = statistics.getStatisticsItem( name );
+                if ( item != null )
+                {
+                    outputStats.put( name, String.valueOf( item.getTotal() ) );
+                }
             }
         }
+
+        try
+        {
+            for ( final LocalDB.DB db : LocalDB.DB.values() )
+            {
+                outputStats.put( "size." + db.name(), this.size( db ) );
+            }
+        }
+        catch ( LocalDBException e )
+        {
+            LOGGER.debug( "error while calculating sizes for localDB debug output: "  + e.getMessage() );
+        }
+
         return outputStats;
     }
 

+ 1 - 1
server/src/main/java/password/pwm/util/logging/LocalDBLogger.java

@@ -73,7 +73,7 @@ public class LocalDBLogger implements PwmService
     private volatile STATUS status = STATUS.NEW;
     private boolean hasShownReadError = false;
 
-    private static final String STORAGE_FORMAT_VERSION = "2";
+    private static final String STORAGE_FORMAT_VERSION = "3";
 
     public LocalDBLogger( final PwmApplication pwmApplication, final LocalDB localDB, final LocalDBLoggerSettings settings )
             throws LocalDBException

+ 12 - 52
server/src/main/java/password/pwm/util/logging/LocalDBSearchQuery.java

@@ -22,58 +22,18 @@
 
 package password.pwm.util.logging;
 
+import lombok.Builder;
+import lombok.Value;
+import password.pwm.util.java.TimeDuration;
+
+@Value
+@Builder
 public class LocalDBSearchQuery
 {
-    private final PwmLogLevel minimumLevel;
-    private final int maxEvents;
-    private final String username;
-    private final String text;
-    private final long maxQueryTime;
-    private final LocalDBLogger.EventType eventType;
-
-    public LocalDBSearchQuery(
-            final PwmLogLevel minimumLevel,
-            final int count,
-            final String username,
-            final String text,
-            final long maxQueryTime,
-            final LocalDBLogger.EventType eventType )
-    {
-        this.eventType = eventType;
-        this.maxQueryTime = maxQueryTime;
-        this.text = text;
-        this.username = username;
-        this.maxEvents = count;
-        this.minimumLevel = minimumLevel;
-    }
-
-    public PwmLogLevel getMinimumLevel( )
-    {
-        return minimumLevel;
-    }
-
-    public int getMaxEvents( )
-    {
-        return maxEvents;
-    }
-
-    public String getUsername( )
-    {
-        return username;
-    }
-
-    public String getText( )
-    {
-        return text;
-    }
-
-    public long getMaxQueryTime( )
-    {
-        return maxQueryTime;
-    }
-
-    public LocalDBLogger.EventType getEventType( )
-    {
-        return eventType;
-    }
+    private PwmLogLevel minimumLevel;
+    private int maxEvents;
+    private String username;
+    private String text;
+    private TimeDuration maxQueryTime;
+    private LocalDBLogger.EventType eventType;
 }

+ 11 - 4
server/src/main/java/password/pwm/util/logging/LocalDBSearchResults.java

@@ -76,20 +76,27 @@ public class LocalDBSearchResults implements Iterator<PwmLogEvent>
         return returnEvent;
     }
 
-    private boolean isTimedout( )
+    private boolean isTimedOut( )
     {
-        return TimeDuration.fromCurrent( startTime ).isLongerThan( new TimeDuration( searchParameters.getMaxQueryTime() ) );
+        return searchParameters.getMaxQueryTime() != null
+                && TimeDuration.fromCurrent( startTime ).isLongerThan( searchParameters.getMaxQueryTime() );
+    }
+
+    private boolean isSizeExceeded( )
+    {
+        return searchParameters.getMaxEvents() > 0
+                && eventCount >= searchParameters.getMaxEvents();
     }
 
     private PwmLogEvent readNextEvent( )
     {
-        if ( eventCount >= searchParameters.getMaxEvents() || isTimedout() )
+        if ( isSizeExceeded() || isTimedOut() )
         {
             finishTime = Instant.now();
             return null;
         }
 
-        while ( !isTimedout() && localDBIterator.hasNext() )
+        while ( !isTimedOut() && localDBIterator.hasNext() )
         {
             final String nextDbValue = localDBIterator.next();
             if ( nextDbValue == null )

+ 28 - 4
server/src/main/java/password/pwm/util/logging/PwmLogEvent.java

@@ -23,25 +23,30 @@
 package password.pwm.util.logging;
 
 import com.google.gson.annotations.SerializedName;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
+import lombok.Value;
+import org.apache.commons.csv.CSVPrinter;
+import password.pwm.PwmConstants;
 import password.pwm.bean.SessionLabel;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.Serializable;
 import java.io.StringWriter;
 import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
 
-@Getter
-@EqualsAndHashCode
+@Value
 public class PwmLogEvent implements Serializable, Comparable
 {
 
     private static final int MAX_MESSAGE_LENGTH = 50_000;
 
+    @SerializedName( "l" )
     private final PwmLogLevel level;
 
     @SerializedName( "t" )
@@ -275,6 +280,25 @@ public class PwmLogEvent implements Serializable, Comparable
         return toLogString( true );
     }
 
+    public String toCsvLine( ) throws IOException
+    {
+        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        final CSVPrinter csvPrinter = JavaHelper.makeCsvPrinter( byteArrayOutputStream );
+        final List<String> dataRow = new ArrayList<>();
+        dataRow.add( JavaHelper.toIsoDate( getDate() ) );
+        dataRow.add( getLevel().name() );
+        dataRow.add( getSource() );
+        dataRow.add( getLabel() );
+        dataRow.add( getActor() );
+        dataRow.add( getTopic() );
+        dataRow.add( getMessage() );
+        dataRow.add( getThrowable() == null ? "" : JavaHelper.readHostileExceptionMessage( getThrowable() ) );
+        csvPrinter.printRecord( dataRow );
+        csvPrinter.flush();
+        return byteArrayOutputStream.toString( PwmConstants.DEFAULT_CHARSET.name() );
+
+    }
+
     public String toLogString( final boolean includeTimeStamp )
     {
         final StringBuilder sb = new StringBuilder();

+ 1 - 0
server/src/main/resources/password/pwm/AppProperty.properties

@@ -78,6 +78,7 @@ db.connections.timeoutMs=30000
 db.connections.watchdogFrequencySeconds=30
 db.init.haltOnIndexCreateError=false
 db.schema.keyLength=128
+download.filename.sessions.csv=Sessions.csv
 download.filename.statistics.csv=Statistics.csv
 download.filename.reportSummary.csv=UserReportSummary.csv
 download.filename.reportRecords.csv=UserReportRecords.csv

+ 1 - 1
server/src/main/resources/password/pwm/config/PwmSetting.xml

@@ -1600,7 +1600,7 @@
     <setting hidden="false" key="security.cspHeader" level="2">
         <default>
             <!--<value><![CDATA[]]></value>-->
-            <value><![CDATA[default-src 'self'; object-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'self' 'unsafe-eval' 'unsafe-inline' 'nonce-%NONCE%' ; frame-src https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/; report-uri /sspr/public/command/cspReport]]></value>
+            <value><![CDATA[default-src 'self'; form-action 'self'; object-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'self' 'unsafe-eval' 'unsafe-inline' 'nonce-%NONCE%' ; frame-src https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/; report-uri /sspr/public/command/cspReport]]></value>
             <!-- 'unsafe-inline' on script-src is included for backward compatibility of CSP Level1 browsers.  CSP2 and future ignore it when the nonce is specified -->
         </default>
     </setting>

+ 8 - 0
server/src/main/resources/password/pwm/i18n/Admin.properties

@@ -116,6 +116,14 @@ Field_Audit_Narrative=Narrative
 Field_CurrentTime=Current Time
 Field_InstallTime=Install Time
 Field_StartTime=Start Time
+Field_Logs_Timestamp=Timestamp
+Field_Logs_Level=Level
+Field_Logs_Source=Source
+Field_Logs_User=User
+Field_Logs_Label=SessionID
+Field_Logs_Component=Component
+Field_Logs_Detail=Detail
+Field_Logs_Error=Error
 Button_Refresh=Refresh
 Button_Report_Start=Start
 Button_Report_Stop=Stop

+ 3 - 3
server/src/main/resources/password/pwm/i18n/PwmSetting.properties

@@ -30,7 +30,7 @@ Category_Description_AUDIT_CONFIG=Auditing
 Category_Description_AUDIT_FORWARD=Auditing
 Category_Description_AUDITING=Auditing
 Category_Description_BASIC_SSO=Basic Authentication
-Category_Description_CAPTCHA=Captcha functionality uses an implementation of reCAPTCHA to prevent non-human attacks.  If this server faces the public internet, it is strongly recommended to enable the CAPTCHA functionality.  reCAPTCHA information can be found at <a href\="http\://www.google.com/recaptcha" target\="_blank">http\://www.google.com/recaptcha/</a><br/><br/>Registration at the reCAPTCHA site provides a public and private key which you must configure here for reCAPTCHA support.
+Category_Description_CAPTCHA=Captcha functionality uses an implementation of reCAPTCHA to prevent non-human attacks.  If this server faces the public internet, it is strongly recommended to enable the CAPTCHA functionality.  reCAPTCHA information can be found at <a href\="http\://www.google.com/recaptcha" target\="_blank">http\://www.google.com/recaptcha/</a><br/><br/>Registration at the reCAPTCHA site provides a site key and secret which you must configure here for reCAPTCHA support.
 Category_Description_CAS_SSO=
 Category_Description_CHALLENGE_POLICY=Define the challenge policy users use for populating response answers.
 Category_Description_CHALLENGE=Settings that control the Challenge/Response features.  These global settings apply regardless of the challenge policy.  For profile-specific challenge settings, see Profiles --> Challenge Profiles.
@@ -737,8 +737,8 @@ Setting_Label_audit.userEvent.toAddress=User Audit Event Email Alerts
 Setting_Label_basicAuth.enable=Enable Basic Authentication
 Setting_Label_captcha.intruderAttemptTrigger=Captcha Intruder Attempt Trigger
 Setting_Label_captcha.protectedPages=CAPTCHA Protected Pages
-Setting_Label_captcha.recaptcha.privateKey=reCAPTCHA Private Key
-Setting_Label_captcha.recaptcha.publicKey=reCAPTCHA Public Key
+Setting_Label_captcha.recaptcha.privateKey=reCAPTCHA Secret
+Setting_Label_captcha.recaptcha.publicKey=reCAPTCHA Site Key
 Setting_Label_captcha.skip.cookie=Captcha Skip Cookie
 Setting_Label_captcha.skip.param=Captcha Skip Parameter Value
 Setting_Label_cas.clearPassUrl=CAS ClearPass URL

+ 1 - 1
server/src/main/webapp/META-INF/context.xml

@@ -19,6 +19,6 @@
   -->
 
 <Context tldValidation="false" unloadDelay="30000" useHttpOnly="true" mapperContextRootRedirectEnabled="true">
-  <Manager sessionIdLength="64" pathname=""/>
+  <Manager pathname=""/>
   <!--pathname param prevents session persistence across restarts -->
 </Context>

+ 9 - 1
server/src/main/webapp/WEB-INF/jsp/admin-activity.jsp

@@ -79,6 +79,14 @@
                             });
                         </script>
                     </pwm:script>
+                    <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded" id="download-sessions" name="download-sessions">
+                        <button type="submit" class="btn">
+                            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
+                            <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
+                        </button>
+                        <input type="hidden" name="processAction" value="<%=AdminServlet.AdminAction.downloadSessionsCsv%>"/>
+                        <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
+                    </form>
                 </div>
             </div>
             <input name="tabs" type="radio" id="tab-2" class="input"/>
@@ -124,7 +132,7 @@
                                     <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
                                     <pwm:display key="Button_DownloadCSV" bundle="Admin"/>
                                 </button>
-                                <input type="hidden" name="processAction" value="downloadAuditLogCsv"/>
+                                <input type="hidden" name="processAction" value="<%=AdminServlet.AdminAction.downloadAuditLogCsv%>"/>
                                 <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
                             </form>
                         </div>

+ 2 - 3
server/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp

@@ -37,8 +37,7 @@
 <%@ page import="password.pwm.http.PwmRequestAttribute" %>
 <%@ page import="password.pwm.http.bean.DisplayElement" %>
 <!DOCTYPE html>
-<%@ page language="java" session="true" isThreadSafe="true"
-         contentType="text/html" %>
+<%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <% final AppDashboardData appDashboardData = (AppDashboardData)JspUtility.getAttribute(pageContext, PwmRequestAttribute.AppDashboardData); %>
 <% final PwmRequest dashboard_pwmRequest = JspUtility.getPwmRequest(pageContext); %>
@@ -431,7 +430,7 @@
                                 <%= nodeData.getState() %>
                             </td>
                             <td>
-                                <%= JspUtility.freindlyWrite(pageContext, nodeData.isConfigMatch())%>
+                                <%= JspUtility.friendlyWrite(pageContext, nodeData.isConfigMatch())%>
                             </td>
                         </tr>
                         <% } %>

+ 4 - 69
server/src/main/webapp/WEB-INF/jsp/admin-logview-window.jsp

@@ -21,15 +21,6 @@
 --%>
 
 <%@ page import="password.pwm.http.JspUtility" %>
-<%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
-<%@ page import="password.pwm.util.java.StringUtil" %>
-<%@ page import="password.pwm.util.logging.LocalDBLogger" %>
-<%@ page import="password.pwm.util.logging.PwmLogEvent" %>
-<%@ page import="password.pwm.util.logging.PwmLogLevel" %>
-<%@ page import="java.util.Date" %>
-<%@ page import="java.time.Instant" %>
-<%@ page import="password.pwm.util.logging.LocalDBSearchQuery" %>
-<%@ page import="password.pwm.util.logging.LocalDBSearchResults" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
@@ -38,68 +29,12 @@
 <% JspUtility.setFlag(pageContext, PwmRequestFlag.NO_IDLE_TIMEOUT); %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
-<% final PwmRequest pwmRequest = JspUtility.getPwmRequest(pageContext); %>
-<% final LocalDBLogger localDBLogger = pwmRequest.getPwmApplication().getLocalDBLogger(); %>
-<% final String selectedLevel = pwmRequest.readParameterAsString("level", 255);%>
-<% final PwmLogLevel configuredLevel = pwmRequest.getConfig().readSettingAsEnum(PwmSetting.EVENTS_LOCALDB_LOG_LEVEL,PwmLogLevel.class); %>
 <body class="nihilo">
-<% if ("".equals(selectedLevel)) { %>
-<div style="text-align: center;"><pwm:display key="Display_PleaseWait"/></div>
-<pwm:script>
-    <script type="text/javascript">
-        PWM_GLOBAL['startupFunctions'].push(function(){
-            PWM_MAIN.showWaitDialog({loadFunction:function() {
-                PWM_CONFIG.openLogViewer('INFO');
-            }});
-        });
-    </script>
-</pwm:script>
-<% } else { %>
-<div style="width: 100%; text-align:center; background-color: #eeeeee" id="headerDiv">
-    <span class="timestamp"><%=Instant.now().toString()%></span>
-    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-    <select name="level" style="width: auto;" id="select-level">
-        <% for (final PwmLogLevel level : PwmLogLevel.values()) { %>
-        <% final boolean selected = level.toString().equals(selectedLevel); %>
-        <% final boolean disabled = level.compareTo(configuredLevel) < 0; %>
-        <option value="<%=level%>" <%=selected ?" selected": ""%><%=disabled ? " disabled" : ""%>  ><%=level%></option>
-        <% } %>
-    </select>
-    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-    <button class="btn" id="button-refresh">
-        <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-refresh"></span></pwm:if>
-        <pwm:display key="Button_Refresh" bundle="Admin"/>
-    </button>
+<div>
+    <jsp:include page="/WEB-INF/jsp/fragment/log-viewer.jsp"/>
 </div>
-<%
-    PwmLogLevel logLevel; try { logLevel=PwmLogLevel.valueOf(selectedLevel); } catch (Exception e) { logLevel=PwmLogLevel.INFO; }
-    final LocalDBLogger.EventType logType = LocalDBLogger.EventType.Both;
-    final int eventCount = 1000;
-    final long maxTime = 10000;
-    final LocalDBSearchQuery searchParameters = new LocalDBSearchQuery(logLevel, eventCount, "", "", maxTime, logType);
-    final LocalDBSearchResults searchResults = localDBLogger.readStoredEvents(searchParameters);
-%>
-<pre><% while (searchResults.hasNext()) { %>
-    <% final PwmLogEvent logEvent = searchResults.next(); %>
-    <span class="timestamp"><%=logEvent.getDate().toString()%></span>, <%=StringUtil.escapeHtml(logEvent.toLogString(false)) %><%="\n"%>
-    <% } %></pre>
-<% } %>
-<%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 <pwm:script-ref url="/public/resources/js/configmanager.js"/>
-<pwm:script>
-    <script type="text/javascript">
-        PWM_GLOBAL['startupFunctions'].push(function(){
-            var refreshFunction = function(){
-                var levelSelectElement = PWM_MAIN.getObject('select-level');
-                var level=levelSelectElement.options[levelSelectElement.selectedIndex].value;
-                PWM_MAIN.showWaitDialog({loadFunction:function(){PWM_CONFIG.openLogViewer(level)}});
-            };
-            PWM_MAIN.addEventHandler('button-refresh','click',function(){
-                refreshFunction();
-            });
-            document.title = "Log Viewer";
-        });
-    </script>
-</pwm:script>
+<pwm:script-ref url="/public/resources/js/admin.js"/>
+<%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>
 </body>
 </html>

+ 74 - 249
server/src/main/webapp/WEB-INF/jsp/admin-logview.jsp

@@ -21,280 +21,105 @@
 --%>
 
 <%@ page import="password.pwm.i18n.Admin" %>
-<%@ page import="password.pwm.util.java.JavaHelper" %>
-<%@ page import="password.pwm.util.java.JsonUtil" %>
-<%@ page import="password.pwm.util.java.StringUtil" %>
 <%@ page import="password.pwm.util.logging.LocalDBLogger" %>
-<%@ page import="password.pwm.util.logging.PwmLogEvent" %>
-<%@ page import="password.pwm.util.logging.PwmLogLevel" %>
-<%@ page import="java.util.LinkedHashMap" %>
-<%@ page import="java.util.Map" %>
-<%@ page import="password.pwm.util.logging.LocalDBSearchQuery" %>
-<%@ page import="password.pwm.util.logging.LocalDBSearchResults" %>
-<%@ page import="password.pwm.util.java.PwmNumberFormat" %>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true"
          contentType="text/html" %>
 <%@ taglib uri="pwm" prefix="pwm" %>
 <html lang="<pwm:value name="<%=PwmValue.localeCode%>"/>" dir="<pwm:value name="<%=PwmValue.localeDir%>"/>">
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
-<% final PwmRequest pwmRequest = PwmRequest.forRequest(request,response); %>
-<% final PwmNumberFormat numberFormat = PwmNumberFormat.forLocale(pwmRequest.getLocale()); %>
-<% final LocalDBLogger localDBLogger = pwmRequest.getPwmApplication().getLocalDBLogger(); %>
 <body class="nihilo">
 <div id="wrapper">
     <% final String PageName = JspUtility.localizedString(pageContext,"Title_LogViewer",Admin.class);%>
     <jsp:include page="/WEB-INF/jsp/fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="<%=PageName%>"/>
     </jsp:include>
-    <div id="centerbody" style="width: 96%; margin-left: 2%; margin-right: 2%; background: white">
+    <div id="centerbody" class="wide">
         <h1 id="page-content-title"><pwm:display key="Title_LogViewer" bundle="Admin" displayIfMissing="true"/></h1>
         <%@ include file="fragment/admin-nav.jsp" %>
-        <form action="<pwm:current-url/>" method="post" enctype="application/x-www-form-urlencoded"
-              name="searchForm" id="searchForm" class="pwm-form">
-            <input type="hidden" name="pwmFormID" value="<pwm:FormID/>"/>
-            <table style="">
-                <tr style="width:0">
-                    <td class="key" class="noborder">
-                        <label for="level">Level</label>
-                        <br/>
-                        <%--
-                        <% final String selectedLevel = pwmRequest.readParameterAsString("level", "INFO");%>
-                        <select id="level" name="level" style="width: auto;">
-                            <option value="FATAL" <%= "FATAL".equals(selectedLevel) ? "selected=\"selected\"" : "" %>>FATAL
-                            </option>
-                            <option value="ERROR" <%= "ERROR".equals(selectedLevel) ? "selected=\"selected\"" : "" %>>ERROR
-                            </option>
-                            <option value="WARN" <%= "WARN".equals(selectedLevel) ? "selected=\"selected\"" : "" %>>WARN
-                            </option>
-                            <option value="INFO" <%= "INFO".equals(selectedLevel) ? "selected=\"selected\"" : "" %>>INFO
-                            </option>
-                            <option value="DEBUG" <%= "DEBUG".equals(selectedLevel) ? "selected=\"selected\"" : "" %>>DEBUG
-                            </option>
-                            <option value="TRACE" <%= "TRACE".equals(selectedLevel) ? "selected=\"selected\"" : "" %>>TRACE
-                            </option>
-                        </select>
-                        --%>
-                        <% final String selectedLevel = pwmRequest.readParameterAsString("level", "INFO");%>
-                        <% final PwmLogLevel configuredLevel = pwmRequest.getConfig().readSettingAsEnum(PwmSetting.EVENTS_LOCALDB_LOG_LEVEL,PwmLogLevel.class); %>
-                        <select name="level" style="width: auto;" id="select-level">
-                            <% for (final PwmLogLevel level : PwmLogLevel.values()) { %>
-                            <% final boolean optionSelected = level.toString().equals(selectedLevel); %>
-                            <% final boolean disabled = level.compareTo(configuredLevel) < 0; %>
-                            <option value="<%=level%>" <%=optionSelected ?" selected": ""%><%=disabled ? " disabled" : ""%>  ><%=level%></option>
-                            <% } %>
-                        </select>
+        <jsp:include page="/WEB-INF/jsp/fragment/log-viewer.jsp"/>
 
-                    </td>
-                    <td class="key" class="noborder">
-                        <label for="type">Type</label>
-                        <br/>
-                        <% final String selectedType = pwmRequest.readParameterAsString("type", "Both");%>
-                        <select id="type" name="type" style="width:auto">
-                            <option value="User" <%= "User".equals(selectedType) ? "selected=\"selected\"" : "" %>>User</option>
-                            <option value="System" <%= "System".equals(selectedType) ? "selected=\"selected\"" : "" %>>System
-                            </option>
-                            <option value="Both" <%= "Both".equals(selectedType) ? "selected=\"selected\"" : "" %>>Both</option>
-                        </select>
-                    </td>
-                    <td class="key" class="noborder">
-                        Username
-                        <br/>
-                        <input name="username" type="text"
-                               value="<%=pwmRequest.readParameterAsString("username")%>"/>
-                    </td>
-                    <td class="key" class="noborder">
-                        Containing text
-                        <br/>
-                        <input name="text" type="text"
-                               value="<%=pwmRequest.readParameterAsString("text")%>"/>
-                    </td>
-                    <td class="key" class="noborder">
-                        Max Count
-                        <br/>
-                        <% final String selectedCount = pwmRequest.readParameterAsString("count");%>
-                        <select name="count" style="width:auto">
-                            <option value="100" <%= "100".equals(selectedCount) ? "selected=\"selected\"" : "" %>>100</option>
-                            <option value="500" <%= "500".equals(selectedCount) ? "selected=\"selected\"" : "" %>>500</option>
-                            <option value="2000" <%= "2000".equals(selectedCount) ? "selected=\"selected\"" : "" %>>2000
-                            </option>
-                            <option value="5000" <%= "5000".equals(selectedCount) ? "selected=\"selected\"" : "" %>>5000
-                            </option>
-                            <option value="10000" <%= "10000".equals(selectedCount) ? "selected=\"selected\"" : "" %>>10000
-                            </option>
-                            <option value="100000" <%= "100000".equals(selectedCount) ? "selected=\"selected\"" : "" %>>100000
-                            </option>
-                        </select>
-                    </td>
-                    <td class="key" class="noborder">
-                        Max Time
-                        <br/>
-                        <% final String selectedTime = pwmRequest.readParameterAsString("maxTime");%>
-                        <select name="maxTime" style="width: auto">
-                            <option value="10000" <%= "10000".equals(selectedTime) ? "selected=\"selected\"" : "" %>>10 seconds
-                            </option>
-                            <option value="30000" <%= "30000".equals(selectedTime) ? "selected=\"selected\"" : "" %>>30 seconds
-                            </option>
-                            <option value="60000" <%= "60000".equals(selectedTime) ? "selected=\"selected\"" : "" %>>1 minute
-                            </option>
-                            <option value="120000" <%= "120000".equals(selectedTime) ? "selected=\"selected\"" : "" %>>2 minutes
-                            </option>
-                        </select>
-                    </td>
-                    <td class="key" class="noborder">
-                        <label for="displayText">Display</label>
-                        <br/>
-                        <% final String displayText = pwmRequest.readParameterAsString("displayText", "Both");%>
-                        <select id="displayText" name="displayText" style="width: auto">
-                            <option value="false" <%= "false".equals(displayText) ? "selected=\"selected\"" : "" %>>Table</option>
-                            <option value="true" <%= "true".equals(displayText) ? "selected=\"selected\"" : "" %>>Text</option>
-                        </select>
-                    </td>
-                    <td class="key noborder" style="vertical-align: middle">
-                        <button type="submit" name="submit_button" id="submit_button" class="btn">
-                            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-search"></span></pwm:if>
-                            Search
-                        </button>
-                    </td>
-                </tr>
-            </table>
-        </form>
-        <br/>
-        <%
-            PwmLogLevel logLevel = PwmLogLevel.INFO;
-            LocalDBLogger.EventType logType = LocalDBLogger.EventType.Both;
-            int eventCount = 100;
-            long maxTime = 10000;
-            final String username = pwmRequest.readParameterAsString("username");
-            final String text = pwmRequest.readParameterAsString("text");
-            final boolean displayAsText = Boolean.parseBoolean(displayText);
-            try {
-                logLevel = PwmLogLevel.valueOf(PwmRequest.forRequest(request, response).readParameterAsString("level"));
-            } catch (Exception e) {
-            }
-            try {
-                logType = LocalDBLogger.EventType.valueOf(PwmRequest.forRequest(request, response).readParameterAsString("type"));
-            } catch (Exception e) {
-            }
-            try {
-                eventCount = Integer.parseInt(PwmRequest.forRequest(request, response).readParameterAsString("count"));
-            } catch (Exception e) {
-            }
-            try {
-                maxTime = Long.parseLong(PwmRequest.forRequest(request, response).readParameterAsString("maxTime"));
-            } catch (Exception e) {
-            }
-
-            LocalDBSearchResults searchResults = null;
-            try {
-                final LocalDBSearchQuery searchParameters = new LocalDBSearchQuery(logLevel, eventCount, username, text, maxTime, logType);
-                searchResults = localDBLogger.readStoredEvents(searchParameters);
-            } catch (Exception e) {
-                out.write("<p>Unexpected error while searching: " + e.getMessage()+"</p>");
-                e.printStackTrace();
-            }
-        %>
-        <% if (!searchResults.hasNext()) { %>
-        <p style="text-align:center;">No events matched your search. Please refine your search query and try again.</p>
-        <% } else { %>
-        <% if (displayAsText) { %>
-        <hr/>
-        <pre><% while (searchResults.hasNext()) { final PwmLogEvent event = searchResults.next(); %><%=StringUtil.escapeHtml(event.toLogString()) %><%="\n"%><% } %></pre>
-        <hr/>
-        <% } else {%>
-        <pwm:script>
-            <script type="text/javascript">
-                var data = [];
-                <%
-                    while (searchResults.hasNext()) {
-                        final PwmLogEvent event = searchResults.next();
-                        try {
-                            final Map<String, Object> rowData = new LinkedHashMap<String, Object>();
-                            rowData.put("timestamp", event.getDate());
-                            rowData.put("level", event.getLevel().toString());
-                            rowData.put("src", event.getSource());
-                            rowData.put("user", StringUtil.escapeJS(event.getActor()));
-                            rowData.put("component",StringUtil.escapeJS(event.getTopTopic()));
-                            rowData.put("detail",StringUtil.escapeJS(event.getMessage()));
-                %>
-                data.push(<%=JsonUtil.serializeMap(rowData)%>);
-                <%
-                        } catch (IllegalStateException e) { /* ignore */ }
-                    }
-                %>
-            </script>
-        </pwm:script>
-        <div class="WaitDialogBlank" id="WaitDialogBlank">&nbsp;</div>
-        <div id="dgrid">
+        <div id="wrapper-logInteraction">
+            <form action="<pwm:context/>/private/admin" method="post" enctype="application/x-www-form-urlencoded"
+                  name="form-downloadLog" id="CC" class="">
+                <input type="hidden" name="<%=PwmConstants.PARAM_FORM_ID%>" value="<pwm:FormID/>"/>
+                <input type="hidden" name="<%=PwmConstants.PARAM_ACTION_REQUEST%>" value="<%=AdminServlet.AdminAction.downloadLogData%>"/>
+                <table style="max-width: 350px; margin-right: auto; margin-left: auto">
+                    <tr class="noborder">
+                        <td class="noborder">
+                            <label for="downloadType">Type</label>
+                        </td>
+                        <td class="noborder">
+                            <label for="compressionType">Compression</label>
+                        </td>
+                        <td class="noborder">
+                        </td>
+                    </tr>
+                    <tr class="noborder">
+                        <td>
+                            <select id="downloadType" name="downloadType">
+                                <option value="plain">Plain</option>
+                                <option value="csv">CSV</option>
+                            </select>
+                        </td>
+                        <td>
+                            <select id="compressionType" name="compressionType">
+                                <option value="none">None</option>
+                                <option value="zip">ZIP</option>
+                                <option value="gzip">GZip</option>
+                            </select>
+                        </td>
+                        <td>
+                            <button type="submit" name="button-download" id="button-download" class="btn">
+                                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-download"></span></pwm:if>
+                                Download
+                            </button>
+                        </td>
+                    </tr>
+                </table>
+            </form>
+        </div>
+        <div style="text-align: center;">
+            <button type="button" class="btn" id="button-openLogViewerButton">
+                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-list-alt"></span></pwm:if>
+                Pop-Out Log Viewer
+                &nbsp;
+                <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-external-link"></span></pwm:if>
+            </button>
         </div>
-        <pwm:script>
-            <script>
-                function startupPage() {
-                    var headers = {
-                        "timestamp":"Time",
-                        "level":"Level",
-                        "src":"Source Address",
-                        "user":"User",
-                        "component":"Component",
-                        "detail":"Detail"
-                    };
-                    require(["dojo","dojo/_base/declare", "dgrid/Grid", "dgrid/Keyboard", "dgrid/Selection", "dgrid/extensions/ColumnResizer", "dgrid/extensions/ColumnReorder", "dgrid/extensions/ColumnHider", "dojo/domReady!"],
-                            function(dojo, declare, Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider){
-                                var columnHeaders = headers;
-
-                                // Create a new constructor by mixing in the components
-                                var CustomGrid = declare([ Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider ]);
-
-                                // Now, create an instance of our custom grid which
-                                // have the features we added!
-                                var grid = new CustomGrid({
-                                    columns: columnHeaders
-                                }, "dgrid");
-                                grid.set("sort","timestamp");
-                                grid.renderArray(data);
-                                PWM_MAIN.getObject('WaitDialogBlank').style.display = 'none';
-                            });
-                };
-            </script>
-        </pwm:script>
-        <style nonce="<pwm:value name="<%=PwmValue.cspNonce%>"/>" scoped="scoped">
-            .dgrid { height: auto; }
-            .dgrid .dgrid-scroller { position: relative;  overflow: visible; }
-            .dgrid-column-timestamp {width: 80px;}
-            .dgrid-column-level {width: 50px;}
-            .dgrid-column-src {width: 80px;}
-            .dgrid-column-user {width: 70px;}
-            .dgrid-column-component {width: 100px;}
-        </style>
         <pwm:script>
             <script type="text/javascript">
-                PWM_GLOBAL['startupFunctions'].push(function(){
-                    require(["dojo/parser","dijit/form/NumberSpinner","dojo/domReady!"],function(dojoParser){
-                        dojoParser.parse();
-                        startupPage();
+                PWM_GLOBAL['startupFunctions'].push(function() {
+                    PWM_MAIN.addEventHandler('button-openLogViewerButton', 'click', function () {
+                        var windowUrl = PWM_GLOBAL['url-context'] + '/private/admin/administration?processAction=viewLogWindow';
+                        var windowName = 'logViewer';
+                        PWM_MAIN.newWindowOpen(windowUrl, windowName);
                     });
                 });
             </script>
         </pwm:script>
-        <% } %>
-        <p style="text-align:center;">Matched <%= numberFormat.format(searchResults.getReturnedEvents()) %> entries after
-            searching <%= numberFormat.format(searchResults.getReturnedEvents()) %> log entries
-            in <%= searchResults.getSearchTime().asCompactString() %>.</p>
-        <% } %>
-        <div class="footnote">
-        <p>
-            This page shows the debug log
-            history. This history is stored in the LocalDB cache of the debug log. For a
-            permanent log
-            record of events, see the application server's log file.  The LocalDB contains <%=numberFormat.format(localDBLogger.getStoredEventCount())%> events. The oldest event is from
-            <span class="timestamp"><%= JavaHelper.toIsoDate(ContextManager.getPwmApplication(session).getLocalDBLogger().getTailDate()) %></span>.
-        </p><p>
-        The LocalDB is configured to capture events of level
-        <b><%=ContextManager.getPwmApplication(session).getConfig().readSettingAsString(PwmSetting.EVENTS_LOCALDB_LOG_LEVEL)%>
-        </b> and higher.
+
+
+        <div style="max-width: 100%; text-align: center; margin-left: auto; margin-right: auto">
+            <div style="max-width: 600px;  text-align: center;  margin-left: auto; margin-right: auto">
+                <div class="footnote">
+                    <% LocalDBLogger localDBLogger = JspUtility.getPwmRequest(pageContext).getPwmApplication().getLocalDBLogger();%>
+                    <p>
+                        This page shows the debug log
+                        history. This records shown here are stored in the LocalDB.  The LocalDB contains <%=JspUtility.friendlyWrite( pageContext, localDBLogger.getStoredEventCount() )%> events. The oldest event stored in the LocalDB is from
+                        <span class="timestamp"><%= JspUtility.friendlyWrite( pageContext, ContextManager.getPwmApplication(session).getLocalDBLogger().getTailDate() ) %></span>.
+                    </p>
+                    <p>
+                        The LocalDB is configured to capture events of level
+                        <i><%=ContextManager.getPwmApplication(session).getConfig().readSettingAsString(PwmSetting.EVENTS_LOCALDB_LOG_LEVEL)%>
+                        </i> and higher.
+                    </p>
+                </div>
+            </div>
         </div>
-    </p>
     </div>
+
+
     <div class="push"></div>
 </div>
 <%@ include file="/WEB-INF/jsp/fragment/footer.jsp" %>

+ 1 - 1
server/src/main/webapp/WEB-INF/jsp/admin-tokenlookup.jsp

@@ -123,7 +123,7 @@
                     Destination(s)
                 </td>
                 <td>
-                    <%=JspUtility.freindlyWrite(pageContext, JsonUtil.serialize( tokenPayload.getDestination() ) )%>
+                    <%=JspUtility.friendlyWrite(pageContext, JsonUtil.serialize( tokenPayload.getDestination() ) )%>
                 </td>
             </tr>
             <tr>

+ 43 - 43
server/src/main/webapp/WEB-INF/jsp/admin-user-debug.jsp

@@ -77,19 +77,19 @@
             </tr>
             <tr>
                 <td class="key">UserDN</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getUserDN())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getUserDN())%></td>
             </tr>
             <tr>
                 <td class="key">Ldap Profile</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getLdapProfile())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getLdapProfile())%></td>
             </tr>
             <tr>
                 <td class="key">Username</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getUserID())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getUserID())%></td>
             </tr>
             <tr>
                 <td class="key"><%=PwmConstants.PWM_APP_NAME%> GUID</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getUserGUID())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getUserGUID())%></td>
             </tr>
         </table>
         <br/>
@@ -100,7 +100,7 @@
             <tr>
                 <td class="key">Last Login Time</td>
                 <td>
-                    <%=JspUtility.freindlyWrite(pageContext, userInfo.getPasswordLastModifiedTime())%>
+                    <%=JspUtility.friendlyWrite(pageContext, userInfo.getPasswordLastModifiedTime())%>
                     <% if ( userInfo.getPasswordLastModifiedTime() != null ) { %>
                     ( <%=TimeDuration.fromCurrent(userInfo.getPasswordLastModifiedTime()).asCompactString()%> )
                     <% } %>
@@ -109,7 +109,7 @@
             <tr>
                 <td class="key">Account Expiration Time</td>
                 <td>
-                    <%=JspUtility.freindlyWrite(pageContext, userInfo.getAccountExpirationTime())%>
+                    <%=JspUtility.friendlyWrite(pageContext, userInfo.getAccountExpirationTime())%>
                     <% if ( userInfo.getAccountExpirationTime() != null ) { %>
                     ( <%=TimeDuration.fromCurrent(userInfo.getAccountExpirationTime()).asCompactString()%> )
                     <% } %>
@@ -118,7 +118,7 @@
             <tr>
                 <td class="key">Password Expiration</td>
                 <td>
-                    <%=JspUtility.freindlyWrite(pageContext, userInfo.getPasswordExpirationTime())%>
+                    <%=JspUtility.friendlyWrite(pageContext, userInfo.getPasswordExpirationTime())%>
                     <% if ( userInfo.getPasswordExpirationTime() != null ) { %>
                     ( <%=TimeDuration.fromCurrent(userInfo.getPasswordExpirationTime()).asCompactString()%> )
                     <% } %>
@@ -127,7 +127,7 @@
             <tr>
                 <td class="key">Password Last Modified</td>
                 <td>
-                    <%=JspUtility.freindlyWrite(pageContext, userInfo.getPasswordLastModifiedTime())%>
+                    <%=JspUtility.friendlyWrite(pageContext, userInfo.getPasswordLastModifiedTime())%>
                     <% if ( userInfo.getPasswordLastModifiedTime() != null ) { %>
                     ( <%=TimeDuration.fromCurrent(userInfo.getPasswordLastModifiedTime()).asCompactString()%> )
                     <% } %>
@@ -135,38 +135,38 @@
             </tr>
             <tr>
                 <td class="key">Email Address 1</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getUserEmailAddress())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getUserEmailAddress())%></td>
             </tr>
             <tr>
                 <td class="key">Email Address 2</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getUserEmailAddress2())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getUserEmailAddress2())%></td>
             </tr>
             <tr>
                 <td class="key">Email Address 3</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getUserEmailAddress3())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getUserEmailAddress3())%></td>
             </tr>
             <tr>
                 <td class="key">Phone Number 1</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userDebugDataBean.getUserInfo().getUserSmsNumber())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userDebugDataBean.getUserInfo().getUserSmsNumber())%></td>
             </tr>
             <tr>
                 <td class="key">Phone Number 2</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userDebugDataBean.getUserInfo().getUserSmsNumber2())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userDebugDataBean.getUserInfo().getUserSmsNumber2())%></td>
             </tr>
             <tr>
                 <td class="key">Phone Number 3</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userDebugDataBean.getUserInfo().getUserSmsNumber3())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userDebugDataBean.getUserInfo().getUserSmsNumber3())%></td>
             </tr>
             <tr>
                 <td class="key">Username</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, userInfo.getUserID())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, userInfo.getUserID())%></td>
             </tr>
             <tr>
                 <td class="key">
                     <pwm:display key="Field_PasswordExpired"/>
                 </td>
                 <td id="PasswordExpired">
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.getPasswordStatus().isExpired()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.getPasswordStatus().isExpired()) %>
                 </td>
             </tr>
             <tr>
@@ -174,7 +174,7 @@
                     <pwm:display key="Field_PasswordPreExpired"/>
                 </td>
                 <td id="PasswordPreExpired">
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.getPasswordStatus().isPreExpired()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.getPasswordStatus().isPreExpired()) %>
                 </td>
             </tr>
             <tr>
@@ -182,7 +182,7 @@
                     <pwm:display key="Field_PasswordWithinWarningPeriod"/>
                 </td>
                 <td id="PasswordWithinWarningPeriod">
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.getPasswordStatus().isWarnPeriod()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.getPasswordStatus().isWarnPeriod()) %>
                 </td>
             </tr>
             <tr>
@@ -190,7 +190,7 @@
                     <pwm:display key="Field_PasswordViolatesPolicy"/>
                 </td>
                 <td id="PasswordViolatesPolicy">
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.getPasswordStatus().isViolatesPolicy()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.getPasswordStatus().isViolatesPolicy()) %>
                 </td>
             </tr>
             <tr>
@@ -198,7 +198,7 @@
                     Password Readable From LDAP
                 </td>
                 <td>
-                    <%= JspUtility.freindlyWrite(pageContext, userDebugDataBean.isPasswordReadable()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userDebugDataBean.isPasswordReadable()) %>
                 </td>
             </tr>
             <tr>
@@ -206,7 +206,7 @@
                     Requires New Password
                 </td>
                 <td>
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.isRequiresNewPassword()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.isRequiresNewPassword()) %>
                 </td>
             </tr>
             <tr>
@@ -214,7 +214,7 @@
                     Requires Response Setup
                 </td>
                 <td>
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.isRequiresResponseConfig()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.isRequiresResponseConfig()) %>
                 </td>
             </tr>
             <tr>
@@ -222,7 +222,7 @@
                     Requires OTP Setup
                 </td>
                 <td>
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.isRequiresOtpConfig()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.isRequiresOtpConfig()) %>
                 </td>
             </tr>
             <tr>
@@ -230,7 +230,7 @@
                     Requires Profile Update
                 </td>
                 <td>
-                    <%= JspUtility.freindlyWrite(pageContext, userInfo.isRequiresUpdateProfile()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userInfo.isRequiresUpdateProfile()) %>
                 </td>
             </tr>
             <tr>
@@ -238,7 +238,7 @@
                     Password is Within Minimum Lifetime
                 </td>
                 <td>
-                    <%= JspUtility.freindlyWrite(pageContext, userDebugDataBean.isPasswordWithinMinimumLifetime()) %>
+                    <%= JspUtility.friendlyWrite(pageContext, userDebugDataBean.isPasswordWithinMinimumLifetime()) %>
                 </td>
             </tr>
         </table>
@@ -258,7 +258,7 @@
                         <% for (final ProfileType profileType : userDebugDataBean.getProfiles().keySet()) { %>
                         <tr>
                             <td><%=profileType%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, userDebugDataBean.getProfiles().get(profileType))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, userDebugDataBean.getProfiles().get(profileType))%></td>
                         </tr>
                         <% } %>
                     </table>
@@ -275,7 +275,7 @@
                         <% for (final Permission permission : userDebugDataBean.getPermissions().keySet()) { %>
                         <tr>
                             <td><%=permission%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, userDebugDataBean.getPermissions().get(permission))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, userDebugDataBean.getPermissions().get(permission))%></td>
                         </tr>
                         <% } %>
                     </table>
@@ -304,24 +304,24 @@
                         <tr>
                             <td>ID</td>
                             <td><pwm:display key="<%=Display.Value_NotApplicable.toString()%>"/></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, configPolicy.getIdentifier())%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, ldapPolicy.getIdentifier())%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, userPolicy.getIdentifier())%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, configPolicy.getIdentifier())%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, ldapPolicy.getIdentifier())%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, userPolicy.getIdentifier())%></td>
                         </tr>
                         <tr>
                             <td>Display Name</td>
                             <td><pwm:display key="<%=Display.Value_NotApplicable.toString()%>"/></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, configPolicy.getDisplayName(JspUtility.locale(request)))%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, ldapPolicy.getDisplayName(JspUtility.locale(request)))%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, userPolicy.getDisplayName(JspUtility.locale(request)))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, configPolicy.getDisplayName(JspUtility.locale(request)))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, ldapPolicy.getDisplayName(JspUtility.locale(request)))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, userPolicy.getDisplayName(JspUtility.locale(request)))%></td>
                         </tr>
                         <% for (final PwmPasswordRule rule : PwmPasswordRule.values()) { %>
                         <tr>
                             <td><span title="<%=rule.getKey()%>"><%=rule.getLabel(JspUtility.locale(request), JspUtility.getPwmRequest(pageContext).getConfig())%></span></td>
                             <td><%=rule.getRuleType()%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, configPolicy.getValue(rule))%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, ldapPolicy.getValue(rule))%></td>
-                            <td><%=JspUtility.freindlyWrite(pageContext, userPolicy.getValue(rule))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, configPolicy.getValue(rule))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, ldapPolicy.getValue(rule))%></td>
+                            <td><%=JspUtility.friendlyWrite(pageContext, userPolicy.getValue(rule))%></td>
                         </tr>
                         <% } %>
                     </table>
@@ -359,7 +359,7 @@
             </tr>
             <tr>
                 <td>Storage Timestamp</td>
-                <td><%=JspUtility.freindlyWrite(pageContext, responseInfoBean.getTimestamp())%></td>
+                <td><%=JspUtility.friendlyWrite(pageContext, responseInfoBean.getTimestamp())%></td>
             </tr>
             <tr>
                 <td>Answered Challenges</td>
@@ -382,10 +382,10 @@
                                 <%= challenge.isAdminDefined() ? "Admin Defined" : "User Defined" %>
                             </td>
                             <td>
-                                <%= JspUtility.freindlyWrite(pageContext, challenge.isRequired())%>
+                                <%= JspUtility.friendlyWrite(pageContext, challenge.isRequired())%>
                             </td>
                             <td>
-                                <%= JspUtility.freindlyWrite(pageContext, challenge.getChallengeText())%>
+                                <%= JspUtility.friendlyWrite(pageContext, challenge.getChallengeText())%>
                             </td>
                         </tr>
                         <% } %>
@@ -411,7 +411,7 @@
                 <% } else { %>
                 <td>
                     <% for (final Challenge challenge : helpdeskCrMap.keySet()) { %>
-                    <%= JspUtility.freindlyWrite(pageContext, challenge.getChallengeText())%><br/>
+                    <%= JspUtility.friendlyWrite(pageContext, challenge.getChallengeText())%><br/>
                     <% } %>
                 </td>
                 <% } %>
@@ -461,10 +461,10 @@
                                 <%= challenge.isAdminDefined() ? "Admin Defined" : "User Defined" %>
                             </td>
                             <td>
-                                <%= JspUtility.freindlyWrite(pageContext, challenge.getChallengeText())%>
+                                <%= JspUtility.friendlyWrite(pageContext, challenge.getChallengeText())%>
                             </td>
                             <td>
-                                <%= JspUtility.freindlyWrite(pageContext, challenge.isRequired())%>
+                                <%= JspUtility.friendlyWrite(pageContext, challenge.isRequired())%>
                             </td>
                             <td>
                                 <%= challenge.getMinLength() %>
@@ -473,7 +473,7 @@
                                 <%= challenge.getMaxLength() %>
                             </td>
                             <td>
-                                <%= JspUtility.freindlyWrite(pageContext, challenge.isEnforceWordlist())%>
+                                <%= JspUtility.friendlyWrite(pageContext, challenge.isEnforceWordlist())%>
                             </td>
                             <td>
                                 <%= challenge.getMaxQuestionCharsInAnswer() %>

+ 1 - 3
server/src/main/webapp/WEB-INF/jsp/fragment/header-menu.jsp

@@ -66,11 +66,9 @@
                     <pwm:display key="Title_Admin"/>
                 </a>
                 <pwm:if test="<%=PwmIfTest.forcedPageView%>" negate="true">
-                    <a class="header-warning-button" id="header_openLogViewerButton">
+                    <a class="header-warning-button" id="header_openLogViewerButton" href="<pwm:url url="/private/admin/logs"/>">
                         <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-list-alt"></span></pwm:if>
                         <pwm:display key="MenuItem_ViewLog" bundle="Config"/>
-                        &nbsp;
-                        <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-external-link"></span></pwm:if>
                     </a>
                 </pwm:if>
             </pwm:if>

+ 134 - 0
server/src/main/webapp/WEB-INF/jsp/fragment/log-viewer.jsp

@@ -0,0 +1,134 @@
+<%--
+ ~ Password Management Servlets (PWM)
+ ~ http://www.pwm-project.org
+ ~
+ ~ Copyright (c) 2006-2009 Novell, Inc.
+ ~ Copyright (c) 2009-2018 The PWM Project
+ ~
+ ~ This program is free software; you can redistribute it and/or modify
+ ~ it under the terms of the GNU General Public License as published by
+ ~ the Free Software Foundation; either version 2 of the License, or
+ ~ (at your option) any later version.
+ ~
+ ~ This program is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ~ GNU General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU General Public License
+ ~ along with this program; if not, write to the Free Software
+ ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+--%>
+
+<%@ page import="password.pwm.config.PwmSetting" %>
+<%@ page import="password.pwm.http.JspUtility" %>
+<%@ page import="password.pwm.http.tag.conditional.PwmIfTest" %>
+<%@ page import="password.pwm.util.logging.PwmLogLevel" %>
+
+<%@ taglib uri="pwm" prefix="pwm" %>
+
+<div id="logViewer-wrapper">
+    <div>
+        <form action="<pwm:context/>/private/admin" method="post" enctype="application/x-www-form-urlencoded"
+              name="form-loadLog" id="form-loadLog" class="">
+            <table style="max-width: 700px">
+                <tr class="noborder">
+                    <td class="noborder">
+                        <label for="level">Level</label>
+                    </td>
+                    <td class="noborder">
+                        <label for="type">Type</label>
+                    </td>
+                    <td class="noborder">
+                        <label for="username">Username</label>
+                    </td>
+                    <td class="noborder">
+                        <label for="text">Containing text</label>
+                    </td>
+                    <td class="noborder">
+                        <label for="count">Max Rows</label>
+                    </td>
+                    <td class="noborder">
+                        <label for="maxTime">Max Time (Seconds)</label>
+                    </td>
+                    <td class="noborder">
+                        <label for="displayType">Display</label>
+                    </td>
+                    <td class="noborder">
+                    </td>
+                </tr>
+                <tr class="noborder">
+                    <td class="noborder">
+                        <% final PwmLogLevel configuredLevel = JspUtility.getPwmRequest( pageContext ).getConfig().readSettingAsEnum(PwmSetting.EVENTS_LOCALDB_LOG_LEVEL,PwmLogLevel.class); %>
+                        <select name="level" style="width: auto;" id="level">
+                            <% for (final PwmLogLevel level : PwmLogLevel.values()) { %>
+                            <% final boolean disabled = level.compareTo(configuredLevel) < 0; %>
+                            <option value="<%=level%>"<%=disabled ? " disabled" : ""%>><%=level%></option>
+                            <% } %>
+                        </select>
+                    </td>
+                    <td class="noborder">
+                        <select id="type" name="type" style="width:auto">
+                            <option value="User">User</option>
+                            <option value="System">System
+                            </option>
+                            <option value="Both" selected="selected">Both</option>
+                        </select>
+                    </td>
+                    <td class="noborder">
+                        <input name="username" type="text" id="username"/>
+                    </td>
+                    <td class="noborder">
+                        <input style="width: 100%" name="text" type="text" id="text"/>
+                    </td>
+                    <td class="noborder">
+                        <input type="number" id="count" name="count" step="500" value="1000" min="100" max="100000"/>
+                    </td>
+                    <td class="noborder">
+                        <input type="number" id="maxTime" name="maxTime" step="5" value="30" min="10" max="120"/>
+                    </td>
+                    <td class="noborder">
+                        <select id="displayType" name="displayText" style="width: auto">
+                            <option value="lines">Text</option>
+                            <option value="grid">Table</option>
+                        </select>
+                    </td>
+                    <td class="key noborder" style="vertical-align: middle">
+                        <button type="button" name="button-search" id="button-search" class="btn" style="white-space: nowrap">
+                            <pwm:if test="<%=PwmIfTest.showIcons%>"><span class="btn-icon pwm-icon pwm-icon-search"></span></pwm:if>
+                            Load Data
+                        </button>
+                    </td>
+                </tr>
+            </table>
+        </form>
+    </div>
+    <br/>
+    <div id="div-noResultsMessage" class="hidden" style="min-height: 200px">
+        <p style="text-align:center;" >
+            No events matched the search settings. Please refine your search query and try again.
+        </p>
+    </div>
+    <div style="margin: 20px; min-height: 200px">
+        <div id="wrapper-logViewerGrid" class="hidden">
+            <div id="logViewerGrid" >
+            </div>
+        </div>
+        <div id="wrapper-lineViewer" class="hidden">
+            <div id="lineViewer" class="logViewer">
+
+            </div>
+        </div>
+    </div>
+    <pwm:script>
+        <script type="text/javascript">
+            PWM_GLOBAL['startupFunctions'].push(function(){
+                PWM_MAIN.addEventHandler('button-search','click',function(){
+                    PWM_ADMIN.refreshLogData();
+                });
+                PWM_ADMIN.initLogGrid();
+                PWM_ADMIN.refreshLogData();
+            });
+        </script>
+    </pwm:script>
+</div>

+ 118 - 2
server/src/main/webapp/public/resources/js/admin.js

@@ -438,14 +438,130 @@ PWM_ADMIN.auditSystemHeaders = function() {
     ];
 };
 
-PWM_ADMIN.makeGrid = function() {
+PWM_ADMIN.logHeaders = function() {
+    return [
+        {field:"d",label:PWM_ADMIN.showString('Field_Logs_Timestamp')},
+        {field:"l",label:PWM_ADMIN.showString('Field_Logs_Level')},
+        {field:"s",label:PWM_ADMIN.showString('Field_Logs_Source'),hidden:true},
+        {field:"b",label:PWM_ADMIN.showString('Field_Logs_Label')},
+        {field:"a",label:PWM_ADMIN.showString('Field_Logs_User'),hidden:true},
+        {field:"t",label:PWM_ADMIN.showString('Field_Logs_Component'),hidden:true},
+        {field:"m",label:PWM_ADMIN.showString('Field_Logs_Detail')},
+        {field:"e",label:PWM_ADMIN.showString('Field_Logs_Error'),hidden:true}
+    ];
+};
+
+PWM_ADMIN.initLogGrid=function() {
     require(["dojo","dojo/_base/declare", "dgrid/Grid", "dgrid/Keyboard", "dgrid/Selection", "dgrid/extensions/ColumnResizer", "dgrid/extensions/ColumnReorder", "dgrid/extensions/ColumnHider", "dgrid/extensions/DijitRegistry"],
         function(dojo, declare, Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry){
-            return declare([ Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry ]);
+            // Create a new constructor by mixing in the components
+            var CustomGrid = declare([ Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry ]);
+
+            // Now, create an instance of our custom userGrid
+            PWM_VAR['logViewerGrid'] = new CustomGrid({columns: PWM_ADMIN.logHeaders()}, "logViewerGrid");
+            PWM_VAR['logViewerGrid'].on(".dgrid-row:click", function(evt){
+                PWM_ADMIN.detailView(evt, PWM_ADMIN.logHeaders(), PWM_VAR['logViewerGrid']);
+            });
         }
     );
+
+    var saveSettings = function() {
+        var logSettings = PWM_ADMIN.readLogFormData();
+        PWM_MAIN.Preferences.writeSessionStorage('logSettings',logSettings);
+    };
+
+    PWM_MAIN.addEventHandler('form-loadLog','change', saveSettings);
+    PWM_MAIN.addEventHandler('form-downloadLog','change', saveSettings);
+
+    var loadSettings = function () {
+        var settings = PWM_MAIN.Preferences.readSessionStorage('logSettings');
+        if (settings) {
+            PWM_MAIN.getObject('username').value = settings['username'];
+            PWM_MAIN.getObject('text').value = settings['text'];
+            PWM_MAIN.getObject('count').value = settings['count'];
+            PWM_MAIN.getObject('maxTime').value = settings['maxTime'];
+            PWM_MAIN.JSLibrary.setValueOfSelectElement('type',settings['type']);
+            PWM_MAIN.JSLibrary.setValueOfSelectElement('level',settings['level']);
+            PWM_MAIN.JSLibrary.setValueOfSelectElement('displayType', settings['displayType']);
+            if (PWM_MAIN.getObject('form-downloadLog')) {
+                PWM_MAIN.JSLibrary.setValueOfSelectElement('downloadType', settings['downloadType']);
+                PWM_MAIN.JSLibrary.setValueOfSelectElement('compressionType', settings['compressionType']);
+            }
+        }
+    };
+    loadSettings();
+};
+
+PWM_ADMIN.readLogFormData = function() {
+    var settings = {};
+    settings['username'] = PWM_MAIN.getObject('username').value;
+    settings['text'] = PWM_MAIN.getObject('text').value;
+    settings['count'] = PWM_MAIN.getObject('count').value;
+    settings['maxTime'] = PWM_MAIN.getObject('maxTime').value;
+    settings['type'] = PWM_MAIN.JSLibrary.readValueOfSelectElement('type');
+    settings['level'] = PWM_MAIN.JSLibrary.readValueOfSelectElement('level');
+    settings['displayType'] = PWM_MAIN.JSLibrary.readValueOfSelectElement('displayType');
+    if (PWM_MAIN.getObject('form-downloadLog')) {
+        settings['downloadType'] = PWM_MAIN.JSLibrary.readValueOfSelectElement('downloadType');
+        settings['compressionType'] = PWM_MAIN.JSLibrary.readValueOfSelectElement('compressionType');
+    }
+    return settings;
 };
 
+PWM_ADMIN.refreshLogData = function() {
+    PWM_MAIN.getObject('button-search').disabled = true;
+    var logSettings = PWM_ADMIN.readLogFormData();
+
+    var processFunction = function(data) {
+        console.time('someFunction');
+
+        var records = data['data']['records'];
+        if (PWM_MAIN.JSLibrary.isEmpty(records)) {
+            PWM_MAIN.removeCssClass('div-noResultsMessage', 'hidden');
+            PWM_MAIN.addCssClass('wrapper-logViewerGrid', 'hidden');
+            PWM_MAIN.addCssClass('wrapper-lineViewer', 'hidden');
+
+        } else {
+            if (data['data']['display'] === 'grid') {
+                PWM_MAIN.addCssClass('div-noResultsMessage', 'hidden');
+                PWM_MAIN.removeCssClass('wrapper-logViewerGrid', 'hidden');
+                PWM_MAIN.addCssClass('wrapper-lineViewer', 'hidden');
+                var grid = PWM_VAR['logViewerGrid'];
+                grid.refresh();
+                grid.renderArray(records);
+                grid.set("timestamp", { attribute : 'createTime', ascending: false, descending: true });
+            } else {
+                PWM_MAIN.addCssClass('div-noResultsMessage', 'hidden');
+                PWM_MAIN.addCssClass('wrapper-logViewerGrid', 'hidden');
+                PWM_MAIN.removeCssClass('wrapper-lineViewer', 'hidden');
+                var textOutput = '';
+
+                for (var iterator in records) {
+                    (function(record) {
+                        textOutput += records[record];
+                        textOutput += "\n";
+                    }(iterator));
+                }
+                PWM_MAIN.getObject('lineViewer').textContent = textOutput;
+            }
+        }
+        console.timeEnd('someFunction');
+
+        PWM_MAIN.getObject('button-search').disabled = false;
+        PWM_MAIN.closeWaitDialog();
+    };
+
+    var url = PWM_MAIN.addParamToUrl(PWM_GLOBAL['url-context'] + '/private/admin',  'processAction', 'readLogData');
+    var options = {};
+    options.content = logSettings;
+
+    PWM_MAIN.showWaitDialog({loadFunction:function(){
+            PWM_MAIN.ajaxRequest(url,processFunction,options);
+        }
+    });
+};
+
+
 PWM_ADMIN.initAuditGrid=function() {
     require(["dojo","dojo/_base/declare", "dgrid/Grid", "dgrid/Keyboard", "dgrid/Selection", "dgrid/extensions/ColumnResizer", "dgrid/extensions/ColumnReorder", "dgrid/extensions/ColumnHider", "dgrid/extensions/DijitRegistry"],
         function(dojo, declare, Grid, Keyboard, Selection, ColumnResizer, ColumnReorder, ColumnHider, DijitRegistry){

+ 0 - 9
server/src/main/webapp/public/resources/js/configmanager.js

@@ -192,12 +192,6 @@ PWM_CONFIG.showString=function (key, options) {
 
 };
 
-PWM_CONFIG.openLogViewer=function(level) {
-    var windowUrl = PWM_GLOBAL['url-context'] + '/private/admin/Administration?processAction=viewLogWindow' + ((level) ? '&level=' + level : '');
-    var windowName = 'logViewer';
-    PWM_MAIN.newWindowOpen(windowUrl,windowName);
-};
-
 PWM_CONFIG.showHeaderHealth = function() {
     var refreshUrl = PWM_GLOBAL['url-context'] + "/public/api?processAction=health";
     var parentDiv = PWM_MAIN.getObject('panel-header-healthData');
@@ -332,9 +326,6 @@ PWM_CONFIG.heartbeatCheck = function() {
 };
 
 PWM_CONFIG.initConfigHeader = function() {
-    PWM_MAIN.addEventHandler('header_openLogViewerButton', 'click', function () {
-        PWM_CONFIG.openLogViewer(null)
-    });
     PWM_MAIN.addEventHandler('panel-header-healthData','click',function(){
         PWM_MAIN.goto('/private/config/manager');
     });

+ 18 - 0
server/src/main/webapp/public/resources/style.css

@@ -1720,3 +1720,21 @@ table.ias-table, table.ias-table td {
     background-size: 100px 100px;
     width: auto;
     height: 80px;
+}
+
+.hidden {
+   display: none;
+}
+
+.logViewer {
+    border: 0;
+    background-color: #DDDDDD;
+    border-radius: 7px;
+    padding: 10px;
+    height: auto;
+    min-height: 100px;
+    max-height: 50vh;
+    overflow: auto;
+    font-family: monospace;
+    white-space: pre;
+}