Skip to content

Commit

Permalink
Merge pull request #135 from wultra/issues/merge-upstream
Browse files Browse the repository at this point in the history
Merge upstream
  • Loading branch information
banterCZ authored Jan 8, 2024
2 parents 96b5911 + de86f41 commit c76a915
Show file tree
Hide file tree
Showing 16 changed files with 466 additions and 78 deletions.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM ibm-semeru-runtimes:open-17.0.8_7-jre
FROM ibm-semeru-runtimes:open-17.0.9_9-jre
LABEL maintainer="[email protected]"

# Prepare environment variables
Expand All @@ -8,7 +8,7 @@ ENV JAVA_HOME=/opt/java/openjdk \
PKG_RELEASE=1~jammy \
TOMCAT_HOME=/usr/local/tomcat \
TOMCAT_MAJOR=10 \
TOMCAT_VERSION=10.1.13 \
TOMCAT_VERSION=10.1.17 \
TZ=UTC

ENV PATH=$PATH:$LB_HOME:$TOMCAT_HOME/bin
Expand All @@ -20,7 +20,7 @@ RUN apt-get -y update \

# Install tomcat
RUN curl -jkSL -o /tmp/apache-tomcat.tar.gz http://archive.apache.org/dist/tomcat/tomcat-${TOMCAT_MAJOR}/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz \
&& [ "406c0c367ac6ad95bb724ecc3a3c340ad7ded8c62287d657811eeec496eaaca1f5add52d2f46111da1426ae67090c543f6deccfeb5fdf4bdae32f9b39e773265 /tmp/apache-tomcat.tar.gz" = "$(sha512sum /tmp/apache-tomcat.tar.gz)" ] \
&& [ "ff9670f9cd49a604e47edfbcfb5855fe59342048c3278ea8736276b51327adf2d076973f3ad1b8aa7870ef26c28cf7111527be810b445c9927f2a457795f5cb6 /tmp/apache-tomcat.tar.gz" = "$(sha512sum /tmp/apache-tomcat.tar.gz)" ] \
&& gunzip /tmp/apache-tomcat.tar.gz \
&& tar -C /opt -xf /tmp/apache-tomcat.tar \
&& ln -s /opt/apache-tomcat-$TOMCAT_VERSION $TOMCAT_HOME
Expand Down
8 changes: 4 additions & 4 deletions docs-private/Developer-How-To-Start.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ mvn clean package
### Build the docker image

```shell
docker build . -t enrollment-server:1.5.0
docker build . -t enrollment-server:1.6.0
```


### Prepare environment variables

* Copy `deploy/env.list.tmp` to `./env.list` and edit the values to use it via `docker run --env-file env.list IMAGE`
* Or set environment variables via `docker run -e ENROLLMENT_SERVER_DATASOURCE_USERNAME='powerauth' IMAGE`
* Copy `deploy/env.list.tmp` to `./env.list` and edit the values to use it via `docker run --env-file env.list enrollment-server:1.6.0`
* Or set environment variables via `docker run -e ENROLLMENT_SERVER_DATASOURCE_USERNAME='powerauth' enrollment-server:1.6.0`


### Run the docker image

```shell
docker run -p 80:8080 -e ENROLLMENT_SERVER_DATASOURCE_URL='jdbc:postgresql://host.docker.internal:5432/powerauth' -e ENROLLMENT_SERVER_DATASOURCE_USERNAME='powerauth' -e ENROLLMENT_SERVER_DATASOURCE_PASSWORD='' enrollment-server:1.5.0
docker run -p 80:8080 -e ENROLLMENT_SERVER_DATASOURCE_URL='jdbc:postgresql://host.docker.internal:5432/powerauth' -e ENROLLMENT_SERVER_DATASOURCE_USERNAME='powerauth' -e ENROLLMENT_SERVER_DATASOURCE_PASSWORD='' enrollment-server:1.6.0
```


Expand Down
6 changes: 6 additions & 0 deletions docs/Configuration-Properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,9 @@ Sample setting of logging pattern:
```properties
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint}%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
```


## Monitoring and Observability

The WAR file includes the `micrometer-registry-prometheus` dependency.
Discuss its configuration with the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/3.1.x/reference/html/actuator.html#actuator.metrics).
6 changes: 6 additions & 0 deletions docs/onboarding/Configuration-Properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,9 @@ Sample setting of logging pattern:
```properties
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint}%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
```


## Monitoring and Observability

The WAR file includes the `micrometer-registry-prometheus` dependency.
Discuss its configuration with the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/3.1.x/reference/html/actuator.html#actuator.metrics).
6 changes: 6 additions & 0 deletions enrollment-server-onboarding/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@
<artifactId>logstash-logback-encoder</artifactId>
</dependency>

<!-- Monitoring -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- Test Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,10 @@ powerauth.service.correlation-header.enabled=false
powerauth.service.correlation-header.name=X-Correlation-ID
powerauth.service.correlation-header.value.validation-regexp=[a-zA-Z0-9\\-]{8,1024}
# For logging correlation HTTP headers enable the pattern and update correlation header name in the pattern
#logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
#logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}

# Monitoring
#management.endpoint.metrics.enabled=true
#management.endpoints.web.exposure.include=health, prometheus
#management.endpoint.prometheus.enabled=true
#management.prometheus.metrics.export.enabled=true
6 changes: 6 additions & 0 deletions enrollment-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@
<artifactId>logstash-logback-encoder</artifactId>
</dependency>

<!-- Monitoring -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

<!-- Test Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ public ObjectResponse<OperationListResponse> operationList(@Parameter(hidden = t
if (auth != null) {
final String userId = auth.getUserId();
final String applicationId = auth.getApplicationId();
final List<String> activationFlags = auth.getActivationContext().getActivationFlags();
final String activationId = auth.getActivationContext().getActivationId();
final String language = locale.getLanguage();
final OperationListResponse listResponse = mobileTokenService.operationListForUser(userId, applicationId, language, activationFlags, true);
final OperationListResponse listResponse = mobileTokenService.operationListForUser(userId, applicationId, language, activationId, true);
final Date currentTimestamp = new Date();
return new MobileTokenResponse<>(listResponse, currentTimestamp);
} else {
Expand Down Expand Up @@ -211,9 +211,9 @@ public ObjectResponse<OperationListResponse> operationListAll(@Parameter(hidden
if (auth != null) {
final String userId = auth.getUserId();
final String applicationId = auth.getApplicationId();
final List<String> activationFlags = auth.getActivationContext().getActivationFlags();
final String activationId = auth.getActivationContext().getActivationId();
final String language = locale.getLanguage();
final OperationListResponse listResponse = mobileTokenService.operationListForUser(userId, applicationId, language, activationFlags, false);
final OperationListResponse listResponse = mobileTokenService.operationListForUser(userId, applicationId, language, activationId, false);
return new ObjectResponse<>(listResponse);
} else {
throw new MobileTokenAuthException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,30 +87,33 @@ public MobileTokenService(PowerAuthClient powerAuthClient, MobileTokenConverter
}

/**
* Get the operation list with operations of a given users. The service either returns only pending
* operations or all operations, depending on the provided flag.
* Retrieves a list of operations for a specified user. This method can return
* either all operations or only those that are pending, based on the 'pendingOnly' flag.
* It processes each operation detail, converts them into a consistent format, and
* filters out operations without a corresponding template.
*
* @param userId User ID.
* @param applicationId Application ID.
* @param language Language.
* @param activationFlags Activation flags to condition the operation against.
* @param pendingOnly Flag indicating if only pending or all operation should be returned.
* @return Response with pending or all operations, depending on the "pendingOnly" flag.
* @throws PowerAuthClientException In the case that PowerAuth service call fails.
* @throws MobileTokenConfigurationException In the case of system misconfiguration.
* @param userId User ID for which the operation list is requested.
* @param applicationId Application ID associated with the operations.
* @param language Language for operation template localization.
* @param activationId Optional activation ID to filter operations.
* @param pendingOnly Flag indicating whether to fetch only pending operations or all.
* @return A consolidated list of operations formatted as 'OperationListResponse'.
* @throws PowerAuthClientException If there's an issue with the PowerAuth service call.
* @throws MobileTokenConfigurationException For any system configuration issues.
*/
public OperationListResponse operationListForUser(
@NotNull String userId,
@NotNull String applicationId,
@NotNull String language,
List<String> activationFlags,
String activationId,
boolean pendingOnly) throws PowerAuthClientException, MobileTokenConfigurationException {

final OperationListForUserRequest request = new OperationListForUserRequest();
request.setUserId(userId);
request.setApplications(List.of(applicationId));
request.setPageNumber(0);
request.setPageSize(OPERATION_LIST_LIMIT);
request.setActivationId(activationId);
final MultiValueMap<String, String> queryParams = httpCustomizationService.getQueryParams();
final MultiValueMap<String, String> httpHeaders = httpCustomizationService.getHttpHeaders();
final com.wultra.security.powerauth.client.model.response.OperationListResponse operations =
Expand All @@ -120,16 +123,13 @@ public OperationListResponse operationListForUser(

final OperationListResponse responseObject = new OperationListResponse();
for (OperationDetailResponse operationDetail: operations) {
final String activationFlag = operationDetail.getActivationFlag();
if (activationFlag == null || activationFlags.contains(activationFlag)) { // only return data if there is no flag, or if flag matches flags of activation
final Optional<OperationTemplateEntity> operationTemplate = operationTemplateService.findTemplate(operationDetail.getOperationType(), language);
if (operationTemplate.isEmpty()) {
logger.warn("No template found for operationType={}, skipping the entry.", operationDetail.getOperationType());
continue;
}
final Operation operation = mobileTokenConverter.convert(operationDetail, operationTemplate.get());
responseObject.add(operation);
final Optional<OperationTemplateEntity> operationTemplate = operationTemplateService.findTemplate(operationDetail.getOperationType(), language);
if (operationTemplate.isEmpty()) {
logger.warn("No template found for operationType={}, skipping the entry.", operationDetail.getOperationType());
continue;
}
final Operation operation = mobileTokenConverter.convert(operationDetail, operationTemplate.get());
responseObject.add(operation);
}
return responseObject;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,29 +285,39 @@ private static Optional<Attribute> buildAmountAttribute(final OperationTemplateP
if (currency.isEmpty()) {
return Optional.empty();
}
final BigDecimal amountRaw;
try {
amountRaw = new BigDecimal(amount.get());
} catch (NumberFormatException ex) {
logger.warn("Invalid number format: {}, skipping the AMOUNT attribute!", amount);
return Optional.empty();
}

final Locale locale = LocaleContextHolder.getLocale();
final String currencyRaw = currency.get();
final String currencyFormatted = MonetaryConverter.formatCurrency(currencyRaw, locale);
final String amountFormatted = MonetaryConverter.formatAmount(amountRaw, currencyRaw, locale);
final String valueFormatted = MonetaryConverter.formatValue(amountRaw, currencyRaw, locale);
final AmountFormatted amountFormatted = createAmountFormatted(amount.get(), currencyRaw, "AMOUNT");
return Optional.of(AmountAttribute.builder()
.id(id)
.label(text)
.amount(amountRaw)
.amountFormatted(amountFormatted)
.amount(amountFormatted.amountRaw())
.amountFormatted(amountFormatted.amountFormatted())
.currency(currencyRaw)
.currencyFormatted(currencyFormatted)
.valueFormatted(valueFormatted)
.valueFormatted(amountFormatted.valueFormatted())
.build());
}

private static AmountFormatted createAmountFormatted(final String amount, final String currencyRaw, final String attribute) {
final Locale locale = LocaleContextHolder.getLocale();

try {
final BigDecimal amountRaw = new BigDecimal(amount);
final String amountFormatted = MonetaryConverter.formatAmount(amountRaw, currencyRaw, locale);
final String valueFormatted = MonetaryConverter.formatValue(amountRaw, currencyRaw, locale);
return new AmountFormatted(amountRaw, amountFormatted, valueFormatted);
} catch (NumberFormatException e) {
logger.warn("Invalid number format: {}, the raw value is not filled in into {} attribute!", amount, attribute);
logger.trace("Invalid number format: {}, the raw value is not filled in into {} attribute!", amount, attribute, e);
// fallback - pass 'not a number' directly to the formatted field
final String valueFormatted = amount + " " + currencyRaw;
return new AmountFormatted(null, amount, valueFormatted);
}
}

private static Optional<Attribute> buildAmountConversionAttribute(final OperationTemplateParam templateParam, final Map<String, String> params) {
final String id = templateParam.getId();
final String text = templateParam.getText();
Expand All @@ -326,39 +336,29 @@ private static Optional<Attribute> buildAmountConversionAttribute(final Operatio
.map(Boolean::parseBoolean)
.orElse(false);

final BigDecimal sourceAmountRaw;
final BigDecimal targetAmountRaw;
try {
sourceAmountRaw = new BigDecimal(sourceAmount.get());
targetAmountRaw = new BigDecimal(targetAmount.get());
} catch (NumberFormatException ex) {
logger.warn("Invalid number format: {}, skipping the AMOUNT_CONVERSION attribute!", sourceAmount);
return Optional.empty();
}

final Locale locale = LocaleContextHolder.getLocale();
final String sourceCurrencyRaw = sourceCurrency.get();
final String sourceCurrencyFormatted = MonetaryConverter.formatCurrency(sourceCurrencyRaw, locale);
final String sourceAmountFormatted = MonetaryConverter.formatAmount(sourceAmountRaw, sourceCurrencyRaw, locale);
final String sourceValueFormatted = MonetaryConverter.formatValue(sourceAmountRaw, sourceCurrencyRaw, locale);
final AmountFormatted sourceAmountFormatted = createAmountFormatted(sourceAmount.get(), sourceCurrencyRaw, "AMOUNT_CONVERSION");

final String targetCurrencyRaw = targetCurrency.get();
final AmountFormatted targetAmountFormatted = createAmountFormatted(targetAmount.get(), targetCurrencyRaw, "AMOUNT_CONVERSION");

final String sourceCurrencyFormatted = MonetaryConverter.formatCurrency(sourceCurrencyRaw, locale);
final String targetCurrencyFormatted = MonetaryConverter.formatCurrency(targetCurrencyRaw, locale);
final String targetAmountFormatted = MonetaryConverter.formatAmount(targetAmountRaw, targetCurrencyRaw, locale);
final String targetValueFormatted = MonetaryConverter.formatValue(targetAmountRaw, targetCurrencyRaw, locale);
return Optional.of(AmountConversionAttribute.builder()
.id(id)
.label(text)
.dynamic(dynamic)
.sourceAmount(sourceAmountRaw)
.sourceAmountFormatted(sourceAmountFormatted)
.sourceAmount(sourceAmountFormatted.amountRaw())
.sourceAmountFormatted(sourceAmountFormatted.amountFormatted())
.sourceCurrency(sourceCurrencyRaw)
.sourceCurrencyFormatted(sourceCurrencyFormatted)
.sourceValueFormatted(sourceValueFormatted)
.targetAmount(targetAmountRaw)
.targetAmountFormatted(targetAmountFormatted)
.sourceValueFormatted(sourceAmountFormatted.valueFormatted())
.targetAmount(targetAmountFormatted.amountRaw())
.targetAmountFormatted(targetAmountFormatted.amountFormatted())
.targetCurrency(targetCurrencyRaw)
.targetCurrencyFormatted(targetCurrencyFormatted)
.targetValueFormatted(targetValueFormatted)
.targetValueFormatted(targetAmountFormatted.valueFormatted())
.build());
}

Expand Down Expand Up @@ -397,7 +397,7 @@ private static Optional<String> fetchTemplateParamValue(final OperationTemplateP
return Optional.empty();
}
if (params == null) {
logger.warn("Params of OperationDetailResponse is null");
logger.warn("Params of OperationTemplateParam is null");
return Optional.empty();
}
return Optional.ofNullable(templateParams.get(key))
Expand All @@ -408,4 +408,6 @@ private static String fetchTemplateParamValueNullable(final OperationTemplatePar
return fetchTemplateParamValue(templateParam, params, key)
.orElse(null);
}

private record AmountFormatted(BigDecimal amountRaw, String amountFormatted, String valueFormatted) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.UnknownCurrencyException;
import javax.money.MonetaryException;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Currency;
Expand Down Expand Up @@ -113,7 +113,7 @@ private static int getFractionDigits(String code) {
try {
final CurrencyUnit currencyUnit = Monetary.getCurrency(code);
return currencyUnit.getDefaultFractionDigits();
} catch (UnknownCurrencyException e) {
} catch (MonetaryException e) {
logger.debug("No currency mapping for code={}, most probably not FIAT", code);
logger.trace("No currency mapping for code={}", code, e);
return DEFAULT_MINIMAL_FRACTION_DIGITS;
Expand Down
Loading

0 comments on commit c76a915

Please sign in to comment.