mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-13 13:03:11 +00:00
DS-3542: Only trust X-Forwared-For headers from trusted proxies
This commit is contained in:
@@ -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 {
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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"/>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user