diff --git a/audit-base/pom.xml b/audit-base/pom.xml index f767ab6..7cbfd22 100644 --- a/audit-base/pom.xml +++ b/audit-base/pom.xml @@ -59,8 +59,8 @@ + org.apache.maven.plugins maven-surefire-plugin - 3.2.2 diff --git a/http-common/pom.xml b/http-common/pom.xml index c53e75b..9e1b483 100644 --- a/http-common/pom.xml +++ b/http-common/pom.xml @@ -29,6 +29,20 @@ spring-boot-starter-test test + + com.fasterxml.jackson.core + jackson-databind + test + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + \ No newline at end of file diff --git a/http-common/src/main/java/com/wultra/core/http/common/headers/UserAgent.java b/http-common/src/main/java/com/wultra/core/http/common/headers/UserAgent.java index 9935ff5..08d328b 100644 --- a/http-common/src/main/java/com/wultra/core/http/common/headers/UserAgent.java +++ b/http-common/src/main/java/com/wultra/core/http/common/headers/UserAgent.java @@ -26,6 +26,7 @@ * Utility class for processing our standard user agent strings. * * @author Petr Dvorak, petr@wultra.com + * @author Lubos Racansky, lubos.racansky@wultra.com */ @Slf4j public final class UserAgent { @@ -43,15 +44,18 @@ public static class Device { private String model; } + private static final String PREFIX = "((^PowerAuthNetworking)|.*PowerAuth2)/(?\\d+\\.\\d+\\.\\d+)"; + private static final Pattern PATTERN_PREFIX = Pattern.compile(PREFIX + ".*"); + + private static final String LANGUAGE_AND_CONNECTION = "(\\((?[a-zA-Z]{2}); (?[a-zA-Z0-9]+)\\) )?"; + private static final String PRODUCT_AND_VERSION = "((?[a-zA-Z0-9-_.]+)/(?[0-9.]+(-[^ ]*)?) )?"; + private static final String PLATFORM_OS_VERSION_MODEL = "(\\(((?[^;]+); )?(?[^/ ]+)[/ ](?[^;,]+)[;,] (?[^)]+)\\))?"; + private static final Pattern PATTERN_V1 = Pattern.compile(PREFIX + " " + LANGUAGE_AND_CONNECTION + PRODUCT_AND_VERSION + PLATFORM_OS_VERSION_MODEL + ".*"); + private UserAgent() { + throw new IllegalStateException("Should not be instantiated"); } - private static final Pattern patternPrefix = Pattern.compile("^PowerAuthNetworking/(?[0-9]+\\.[0-9]+\\.[0-9]+).*"); - private static final Pattern patternV1 = Pattern.compile("^PowerAuthNetworking/(?[0-9]+\\.[0-9]+\\.[0-9]+) " + - "\\((?[a-zA-Z]{2}); (?[a-zA-Z0-9]+)\\) " + - "(?[a-zA-Z0-9-_.]+)/(?[0-9.]+) .*" + - "\\((?[^;]+); (?[^/]+)/(?[^;]+); (?[^)]+)\\)$"); - /** * Parse client user from the HTTP header value. * @@ -61,7 +65,7 @@ private UserAgent() { public static Optional parse(String userAgent) { // Identify if the user agent is ours and in what version logger.debug("Parsing user agent value: {}", userAgent); - final Matcher matcherPrefix = patternPrefix.matcher(userAgent); + final Matcher matcherPrefix = PATTERN_PREFIX.matcher(userAgent); if (!matcherPrefix.matches()) { return Optional.empty(); } @@ -83,7 +87,7 @@ public static Optional parse(String userAgent) { * @return Parsed device info, or empty if the user agent header cannot be parsed. */ private static Optional parseUserAgentV1(String userAgent) { - final Matcher matcher = patternV1.matcher(userAgent); + final Matcher matcher = PATTERN_V1.matcher(userAgent); if (matcher.matches()) { final Device device = new Device(); device.setNetworkVersion(matcher.group("networkVersion")); diff --git a/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java b/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java index 9696c94..3796254 100644 --- a/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java +++ b/http-common/src/test/java/com/wultra/core/http/common/headers/UserAgentTest.java @@ -15,7 +15,13 @@ */ package com.wultra.core.http.common.headers; -import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import java.util.Optional; @@ -23,27 +29,106 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** - * Test for the user agent parser. + * Test for {@link UserAgent}. * * @author Petr Dvorak, petr@wultra.com + * @author Lubos Racansky, lubos.racansky@wultra.com */ class UserAgentTest { - @Test - void parse() { - final String sample = "PowerAuthNetworking/1.1.7 (en; cellular) com.wultra.app.Mobile-Token.wultra_test/2.0.0 (Apple; iOS/16.6.1; iphone12,3)"; - final Optional deviceOptional = UserAgent.parse(sample); - assertTrue(deviceOptional.isPresent()); - - final UserAgent.Device device = deviceOptional.get(); - assertEquals("1.1.7", device.getNetworkVersion()); - assertEquals("en", device.getLanguage()); - assertEquals("cellular", device.getConnection()); - assertEquals("com.wultra.app.Mobile-Token.wultra_test", device.getProduct()); - assertEquals("2.0.0", device.getVersion()); - assertEquals("Apple", device.getPlatform()); - assertEquals("iOS", device.getOs()); - assertEquals("16.6.1", device.getOsVersion()); - assertEquals("iphone12,3", device.getModel()); + @ParameterizedTest + @MethodSource("provideUserAgents") + void testParse(final String userAgent, final UserAgent.Device expectedDevice) { + final Optional deviceOptional = UserAgent.parse(userAgent); + assertTrue(deviceOptional.isPresent(), "Unable to parse user-agent: " + userAgent); + assertEquals(expectedDevice, deviceOptional.get()); + } + + private static Stream provideUserAgents() throws JsonProcessingException { + return Stream.of( + Arguments.of("PowerAuthNetworking/1.1.7 (en; cellular) com.wultra.app.Mobile-Token.wultra_test/2.0.0 (Apple; iOS/16.6.1; iphone12,3)", readDevice(""" + { + "networkVersion": "1.1.7", + "language": "en", + "connection": "cellular", + "product": "com.wultra.app.Mobile-Token.wultra_test", + "version": "2.0.0", + "platform": "Apple", + "os": "iOS", + "osVersion": "16.6.1", + "model": "iphone12,3" + } + """)), + Arguments.of("PowerAuthNetworking/1.2.1 (uk; wifi) com.wultra.android.mtoken.gdnexttest/1.0.0-gdnexttest (samsung; Android/13; SM-A047F)", readDevice(""" + { + "networkVersion": "1.2.1", + "language": "uk", + "connection": "wifi", + "product": "com.wultra.android.mtoken.gdnexttest", + "version": "1.0.0-gdnexttest", + "platform": "samsung", + "os": "Android", + "osVersion": "13", + "model": "SM-A047F" + } + """)), + Arguments.of("PowerAuthNetworking/1.1.7 (en; unknown) com.wultra.app.MobileToken.wtest/2.0.0 (Apple; iOS/16.6.1; iphone10,6)", readDevice(""" + { + "networkVersion": "1.1.7", + "language": "en", + "connection": "unknown", + "product": "com.wultra.app.MobileToken.wtest", + "version": "2.0.0", + "platform": "Apple", + "os": "iOS", + "osVersion": "16.6.1", + "model": "iphone10,6" + } + """)), + Arguments.of("PowerAuthNetworking/1.1.7 (en; wifi) com.wultra.app.MobileToken.wtest/2.0.0 (Apple; iOS/16.7.1; iphone10,6)", readDevice(""" + { + "networkVersion": "1.1.7", + "language": "en", + "connection": "wifi", + "product": "com.wultra.app.MobileToken.wtest", + "version": "2.0.0", + "platform": "Apple", + "os": "iOS", + "osVersion": "16.7.1", + "model": "iphone10,6" + } + """)), + // MainBundle/Version PowerAuth2/Version (iOS Version, deviceString) + Arguments.of("PowerAuth2TestsHostApp-ios/1.0 PowerAuth2/1.7.8 (iOS 17.0, simulator)", readDevice(""" + { + "networkVersion": "1.7.8", + "os": "iOS", + "osVersion": "17.0", + "model": "simulator" + } + """)), + // PowerAuth2/Version (Android Version, Build.MANUFACTURER Build.MODEL) + Arguments.of("PowerAuth2/1.7.8 (Android 13, Google Pixel 4)", readDevice(""" + { + "networkVersion": "1.7.8", + "os": "Android", + "osVersion": "13", + "model": "Google Pixel 4" + } + """)), + Arguments.of("MobileToken/1.2.0 PowerAuth2/1.7.8 (iOS 15.7.9, iPhone9,3)", readDevice(""" + { + "networkVersion": "1.7.8", + "os": "iOS", + "osVersion": "15.7.9", + "model": "iPhone9,3" + } + """)) + ); } -} \ No newline at end of file + + private static UserAgent.Device readDevice(final String json) throws JsonProcessingException { + return new ObjectMapper().readValue(json, UserAgent.Device.class); + } + +} diff --git a/pom.xml b/pom.xml index ba93e64..71db424 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,7 @@ UTF-8 17 17 + 3.2.2 3.1.6 @@ -96,6 +97,16 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + org.apache.maven.plugins diff --git a/rest-client-base/pom.xml b/rest-client-base/pom.xml index 2773afd..4e58e14 100644 --- a/rest-client-base/pom.xml +++ b/rest-client-base/pom.xml @@ -66,8 +66,8 @@ + org.apache.maven.plugins maven-surefire-plugin - 3.2.2