浏览代码

telemetry updates and refactoring

Jason Rivard 3 年之前
父节点
当前提交
a1760baf4d
共有 44 个文件被更改,包括 394 次插入536 次删除
  1. 10 30
      data-service/pom.xml
  2. 3 0
      data-service/src/main/java/password/pwm/receiver/ContextManager.java
  3. 6 13
      data-service/src/main/java/password/pwm/receiver/Logger.java
  4. 8 0
      data-service/src/main/java/password/pwm/receiver/PublishVersionServlet.java
  5. 2 2
      data-service/src/main/java/password/pwm/receiver/SummaryBean.java
  6. 9 0
      data-service/src/main/java/password/pwm/receiver/TelemetryRestReceiver.java
  7. 5 1
      data-service/src/main/java/password/pwm/receiver/TelemetryViewerServlet.java
  8. 31 0
      data-service/src/main/webapp/WEB-INF/classes/logback.xml
  9. 0 22
      lib-data/pom.xml
  10. 1 2
      lib-data/src/main/java/password/pwm/bean/pub/PublicUserInfoBean.java
  11. 1 1
      lib-data/src/main/java/password/pwm/bean/pub/PublishVersionBean.java
  12. 3 0
      lib-data/src/main/java/password/pwm/bean/pub/PublishedBean.java
  13. 0 22
      lib-util/pom.xml
  14. 0 40
      lib-util/src/main/java/password/pwm/util/java/CrcChecksumConsumer.java
  15. 0 46
      lib-util/src/main/java/password/pwm/util/java/CrcChecksumInputStream.java
  16. 0 45
      lib-util/src/main/java/password/pwm/util/java/CrcChecksumOutputStream.java
  17. 9 7
      lib-util/src/main/java/password/pwm/util/java/JavaHelper.java
  18. 0 147
      lib-util/src/main/java/password/pwm/util/java/LengthLimitedInputStream.java
  19. 78 0
      lib-util/src/main/java/password/pwm/util/java/ThresholdInputStream.java
  20. 2 1
      onejar/src/main/java/password/pwm/onejar/TomcatOnejarRunner.java
  21. 46 3
      pom.xml
  22. 0 46
      server/pom.xml
  23. 1 3
      server/src/main/java/password/pwm/PwmAboutProperty.java
  24. 1 1
      server/src/main/java/password/pwm/PwmConstants.java
  25. 35 2
      server/src/main/java/password/pwm/PwmEnvironment.java
  26. 1 2
      server/src/main/java/password/pwm/bean/RemoteVerificationRequestBean.java
  27. 1 2
      server/src/main/java/password/pwm/bean/RemoteVerificationResponseBean.java
  28. 1 2
      server/src/main/java/password/pwm/http/JspUtility.java
  29. 6 6
      server/src/main/java/password/pwm/http/PwmCookiePath.java
  30. 1 1
      server/src/main/java/password/pwm/http/PwmRequest.java
  31. 2 3
      server/src/main/java/password/pwm/http/servlet/admin/UserDebugDataBean.java
  32. 7 5
      server/src/main/java/password/pwm/http/servlet/resource/ResourceServletService.java
  33. 17 3
      server/src/main/java/password/pwm/http/tag/conditional/PwmIfTest.java
  34. 9 0
      server/src/main/java/password/pwm/svc/AbstractPwmService.java
  35. 2 0
      server/src/main/java/password/pwm/svc/PwmService.java
  36. 0 8
      server/src/main/java/password/pwm/svc/PwmServiceEnum.java
  37. 17 17
      server/src/main/java/password/pwm/svc/PwmServiceManager.java
  38. 60 27
      server/src/main/java/password/pwm/svc/version/VersionCheckService.java
  39. 2 2
      server/src/main/java/password/pwm/util/debug/FileInfoDebugItemGenerator.java
  40. 7 19
      server/src/main/java/password/pwm/util/java/FileSystemUtility.java
  41. 6 0
      server/src/main/java/password/pwm/util/localdb/LocalDBService.java
  42. 1 2
      webapp/pom.xml
  43. 1 1
      webapp/src/main/webapp/WEB-INF/jsp/admin-dashboard.jsp
  44. 2 2
      webapp/src/main/webapp/WEB-INF/jsp/configguide-ldap_cert.jsp

+ 10 - 30
data-service/pom.xml

@@ -178,8 +178,8 @@
         </dependency>
         <dependency>
             <groupId>javax.servlet.jsp</groupId>
-            <artifactId>jsp-api</artifactId>
-            <version>2.2.1-b03</version>
+            <artifactId>javax.servlet.jsp-api</artifactId>
+            <version>2.3.3</version>
             <scope>provided</scope>
         </dependency>
         <!-- / container dependencies -->
@@ -195,39 +195,19 @@
             <version>1.9.0</version>
         </dependency>
         <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-            <version>3.12.0</version>
-        </dependency>
-        <dependency>
-            <groupId>com.sun.mail</groupId>
-            <artifactId>jakarta.mail</artifactId>
+            <groupId>org.jetbrains.xodus</groupId>
+            <artifactId>xodus-environment</artifactId>
             <version>2.0.1</version>
         </dependency>
         <dependency>
-            <groupId>org.apache.httpcomponents</groupId>
-            <artifactId>httpclient</artifactId>
-            <version>4.5.13</version>
-        </dependency>
-        <dependency>
-            <groupId>ch.qos.reload4j</groupId>
-            <artifactId>reload4j</artifactId>
-            <version>1.2.21</version>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>2.0.0-alpha7</version>
         </dependency>
         <dependency>
-            <groupId>org.jdom</groupId>
-            <artifactId>jdom2</artifactId>
-            <version>2.0.6.1</version>
-        </dependency>
-        <dependency>
-            <groupId>com.google.code.gson</groupId>
-            <artifactId>gson</artifactId>
-            <version>2.9.0</version>
-        </dependency>
-        <dependency>
-            <groupId>org.jetbrains.xodus</groupId>
-            <artifactId>xodus-environment</artifactId>
-            <version>2.0.1</version>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.3.0-alpha16</version>
         </dependency>
     </dependencies>
 </project>

+ 3 - 0
data-service/src/main/java/password/pwm/receiver/ContextManager.java

@@ -28,6 +28,7 @@ import javax.servlet.annotation.WebListener;
 @WebListener
 public class ContextManager implements ServletContextListener
 {
+    private static final Logger LOGGER = Logger.createLogger( ContextManager.class );
     private static final String CONTEXT_ATTR = "contextManager";
     private PwmReceiverApp app;
 
@@ -36,6 +37,7 @@ public class ContextManager implements ServletContextListener
     {
         app = new PwmReceiverApp();
         sce.getServletContext().setAttribute( CONTEXT_ATTR, this );
+        LOGGER.info( "open for bidness" );
     }
 
     @Override
@@ -43,6 +45,7 @@ public class ContextManager implements ServletContextListener
     {
         app.close();
         app = null;
+        LOGGER.info( "cya!" );
     }
 
     public PwmReceiverApp getApp( )

+ 6 - 13
data-service/src/main/java/password/pwm/receiver/Logger.java

@@ -20,16 +20,13 @@
 
 package password.pwm.receiver;
 
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-
 public class Logger
 {
-    private final String name;
+    private final org.slf4j.Logger logger;
 
     private Logger( final Class<?> classname )
     {
-        this.name = classname.getName();
+        this.logger = org.slf4j.LoggerFactory.getLogger( classname );
     }
 
     public static Logger createLogger( final Class<?> classname )
@@ -37,17 +34,13 @@ public class Logger
         return new Logger( classname );
     }
 
-    public void info( final CharSequence input )
+    public void info( final String input )
     {
-        System.out.println( "PwmReceiver: "
-                + Instant.now().truncatedTo( ChronoUnit.SECONDS ).toString()
-                + ", INFO , " + name + ", " + input );
+        logger.info( input );
     }
 
-    public void debug( final CharSequence input )
+    public void debug( final String input )
     {
-        System.out.println( "PwmReceiver: "
-                + Instant.now().toString()
-                + ", DEBUG, " + name + ", " + input );
+        logger.debug( input );
     }
 }

+ 8 - 0
data-service/src/main/java/password/pwm/receiver/PublishVersionServlet.java

@@ -21,6 +21,7 @@
 package password.pwm.receiver;
 
 import password.pwm.bean.pub.PublishVersionBean;
+import password.pwm.util.java.AtomicLoopIntIncrementer;
 import password.pwm.ws.server.RestResultBean;
 
 import javax.servlet.annotation.WebServlet;
@@ -37,10 +38,17 @@ import java.util.Collections;
 )
 public class PublishVersionServlet extends HttpServlet
 {
+    private static final Logger LOGGER = Logger.createLogger( PublishVersionServlet.class );
+    private static final AtomicLoopIntIncrementer REQ_COUNTER = new AtomicLoopIntIncrementer();
+
+
     @Override
     protected void doGet( final HttpServletRequest req, final HttpServletResponse resp )
             throws IOException
     {
+        final int requestId = REQ_COUNTER.next();
+        LOGGER.debug( "http request #" + requestId + " for version" );
+
         final ContextManager contextManager = ContextManager.getContextManager( req.getServletContext() );
         final PwmReceiverApp app = contextManager.getApp();
         final PublishVersionBean publishVersionBean = new PublishVersionBean(

+ 2 - 2
data-service/src/main/java/password/pwm/receiver/SummaryBean.java

@@ -90,7 +90,7 @@ public class SummaryBean
                         .osVersion( bean.getAbout().get( PwmAboutProperty.java_osVersion.name() ) )
                         .servletName( bean.getAbout().get( PwmAboutProperty.java_appServerInfo.name() ) )
                         .dbVendor( dbVendor )
-                        .appliance( Boolean.parseBoolean( bean.getAbout().get( PwmAboutProperty.app_mode_appliance.name() ) ) )
+                        .platform( bean.getAbout().get( PwmAboutProperty.app_deployment_type.name() ) )
                         .javaVm( javaVmInfo( bean, "n/a" ) )
                         .build();
 
@@ -200,6 +200,6 @@ public class SummaryBean
         private String servletName;
         private String dbVendor;
         private String javaVm;
-        private boolean appliance;
+        private String platform;
     }
 }

+ 9 - 0
data-service/src/main/java/password/pwm/receiver/TelemetryRestReceiver.java

@@ -26,6 +26,7 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.ServletUtility;
 import password.pwm.i18n.Message;
+import password.pwm.util.java.AtomicLoopIntIncrementer;
 import password.pwm.util.json.JsonFactory;
 import password.pwm.ws.server.RestResultBean;
 
@@ -44,12 +45,19 @@ import java.io.IOException;
 
 public class TelemetryRestReceiver extends HttpServlet
 {
+    private static final Logger LOGGER = Logger.createLogger( TelemetryViewerServlet.class );
+    private static final AtomicLoopIntIncrementer REQ_COUNTER = new AtomicLoopIntIncrementer();
+
+
     @Override
     protected void doPost( final HttpServletRequest req, final HttpServletResponse resp )
             throws ServletException, IOException
     {
         try
         {
+            final int requestId = REQ_COUNTER.next();
+            LOGGER.debug( "http rest request #" + requestId + " for telemetry update" );
+
             final String input = ServletUtility.readRequestBodyAsString( req, 1024 * 1024 );
             final TelemetryPublishBean telemetryPublishBean = JsonFactory.get().deserialize( input, TelemetryPublishBean.class );
             final Storage storage = ContextManager.getContextManager( this.getServletContext() ).getApp().getStorage();
@@ -57,6 +65,7 @@ public class TelemetryRestReceiver extends HttpServlet
 
             final RestResultBean restResultBean = RestResultBean.forSuccessMessage( null, null, null, Message.Success_Unknown );
             ReceiverUtil.outputJsonResponse( req, resp, restResultBean );
+            LOGGER.debug( "http rest request #" + requestId + " received from " + telemetryPublishBean.getSiteDescription() );
         }
         catch ( final PwmUnrecoverableException e )
         {

+ 5 - 1
data-service/src/main/java/password/pwm/receiver/TelemetryViewerServlet.java

@@ -20,6 +20,7 @@
 
 package password.pwm.receiver;
 
+import password.pwm.util.java.AtomicLoopIntIncrementer;
 import password.pwm.util.java.StringUtil;
 
 import javax.servlet.ServletException;
@@ -41,14 +42,17 @@ public class TelemetryViewerServlet extends HttpServlet
 {
     private static final Logger LOGGER = Logger.createLogger( TelemetryViewerServlet.class );
     private static final String PARAM_DAYS = "days";
+    private static final AtomicLoopIntIncrementer REQ_COUNTER = new AtomicLoopIntIncrementer();
 
     public static final String SUMMARY_ATTR = "SummaryBean";
 
+
     @Override
     protected void doGet( final HttpServletRequest req, final HttpServletResponse resp )
             throws ServletException, IOException
     {
-        LOGGER.debug( "http request for viewer" );
+        final int requestId = REQ_COUNTER.next();
+        LOGGER.debug( "http request #" + requestId + " for viewer" );
         final String daysString = req.getParameter( PARAM_DAYS );
         final int days = StringUtil.isEmpty( daysString ) ? 30 : Integer.parseInt( daysString );
         final ContextManager contextManager = ContextManager.getContextManager( req.getServletContext() );

+ 31 - 0
data-service/src/main/webapp/WEB-INF/classes/logback.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Password Management Servlets (PWM)
+  ~ http://www.pwm-project.org
+  ~
+  ~ Copyright (c) 2006-2009 Novell, Inc.
+  ~ Copyright (c) 2009-2021 The PWM Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d{yyyy-MM-dd'T'HH:mm:ss'Z'}, %-5level, %logger{36}, %m%n</pattern>
+        </encoder>
+    </appender>
+    <root level="trace">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>

+ 0 - 22
lib-data/pom.xml

@@ -37,28 +37,6 @@
     </profiles>
     <build>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <version>3.0.0-M7</version>
-                <executions>
-                    <execution>
-                        <id>default-test</id>
-                        <goals>
-                            <goal>test</goal>
-                        </goals>
-                        <phase>test</phase>
-                        <configuration>
-                            <trimStackTrace>false</trimStackTrace>
-                            <skipTests>${skipTests}</skipTests>
-                            <excludes>
-                                <exclude>**/ExtendedTest*.java</exclude>
-                                <exclude>**/*ExtendedTest*.java</exclude>
-                            </excludes>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>

+ 1 - 2
lib-data/src/main/java/password/pwm/bean/pub/PublicUserInfoBean.java

@@ -24,14 +24,13 @@ import lombok.Builder;
 import lombok.Value;
 import password.pwm.bean.PasswordStatus;
 
-import java.io.Serializable;
 import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 
 @Value
 @Builder
-public class PublicUserInfoBean implements Serializable
+public class PublicUserInfoBean implements PublishedBean
 {
     private String userDN;
     private String ldapProfile;

+ 1 - 1
lib-data/src/main/java/password/pwm/bean/pub/PublishVersionBean.java

@@ -26,7 +26,7 @@ import password.pwm.bean.VersionNumber;
 import java.util.Map;
 
 @Value
-public class PublishVersionBean
+public class PublishVersionBean implements PublishedBean
 {
     private final Map<VersionKey, VersionNumber> versions;
 

+ 3 - 0
lib-data/src/main/java/password/pwm/bean/pub/PublishedBean.java

@@ -20,6 +20,9 @@
 
 package password.pwm.bean.pub;
 
+/**
+ * Marker interface to indicate a class is being used in public services and must retain backward compatibility.
+ */
 public interface PublishedBean
 {
 }

+ 0 - 22
lib-util/pom.xml

@@ -37,28 +37,6 @@
     </profiles>
     <build>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <version>3.0.0-M7</version>
-                <executions>
-                    <execution>
-                        <id>default-test</id>
-                        <goals>
-                            <goal>test</goal>
-                        </goals>
-                        <phase>test</phase>
-                        <configuration>
-                            <trimStackTrace>false</trimStackTrace>
-                            <skipTests>${skipTests}</skipTests>
-                            <excludes>
-                                <exclude>**/ExtendedTest*.java</exclude>
-                                <exclude>**/*ExtendedTest*.java</exclude>
-                            </excludes>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>

+ 0 - 40
lib-util/src/main/java/password/pwm/util/java/CrcChecksumConsumer.java

@@ -1,40 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2021 The PWM Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package password.pwm.util.java;
-
-import java.util.function.Consumer;
-import java.util.zip.CRC32;
-
-class CrcChecksumConsumer implements Consumer<byte[]>
-{
-    private final CRC32 crc32 = new CRC32();
-
-    @Override
-    public void accept( final byte[] bytes )
-    {
-        crc32.update( bytes );
-    }
-
-    public long checksum()
-    {
-        return crc32.getValue();
-    }
-}

+ 0 - 46
lib-util/src/main/java/password/pwm/util/java/CrcChecksumInputStream.java

@@ -1,46 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2021 The PWM Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package password.pwm.util.java;
-
-import java.io.InputStream;
-
-public class CrcChecksumInputStream extends CopyingInputStream
-{
-    private final CrcChecksumConsumer crcChecksumConsumer;
-
-    private CrcChecksumInputStream( final InputStream realStream, final CrcChecksumConsumer crcChecksumConsumer )
-    {
-        super( realStream, crcChecksumConsumer );
-        this.crcChecksumConsumer = crcChecksumConsumer;
-    }
-
-    public static CrcChecksumInputStream newChecksumInputStream( final InputStream wrappedStream )
-    {
-        final CrcChecksumConsumer crcChecksumConsumer = new CrcChecksumConsumer();
-        return new CrcChecksumInputStream( wrappedStream, crcChecksumConsumer );
-    }
-
-    public long checksum()
-    {
-        return crcChecksumConsumer.checksum();
-    }
-
-}

+ 0 - 45
lib-util/src/main/java/password/pwm/util/java/CrcChecksumOutputStream.java

@@ -1,45 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2021 The PWM Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package password.pwm.util.java;
-
-import java.io.OutputStream;
-
-public class CrcChecksumOutputStream extends CopyingOutputStream
-{
-    private final CrcChecksumConsumer crcChecksumConsumer;
-
-    private CrcChecksumOutputStream( final OutputStream realStream, final CrcChecksumConsumer crcChecksumConsumer )
-    {
-        super( realStream, crcChecksumConsumer );
-        this.crcChecksumConsumer = crcChecksumConsumer;
-    }
-
-    public static CrcChecksumOutputStream newChecksumOutputStream( final OutputStream wrappedStream )
-    {
-        final CrcChecksumConsumer crcChecksumConsumer = new CrcChecksumConsumer();
-        return new CrcChecksumOutputStream( wrappedStream, crcChecksumConsumer );
-    }
-
-    public long checksum()
-    {
-        return crcChecksumConsumer.checksum();
-    }
-}

+ 9 - 7
lib-util/src/main/java/password/pwm/util/java/JavaHelper.java

@@ -216,12 +216,14 @@ public class JavaHelper
             return Optional.empty();
         }
         final StringWriter stringWriter = new StringWriter();
-        final InputStreamReader reader = new InputStreamReader( input, charset );
-        IOUtils.copyLarge( reader, stringWriter, 0, maximumLength );
-        final String value = stringWriter.toString();
-        return ( value.length() > 0 )
-                ? Optional.of( value )
-                : Optional.empty();
+        try ( InputStreamReader reader = new InputStreamReader( ThresholdInputStream.newThresholdInputStream( input, maximumLength ), charset ) )
+        {
+            IOUtils.copyLarge( reader, stringWriter, 0, maximumLength );
+            final String value = stringWriter.toString();
+            return ( value.length() > 0 )
+                    ? Optional.of( value )
+                    : Optional.empty();
+        }
     }
 
     public static void closeQuietly( final Closeable closable )
@@ -232,7 +234,7 @@ public class JavaHelper
     public static ImmutableByteArray copyToBytes( final InputStream inputStream, final int maxLength )
             throws IOException
     {
-        try ( InputStream limitedInputStream = new LengthLimitedInputStream( inputStream, maxLength ) )
+        try ( InputStream limitedInputStream = ThresholdInputStream.newThresholdInputStream( inputStream, maxLength ) )
         {
             final byte[] bytes = IOUtils.toByteArray( limitedInputStream );
             return ImmutableByteArray.of( bytes );

+ 0 - 147
lib-util/src/main/java/password/pwm/util/java/LengthLimitedInputStream.java

@@ -1,147 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2021 The PWM Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package password.pwm.util.java;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class LengthLimitedInputStream extends InputStream
-{
-    private final InputStream realStream;
-    private final long maxBytes;
-
-    private long lengthCount = 0;
-
-    public LengthLimitedInputStream( final InputStream inputStream, final long maxBytes )
-    {
-        this.realStream = inputStream;
-        this.maxBytes = maxBytes;
-    }
-
-    private void checkLength( final long addLength )
-            throws IOException
-    {
-        if ( addLength > 0 )
-        {
-            this.lengthCount += addLength;
-            if ( lengthCount > maxBytes )
-            {
-                throw new IOException( "maximum input length exceeded" );
-            }
-        }
-    }
-
-    @Override
-    public int read( final byte[] b ) throws IOException
-    {
-        final int bytesRead = realStream.read( b );
-        checkLength( bytesRead );
-        return bytesRead;
-    }
-
-    @Override
-    public int read( final byte[] b, final int off, final int len ) throws IOException
-    {
-        final int bytesRead = realStream.read( b, off, len );
-        checkLength( bytesRead );
-        return bytesRead;
-    }
-
-    @Override
-    public byte[] readAllBytes() throws IOException
-    {
-        final byte[] bytesRead = realStream.readAllBytes();
-        checkLength( bytesRead == null ? 0 : bytesRead.length );
-        return bytesRead;
-    }
-
-    @Override
-    public byte[] readNBytes( final int len ) throws IOException
-    {
-        final byte[] readBytes = realStream.readNBytes( len );
-        checkLength( readBytes == null ? 0 : readBytes.length );
-        return readBytes;
-    }
-
-    @Override
-    public int readNBytes( final byte[] b, final int off, final int len ) throws IOException
-    {
-        final int bytesRead = realStream.readNBytes( b, off, len );
-        checkLength( bytesRead );
-        return bytesRead;
-    }
-
-    @Override
-    public long skip( final long n ) throws IOException
-    {
-        return realStream.skip( n );
-    }
-
-    @Override
-    public int available() throws IOException
-    {
-        return realStream.available();
-    }
-
-    @Override
-    public void close() throws IOException
-    {
-        realStream.close();
-    }
-
-    @Override
-    public synchronized void mark( final int readlimit )
-    {
-        realStream.mark( readlimit );
-    }
-
-    @Override
-    public synchronized void reset() throws IOException
-    {
-        realStream.reset();
-    }
-
-    @Override
-    public boolean markSupported()
-    {
-        return realStream.markSupported();
-    }
-
-    @Override
-    public long transferTo( final OutputStream out ) throws IOException
-    {
-        final long bytesRead = realStream.transferTo( out );
-        checkLength( bytesRead );
-        return bytesRead;
-    }
-
-    @Override
-    public int read() throws IOException
-    {
-        final int byteValue = realStream.read();
-        if ( byteValue > -1 )
-        {
-            checkLength( 1 );
-        }
-        return byteValue;
-    }
-}

+ 78 - 0
lib-util/src/main/java/password/pwm/util/java/ThresholdInputStream.java

@@ -0,0 +1,78 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2021 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+import org.apache.commons.io.input.ObservableInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ThresholdInputStream extends ObservableInputStream
+{
+    private ThresholdInputStream( final InputStream inputStream, final Observer observer )
+    {
+        super( inputStream, observer );
+    }
+
+    public static ThresholdInputStream newThresholdInputStream( final InputStream inputStream, final long maxBytes )
+    {
+        return new ThresholdInputStream( inputStream, new ThresholdObserver( maxBytes ) );
+    }
+
+    private static class ThresholdObserver extends Observer
+    {
+        private final long maxBytes;
+
+        private long lengthCount = 0;
+
+        ThresholdObserver( final long maxBytes )
+        {
+            this.maxBytes = maxBytes;
+        }
+
+        private void checkLength( final long addLength )
+                throws IOException
+        {
+            if ( addLength > 0 )
+            {
+                this.lengthCount += addLength;
+                if ( lengthCount > maxBytes )
+                {
+                    throw new IOException( "maximum input length exceeded" );
+                }
+            }
+        }
+
+        @Override
+        public void data( final int input )
+                throws IOException
+        {
+            checkLength( 1 );
+        }
+
+        @Override
+        public void data( final byte[] input, final int offset, final int length )
+                throws IOException
+        {
+            checkLength( length - offset );
+        }
+    }
+}

+ 2 - 1
onejar/src/main/java/password/pwm/onejar/TomcatOnejarRunner.java

@@ -255,8 +255,9 @@ public class TomcatOnejarRunner
     {
         final String envVarPrefix = Resource.envVarPrefix.getValue();
         System.setProperty( envVarPrefix + "_APPLICATIONPATH", onejarConfig.getApplicationPath().getAbsolutePath() );
-        System.setProperty( envVarPrefix + "_APPLICATIONFLAGS", "ManageHttps" );
+        System.setProperty( envVarPrefix + "_APPLICATIONFLAGS", "[\"ManageHttps\",\"Onejar\"]" );
         System.setProperty( envVarPrefix + "_APPLICATIONPARAMFILE", onejarConfig.getPwmAppPropertiesFile().getAbsolutePath() );
+        System.setProperty( "ONEJAR_ENV", "TRUE" );
     }
 
     private void outputPwmAppProperties( final OnejarConfig onejarConfig ) throws IOException

+ 46 - 3
pom.xml

@@ -255,7 +255,7 @@
                     <dependency>
                         <groupId>com.puppycrawl.tools</groupId>
                         <artifactId>checkstyle</artifactId>
-                        <version>10.3</version>
+                        <version>10.3.1</version>
                     </dependency>
                 </dependencies>
                 <executions>
@@ -339,7 +339,7 @@
                     <dependency>
                         <groupId>com.github.spotbugs</groupId>
                         <artifactId>spotbugs</artifactId>
-                        <version>4.7.0</version>
+                        <version>4.7.1</version>
                     </dependency>
                 </dependencies>
                 <configuration>
@@ -444,9 +444,52 @@
                 <artifactId>maven-dependency-plugin</artifactId>
                 <version>3.3.0</version>
             </plugin>
+
         </plugins>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>3.0.0-M7</version>
+                    <executions>
+                        <execution>
+                            <id>default-test</id>
+                            <goals>
+                                <goal>test</goal>
+                            </goals>
+                            <phase>test</phase>
+                            <configuration>
+                                <trimStackTrace>false</trimStackTrace>
+                                <skipTests>${skipTests}</skipTests>
+                                <excludes>
+                                    <exclude>**/ExtendedTest*.java</exclude>
+                                    <exclude>**/*ExtendedTest*.java</exclude>
+                                </excludes>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>extended-test</id>
+                            <goals>
+                                <goal>test</goal>
+                            </goals>
+                            <phase>test</phase>
+                            <configuration>
+                                <skipTests>${skipExtendedTests}</skipTests>
+                                <includes>
+                                    <include>**/ExtendedTest*.java</include>
+                                    <include>**/*ExtendedTest*.java</include>
+                                </includes>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+            </plugins>
+
+        </pluginManagement>
     </build>
 
+
     <!-- common dependencies -->
     <dependencies>
         <dependency>
@@ -458,7 +501,7 @@
         <dependency>
             <groupId>com.github.spotbugs</groupId>
             <artifactId>spotbugs-annotations</artifactId>
-            <version>4.7.0</version>
+            <version>4.7.1</version>
             <scope>provided</scope>
         </dependency>
 

+ 0 - 46
server/pom.xml

@@ -38,42 +38,6 @@
     </profiles>
     <build>
         <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <version>3.0.0-M7</version>
-                <executions>
-                    <execution>
-                        <id>default-test</id>
-                        <goals>
-                            <goal>test</goal>
-                        </goals>
-                        <phase>test</phase>
-                        <configuration>
-                            <trimStackTrace>false</trimStackTrace>
-                            <skipTests>${skipTests}</skipTests>
-                            <excludes>
-                                <exclude>**/ExtendedTest*.java</exclude>
-                                <exclude>**/*ExtendedTest*.java</exclude>
-                            </excludes>
-                        </configuration>
-                    </execution>
-                    <execution>
-                        <id>extended-test</id>
-                        <goals>
-                            <goal>test</goal>
-                        </goals>
-                        <phase>test</phase>
-                        <configuration>
-                            <skipTests>${skipExtendedTests}</skipTests>
-                            <includes>
-                                <include>**/ExtendedTest*.java</include>
-                                <include>**/*ExtendedTest*.java</include>
-                            </includes>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
             <plugin>
                 <artifactId>maven-resources-plugin</artifactId>
                 <version>3.2.0</version>
@@ -279,16 +243,6 @@
             <artifactId>bcpkix-jdk15on</artifactId>
             <version>1.70</version>
         </dependency>
-        <dependency>
-            <groupId>jaxen</groupId>
-            <artifactId>jaxen</artifactId>
-            <version>1.2.0</version>
-        </dependency>
-        <dependency>
-            <groupId>org.jdom</groupId>
-            <artifactId>jdom2</artifactId>
-            <version>2.0.6.1</version>
-        </dependency>
         <dependency>
             <groupId>org.xeustechnologies</groupId>
             <artifactId>jcl-core</artifactId>

+ 1 - 3
server/src/main/java/password/pwm/PwmAboutProperty.java

@@ -46,7 +46,6 @@ import java.util.function.Function;
 
 public enum PwmAboutProperty
 {
-
     app_version( "App Version", pwmApplication -> PwmConstants.SERVLET_VERSION ),
     app_chaiApiVersion( "App Chai Version", pwmApplication -> PwmConstants.CHAI_API_VERSION ),
     app_currentTime( "App Current Time", pwmApplication -> format( Instant.now() ) ),
@@ -55,8 +54,7 @@ public enum PwmAboutProperty
     app_siteUrl( "App Site URL", pwmApplication -> pwmApplication.getConfig().readSettingAsString( PwmSetting.PWM_SITE_URL ) ),
     app_instanceID( "App InstanceID", PwmApplication::getInstanceID ),
     app_trialMode( null, pwmApplication -> Boolean.toString( PwmConstants.TRIAL_MODE ) ),
-    app_mode_appliance( null, pwmApplication -> Boolean.toString( pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.Appliance ) ) ),
-    app_mode_docker( null, pwmApplication -> Boolean.toString( pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.Docker ) ) ),
+    app_deployment_type( null, pwmApplication -> pwmApplication.getPwmEnvironment().getDeploymentPlatform().name() ),
     app_mode_manageHttps( null, pwmApplication -> Boolean.toString( pwmApplication.getPwmEnvironment().getFlags().contains( PwmEnvironment.ApplicationFlag.ManageHttps ) ) ),
     app_applicationPath( null, pwmApplication -> pwmApplication.getPwmEnvironment().getApplicationPath().getAbsolutePath() ),
     app_environmentFlags( null, pwmApplication -> StringUtil.collectionToString( pwmApplication.getPwmEnvironment().getFlags() ) ),

+ 1 - 1
server/src/main/java/password/pwm/PwmConstants.java

@@ -74,7 +74,7 @@ public abstract class PwmConstants
     public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
     public static final List<String> HIGHLIGHT_LOCALES = StringUtil.splitAndTrim( readPwmConstantsBundle( "locale.highlightList" ), "," );
 
-    public static final CSVFormat DEFAULT_CSV_FORMAT = CSVFormat.DEFAULT;
+    public static final CSVFormat DEFAULT_CSV_FORMAT = CSVFormat.Builder.create( CSVFormat.DEFAULT ).setCommentMarker( '#' ).build();
 
     public static final String DEFAULT_DATETIME_FORMAT_STR = readPwmConstantsBundle( "locale.defaultDateTimeFormat" );
     public static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone( readPwmConstantsBundle( "locale.defaultTimeZone" ) );

+ 35 - 2
server/src/main/java/password/pwm/PwmEnvironment.java

@@ -20,6 +20,7 @@
 
 package password.pwm;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import lombok.Builder;
 import lombok.Singular;
 import lombok.Value;
@@ -31,6 +32,7 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.ContextManager;
 import password.pwm.util.java.CollectionUtil;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.LazySupplier;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.json.JsonFactory;
 import password.pwm.util.logging.PwmLogger;
@@ -70,6 +72,8 @@ public class PwmEnvironment
     @Singular
     private Map<ApplicationParameter, String> parameters;
 
+    private final LazySupplier<DeploymentPlatform> deploymentPlatformLazySupplier = new LazySupplier<>( this::determineDeploymentPlatform );
+
     public enum ApplicationParameter
     {
         AutoExportHttpsKeyStoreFile,
@@ -91,8 +95,6 @@ public class PwmEnvironment
 
     public enum ApplicationFlag
     {
-        Appliance,
-        Docker,
         ManageHttps,
         NoFileLock,
         CommandLineInstance,;
@@ -103,6 +105,14 @@ public class PwmEnvironment
         }
     }
 
+    public enum DeploymentPlatform
+    {
+        War,
+        Onejar,
+        Docker,
+        Appliance,;
+    }
+
     public enum EnvironmentParameter
     {
         applicationPath,
@@ -372,4 +382,27 @@ public class PwmEnvironment
 
         return mode;
     }
+
+    public DeploymentPlatform getDeploymentPlatform()
+    {
+        return deploymentPlatformLazySupplier.get();
+    }
+
+    @SuppressFBWarnings( "DMI_HARDCODED_ABSOLUTE_FILENAME" )
+    private DeploymentPlatform determineDeploymentPlatform()
+    {
+        final File dockerEnvFile = new File( "/.dockerenv" );
+
+        if ( dockerEnvFile.exists() )
+        {
+            return DeploymentPlatform.Docker;
+        }
+
+        final String envValue = System.getProperty( "ONEJAR_ENV", "FALSE" );
+        if ( Boolean.getBoolean( envValue ) )
+        {
+            return DeploymentPlatform.Onejar;
+        }
+        return DeploymentPlatform.War;
+    }
 }

+ 1 - 2
server/src/main/java/password/pwm/bean/RemoteVerificationRequestBean.java

@@ -24,12 +24,11 @@ import lombok.Builder;
 import lombok.Value;
 import password.pwm.bean.pub.PublicUserInfoBean;
 
-import java.io.Serializable;
 import java.util.Map;
 
 @Value
 @Builder
-public class RemoteVerificationRequestBean implements Serializable
+public class RemoteVerificationRequestBean
 {
     private final String responseSessionID;
     private final PublicUserInfoBean userInfo;

+ 1 - 2
server/src/main/java/password/pwm/bean/RemoteVerificationResponseBean.java

@@ -23,11 +23,10 @@ package password.pwm.bean;
 import lombok.Value;
 import password.pwm.VerificationMethodSystem;
 
-import java.io.Serializable;
 import java.util.List;
 
 @Value
-public class RemoteVerificationResponseBean implements Serializable
+public class RemoteVerificationResponseBean
 {
     private String displayInstructions;
     private VerificationMethodSystem.VerificationState verificationState;

+ 1 - 2
server/src/main/java/password/pwm/http/JspUtility.java

@@ -34,7 +34,6 @@ import javax.servlet.ServletRequest;
 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;
@@ -72,7 +71,7 @@ public abstract class JspUtility
         }
     }
 
-    public static Serializable getAttribute( final PageContext pageContext, final PwmRequestAttribute requestAttr )
+    public static Object getAttribute( final PageContext pageContext, final PwmRequestAttribute requestAttr )
     {
         final PwmRequest pwmRequest = forRequest( pageContext.getRequest() );
         return pwmRequest.getAttribute( requestAttr );

+ 6 - 6
server/src/main/java/password/pwm/http/PwmCookiePath.java

@@ -28,20 +28,20 @@ import java.util.function.Function;
 public enum PwmCookiePath
 {
     Domain( ( pwmRequest ) -> "/" ),
-    Private( ( pwmRequest ) -> PwmConstants.URL_PREFIX_PRIVATE ),
+    Private( ( pwmRequest ) -> pwmRequest.getBasePath() + PwmConstants.URL_PREFIX_PRIVATE ),
     CurrentURL( ( pwmRequest ) -> pwmRequest.getURL().toString() ),
-    PwmServlet( ( pwmRequest ) -> pwmRequest.getURL().determinePwmServletPath() ),;
+    PwmServlet( ( pwmRequest ) -> pwmRequest.getBasePath() + pwmRequest.getURL().determinePwmServletPath() ),;
 
-    private final transient Function<PwmRequest, String> suffixFunction;
+    private final transient Function<PwmRequest, String> pathFunction;
 
-    PwmCookiePath( final Function<PwmRequest, String> suffixFunction )
+    PwmCookiePath( final Function<PwmRequest, String> pathFunction )
     {
-        this.suffixFunction = suffixFunction;
+        this.pathFunction = pathFunction;
     }
 
     String toStringPath( final PwmRequest pwmRequest )
             throws PwmUnrecoverableException
     {
-        return pwmRequest.getBasePath() + suffixFunction.apply( pwmRequest );
+        return pathFunction.apply( pwmRequest );
     }
 }

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

@@ -386,7 +386,7 @@ public class PwmRequest extends PwmHttpRequestWrapper
         return true;
     }
 
-    public void setAttribute( final PwmRequestAttribute name, final Serializable value )
+    public void setAttribute( final PwmRequestAttribute name, final Object value )
     {
         this.getHttpServletRequest().setAttribute( name.toString(), value );
     }

+ 2 - 3
server/src/main/java/password/pwm/http/servlet/admin/UserDebugDataBean.java

@@ -26,15 +26,14 @@ import password.pwm.Permission;
 import password.pwm.bean.pub.PublicUserInfoBean;
 import password.pwm.config.profile.ProfileDefinition;
 import password.pwm.config.profile.PwmPasswordPolicy;
-import password.pwm.user.UserInfo;
 import password.pwm.svc.pwnotify.PwNotifyUserStatus;
+import password.pwm.user.UserInfo;
 
-import java.io.Serializable;
 import java.util.Map;
 
 @Value
 @Builder
-public class UserDebugDataBean implements Serializable
+public class UserDebugDataBean
 {
     private static final long serialVersionUID = 1L;
 

+ 7 - 5
server/src/main/java/password/pwm/http/servlet/resource/ResourceServletService.java

@@ -33,7 +33,6 @@ import password.pwm.health.HealthRecord;
 import password.pwm.http.PwmRequest;
 import password.pwm.svc.AbstractPwmService;
 import password.pwm.svc.PwmService;
-import password.pwm.util.java.CrcChecksumOutputStream;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.Percent;
@@ -41,12 +40,15 @@ import password.pwm.util.java.StatisticAverageBundle;
 import password.pwm.util.java.StatisticCounterBundle;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.secure.PwmHashAlgorithm;
 
 import javax.servlet.ServletContext;
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.security.DigestOutputStream;
 import java.time.Instant;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -269,7 +271,7 @@ public class ResourceServletService extends AbstractPwmService implements PwmSer
     private String checksumAllResources( final PwmDomain pwmDomain )
             throws IOException
     {
-        try ( CrcChecksumOutputStream checksumStream = CrcChecksumOutputStream.newChecksumOutputStream( OutputStream.nullOutputStream() ) )
+        try ( DigestOutputStream checksumStream = new DigestOutputStream( OutputStream.nullOutputStream(), PwmHashAlgorithm.SHA512.newMessageDigest() ) )
         {
             checksumResourceFilePath( pwmDomain, checksumStream );
 
@@ -291,11 +293,11 @@ public class ResourceServletService extends AbstractPwmService implements PwmSer
                     }
                 }
             }
-            return Long.toString( checksumStream.checksum(), 36 );
+            return JavaHelper.binaryArrayToHex( checksumStream.getMessageDigest().digest() );
         }
     }
 
-    private static void checksumResourceFilePath( final PwmDomain pwmDomain, final CrcChecksumOutputStream checksumStream )
+    private static void checksumResourceFilePath( final PwmDomain pwmDomain, final DigestOutputStream checksumStream )
     {
         if ( pwmDomain.getPwmApplication().getPwmEnvironment().getContextManager() != null )
         {
@@ -316,7 +318,7 @@ public class ResourceServletService extends AbstractPwmService implements PwmSer
                                 while ( iter.hasNext()  )
                                 {
                                     final FileSystemUtility.FileSummaryInformation fileSummaryInformation = iter.next();
-                                    checksumStream.write( JavaHelper.longToBytes( fileSummaryInformation.getChecksum() ) );
+                                    checksumStream.write( fileSummaryInformation.getSha512Hash().getBytes( StandardCharsets.UTF_8 ) );
                                 }
 
                             }

+ 17 - 3
server/src/main/java/password/pwm/http/tag/conditional/PwmIfTest.java

@@ -93,7 +93,7 @@ public enum PwmIfTest
     forwardUrlDefined( new ForwardUrlDefinedTest() ),
 
     trialMode( new TrialModeTest() ),
-    appliance( new EnvironmentFlagTest( PwmEnvironment.ApplicationFlag.Appliance ) ),
+    appliance( new DeploymentTypeTest( PwmEnvironment.DeploymentPlatform.Appliance ) ),
 
     healthWarningsVisible( new HealthWarningsVisibleTest() ),
 
@@ -564,7 +564,6 @@ public enum PwmIfTest
         }
     }
 
-
     private static class MultiDomainTest implements Test
     {
         @Override
@@ -573,5 +572,20 @@ public enum PwmIfTest
             return pwmRequest.getPwmApplication().isMultiDomain();
         }
     }
-}
 
+    private static class DeploymentTypeTest implements Test
+    {
+        private final PwmEnvironment.DeploymentPlatform deploymentPlatform;
+
+        DeploymentTypeTest( final PwmEnvironment.DeploymentPlatform deploymentPlatform )
+        {
+            this.deploymentPlatform = deploymentPlatform;
+        }
+
+        @Override
+        public boolean test( final PwmRequest pwmRequest, final PwmIfOptions options ) throws PwmUnrecoverableException
+        {
+            return pwmRequest.getPwmApplication().getPwmEnvironment().getDeploymentPlatform() == deploymentPlatform;
+        }
+    }
+}

+ 9 - 0
server/src/main/java/password/pwm/svc/AbstractPwmService.java

@@ -56,6 +56,14 @@ public abstract class AbstractPwmService implements PwmService
         return status;
     }
 
+    @Override
+    public String name()
+    {
+        return "[" + this.getClass().getSimpleName()
+                + ( domainID == null || domainID.isSystem() ? "" : "/" + domainID.stringValue() )
+                + "]";
+    }
+
     public final void init( final PwmApplication pwmApplication, final DomainID domainID )
             throws PwmException
     {
@@ -129,6 +137,7 @@ public abstract class AbstractPwmService implements PwmService
             returnRecords.add( HealthRecord.forMessage(
                     DomainID.systemId(),
                     HealthMessage.ServiceClosed,
+                    name(),
                     startupError.toDebugStr() ) );
         }
 

+ 2 - 0
server/src/main/java/password/pwm/svc/PwmService.java

@@ -49,6 +49,8 @@ public interface PwmService
         CLOSED,
     }
 
+    String name();
+
     STATUS status( );
 
     void init( PwmApplication pwmApplication, DomainID domainID ) throws PwmException;

+ 0 - 8
server/src/main/java/password/pwm/svc/PwmServiceEnum.java

@@ -20,7 +20,6 @@
 
 package password.pwm.svc;
 
-import password.pwm.bean.DomainID;
 import password.pwm.config.PwmSettingScope;
 import password.pwm.health.HealthService;
 import password.pwm.ldap.LdapDomainService;
@@ -113,11 +112,4 @@ public enum PwmServiceEnum
     {
         return clazz;
     }
-
-    public String serviceName( final DomainID domainID )
-    {
-        return "[" + getPwmServiceClass().getSimpleName()
-                + ( domainID.isSystem() ? "" : "/" + domainID.stringValue() )
-                + "]";
-    }
 }

+ 17 - 17
server/src/main/java/password/pwm/svc/PwmServiceManager.java

@@ -101,7 +101,7 @@ public class PwmServiceManager
             {
                 if ( runningServices.containsKey( serviceClassEnum ) )
                 {
-                    shutDownService( serviceClassEnum, runningServices.get( serviceClassEnum ) );
+                    shutDownService( runningServices.get( serviceClassEnum ) );
                     statCounter.increment( InitializationStats.restarts );
                 }
                 else
@@ -116,7 +116,7 @@ public class PwmServiceManager
             {
                 if ( runningServices.containsKey( serviceClassEnum ) )
                 {
-                    shutDownService( serviceClassEnum, runningServices.get( serviceClassEnum ) );
+                    shutDownService( runningServices.get( serviceClassEnum ) );
                     statCounter.increment( InitializationStats.stops );
                 }
             }
@@ -138,8 +138,6 @@ public class PwmServiceManager
         final Instant startTime = Instant.now();
         final PwmService newServiceInstance;
 
-        final String serviceName = pwmServiceEnum.serviceName( domainID );
-
         try
         {
             final Class<? extends PwmService> serviceClass = pwmServiceEnum.getPwmServiceClass();
@@ -147,27 +145,28 @@ public class PwmServiceManager
         }
         catch ( final Exception e )
         {
-            final String errorMsg = "unexpected error instantiating " + debugSvcType() + " class '" + serviceName + "', error: " + e;
+            final String errorMsg = "unexpected error instantiating " + debugSvcType() + " class '" + pwmServiceEnum.name() + "', error: " + e;
             LOGGER.fatal( () -> errorMsg, e );
             throw new PwmUnrecoverableException( new ErrorInformation( PwmError.ERROR_STARTUP_ERROR, errorMsg ) );
         }
 
         try
         {
-            LOGGER.trace( sessionLabel, () -> "initializing service " + serviceName );
+            LOGGER.trace( sessionLabel, () -> "initializing service " + newServiceInstance.name() );
             newServiceInstance.init( pwmApplication, domainID );
             final TimeDuration startupDuration = TimeDuration.fromCurrent( startTime );
             LOGGER.debug( sessionLabel, () -> "completed initialization of " + debugSvcType()
-                    + " " + serviceName + " in " + startupDuration.asCompactString()
+                    + " " + newServiceInstance.name() + " in " + startupDuration.asCompactString()
                     + ", status=" + newServiceInstance.status() );
         }
         catch ( final PwmException e )
         {
-            LOGGER.warn( sessionLabel, () -> "error instantiating " + debugSvcType() + " class '" + serviceName + "', service will remain unavailable, error: " + e.getMessage() );
+            LOGGER.warn( sessionLabel, () -> "error instantiating " + debugSvcType() + " class '" + newServiceInstance.name()
+                    + "', service will remain unavailable, error: " + e.getMessage() );
         }
         catch ( final Exception e )
         {
-            String errorMsg = "unexpected error instantiating " + debugSvcType() + " class '" + serviceName + "', cannot load, error: " + e.getMessage();
+            String errorMsg = "unexpected error instantiating " + debugSvcType() + " class '" + newServiceInstance.name() + "', cannot load, error: " + e.getMessage();
             if ( e.getCause() != null )
             {
                 errorMsg += ", cause: " + e.getCause();
@@ -186,7 +185,9 @@ public class PwmServiceManager
             return;
         }
 
-        LOGGER.trace( sessionLabel, () -> "beginning to close all services" );
+        final int serviceCount = availableServices.size();
+
+        LOGGER.trace( sessionLabel, () -> "beginning to close " + serviceCount + " " + debugSvcType() + "s" );
         final Instant startTime = Instant.now();
 
 
@@ -196,30 +197,29 @@ public class PwmServiceManager
         {
             if ( runningServices.containsKey( pwmServiceEnum ) )
             {
-                shutDownService( pwmServiceEnum, runningServices.get( pwmServiceEnum ) );
+                shutDownService( runningServices.get( pwmServiceEnum ) );
             }
         }
         initialized = false;
 
-        LOGGER.trace( sessionLabel, () -> "closed all services", TimeDuration.fromCurrent( startTime ) );
+        LOGGER.trace( sessionLabel, () -> "closed " + serviceCount + " " + debugSvcType() + "s", TimeDuration.fromCurrent( startTime ) );
     }
 
-    private void shutDownService( final PwmServiceEnum pwmServiceEnum, final PwmService serviceInstance )
+    private void shutDownService( final PwmService serviceInstance )
     {
-
-        LOGGER.trace( sessionLabel, () -> "closing " + debugSvcType() + " " + pwmServiceEnum.serviceName( domainID ) );
+        LOGGER.trace( sessionLabel, () -> "closing " + debugSvcType() + " " + serviceInstance.name() );
 
         try
         {
             final Instant startTime = Instant.now();
             serviceInstance.shutdown();
             final TimeDuration timeDuration = TimeDuration.fromCurrent( startTime );
-            LOGGER.trace( sessionLabel, () -> "successfully closed " + debugSvcType() + " " + pwmServiceEnum.serviceName( domainID )
+            LOGGER.trace( sessionLabel, () -> "successfully closed " + debugSvcType() + " " + serviceInstance.name()
                     + " (" + timeDuration.asCompactString() + ")" );
         }
         catch ( final Exception e )
         {
-            LOGGER.error( sessionLabel, () -> "error closing " + debugSvcType() + " " + pwmServiceEnum.serviceName( domainID ) + ": " + e.getMessage(), e );
+            LOGGER.error( sessionLabel, () -> "error closing " + debugSvcType() + " " + serviceInstance.name() + ": " + e.getMessage(), e );
         }
     }
 

+ 60 - 27
server/src/main/java/password/pwm/svc/version/VersionCheckService.java

@@ -27,7 +27,6 @@ import password.pwm.AppProperty;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.DomainID;
-import password.pwm.bean.SessionLabel;
 import password.pwm.bean.VersionNumber;
 import password.pwm.bean.pub.PublishVersionBean;
 import password.pwm.config.AppConfig;
@@ -157,9 +156,17 @@ public class VersionCheckService extends AbstractPwmService
                 ? settings.getCheckIntervalError()
                 : settings.getCheckInterval();
 
-        this.nextScheduledCheck = localCache.getLastCheckTimestamp() == null
-                ? Instant.now().plus( 10, ChronoUnit.SECONDS )
-                : localCache.getLastCheckTimestamp().plus( idealDurationUntilNextCheck.asDuration() );
+        if ( localCache.getLastCheckTimestamp() == null )
+        {
+            this.nextScheduledCheck = Instant.now().plus( 10, ChronoUnit.SECONDS );
+        }
+        else
+        {
+            final Instant nextIdealTimestamp = localCache.getLastCheckTimestamp().plus( idealDurationUntilNextCheck.asDuration() );
+            this.nextScheduledCheck = nextIdealTimestamp.isBefore( Instant.now() )
+                    ? Instant.now().plus( 10, ChronoUnit.SECONDS )
+                    : nextIdealTimestamp;
+        }
 
         final TimeDuration delayUntilNextExecution = TimeDuration.fromCurrent( this.nextScheduledCheck );
 
@@ -176,27 +183,49 @@ public class VersionCheckService extends AbstractPwmService
             return;
         }
 
-        cacheHolder.setVersionCheckInfoCache( executeFetch( pwmApplication, getSessionLabel(), settings ) );
+        try
+        {
+            processReturnedVersionBean( executeFetch() );
+        }
+        catch ( final PwmUnrecoverableException e )
+        {
+            cacheHolder.setVersionCheckInfoCache( VersionCheckInfoCache.builder()
+                    .lastError( e.getErrorInformation() )
+                    .lastCheckTimestamp( Instant.now() )
+                    .build() );
+        }
 
         scheduleNextCheck();
     }
 
-    private static VersionCheckInfoCache executeFetch(
-            final PwmApplication pwmApplication,
-            final SessionLabel sessionLabel,
-            final VersionCheckSettings settings
-    )
+    private void processReturnedVersionBean( final PublishVersionBean publishVersionBean )
+    {
+        final VersionNumber currentVersion = publishVersionBean.getVersions().get( PublishVersionBean.VersionKey.current );
+
+        LOGGER.trace( getSessionLabel(), () -> "successfully fetched current version information from cloud service: "
+                + currentVersion );
+
+        cacheHolder.setVersionCheckInfoCache( VersionCheckInfoCache.builder()
+                .currentVersion( currentVersion )
+                .lastCheckTimestamp( Instant.now() )
+                .build() );
+    }
+
+    private PublishVersionBean executeFetch()
+            throws PwmUnrecoverableException
     {
         final Instant startTime = Instant.now();
         try
         {
-            final PwmHttpClient pwmHttpClient = pwmApplication.getHttpClientService().getPwmHttpClient( sessionLabel );
+            final PwmHttpClient pwmHttpClient = pwmApplication.getHttpClientService().getPwmHttpClient( getSessionLabel() );
             final PwmHttpClientRequest request = PwmHttpClientRequest.builder()
                     .url( settings.getUrl() )
+                    .header( HttpHeader.ContentType.getHttpName(), HttpContentType.json.getHeaderValueWithEncoding() )
                     .header( HttpHeader.Accept.getHttpName(), HttpContentType.json.getHeaderValueWithEncoding() )
+                    .body( JsonFactory.get().serialize( makeRequestBody() ) )
                     .build();
 
-            LOGGER.trace( sessionLabel, () -> "sending cloud version request to: " + settings.getUrl() );
+            LOGGER.trace( getSessionLabel(), () -> "sending cloud version request to: " + settings.getUrl() );
             final PwmHttpClientResponse response = pwmHttpClient.makeRequest( request );
 
             if ( response.getStatusCode() == 200 )
@@ -204,21 +233,13 @@ public class VersionCheckService extends AbstractPwmService
                 final Type restResultBeanType = JsonFactory.get().newParameterizedType( RestResultBean.class, PublishVersionBean.class );
                 final String body = response.getBody();
                 final RestResultBean<PublishVersionBean> restResultBean = JsonFactory.get().deserialize( body, restResultBeanType );
-                final PublishVersionBean publishVersionBean = restResultBean.getData();
-
-                final VersionNumber currentVersion = publishVersionBean.getVersions().get( PublishVersionBean.VersionKey.current );
+                return restResultBean.getData();
 
-                LOGGER.trace( sessionLabel, () -> "successfully fetched current version information from cloud service: "
-                        + currentVersion, TimeDuration.fromCurrent( startTime ) );
 
-                return VersionCheckInfoCache.builder()
-                        .currentVersion( currentVersion )
-                        .lastCheckTimestamp( Instant.now() )
-                        .build();
             }
             else
             {
-                LOGGER.debug( sessionLabel, () -> "error reading cloud current version information: " + response );
+                LOGGER.debug( getSessionLabel(), () -> "error reading cloud current version information: " + response );
                 final String msg = "error reading cloud current version information: " + response.getStatusLine();
                 throw PwmUnrecoverableException.newException( PwmError.ERROR_UNREACHABLE_CLOUD_SERVICE, msg );
             }
@@ -237,14 +258,26 @@ public class VersionCheckService extends AbstractPwmService
                 errorInformation = new ErrorInformation( PwmError.ERROR_UNREACHABLE_CLOUD_SERVICE, errorMsg );
             }
 
-            LOGGER.debug( sessionLabel, () -> "error fetching current version from cloud: "
+            LOGGER.debug( getSessionLabel(), () -> "error fetching current version from cloud: "
                     + e.getMessage(), TimeDuration.fromCurrent( startTime ) );
 
-            return VersionCheckInfoCache.builder()
-                    .lastError( errorInformation )
-                    .lastCheckTimestamp( Instant.now() )
-                    .build();
+            throw new PwmUnrecoverableException( errorInformation );
+        }
+    }
+
+    private PublishVersionBean makeRequestBody()
+    {
+        VersionNumber versionNumber = VersionNumber.ZERO;
+        try
+        {
+            versionNumber = VersionNumber.parse( PwmConstants.BUILD_VERSION );
         }
+        catch ( final Exception e )
+        {
+            LOGGER.trace( getSessionLabel(), () -> "error reading local version number " + e.getMessage() );
+        }
+
+        return new PublishVersionBean( Collections.singletonMap( PublishVersionBean.VersionKey.current, versionNumber ) );
     }
 
     @Override

+ 2 - 2
server/src/main/java/password/pwm/util/debug/FileInfoDebugItemGenerator.java

@@ -93,7 +93,7 @@ class FileInfoDebugItemGenerator implements AppItemGenerator
             headerRow.add( "Filename" );
             headerRow.add( "Last Modified" );
             headerRow.add( "Size" );
-            headerRow.add( "Checksum" );
+            headerRow.add( "Sha512Hash" );
             csvPrinter.printComment( StringUtil.join( headerRow, "," ) );
         }
 
@@ -108,7 +108,7 @@ class FileInfoDebugItemGenerator implements AppItemGenerator
                 dataRow.add( fileSummaryInformation.getFilename() );
                 dataRow.add( StringUtil.toIsoDate( fileSummaryInformation.getModified() ) );
                 dataRow.add( String.valueOf( fileSummaryInformation.getSize() ) );
-                dataRow.add( Long.toString( fileSummaryInformation.getChecksum() ) );
+                dataRow.add( fileSummaryInformation.getSha512Hash() );
                 csvPrinter.printRecord( dataRow );
             }
             catch ( final Exception e )

+ 7 - 19
server/src/main/java/password/pwm/util/java/FileSystemUtility.java

@@ -22,11 +22,12 @@ package password.pwm.util.java;
 
 import lombok.Value;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.secure.PwmHashAlgorithm;
+import password.pwm.util.secure.SecureEngine;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.Serializable;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -142,14 +143,14 @@ public class FileSystemUtility
         private final String filepath;
         private final Instant modified;
         private final long size;
-        private final long checksum;
+        private final String sha512Hash;
 
         public static FileSummaryInformation fromFile( final File file )
         {
-            final long crc32;
+            final String sha512Hash;
             try
             {
-                crc32 = crc32( file );
+                sha512Hash = SecureEngine.hash( new FileInputStream( file ), PwmHashAlgorithm.SHA512 );
             }
             catch ( final IOException exception )
             {
@@ -161,7 +162,7 @@ public class FileSystemUtility
                     file.getParentFile().getAbsolutePath(),
                     Instant.ofEpochMilli( file.lastModified() ),
                     file.length(),
-                    crc32
+                    sha512Hash
             );
         }
     }
@@ -182,17 +183,4 @@ public class FileSystemUtility
             Files.delete( nextPath );
         }
     }
-
-    private static long crc32( final File file )
-            throws IOException
-    {
-        try ( InputStream fileInputStream = Files.newInputStream( file.toPath() ) )
-        {
-            try ( CrcChecksumOutputStream crcChecksumOutputStream = CrcChecksumOutputStream.newChecksumOutputStream( OutputStream.nullOutputStream() ) )
-            {
-                JavaHelper.copy( fileInputStream, crcChecksumOutputStream );
-                return crcChecksumOutputStream.checksum();
-            }
-        }
-    }
 }

+ 6 - 0
server/src/main/java/password/pwm/util/localdb/LocalDBService.java

@@ -52,6 +52,12 @@ public class LocalDBService implements PwmService
         return STATUS.CLOSED;
     }
 
+    @Override
+    public String name()
+    {
+        return LocalDBService.class.getSimpleName();
+    }
+
     @Override
     public void init( final PwmApplication pwmApplication, final DomainID domainID ) throws PwmException
     {

+ 1 - 2
webapp/pom.xml

@@ -147,7 +147,7 @@
             <plugin>
                 <groupId>com.github.jinnovations</groupId>
                 <artifactId>attribution-maven-plugin</artifactId>
-                <version>0.9.9-SNAPSHOT</version>
+                <version>0.9.8</version>
                 <executions>
                     <execution>
                         <goals>
@@ -157,7 +157,6 @@
                     </execution>
                 </executions>
                 <configuration>
-                    <threads>20</threads>
                     <outputFile>${project.build.directory}/classes/attribution.xml</outputFile>
                     <dependencyOverrides>
                         <dependencyOverride>

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

@@ -475,7 +475,7 @@
                     <% } %>
                 </div>
                 <% } else { %>
-                <div class="footnote">Node data is not yet available.</div>
+                <div class="footnote">Node data is not yet available, please check again in a few minutes.</div>
                 <% } %>
             </div>
 

+ 2 - 2
webapp/src/main/webapp/WEB-INF/jsp/configguide-ldap_cert.jsp

@@ -107,7 +107,7 @@
             <div id="outline_ldapcert-options" class="setting_outline">
                 <div class="setting_title">Certificate Settings</div>
                 <div class="setting_body">
-                    <% if (!JspUtility.getPwmRequest(pageContext).getPwmApplication().getPwmEnvironment().getFlags().contains(PwmEnvironment.ApplicationFlag.Appliance)) { %>
+                    <pwm:if test="<%=PwmIfTest.appliance%>" negate="true">
                     <div style="padding-left: 5px; padding-top: 5px">
                         At least one of the following options must be selected to continue.
                     </div>
@@ -121,7 +121,7 @@
                         (Import/remove certificate manually into Java keystore to change)
                     </div>
                     <br/>
-                    <% } %>
+                    </pwm:if>
                     <div id="titlePane_useConfig" style="padding-left: 5px; padding-top: 5px">
                         Use application to manage certificate(s) and import certificates into configuration file
                         <br/>