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));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,4 +56,5 @@ public final class AuditData implements Serializable {
|
||||
private final Long clientId;
|
||||
private final Long loanId;
|
||||
private final String url;
|
||||
private final String ip;
|
||||
}
|
||||
|
||||
+8
-6
@@ -110,11 +110,12 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
|
||||
+ "ck.username as checker, aud.checked_on_date as checkedOnDate, aud.checked_on_date_utc as checkedOnDateUTC, ev.enum_message_property as processingResult "
|
||||
+ commandAsJsonString + ", "
|
||||
+ " o.name as officeName, gl.level_name as groupLevelName, g.display_name as groupName, c.display_name as clientName, "
|
||||
+ " l.account_no as loanAccountNo, s.account_no as savingsAccountNo " + " from m_portfolio_command_source aud "
|
||||
+ " left join m_appuser mk on mk.id = aud.maker_id" + " left join m_appuser ck on ck.id = aud.checker_id"
|
||||
+ " left join m_office o on o.id = aud.office_id" + " left join m_group g on g.id = aud.group_id"
|
||||
+ " left join m_group_level gl on gl.id = g.level_id" + " left join m_client c on c.id = aud.client_id"
|
||||
+ " left join m_loan l on l.id = aud.loan_id" + " left join m_savings_account s on s.id = aud.savings_account_id"
|
||||
+ " l.account_no as loanAccountNo, s.account_no as savingsAccountNo , aud.client_ip as ip "
|
||||
+ " from m_portfolio_command_source aud " + " left join m_appuser mk on mk.id = aud.maker_id"
|
||||
+ " left join m_appuser ck on ck.id = aud.checker_id" + " left join m_office o on o.id = aud.office_id"
|
||||
+ " left join m_group g on g.id = aud.group_id" + " left join m_group_level gl on gl.id = g.level_id"
|
||||
+ " left join m_client c on c.id = aud.client_id" + " left join m_loan l on l.id = aud.loan_id"
|
||||
+ " left join m_savings_account s on s.id = aud.savings_account_id"
|
||||
+ " left join r_enum_value ev on ev.enum_name = 'status' and ev.enum_id = aud.status";
|
||||
|
||||
// data scoping: head office (hierarchy = ".") can see all audit
|
||||
@@ -158,13 +159,14 @@ public class AuditReadPlatformServiceImpl implements AuditReadPlatformService {
|
||||
final String clientName = rs.getString("clientName");
|
||||
final String loanAccountNo = rs.getString("loanAccountNo");
|
||||
final String savingsAccountNo = rs.getString("savingsAccountNo");
|
||||
final String ip = rs.getString("ip");
|
||||
|
||||
ZonedDateTime madeOnDate = madeOnDateUTC != null ? madeOnDateUTC.toZonedDateTime() : madeOnDateTenant;
|
||||
ZonedDateTime checkedOnDate = checkedOnDateUTC != null ? checkedOnDateUTC.toZonedDateTime() : checkedOnDateTenant;
|
||||
|
||||
return new AuditData(id, actionName, entityName, resourceId, subresourceId, maker, madeOnDate, checker, checkedOnDate,
|
||||
processingResult, commandAsJson, officeName, groupLevelName, groupName, clientName, loanAccountNo, savingsAccountNo,
|
||||
clientId, loanId, resourceGetUrl);
|
||||
clientId, loanId, resourceGetUrl, ip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -32,6 +32,7 @@ import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadP
|
||||
import org.apache.fineract.infrastructure.cache.service.CacheWritePlatformService;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder;
|
||||
import org.apache.fineract.infrastructure.core.filters.CallerIpTrackingFilter;
|
||||
import org.apache.fineract.infrastructure.core.filters.CorrelationHeaderFilter;
|
||||
import org.apache.fineract.infrastructure.core.filters.IdempotencyStoreFilter;
|
||||
import org.apache.fineract.infrastructure.core.filters.IdempotencyStoreHelper;
|
||||
@@ -172,7 +173,9 @@ public class SecurityConfig {
|
||||
} else {
|
||||
http.addFilterAfter(idempotencyStoreFilter(), FineractInstanceModeApiFilter.class); //
|
||||
}
|
||||
|
||||
if (fineractProperties.getIpTracking().isEnabled()) {
|
||||
http.addFilterAfter(callerIpTrackingFilter(), RequestResponseFilter.class);
|
||||
}
|
||||
if (fineractProperties.getSecurity().getTwoFactor().isEnabled()) {
|
||||
http.addFilterAfter(twoFactorAuthenticationFilter(), CorrelationHeaderFilter.class);
|
||||
} else {
|
||||
@@ -219,6 +222,10 @@ public class SecurityConfig {
|
||||
return new CorrelationHeaderFilter(fineractProperties, mdcWrapper);
|
||||
}
|
||||
|
||||
public CallerIpTrackingFilter callerIpTrackingFilter() {
|
||||
return new CallerIpTrackingFilter(fineractProperties);
|
||||
}
|
||||
|
||||
public TenantAwareBasicAuthenticationFilter tenantAwareBasicAuthenticationFilter() throws Exception {
|
||||
TenantAwareBasicAuthenticationFilter filter = new TenantAwareBasicAuthenticationFilter(authenticationManagerBean(),
|
||||
basicAuthenticationEntryPoint(), toApiJsonSerializer, configurationDomainService, cacheWritePlatformService,
|
||||
|
||||
@@ -62,6 +62,7 @@ fineract.correlation.enabled=${FINERACT_LOGGING_HTTP_CORRELATION_ID_ENABLED:fals
|
||||
fineract.correlation.header-name=${FINERACT_LOGGING_HTTP_CORRELATION_ID_HEADER_NAME:X-Correlation-ID}
|
||||
|
||||
fineract.job.stuck-retry-threshold=${FINERACT_JOB_STUCK_RETRY_THRESHOLD:5}
|
||||
fineract.ip-tracking.enabled=${FINERACT_CLIENT_IP_TRACKING_ENABLED:false}
|
||||
fineract.job.loan-cob-enabled=${FINERACT_JOB_LOAN_COB_ENABLED:true}
|
||||
|
||||
fineract.partitioned-job.partitioned-job-properties[0].job-name=LOAN_COB
|
||||
|
||||
@@ -211,4 +211,5 @@
|
||||
<include file="parts/0190_buy_down_fee_amortization.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0191_add_LoanApprovedAmountChangedBusinessEvent.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0192_create_loan_approved_amount_history.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0193_add_column_ip.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_portfolio_command_source">
|
||||
<column name="client_ip" type="varchar(100)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* 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 static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.core.service.IpAddressUtils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
public class IpTrackingFilterTest {
|
||||
|
||||
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" };
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
@Mock
|
||||
private FilterChain filterChain;
|
||||
|
||||
private FineractProperties fineractProperties;
|
||||
private CallerIpTrackingFilter underTest;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
fineractProperties = new FineractProperties();
|
||||
FineractProperties.FineractIpTrackingProperties props = new FineractProperties.FineractIpTrackingProperties();
|
||||
props.setEnabled(true);
|
||||
fineractProperties.setIpTracking(props);
|
||||
underTest = new CallerIpTrackingFilter(fineractProperties);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "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" })
|
||||
void testGetClientIp_UsesCorrectHeaderEnabled(String headerName) throws ServletException, IOException {
|
||||
// given
|
||||
given(request.getHeader(headerName)).willReturn("192.168.1.100");
|
||||
|
||||
// when
|
||||
underTest.doFilterInternal(request, response, filterChain);
|
||||
|
||||
// then
|
||||
verify(request, times(1)).setAttribute("IP", "192.168.1.100");
|
||||
verify(filterChain).doFilter(request, response);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "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" })
|
||||
void testGetClientIpAddress_UsesCorrectHeaderDisabled(String headerName) throws ServletException, IOException {
|
||||
// given
|
||||
FineractProperties.FineractIpTrackingProperties props = new FineractProperties.FineractIpTrackingProperties();
|
||||
props.setEnabled(false);
|
||||
fineractProperties.setIpTracking(props);
|
||||
underTest = new CallerIpTrackingFilter(fineractProperties);
|
||||
|
||||
given(request.getHeader(headerName)).willReturn("192.168.1.100");
|
||||
|
||||
// when
|
||||
underTest.doFilterInternal(request, response, filterChain);
|
||||
|
||||
// then
|
||||
verify(request, never()).setAttribute("IP", "192.168.1.100");
|
||||
verify(filterChain).doFilter(request, response);
|
||||
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "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" })
|
||||
void testGetClientIpAddress_UsesCorrectHeaderIpAdressUtilsEnable(String headerName) throws ServletException, IOException {
|
||||
// given
|
||||
given(request.getHeader(headerName)).willReturn("192.168.1.100");
|
||||
|
||||
// when
|
||||
underTest.doFilterInternal(request, response, filterChain);
|
||||
|
||||
// then
|
||||
verify(request, times(1)).setAttribute("IP", IpAddressUtils.getClientIp());
|
||||
verify(filterChain).doFilter(request, response);
|
||||
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "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" })
|
||||
void testGetClientIpAddress_UsesCorrectHeaderIpAdressUtilsDisabled(String headerName) throws ServletException, IOException {
|
||||
// given
|
||||
FineractProperties.FineractIpTrackingProperties props = new FineractProperties.FineractIpTrackingProperties();
|
||||
props.setEnabled(false);
|
||||
fineractProperties.setIpTracking(props);
|
||||
underTest = new CallerIpTrackingFilter(fineractProperties);
|
||||
given(request.getHeader(headerName)).willReturn("192.168.1.100");
|
||||
|
||||
// when
|
||||
underTest.doFilterInternal(request, response, filterChain);
|
||||
|
||||
// then
|
||||
verify(request, never()).setAttribute("IP", IpAddressUtils.getClientIp());
|
||||
verify(filterChain).doFilter(request, response);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
fineract.node-id=1
|
||||
|
||||
fineract.security.basicauth.enabled=true
|
||||
fineract.ip-tracking.enabled=false
|
||||
fineract.security.oauth.enabled=false
|
||||
fineract.security.2fa.enabled=false
|
||||
fineract.security.hsts.enabled=false
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.integrationtests.common.AuditHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class IpTrackingIntegrationTest {
|
||||
|
||||
private AuditHelper auditHelper;
|
||||
private static final String EXPECTED_LOCAL_IP = "127.0.0.1";
|
||||
private RequestSpecification requestSpec;
|
||||
private ResponseSpecification responseSpec;
|
||||
private ResponseSpecification responseSpecForSearch;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
Utils.initializeRESTAssured();
|
||||
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
|
||||
this.requestSpec.header("Fineract-Platform-TenantId", "default");
|
||||
this.requestSpec.auth().basic("mifos", "password");
|
||||
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
this.responseSpecForSearch = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
this.auditHelper = new AuditHelper(this.requestSpec, this.responseSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void capturesIpAddressWhenCreatingClient() throws Exception {
|
||||
assumeTrue(Boolean.parseBoolean(System.getenv().getOrDefault("FINERACT_CLIENT_IP_TRACKING_ENABLED", "true")),
|
||||
"Saltando test porque el tracking de IP está deshabilitado");
|
||||
|
||||
// given
|
||||
final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientId);
|
||||
List<HashMap<String, Object>> auditsRecieved = auditHelper.getAuditDetails(clientId, "CREATE", "CLIENT");
|
||||
|
||||
// when
|
||||
String ip = auditsRecieved.get(0).get("ip").toString();
|
||||
|
||||
assumeTrue(!ip.isEmpty(), "IP not arrived: skipping capture test when enabled");
|
||||
// then
|
||||
assertEquals(EXPECTED_LOCAL_IP, ip, "Expected local IP when tracking is enabled");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user