-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Fix #223: Improve User-Agent parsing and add rests * Reformat code * Add test data * Refactor to parametrized test * Add surefire * Add failure message * Multiple prefixes * Make v1 elements optional * Parse network, os, osVersion and model from another examples * Rename constant * Prefer \\d * Explicit operator precedence * Extract sub patterns --------- Co-authored-by: Lubos Racansky <[email protected]>
- Loading branch information
1 parent
75fed3d
commit 858e37c
Showing
6 changed files
with
143 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
* Utility class for processing our standard user agent strings. | ||
* | ||
* @author Petr Dvorak, [email protected] | ||
* @author Lubos Racansky, [email protected] | ||
*/ | ||
@Slf4j | ||
public final class UserAgent { | ||
|
@@ -43,15 +44,18 @@ public static class Device { | |
private String model; | ||
} | ||
|
||
private static final String PREFIX = "((^PowerAuthNetworking)|.*PowerAuth2)/(?<networkVersion>\\d+\\.\\d+\\.\\d+)"; | ||
private static final Pattern PATTERN_PREFIX = Pattern.compile(PREFIX + ".*"); | ||
|
||
private static final String LANGUAGE_AND_CONNECTION = "(\\((?<language>[a-zA-Z]{2}); (?<connection>[a-zA-Z0-9]+)\\) )?"; | ||
private static final String PRODUCT_AND_VERSION = "((?<product>[a-zA-Z0-9-_.]+)/(?<version>[0-9.]+(-[^ ]*)?) )?"; | ||
private static final String PLATFORM_OS_VERSION_MODEL = "(\\(((?<platform>[^;]+); )?(?<os>[^/ ]+)[/ ](?<osVersion>[^;,]+)[;,] (?<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/(?<networkVersion>[0-9]+\\.[0-9]+\\.[0-9]+).*"); | ||
private static final Pattern patternV1 = Pattern.compile("^PowerAuthNetworking/(?<networkVersion>[0-9]+\\.[0-9]+\\.[0-9]+) " + | ||
"\\((?<language>[a-zA-Z]{2}); (?<connection>[a-zA-Z0-9]+)\\) " + | ||
"(?<product>[a-zA-Z0-9-_.]+)/(?<version>[0-9.]+) .*" + | ||
"\\((?<platform>[^;]+); (?<os>[^/]+)/(?<osVersion>[^;]+); (?<model>[^)]+)\\)$"); | ||
|
||
/** | ||
* Parse client user from the HTTP header value. | ||
* | ||
|
@@ -61,7 +65,7 @@ private UserAgent() { | |
public static Optional<Device> 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<Device> parse(String userAgent) { | |
* @return Parsed device info, or empty if the user agent header cannot be parsed. | ||
*/ | ||
private static Optional<Device> 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")); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,35 +15,120 @@ | |
*/ | ||
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; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
/** | ||
* Test for the user agent parser. | ||
* Test for {@link UserAgent}. | ||
* | ||
* @author Petr Dvorak, [email protected] | ||
* @author Lubos Racansky, [email protected] | ||
*/ | ||
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<UserAgent.Device> 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<UserAgent.Device> deviceOptional = UserAgent.parse(userAgent); | ||
assertTrue(deviceOptional.isPresent(), "Unable to parse user-agent: " + userAgent); | ||
assertEquals(expectedDevice, deviceOptional.get()); | ||
} | ||
|
||
private static Stream<Arguments> 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" | ||
} | ||
""")) | ||
); | ||
} | ||
} | ||
|
||
private static UserAgent.Device readDevice(final String json) throws JsonProcessingException { | ||
return new ObjectMapper().readValue(json, UserAgent.Device.class); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters