Skip to content

Commit

Permalink
Merge pull request #245 from wultra/develop
Browse files Browse the repository at this point in the history
Merge develop to master
  • Loading branch information
banterCZ authored Dec 12, 2023
2 parents a5d0eb0 + d3a6b66 commit 7f19099
Show file tree
Hide file tree
Showing 16 changed files with 428 additions and 44 deletions.
2 changes: 1 addition & 1 deletion annotations/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>io.getlime.core</groupId>
<artifactId>lime-java-core-parent</artifactId>
<version>1.7.0</version>
<version>1.8.0</version>
</parent>

<artifactId>annotations</artifactId>
Expand Down
4 changes: 2 additions & 2 deletions audit-base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.getlime.core</groupId>
<artifactId>lime-java-core-parent</artifactId>
<version>1.7.0</version>
<version>1.8.0</version>
</parent>

<artifactId>audit-base</artifactId>
Expand Down Expand Up @@ -59,8 +59,8 @@
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
</plugin>
</plugins>
</build>
Expand Down
51 changes: 51 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.getlime.core</groupId>
<artifactId>lime-java-core-parent</artifactId>
<version>1.8.0</version>
</parent>

<artifactId>core-bom</artifactId>
<description>Bill of Materials for Wultra Core</description>
<packaging>pom</packaging>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.getlime.core</groupId>
<artifactId>annotations</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>io.getlime.core</groupId>
<artifactId>audit-base</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>io.getlime.core</groupId>
<artifactId>http-common</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>io.getlime.core</groupId>
<artifactId>rest-client-base</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>io.getlime.core</groupId>
<artifactId>rest-model-base</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

</project>
20 changes: 19 additions & 1 deletion http-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>io.getlime.core</groupId>
<artifactId>lime-java-core-parent</artifactId>
<version>1.7.0</version>
<version>1.8.0</version>
</parent>

<artifactId>http-common</artifactId>
Expand All @@ -19,12 +19,30 @@
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2023 Wultra s.r.o.
*
* 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 com.wultra.core.http.common.headers;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Utility class for processing our standard user agent strings.
*
* @author Petr Dvorak, [email protected]
* @author Lubos Racansky, [email protected]
*/
@Slf4j
public final class UserAgent {

@Data
public static class Device {
private String networkVersion;
private String language;
private String connection;
private String product;
private String version;
private String platform;
private String os;
private String osVersion;
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");
}

/**
* Parse client user from the HTTP header value.
*
* @param userAgent User-Agent Header String
* @return Parsed device info, or empty if the user agent header cannot be parsed.
*/
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 = PATTERN_PREFIX.matcher(userAgent);
if (!matcherPrefix.matches()) {
return Optional.empty();
}
final String networkVersion = matcherPrefix.group("networkVersion");
logger.debug("Declared networkVersion: {}", networkVersion);
if (!networkVersion.startsWith("1.")) { // simplistic matching for current v1.x clients
return Optional.empty();
}

// Parse the device object
return parseUserAgentV1(userAgent);
}

/**
* Private method for parsing client user from the v1.x mobile clients. It is added for convenience
* when new versions with another formats will be eventually introduced.
*
* @param userAgent User-Agent Header String
* @return Parsed device info, or empty if the user agent header cannot be parsed.
*/
private static Optional<Device> parseUserAgentV1(String userAgent) {
final Matcher matcher = PATTERN_V1.matcher(userAgent);
if (matcher.matches()) {
final Device device = new Device();
device.setNetworkVersion(matcher.group("networkVersion"));
device.setLanguage(matcher.group("language"));
device.setConnection(matcher.group("connection"));
device.setProduct(matcher.group("product"));
device.setVersion(matcher.group("version"));
device.setPlatform(matcher.group("platform"));
device.setOs(matcher.group("os"));
device.setOsVersion(matcher.group("osVersion"));
device.setModel(matcher.group("model"));
return Optional.of(device);
}
logger.debug("The user agent value does not match v1 client format");
return Optional.empty();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright 2023 Wultra s.r.o.
*
* 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 com.wultra.core.http.common.headers;

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 {@link UserAgent}.
*
* @author Petr Dvorak, [email protected]
* @author Lubos Racansky, [email protected]
*/
class UserAgentTest {

@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);
}

}
Loading

0 comments on commit 7f19099

Please sign in to comment.