DS-3542: Only trust X-Forwared-For headers from trusted proxies

This commit is contained in:
Tom Desair
2018-08-23 13:59:21 +02:00
parent b0e076ddb1
commit bd464e03f5
15 changed files with 269 additions and 135 deletions

View File

@@ -18,13 +18,14 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.core.LogManager; import org.dspace.core.LogManager;
import org.dspace.core.factory.CoreServiceFactory;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.eperson.factory.EPersonServiceFactory; import org.dspace.eperson.factory.EPersonServiceFactory;
import org.dspace.eperson.service.GroupService; import org.dspace.eperson.service.GroupService;
import org.dspace.service.ClientInfoService;
import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.services.factory.DSpaceServicesFactory;
/** /**
@@ -67,6 +68,7 @@ public class IPAuthentication implements AuthenticationMethod {
protected List<IPMatcher> ipNegativeMatchers; protected List<IPMatcher> ipNegativeMatchers;
protected GroupService groupService; protected GroupService groupService;
protected ClientInfoService clientInfoService;
/** /**
@@ -91,6 +93,7 @@ public class IPAuthentication implements AuthenticationMethod {
ipMatcherGroupIDs = new HashMap<>(); ipMatcherGroupIDs = new HashMap<>();
ipMatcherGroupNames = new HashMap<>(); ipMatcherGroupNames = new HashMap<>();
groupService = EPersonServiceFactory.getInstance().getGroupService(); groupService = EPersonServiceFactory.getInstance().getGroupService();
clientInfoService = CoreServiceFactory.getInstance().getClientInfoService();
List<String> propNames = DSpaceServicesFactory.getInstance().getConfigurationService() List<String> propNames = DSpaceServicesFactory.getInstance().getConfigurationService()
.getPropertyKeys("authentication-ip"); .getPropertyKeys("authentication-ip");
@@ -169,18 +172,7 @@ public class IPAuthentication implements AuthenticationMethod {
List<Group> groups = new ArrayList<Group>(); List<Group> groups = new ArrayList<Group>();
// Get the user's IP address // Get the user's IP address
String addr = request.getRemoteAddr(); String addr = clientInfoService.getClientIp(request);
if (useProxies == null) {
useProxies = ConfigurationManager.getBooleanProperty("useProxies", false);
}
if (useProxies && request.getHeader("X-Forwarded-For") != null) {
/* This header is a comma delimited list */
for (String xfip : request.getHeader("X-Forwarded-For").split(",")) {
if (!request.getHeader("X-Forwarded-For").contains(addr)) {
addr = xfip.trim();
}
}
}
for (IPMatcher ipm : ipMatchers) { for (IPMatcher ipm : ipMatchers) {
try { try {

View File

@@ -10,6 +10,7 @@ package org.dspace.core.factory;
import org.dspace.core.service.LicenseService; import org.dspace.core.service.LicenseService;
import org.dspace.core.service.NewsService; import org.dspace.core.service.NewsService;
import org.dspace.core.service.PluginService; import org.dspace.core.service.PluginService;
import org.dspace.service.ClientInfoService;
import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.services.factory.DSpaceServicesFactory;
/** /**
@@ -26,6 +27,8 @@ public abstract class CoreServiceFactory {
public abstract PluginService getPluginService(); public abstract PluginService getPluginService();
public abstract ClientInfoService getClientInfoService();
public static CoreServiceFactory getInstance() { public static CoreServiceFactory getInstance() {
return DSpaceServicesFactory.getInstance().getServiceManager() return DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("coreServiceFactory", CoreServiceFactory.class); .getServiceByName("coreServiceFactory", CoreServiceFactory.class);

View File

@@ -10,6 +10,7 @@ package org.dspace.core.factory;
import org.dspace.core.service.LicenseService; import org.dspace.core.service.LicenseService;
import org.dspace.core.service.NewsService; import org.dspace.core.service.NewsService;
import org.dspace.core.service.PluginService; import org.dspace.core.service.PluginService;
import org.dspace.service.ClientInfoService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
/** /**
@@ -29,6 +30,9 @@ public class CoreServiceFactoryImpl extends CoreServiceFactory {
@Autowired(required = true) @Autowired(required = true)
private PluginService pluginService; private PluginService pluginService;
@Autowired(required = true)
private ClientInfoService clientInfoService;
@Override @Override
public LicenseService getLicenseService() { public LicenseService getLicenseService() {
return licenseService; return licenseService;
@@ -43,4 +47,8 @@ public class CoreServiceFactoryImpl extends CoreServiceFactory {
public PluginService getPluginService() { public PluginService getPluginService() {
return pluginService; return pluginService;
} }
public ClientInfoService getClientInfoService() {
return clientInfoService;
}
} }

View File

@@ -25,8 +25,8 @@ import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.dspace.content.factory.ContentServiceFactory; import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants; import org.dspace.core.Constants;
import org.dspace.service.ClientInfoService;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.services.model.Event; import org.dspace.services.model.Event;
import org.dspace.usage.AbstractUsageEventListener; import org.dspace.usage.AbstractUsageEventListener;
@@ -50,6 +50,7 @@ public class GoogleRecorderEventListener extends AbstractUsageEventListener {
protected ContentServiceFactory contentServiceFactory; protected ContentServiceFactory contentServiceFactory;
protected ConfigurationService configurationService; protected ConfigurationService configurationService;
protected ClientInfoService clientInfoService;
public GoogleRecorderEventListener() { public GoogleRecorderEventListener() {
// httpclient is threadsafe so we only need one. // httpclient is threadsafe so we only need one.
@@ -62,10 +63,15 @@ public class GoogleRecorderEventListener extends AbstractUsageEventListener {
} }
@Autowired @Autowired
public void setConfigurationService(final ConfigurationService configurationService) { public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService; this.configurationService = configurationService;
} }
@Autowired
public void setClientInfoService(ClientInfoService clientInfoService) {
this.clientInfoService = clientInfoService;
}
@Override @Override
public void receiveEvent(Event event) { public void receiveEvent(Event event) {
if ((event instanceof UsageEvent)) { if ((event instanceof UsageEvent)) {
@@ -187,22 +193,7 @@ public class GoogleRecorderEventListener extends AbstractUsageEventListener {
} }
private String getIPAddress(HttpServletRequest request) { private String getIPAddress(HttpServletRequest request) {
String clientIP = request.getRemoteAddr(); return clientInfoService.getClientIp(request);
if (ConfigurationManager.getBooleanProperty("useProxies", false) && request
.getHeader("X-Forwarded-For") != null) {
/* This header is a comma delimited list */
for (String xfip : request.getHeader("X-Forwarded-For").split(",")) {
/* proxy itself will sometime populate this header with the same value in
remote address. ordering in spec is vague, we'll just take the last
not equal to the proxy
*/
if (!request.getHeader("X-Forwarded-For").contains(clientIP)) {
clientIP = xfip.trim();
}
}
}
return clientIP;
} }
} }

View File

@@ -0,0 +1,37 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.service;
import javax.servlet.http.HttpServletRequest;
/**
* Service that can be used to retrieve information about DSpace clients
*/
public interface ClientInfoService {
/**
* Get the client IP of this request taking into account the X-Forwarded-For header and the "useProxies" setting
* @param request The client HTTP request
* @return The IP address of the originating client
*/
String getClientIp(HttpServletRequest request);
/**
* Get the client IP of this request taking into account the X-Forwarded-For header and the "useProxies" setting
* @param remoteIp the remote address of the current request
* @param xForwardedForHeaderValue The value of the X-Forwarded-For header
* @return The IP address of the originating client
*/
String getClientIp(String remoteIp, String xForwardedForHeaderValue);
/**
* Does DSpace take into account HTTP proxy headers or not
* @return true if this is the case, false otherwise
*/
boolean isUseProxiesEnabled();
}

View File

@@ -0,0 +1,129 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.service.impl;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.dspace.service.ClientInfoService;
import org.dspace.services.ConfigurationService;
import org.dspace.statistics.util.IPTable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Implementation of {@link ClientInfoService} that can provide information on DSpace client requests
*/
public class ClientInfoServiceImpl implements ClientInfoService {
private static final String X_FORWARDED_FOR_HEADER = "X-Forwarded-For";
private static final Logger log = LoggerFactory.getLogger(ClientInfoServiceImpl.class);
private Boolean useProxiesEnabled;
private ConfigurationService configurationService;
/**
* Sparse HashTable structure to hold IP address ranges of trusted proxies
*/
private IPTable trustedProxies;
@Autowired(required = true)
public ClientInfoServiceImpl(ConfigurationService configurationService) {
this.configurationService = configurationService;
this.trustedProxies = parseTrustedProxyRanges(
configurationService.getArrayProperty("proxies.trusted.ipranges"));
}
@Override
public String getClientIp(HttpServletRequest request) {
return getClientIp(request.getRemoteAddr(), request.getHeader(X_FORWARDED_FOR_HEADER));
}
@Override
public String getClientIp(String remoteIp, String xForwardedForHeaderValue) {
String ip = remoteIp;
if (isUseProxiesEnabled()) {
String xForwardedForIp = getXForwardedForIpValue(remoteIp, xForwardedForHeaderValue);
if (StringUtils.isNotBlank(xForwardedForIp) && isRequestFromTrustedProxy(ip)) {
ip = xForwardedForIp;
}
} else if (StringUtils.isNotBlank(xForwardedForHeaderValue)) {
log.warn(
"X-Forwarded-For header detected but useProxiesEnabled is not enabled. " +
"If your dspace is behind a proxy set it to true");
}
return ip;
}
@Override
public boolean isUseProxiesEnabled() {
if (useProxiesEnabled == null) {
useProxiesEnabled = configurationService.getBooleanProperty("useProxies", true);
log.info("useProxies=" + useProxiesEnabled);
}
return useProxiesEnabled;
}
private IPTable parseTrustedProxyRanges(String[] proxyProperty) {
if (ArrayUtils.isEmpty(proxyProperty)) {
return null;
} else {
//Load all supplied proxy IP ranges into the IP table
IPTable ipTable = new IPTable();
try {
for (String proxyRange : proxyProperty) {
ipTable.add(proxyRange);
}
} catch (IPTable.IPFormatException e) {
log.error("Property proxies.trusted.ipranges contains an invalid IP range", e);
ipTable = null;
}
return ipTable;
}
}
private boolean isRequestFromTrustedProxy(String ipAddress) {
try {
return trustedProxies == null || trustedProxies.contains(ipAddress);
} catch (IPTable.IPFormatException e) {
log.error("Request contains invalid remote address", e);
return false;
}
}
private String getXForwardedForIpValue(String remoteIp, String xForwardedForValue) {
String ip = null;
/* This header is a comma delimited list */
String headerValue = StringUtils.trimToEmpty(xForwardedForValue);
for (String xfip : headerValue.split(",")) {
/* proxy itself will sometime populate this header with the same value in
remote address. ordering in spec is vague, we'll just take the last
not equal to the proxy
*/
if (!StringUtils.equals(remoteIp, xfip) && StringUtils.isNotBlank(xfip)
//if we have trusted proxies, we'll assume that they are not the client IP
&& (trustedProxies == null || !isRequestFromTrustedProxy(xfip))) {
ip = xfip.trim();
}
}
return ip;
}
}

View File

@@ -82,6 +82,7 @@ import org.dspace.core.Constants;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.service.ClientInfoService;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.statistics.service.SolrLoggerService; import org.dspace.statistics.service.SolrLoggerService;
import org.dspace.statistics.util.DnsLookup; import org.dspace.statistics.util.DnsLookup;
@@ -115,8 +116,6 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
protected DatabaseReader locationService; protected DatabaseReader locationService;
protected boolean useProxies;
private static List<String> statisticYearCores = new ArrayList<String>(); private static List<String> statisticYearCores = new ArrayList<String>();
private static boolean statisticYearCoresInit = false; private static boolean statisticYearCoresInit = false;
@@ -126,6 +125,8 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
protected ContentServiceFactory contentServiceFactory; protected ContentServiceFactory contentServiceFactory;
@Autowired(required = true) @Autowired(required = true)
private ConfigurationService configurationService; private ConfigurationService configurationService;
@Autowired(required = true)
private ClientInfoService clientInfoService;
public static enum StatisticsType { public static enum StatisticsType {
VIEW("view"), VIEW("view"),
@@ -192,9 +193,6 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
log.error("The required 'dbfile' configuration is missing in solr-statistics.cfg!"); log.error("The required 'dbfile' configuration is missing in solr-statistics.cfg!");
} }
locationService = service; locationService = service;
useProxies = configurationService.getBooleanProperty("useProxies");
log.info("useProxies=" + useProxies);
} }
@Override @Override
@@ -296,26 +294,7 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
// Save our basic info that we already have // Save our basic info that we already have
if (request != null) { if (request != null) {
String ip = request.getRemoteAddr(); String ip = clientInfoService.getClientIp(request);
if (isUseProxies() && request.getHeader("X-Forwarded-For") != null) {
/* This header is a comma delimited list */
for (String xfip : request.getHeader("X-Forwarded-For").split(",")) {
/* proxy itself will sometime populate this header with the same value in
remote address. ordering in spec is vague, we'll just take the last
not equal to the proxy
*/
if (!request.getHeader("X-Forwarded-For").contains(ip)) {
ip = xfip.trim();
}
}
}
if (!isUseProxies() && request.getHeader("X-Forwarded-For") != null) {
log.warn(
"X-Forwarded-For header detected but useProxies is not enabled. If your dspace is behind a proxy " +
"set it to true");
}
doc1.addField("ip", ip); doc1.addField("ip", ip);
//Also store the referrer //Also store the referrer
@@ -391,23 +370,7 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
SolrInputDocument doc1 = new SolrInputDocument(); SolrInputDocument doc1 = new SolrInputDocument();
// Save our basic info that we already have // Save our basic info that we already have
if (!isUseProxies() && xforwardedfor != null) { ip = clientInfoService.getClientIp(ip, xforwardedfor);
log.warn(
"X-Forwarded-For header detected but useProxies is not enabled. If your dspace is behind a proxy set " +
"it to true");
}
if (isUseProxies() && xforwardedfor != null) {
/* This header is a comma delimited list */
for (String xfip : xforwardedfor.split(",")) {
/* proxy itself will sometime populate this header with the same value in
remote address. ordering in spec is vague, we'll just take the last
not equal to the proxy
*/
if (!xforwardedfor.contains(ip)) {
ip = xfip.trim();
}
}
doc1.addField("ip", ip); doc1.addField("ip", ip);
try { try {
@@ -451,7 +414,6 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
log.error("Unable to get location of request: {}", e.getMessage()); log.error("Unable to get location of request: {}", e.getMessage());
} }
} }
}
if (dspaceObject != null) { if (dspaceObject != null) {
doc1.addField("id", dspaceObject.getID()); doc1.addField("id", dspaceObject.getID());
@@ -604,7 +566,7 @@ public class SolrLoggerServiceImpl implements SolrLoggerService, InitializingBea
@Override @Override
public boolean isUseProxies() { public boolean isUseProxies() {
return useProxies; return clientInfoService.isUseProxiesEnabled();
} }
@Override @Override

View File

@@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.configuration.ConversionException; import org.apache.commons.configuration.ConversionException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.dspace.service.ClientInfoService;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -40,8 +41,6 @@ public class SpiderDetectorServiceImpl implements SpiderDetectorService {
private static final Logger log = LoggerFactory.getLogger(SpiderDetectorServiceImpl.class); private static final Logger log = LoggerFactory.getLogger(SpiderDetectorServiceImpl.class);
private Boolean useProxies;
private Boolean useCaseInsensitiveMatching; private Boolean useCaseInsensitiveMatching;
private final List<Pattern> agents private final List<Pattern> agents
@@ -51,6 +50,7 @@ public class SpiderDetectorServiceImpl implements SpiderDetectorService {
= Collections.synchronizedList(new ArrayList<Pattern>()); = Collections.synchronizedList(new ArrayList<Pattern>());
private ConfigurationService configurationService; private ConfigurationService configurationService;
private ClientInfoService clientInfoService;
/** /**
* Sparse HashTable structure to hold IP address ranges. * Sparse HashTable structure to hold IP address ranges.
@@ -58,8 +58,9 @@ public class SpiderDetectorServiceImpl implements SpiderDetectorService {
private IPTable table = null; private IPTable table = null;
@Autowired(required = true) @Autowired(required = true)
public SpiderDetectorServiceImpl(ConfigurationService configurationService) { public SpiderDetectorServiceImpl(ConfigurationService configurationService, ClientInfoService clientInfoService) {
this.configurationService = configurationService; this.configurationService = configurationService;
this.clientInfoService = clientInfoService;
} }
public IPTable getTable() { public IPTable getTable() {
@@ -104,7 +105,7 @@ public class SpiderDetectorServiceImpl implements SpiderDetectorService {
} }
// No. See if any IP addresses match // No. See if any IP addresses match
if (isUseProxies() && proxyIPs != null) { if (clientInfoService.isUseProxiesEnabled() && proxyIPs != null) {
/* This header is a comma delimited list */ /* This header is a comma delimited list */
for (String xfip : proxyIPs.split(",")) { for (String xfip : proxyIPs.split(",")) {
if (isSpider(xfip)) { if (isSpider(xfip)) {
@@ -306,12 +307,4 @@ public class SpiderDetectorServiceImpl implements SpiderDetectorService {
return useCaseInsensitiveMatching; return useCaseInsensitiveMatching;
} }
private boolean isUseProxies() {
if (useProxies == null) {
useProxies = configurationService.getBooleanProperty("useProxies");
}
return useProxies;
}
} }

View File

@@ -38,7 +38,6 @@ public class MockSolrLoggerServiceImpl
File locationDb = new File(locationDbPath); File locationDb = new File(locationDbPath);
locationDb.createNewFile(); locationDb.createNewFile();
locationService = new DatabaseReader.Builder(locationDb).build(); locationService = new DatabaseReader.Builder(locationDb).build();
useProxies = configurationService.getBooleanProperty("useProxies");
} }
} }

View File

@@ -13,6 +13,8 @@ import static org.junit.Assert.assertTrue;
import mockit.Mock; import mockit.Mock;
import mockit.MockUp; import mockit.MockUp;
import org.dspace.AbstractDSpaceTest; import org.dspace.AbstractDSpaceTest;
import org.dspace.core.factory.CoreServiceFactory;
import org.dspace.service.ClientInfoService;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.dspace.services.factory.DSpaceServicesFactory; import org.dspace.services.factory.DSpaceServicesFactory;
import org.dspace.statistics.SolrLoggerServiceImpl; import org.dspace.statistics.SolrLoggerServiceImpl;
@@ -32,14 +34,15 @@ public class SpiderDetectorServiceImplTest extends AbstractDSpaceTest {
private ConfigurationService configurationService; private ConfigurationService configurationService;
private ClientInfoService clientInfoService;
private SpiderDetectorService spiderDetectorService; private SpiderDetectorService spiderDetectorService;
@Before @Before
public void init() { public void init() {
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService(); configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
spiderDetectorService = new SpiderDetectorServiceImpl(configurationService); clientInfoService = CoreServiceFactory.getInstance().getClientInfoService();
spiderDetectorService = new SpiderDetectorServiceImpl(configurationService, clientInfoService);
} }
@Test @Test
@@ -60,7 +63,7 @@ public class SpiderDetectorServiceImplTest extends AbstractDSpaceTest {
@Test @Test
public void testCaseInsensitiveMatching() throws Exception { public void testCaseInsensitiveMatching() throws Exception {
configurationService.setProperty("usage-statistics.bots.case-insensitive", true); configurationService.setProperty("usage-statistics.bots.case-insensitive", true);
spiderDetectorService = new SpiderDetectorServiceImpl(configurationService); spiderDetectorService = new SpiderDetectorServiceImpl(configurationService, clientInfoService);
DummyHttpServletRequest req = new DummyHttpServletRequest(); DummyHttpServletRequest req = new DummyHttpServletRequest();
req.setAddress(NOT_A_BOT_ADDRESS); // avoid surprises req.setAddress(NOT_A_BOT_ADDRESS); // avoid surprises
@@ -265,7 +268,7 @@ public class SpiderDetectorServiceImplTest extends AbstractDSpaceTest {
public void testBothLowerAndUpperCaseGetMatched() { public void testBothLowerAndUpperCaseGetMatched() {
configurationService.setProperty("usage-statistics.bots.case-insensitive", true); configurationService.setProperty("usage-statistics.bots.case-insensitive", true);
spiderDetectorService = new SpiderDetectorServiceImpl(configurationService); spiderDetectorService = new SpiderDetectorServiceImpl(configurationService, clientInfoService);
DummyHttpServletRequest req = new DummyHttpServletRequest(); DummyHttpServletRequest req = new DummyHttpServletRequest();
req.setAddress(NOT_A_BOT_ADDRESS); // avoid surprises req.setAddress(NOT_A_BOT_ADDRESS); // avoid surprises
@@ -297,7 +300,7 @@ public class SpiderDetectorServiceImplTest extends AbstractDSpaceTest {
@Test @Test
public void testNonBooleanConfig() { public void testNonBooleanConfig() {
configurationService.setProperty("usage-statistics.bots.case-insensitive", "RandomNonBooleanString"); configurationService.setProperty("usage-statistics.bots.case-insensitive", "RandomNonBooleanString");
spiderDetectorService = new SpiderDetectorServiceImpl(configurationService); spiderDetectorService = new SpiderDetectorServiceImpl(configurationService, clientInfoService);
DummyHttpServletRequest req = new DummyHttpServletRequest(); DummyHttpServletRequest req = new DummyHttpServletRequest();
req.setAddress(NOT_A_BOT_ADDRESS); // avoid surprises req.setAddress(NOT_A_BOT_ADDRESS); // avoid surprises

View File

@@ -38,6 +38,7 @@ import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group; import org.dspace.eperson.Group;
import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.EPersonService;
import org.dspace.service.ClientInfoService;
import org.dspace.services.ConfigurationService; import org.dspace.services.ConfigurationService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -71,6 +72,9 @@ public class JWTTokenHandler implements InitializingBean {
@Autowired @Autowired
private EPersonService ePersonService; private EPersonService ePersonService;
@Autowired
private ClientInfoService clientInfoService;
private String jwtKey; private String jwtKey;
private long expirationTime; private long expirationTime;
private boolean includeIP; private boolean includeIP;
@@ -276,11 +280,7 @@ public class JWTTokenHandler implements InitializingBean {
} }
private String getIpAddress(HttpServletRequest request) { private String getIpAddress(HttpServletRequest request) {
String ipAddress = request.getHeader("X-FORWARDED-FOR"); return clientInfoService.getClientIp(request);
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}
return ipAddress;
} }
private EPerson updateSessionSalt(final Context context, final Date previousLoginDate) throws SQLException { private EPerson updateSessionSalt(final Context context, final Date previousLoginDate) throws SQLException {

View File

@@ -23,6 +23,7 @@ import com.nimbusds.jwt.SignedJWT;
import org.dspace.core.Context; import org.dspace.core.Context;
import org.dspace.eperson.EPerson; import org.dspace.eperson.EPerson;
import org.dspace.eperson.service.EPersonService; import org.dspace.eperson.service.EPersonService;
import org.dspace.service.ClientInfoService;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -62,6 +63,9 @@ public class JWTTokenHandlerTest {
@Mock @Mock
private EPersonClaimProvider ePersonClaimProvider; private EPersonClaimProvider ePersonClaimProvider;
@Mock
private ClientInfoService clientInfoService;
@Spy @Spy
private List<JWTClaimProvider> jwtClaimProviders = new ArrayList<>(); private List<JWTClaimProvider> jwtClaimProviders = new ArrayList<>();
@@ -71,6 +75,7 @@ public class JWTTokenHandlerTest {
when(ePerson.getSessionSalt()).thenReturn("01234567890123456789012345678901"); when(ePerson.getSessionSalt()).thenReturn("01234567890123456789012345678901");
when(ePerson.getLastActive()).thenReturn(new Date()); when(ePerson.getLastActive()).thenReturn(new Date());
when(context.getCurrentUser()).thenReturn(ePerson); when(context.getCurrentUser()).thenReturn(ePerson);
when(clientInfoService.getClientIp(any())).thenReturn("123.123.123.123");
when(ePersonClaimProvider.getKey()).thenReturn("eid"); when(ePersonClaimProvider.getKey()).thenReturn("eid");
when(ePersonClaimProvider.getValue(any(), Mockito.any(HttpServletRequest.class))).thenReturn("epersonID"); when(ePersonClaimProvider.getValue(any(), Mockito.any(HttpServletRequest.class))).thenReturn("epersonID");
jwtClaimProviders.add(ePersonClaimProvider); jwtClaimProviders.add(ePersonClaimProvider);

View File

@@ -42,7 +42,6 @@ public class MockSolrLoggerServiceImpl
File locationDb = new File(locationDbPath); File locationDb = new File(locationDbPath);
locationDb.createNewFile(); locationDb.createNewFile();
locationService = new DatabaseReader.Builder(locationDb).build(); locationService = new DatabaseReader.Builder(locationDb).build();
useProxies = configurationService.getBooleanProperty("useProxies");
} }
@Override @Override

View File

@@ -326,7 +326,19 @@ http.proxy.port =
# If enabled, the logging and the Solr statistics system will look for # If enabled, the logging and the Solr statistics system will look for
# an X-Forwarded-For header. If it finds it, it will use this for the user IP address # an X-Forwarded-For header. If it finds it, it will use this for the user IP address
useProxies = false # Note that server-side rendered Angular UI requests always present the X-Forwarded-For header
# with the original client IP address.
useProxies = true
# If "useProxies" is enabled, the authentication and statistics logging code will read the X-Forwarded-For header in
# order to determine the correct client IP address. But they will only use that header value when the request is coming
# from a trusted proxy server location (e.g. HTTPD on localhost). Leave this property empty to trust X-Forwarded-For
# values of all requests. You can specify a range by only listing the first three ip-address blocks, e.g. 128.177.243
# You can list multiple IP addresses or ranges by comma-separating them.
# If you are running REST & UI on different servers, you should add the UI servers (range) as a proxy.
# For example : proxies.trusted.ipranges = 127.0.0.1, 192.168.2
# This is necessary because Angular Universal will also behave as a proxy server.
proxies.trusted.ipranges = 127.0.0.1
#### Media Filter / Format Filter plugins (through PluginService) #### #### Media Filter / Format Filter plugins (through PluginService) ####
# Media/Format Filters help to full-text index content or # Media/Format Filters help to full-text index content or

View File

@@ -93,6 +93,7 @@
<bean class="org.dspace.license.CreativeCommonsServiceImpl"/> <bean class="org.dspace.license.CreativeCommonsServiceImpl"/>
<bean id="spiderDetectorService" class="org.dspace.statistics.util.SpiderDetectorServiceImpl"/> <bean id="spiderDetectorService" class="org.dspace.statistics.util.SpiderDetectorServiceImpl"/>
<bean id="clientInfoService" class="org.dspace.service.impl.ClientInfoServiceImpl"/>
<bean class="org.dspace.versioning.VersionHistoryServiceImpl"/> <bean class="org.dspace.versioning.VersionHistoryServiceImpl"/>