FINERACT-2314: IP tracking (#4825)
Co-authored-by: Juan Pablo Alvarez Hernandez <work_jpa@hotmailcom>
This commit is contained in:
co-authored by
Juan Pablo Alvarez Hernandez
parent
39050aa01f
commit
b0ca0a24cc
@@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.IpAddressUtils;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
|
||||
@Entity
|
||||
@@ -137,6 +138,9 @@ public class CommandSource extends AbstractPersistableCustom<Long> {
|
||||
@Column(name = "loan_external_id", length = 100)
|
||||
private ExternalId loanExternalId;
|
||||
|
||||
@Column(name = "client_ip", nullable = true)
|
||||
private String clientIp;
|
||||
|
||||
@Column(name = "is_sanitized", nullable = false)
|
||||
private boolean sanitized;
|
||||
|
||||
@@ -162,6 +166,7 @@ public class CommandSource extends AbstractPersistableCustom<Long> {
|
||||
.transactionId(command.getTransactionId()) //
|
||||
.creditBureauId(command.getCreditBureauId()) //
|
||||
.organisationCreditBureauId(command.getOrganisationCreditBureauId()) //
|
||||
.clientIp(IpAddressUtils.getClientIp()) //
|
||||
.loanExternalId(command.getLoanExternalId()).sanitized(sanitized).build(); //
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -596,5 +596,4 @@ public final class JsonCommand {
|
||||
public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Set<String> requestDataParameters) {
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, requestDataParameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -50,6 +50,8 @@ public class FineractProperties {
|
||||
|
||||
private FineractCorrelationProperties correlation;
|
||||
|
||||
private FineractIpTrackingProperties ipTracking;
|
||||
|
||||
private FineractPartitionedJob partitionedJob;
|
||||
|
||||
private FineractRemoteJobMessageHandlerProperties remoteJobMessageHandler;
|
||||
@@ -153,6 +155,13 @@ public class FineractProperties {
|
||||
private String headerName;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractIpTrackingProperties {
|
||||
|
||||
private boolean enabled;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FineractPartitionedJob {
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.fineract.infrastructure.core.filters;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class CallerIpTrackingFilter extends OncePerRequestFilter {
|
||||
|
||||
private final FineractProperties fineractProperties;
|
||||
|
||||
/**
|
||||
* Common headers used to get client IP from different proxies.
|
||||
*
|
||||
* "X-Forwarded-For", // Standard header used by proxies "Proxy-Client-IP", // Used by some Apache proxies
|
||||
* "WL-Proxy-Client-IP", // Used by WebLogic "HTTP_X_FORWARDED_FOR", // Alternative to X-Forwarded-For
|
||||
* "HTTP_X_FORWARDED", // Variation of X-Forwarded "HTTP_X_CLUSTER_CLIENT_IP", // Used in clustered environments
|
||||
* "HTTP_CLIENT_IP", // Fallback, less common "HTTP_FORWARDED_FOR", // Less standard, used in some setups
|
||||
* "HTTP_FORWARDED", // Standardized header (RFC 7239) that can include client IP, proxy info, and protocol
|
||||
* "HTTP_VIA", // Shows intermediate proxies "REMOTE_ADDR" // Server's perceived client IP
|
||||
*/
|
||||
|
||||
private static final String[] IP_HEADER_CANDIDATES = { "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP",
|
||||
"HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED", "HTTP_X_CLUSTER_CLIENT_IP", "HTTP_CLIENT_IP", "HTTP_FORWARDED_FOR",
|
||||
"HTTP_FORWARDED", "HTTP_VIA", "REMOTE_ADDR" };
|
||||
|
||||
public String getClientIpAddress(HttpServletRequest request) {
|
||||
for (String header : IP_HEADER_CANDIDATES) {
|
||||
String ip = request.getHeader(header);
|
||||
if (ip != null && ip.length() != 0 && !ip.isEmpty()) {
|
||||
log.trace("CALLER IP : {}", ip);
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
log.trace("getRemoteAddr method : {}", request.getRemoteAddr());
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
if (fineractProperties.getIpTracking().isEnabled()) {
|
||||
handleClientIp(request, response, filterChain);
|
||||
} else {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleClientIp(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
try {
|
||||
String clientIpAddress = getClientIpAddress(request);
|
||||
if (StringUtils.isNotBlank(clientIpAddress)) {
|
||||
log.trace("Found Client IP in header : {}", clientIpAddress);
|
||||
request.setAttribute("IP", clientIpAddress);
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
request.setAttribute("IP", "");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAsyncDispatch(final HttpServletRequest request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilterErrorDispatch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.fineract.infrastructure.core.service;
|
||||
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
public final class IpAddressUtils {
|
||||
|
||||
private IpAddressUtils() {}
|
||||
|
||||
public static String getClientIp() {
|
||||
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
String clientIp = "";
|
||||
if (attrs != null) {
|
||||
Object ipAttr = attrs.getRequest().getAttribute("IP");
|
||||
if (ipAttr != null) {
|
||||
clientIp = ipAttr.toString();
|
||||
}
|
||||
}
|
||||
return clientIp;
|
||||
}
|
||||
}
|
||||
+2
@@ -56,6 +56,7 @@ public class IdempotencyCommandProcessFailedExceptionTest {
|
||||
|
||||
@Test
|
||||
public void testInconsistentStatus() {
|
||||
|
||||
IdempotentCommandExceptionMapper mapper = new IdempotentCommandExceptionMapper();
|
||||
CommandWrapper command = new CommandWrapper(null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null);
|
||||
@@ -64,5 +65,6 @@ public class IdempotencyCommandProcessFailedExceptionTest {
|
||||
Response result = mapper.toResponse(exception);
|
||||
assertEquals(500, result.getStatus());
|
||||
assertEquals("true", result.getHeaderString(IDEMPOTENT_CACHE_HEADER));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user