diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cd6303d..ec309d3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Change Log +## [vX.X.X](...) +- Updating readme with `customIsSensitve`, `customParametersExtraction` +- Added an option to configure logger without slf4j using `PXConfiguration.setPxLoggerSeverity()` +- Added an option to close PerimeterX [SDKNEW-2781](https://perimeterx.atlassian.net/browse/SDKNEW-2781) ## [v6.5.0](https://github.com/PerimeterX/perimeterx-java-sdk/compare/v6.5.0...HEAD) (2023-03-04) - Adding custom is sensitive configuration option diff --git a/README.md b/README.md index be780b26..2db69682 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ - [Advanced Usage Examples](#advanced-usage) - [Data Enrichment](#data-enrichment) - [Custom Parameters](#custom-parameters) + - [Custom Sensitive Request](#custom-sensitive-request) - [Multiple Application Support](#multi-app-support) - [Configuration](CONFIGURATIONS.md) - [Logging and Troubleshooting](#loggin-troubleshoot) @@ -190,31 +191,51 @@ enforcer.setVerificationHandler(new MyVerificationHandler(config)); ... ``` -#### Custom Parameters +#### Custom Sensitive Request +With the `customIsSensitive` predicate you can force the request to be sensitive. +The input of the function is the same request that sent to the method `pxVerify`. +If the function throws exception, it is equivalent to returning `false`. +Implementing this configuration does NOT override other `sensitive` configurations, like `sensitive_routes`. -With the `customParametersProvider` function you can add up to 10 custom parameters to be sent back to PerimeterX servers. When set, the function is called before setting the payload on every request to PerimetrX servers. +> **Note** +> The request body can only be read once by default. If your function requires reading the body +> consider using RequestWrapper which caches the body. Send the wrapped request to +> `pxVerify` instead of the native one. -MyCustomParametersProvider.java: +In your filter: ```java ... -public class MyCustomParametersProvider implements CustomParametersProvider { - public CustomParameters buildCustomParameters(PXConfiguration pxConfiguration, PXContext pxContext) { - CustomParameters customParameters = new CustomParameters(); - customParameters.setCustomParam1("my_custom_param_1"); - customParameters.setCustomParam2("my_custom_param_2"); +PXConfiguration pxConfiguration = new PXConfiguration.Builder() ... - customParameters.setCustomParam10("my_custom_param_10"); - return customParameters; - } -} + .customIsSensitiveRequest((req) -> req.getHeader("example-header") == "example-value") + .build(); + ``` -Then, in your filter: +#### Custom Parameters + +With the `customParametersExtraction` function you can add up to 10 custom parameters to be sent back to PerimeterX servers. +When set, the function is called before setting the payload on every request to PerimetrX servers. +The input of the function is the same request that sent to the method `pxVerify`. +If the function throws exception, it is equivalent to returning empty custom params. +Implementing this configuration overrides the deprecated configuration `customParameterProvider`. + +> **Note** +> The request body can only be read once by default. If your function requires reading the body +> consider using RequestWrapper which caches the body. Send the wrapped request to +> `pxVerify` instead of the native one. + +In your filter: ```java ... PXConfiguration pxConfiguration = new PXConfiguration.Builder() ... - .customParametersProvider(new MyCustomParametersProvider()) + .customParametersExtraction((req) -> { + CustomParameters customParameters = new CustomParameters(); + customParameters.setCustomParam1("example-value"); + customParameters.setCustomParam2(req.getHeader("example-header")); + return customParameters; + }) .build(); ... ``` @@ -251,6 +272,20 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se For further information please visit [SLF4J](https://www.slf4j.org/manual.html) and [Logback](https://logback.qos.ch). +If you wish to use a basic logger which uses `System.out` and `System.err` to print debug and error accordingly, +Change the value of the static variable to your desired level. +```java +import com.perimeterx.models.configuration.PXConfiguration; +import com.perimeterx.utils.LoggerSeverity; + +PXConfiguration.setPxLoggerSeverity(LoggerSeverity.DEBUG); +``` +> **Note** +> This method can be executed once, no need to execute it every request. + + +--- + The following steps are welcome when contributing to our project. #### Fork/Clone diff --git a/src/main/java/com/perimeterx/api/PerimeterX.java b/src/main/java/com/perimeterx/api/PerimeterX.java index 693f6516..e85a9eee 100644 --- a/src/main/java/com/perimeterx/api/PerimeterX.java +++ b/src/main/java/com/perimeterx/api/PerimeterX.java @@ -61,13 +61,12 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponseWrapper; +import java.io.Closeable; import java.io.IOException; import java.net.URISyntaxException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import java.util.Base64; -import java.util.Optional; import static com.perimeterx.utils.Constants.*; import static java.util.Objects.isNull; @@ -78,7 +77,7 @@ * Created by shikloshi on 03/07/2016. */ -public class PerimeterX { +public class PerimeterX implements Closeable { private static final PXLogger logger = PXLogger.getLogger(PerimeterX.class); @@ -90,6 +89,7 @@ public class PerimeterX { private HostnameProvider hostnameProvider; private VerificationHandler verificationHandler; private ReverseProxy reverseProxy; + private PXHttpClient pxClient = null; private void init(PXConfiguration configuration) throws PXException { logger.debug(PXLogger.LogReason.DEBUG_INITIALIZING_MODULE); @@ -97,7 +97,7 @@ private void init(PXConfiguration configuration) throws PXException { this.configuration = configuration; hostnameProvider = new DefaultHostnameProvider(); ipProvider = new CombinedIPProvider(configuration); - PXHttpClient pxClient = new PXHttpClient(configuration); + this.pxClient = new PXHttpClient(configuration); this.activityHandler = new BufferedActivityHandler(pxClient, this.configuration); if (configuration.isRemoteConfigurationEnabled()) { @@ -337,4 +337,10 @@ public void setVerificationHandler(VerificationHandler verificationHandler) { this.verificationHandler = verificationHandler; } + @Override + public void close() throws IOException { + if(this.pxClient != null) { + this.pxClient.close(); + } + } } \ No newline at end of file diff --git a/src/main/java/com/perimeterx/http/PXHttpClient.java b/src/main/java/com/perimeterx/http/PXHttpClient.java index 1b2f146d..ffb87bad 100644 --- a/src/main/java/com/perimeterx/http/PXHttpClient.java +++ b/src/main/java/com/perimeterx/http/PXHttpClient.java @@ -39,6 +39,7 @@ import org.apache.http.nio.reactor.IOReactorExceptionHandler; import org.apache.http.util.EntityUtils; +import java.io.Closeable; import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.charset.Charset; @@ -50,7 +51,7 @@ *

* Created by shikloshi on 04/07/2016. */ -public class PXHttpClient implements PXClient { +public class PXHttpClient implements PXClient, Closeable { private static final int INACTIVITY_PERIOD_TIME_MS = 1000; private static final long MAX_IDLE_TIME_SEC = 30L; @@ -61,7 +62,7 @@ public class PXHttpClient implements PXClient { private CloseableHttpClient httpClient; private CloseableHttpAsyncClient asyncHttpClient; private PoolingNHttpClientConnectionManager nHttpConnectionManager; - + private final TimerValidateRequestsQueue timerConfigUpdater; private PXConfiguration pxConfiguration; public PXHttpClient(PXConfiguration pxConfiguration) throws PXException { @@ -73,7 +74,7 @@ public PXHttpClient(PXConfiguration pxConfiguration) throws PXException { throw new PXException(e); } - TimerValidateRequestsQueue timerConfigUpdater = new TimerValidateRequestsQueue(nHttpConnectionManager, pxConfiguration); + this.timerConfigUpdater = new TimerValidateRequestsQueue(nHttpConnectionManager, pxConfiguration); timerConfigUpdater.schedule(); } @@ -316,4 +317,17 @@ public void sendEnforcerTelemetry(EnforcerTelemetry enforcerTelemetry) throws IO } } } + + @Override + public void close() throws IOException { + this.timerConfigUpdater.close(); + + if (this.asyncHttpClient != null) { + this.asyncHttpClient.close(); + } + + if (this.httpClient != null) { + this.httpClient.close(); + } + } } diff --git a/src/main/java/com/perimeterx/http/TimerValidateRequestsQueue.java b/src/main/java/com/perimeterx/http/TimerValidateRequestsQueue.java index 87d333a1..a3dcd1e2 100644 --- a/src/main/java/com/perimeterx/http/TimerValidateRequestsQueue.java +++ b/src/main/java/com/perimeterx/http/TimerValidateRequestsQueue.java @@ -4,13 +4,15 @@ import com.perimeterx.utils.PXLogger; import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager; +import java.io.Closeable; import java.util.Timer; import java.util.TimerTask; -public class TimerValidateRequestsQueue extends TimerTask { +public class TimerValidateRequestsQueue extends TimerTask implements Closeable { private static final PXLogger logger = PXLogger.getLogger(TimerValidateRequestsQueue.class); + private Timer timer = null; private PoolingNHttpClientConnectionManager nHttpConnectionManager; private PXConfiguration pxConfiguration; @@ -29,7 +31,16 @@ public void run() { * Sets a new timer object and runs its execution method */ public void schedule() { - Timer timer = new Timer(); + this.close(); + timer = new Timer(); timer.schedule(this, 0, pxConfiguration.getValidateRequestQueueInterval()); } + + @Override + public void close() { + if(timer != null) { + timer.cancel(); + timer = null; + } + } } diff --git a/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java b/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java index b2280ebd..67b94236 100644 --- a/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java +++ b/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java @@ -15,6 +15,7 @@ import com.perimeterx.models.risk.CustomParameters; import com.perimeterx.utils.Constants; import com.perimeterx.utils.FilesUtils; +import com.perimeterx.utils.LoggerSeverity; import com.perimeterx.utils.PXLogger; import lombok.AllArgsConstructor; import lombok.Builder; @@ -46,6 +47,15 @@ @Getter public class PXConfiguration { private static final PXLogger logger = PXLogger.getLogger(PXConfiguration.class); + private static LoggerSeverity loggerSeverity = null; + + public static LoggerSeverity getPxLoggerSeverity() { + return loggerSeverity; + } + + public static void setPxLoggerSeverity(LoggerSeverity severity) { + loggerSeverity = severity; + } @JsonProperty("px_app_id") private String appId; diff --git a/src/main/java/com/perimeterx/utils/ConsoleLogger.java b/src/main/java/com/perimeterx/utils/ConsoleLogger.java new file mode 100644 index 00000000..6eba4d19 --- /dev/null +++ b/src/main/java/com/perimeterx/utils/ConsoleLogger.java @@ -0,0 +1,47 @@ +package com.perimeterx.utils; + +import java.io.PrintStream; +public class ConsoleLogger implements PXLogger { + private final LoggerSeverity severity; + + public ConsoleLogger(LoggerSeverity severity) { + this.severity = severity; + } + + private void log(PrintStream out, String prefix, Object msg, Object... additional) { + StringBuilder builder = new StringBuilder(); + builder.append(prefix); + builder.append(msg); + for (Object arg : additional) { + builder.append(" ").append(arg.toString()); + } + out.println(builder); + } + @Override + public void debug(LogReason reason, Object... args) { + if(severity.level >= LoggerSeverity.DEBUG.level) { + log(System.out, PXLogger.DEBUG_PREFIX, reason, args); + } + } + + @Override + public void debug(String msg, Object... args) { + if(severity.level >= LoggerSeverity.DEBUG.level) { + log(System.out, PXLogger.DEBUG_PREFIX, msg, args); + } + } + + @Override + public void error(LogReason reason, Object... args) { + if(severity.level >= LoggerSeverity.ERROR.level) { + log(System.err, PXLogger.ERROR_PREFIX, reason, args); + } + } + + @Override + public void error(String msg, Object... args) { + if(severity.level >= LoggerSeverity.ERROR.level) { + log(System.err, msg, args); + } + } +} diff --git a/src/main/java/com/perimeterx/utils/HMACUtils.java b/src/main/java/com/perimeterx/utils/HMACUtils.java index ca9c1fbd..81ca6d2f 100644 --- a/src/main/java/com/perimeterx/utils/HMACUtils.java +++ b/src/main/java/com/perimeterx/utils/HMACUtils.java @@ -26,7 +26,7 @@ public static boolean isHMACValid(String encodedString, String hmac, byte[] bCookieHmac = StringUtils.hexStringToByteArray(hmac); isValid = Arrays.equals(bHMAC, bCookieHmac); } catch (Exception e) { - logger.error(PXLogger.LogReason.DEBUG_COOKIE_HMAC_VALIDATION_FAILED, e.getMessage()); + logger.debug(PXLogger.LogReason.DEBUG_COOKIE_HMAC_VALIDATION_FAILED, e.getMessage()); isValid = false; } diff --git a/src/main/java/com/perimeterx/utils/LoggerSeverity.java b/src/main/java/com/perimeterx/utils/LoggerSeverity.java new file mode 100644 index 00000000..3138d579 --- /dev/null +++ b/src/main/java/com/perimeterx/utils/LoggerSeverity.java @@ -0,0 +1,21 @@ +package com.perimeterx.utils; + +import com.fasterxml.jackson.annotation.JsonValue; + +public enum LoggerSeverity { + NONE(0), + ERROR(10), + DEBUG(100); + + + public final int level; + + LoggerSeverity(int level) { + this.level = level; + } + + @JsonValue + public String jsonName() { + return this.name().toLowerCase(); + } +} diff --git a/src/main/java/com/perimeterx/utils/PXLogger.java b/src/main/java/com/perimeterx/utils/PXLogger.java index 633ac699..124ea402 100644 --- a/src/main/java/com/perimeterx/utils/PXLogger.java +++ b/src/main/java/com/perimeterx/utils/PXLogger.java @@ -1,14 +1,10 @@ package com.perimeterx.utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.perimeterx.models.configuration.PXConfiguration; -public class PXLogger { - - private Logger logger; - - private final String DEBUG_PREFIX = "[PerimeterX - DEBUG] "; - private final String ERROR_PREFIX = "[PerimeterX - ERROR] "; +public interface PXLogger { + String DEBUG_PREFIX = "[PerimeterX - DEBUG] "; + String ERROR_PREFIX = "[PerimeterX - ERROR] "; public enum LogReason { DEBUG_INITIALIZING_MODULE("Initializing PerimeterX enforcer"), @@ -48,32 +44,25 @@ public enum LogReason { this.reason = reason; } + @Override public String toString() { return this.reason; } } - public static PXLogger getLogger(Class clazz) { - return new PXLogger(clazz); - } - - private PXLogger(Class clazz) { - logger = LoggerFactory.getLogger(clazz); - } - - public void debug(LogReason reason, Object... args) { - logger.debug(DEBUG_PREFIX + reason, args); + static PXLogger getLogger(Class clazz) { + LoggerSeverity pxLoggerSeverity = PXConfiguration.getPxLoggerSeverity(); + if (pxLoggerSeverity == null) { + return Slf4JLogger.getLogger(clazz); + } else { + return new ConsoleLogger(pxLoggerSeverity); + } } + void debug(LogReason reason, Object... args); - public void debug(String msg, Object... args) { - logger.debug(DEBUG_PREFIX + msg, args); - } + void debug(String msg, Object... args); - public void error(LogReason reason, Object... args) { - logger.error(ERROR_PREFIX + reason, args); - } + void error(LogReason reason, Object... args); - public void error(String msg, Object... args) { - logger.error(ERROR_PREFIX + msg, args); - } -} \ No newline at end of file + void error(String msg, Object... args); +} diff --git a/src/main/java/com/perimeterx/utils/Slf4JLogger.java b/src/main/java/com/perimeterx/utils/Slf4JLogger.java new file mode 100644 index 00000000..5fc21580 --- /dev/null +++ b/src/main/java/com/perimeterx/utils/Slf4JLogger.java @@ -0,0 +1,33 @@ +package com.perimeterx.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Slf4JLogger implements PXLogger { + + private final Logger logger; + + public static PXLogger getLogger(Class clazz) { + return new Slf4JLogger(clazz); + } + private Slf4JLogger(Class clazz) { + logger = LoggerFactory.getLogger(clazz); + } + @Override + public void debug(LogReason reason, Object... args) { + logger.debug(DEBUG_PREFIX + reason, args); + } + @Override + public void debug(String msg, Object... args) { + logger.debug(DEBUG_PREFIX + msg, args); + } + @Override + public void error(LogReason reason, Object... args) { + logger.error(ERROR_PREFIX + reason, args); + } + + @Override + public void error(String msg, Object... args) { + logger.error(ERROR_PREFIX + msg, args); + } +} \ No newline at end of file diff --git a/web/pom.xml b/web/pom.xml index 0f312b9d..08b4b77a 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -28,6 +28,21 @@ 6.5.0 compile + + org.slf4j + slf4j-api + 1.7.25 + + + org.slf4j + slf4j-log4j12 + 1.7.25 + + + log4j + log4j + 1.2.17 + diff --git a/web/src/main/java/com/web/PXFilter.java b/web/src/main/java/com/web/PXFilter.java index 60d7f8e2..8556fa98 100644 --- a/web/src/main/java/com/web/PXFilter.java +++ b/web/src/main/java/com/web/PXFilter.java @@ -54,6 +54,10 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha @Override public void destroy() { - + try { + pxFilter.close(); + } catch (IOException e) { + e.printStackTrace(); + } } } diff --git a/web/src/main/resources/log4j.properties b/web/src/main/resources/log4j.properties new file mode 100644 index 00000000..2b3c489d --- /dev/null +++ b/web/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +# Root logger option +log4j.rootLogger=ERROR, stdout + +# Direct log messages to stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target=System.out +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1} - %m%n \ No newline at end of file