diff --git a/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java b/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java index 2db61a53b1..1d783bb617 100644 --- a/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java +++ b/orcid-pub-web/src/main/java/org/orcid/api/filters/ApiRateLimitFilter.java @@ -3,7 +3,9 @@ import java.io.IOException; import java.io.PrintWriter; import java.time.LocalDate; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -14,6 +16,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.LocaleUtils; +import org.apache.commons.lang3.StringUtils; import org.orcid.core.manager.ClientDetailsEntityCacheManager; import org.orcid.core.manager.OrcidSecurityManager; import org.orcid.core.manager.TemplateManager; @@ -88,6 +91,18 @@ public class ApiRateLimitFilter extends OncePerRequestFilter { @Value("${org.orcid.persistence.panoply.papiExceededRate.production:false}") private boolean enablePanoplyPapiExceededRateInProduction; + + @Value("${org.orcid.papi.rate.limit.ip.whiteSpaceSeparatedWhiteList:127.0.0.1}") + private static String papiWhiteSpaceSeparatedWhiteList; + + private static List papiIpWhiteList; + + static { + + if(StringUtils.isNotBlank(papiWhiteSpaceSeparatedWhiteList)) { + papiIpWhiteList = Arrays.asList(papiWhiteSpaceSeparatedWhiteList.split("\\s")); + } + } private static final String TOO_MANY_REQUESTS_MSG = "Too Many Requests - You have exceeded the daily allowance of API calls.\\n" + "You can increase your daily quota by registering for and using Public API client credentials " @@ -105,7 +120,7 @@ protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServl if (httpServletRequest.getHeader("Authorization") != null) { tokenValue = httpServletRequest.getHeader("Authorization").replaceAll("Bearer|bearer", "").trim(); } - String ipAddress = httpServletRequest.getRemoteAddr(); + String ipAddress = getClientIpAddress(httpServletRequest); String clientId = null; if (tokenValue != null) { @@ -118,9 +133,11 @@ protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServl boolean isAnonymous = (clientId == null); LocalDate today = LocalDate.now(); - if (isAnonymous) { - LOG.info("ApiRateLimitFilter anonymous request"); - this.rateLimitAnonymousRequest(ipAddress, today, httpServletResponse); + if (isAnonymous ) { + if(!isWhiteListed(ipAddress)) { + LOG.info("ApiRateLimitFilter anonymous request"); + this.rateLimitAnonymousRequest(ipAddress, today, httpServletResponse); + } } else { LOG.info("ApiRateLimitFilter client request with clientId: " + clientId); @@ -250,5 +267,28 @@ private void setPapiRateExceededItemInPanoply(PanoplyPapiDailyRateExceededItem i }); } + + //gets actual client IP address, using the headers that the proxy server ads + private String getClientIpAddress(HttpServletRequest request) { + String ipAddress = request.getHeader("X-FORWARDED-FOR"); + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = request.getHeader("X-REAL-IP"); + } + if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) { + ipAddress = request.getRemoteAddr(); + } + if (ipAddress != null && ipAddress.contains(",")) { + ipAddress = ipAddress.split(",")[0].trim(); + } + return ipAddress; + } + + private boolean isWhiteListed(String ipAddress) { + if(papiIpWhiteList != null) { + return papiIpWhiteList.contains(ipAddress); + + } + return false; + } } diff --git a/properties/development.properties b/properties/development.properties index 036dd07a4a..00e90b5de4 100644 --- a/properties/development.properties +++ b/properties/development.properties @@ -1,24 +1,24 @@ ################ -# DATABASE # +# DATABASE # ################ -# Main database +# Main database org.orcid.persistence.db.class=org.postgresql.Driver org.orcid.persistence.db.dataSource=pooledDataSource org.orcid.persistence.db.dialect=org.hibernate.dialect.PostgreSQLDialect org.orcid.persistence.db.generateDdl=false org.orcid.persistence.db.hibernateStatistics=true -org.orcid.persistence.db.idleConnectionTestPeriod=3600 -org.orcid.persistence.db.initialPoolSize=5 +org.orcid.persistence.db.idleConnectionTestPeriod=60 +org.orcid.persistence.db.initialPoolSize=1 org.orcid.persistence.db.maxPoolSize=20 -org.orcid.persistence.db.maxStatements=50 +org.orcid.persistence.db.maxStatements=0 org.orcid.persistence.db.minPoolSize=5 org.orcid.persistence.db.numHelperThreads=10 -org.orcid.persistence.db.password=orcid +org.orcid.persistence.db.password=pheinu0uija4eechohna org.orcid.persistence.db.preferredTestQuery=select 1 org.orcid.persistence.db.showSql=false org.orcid.persistence.db.testConnectionOnCheckin=true -org.orcid.persistence.db.url=jdbc:postgresql://localhost:5432/orcid?stringtype=unspecified +org.orcid.persistence.db.url=jdbc:postgresql://reg-qa-pg-use2-a1:5432/orcid org.orcid.persistence.db.username=orcid # Read only database @@ -26,31 +26,31 @@ org.orcid.persistence.db.readonly.class=org.postgresql.Driver org.orcid.persistence.db.readonly.dataSource=pooledDataSourceReadOnly org.orcid.persistence.db.readonly.dialect=org.hibernate.dialect.PostgreSQLDialect org.orcid.persistence.db.readonly.generateDdl=false -org.orcid.persistence.db.readonly.idleConnectionTestPeriod=3600 -org.orcid.persistence.db.readonly.initialPoolSize=5 +org.orcid.persistence.db.readonly.idleConnectionTestPeriod=60 +org.orcid.persistence.db.readonly.initialPoolSize=1 org.orcid.persistence.db.readonly.maxPoolSize=20 -org.orcid.persistence.db.readonly.maxStatements=50 +org.orcid.persistence.db.readonly.maxStatements=0 org.orcid.persistence.db.readonly.minPoolSize=5 -org.orcid.persistence.db.readonly.password=orcidro +org.orcid.persistence.db.readonly.password=5dyjm5fmcpvksa9562tv org.orcid.persistence.db.readonly.preferredTestQuery=select 1 org.orcid.persistence.db.readonly.showSql=false org.orcid.persistence.db.readonly.testConnectionOnCheckin=true -org.orcid.persistence.db.readonly.url=jdbc:postgresql://localhost:5432/orcid?stringtype=unspecified +org.orcid.persistence.db.readonly.url=jdbc:postgresql://reg-qa-pg-use2-a1:5432/orcid org.orcid.persistence.db.readonly.username=orcidro # Features database -org.orcid.persistence.togglz.cache.ttl=0 +org.orcid.persistence.togglz.cache.ttl=60000 org.orcid.persistence.togglz.db.class=org.postgresql.Driver -org.orcid.persistence.togglz.db.idleConnectionTestPeriod=3600 +org.orcid.persistence.togglz.db.idleConnectionTestPeriod=60 org.orcid.persistence.togglz.db.initialPoolSize=1 -org.orcid.persistence.togglz.db.maxPoolSize=1 +org.orcid.persistence.togglz.db.maxPoolSize=5 org.orcid.persistence.togglz.db.maxStatements=0 -org.orcid.persistence.togglz.db.minPoolSize=1 +org.orcid.persistence.togglz.db.minPoolSize=3 org.orcid.persistence.togglz.db.numHelperThreads=5 -org.orcid.persistence.togglz.db.password=orcid +org.orcid.persistence.togglz.db.password=pheinu0uija4eechohna org.orcid.persistence.togglz.db.preferredTestQuery=select 1 org.orcid.persistence.togglz.db.testConnectionOnCheckin=true -org.orcid.persistence.togglz.db.url=jdbc:postgresql://localhost:5432/features +org.orcid.persistence.togglz.db.url=jdbc:postgresql://reg-qa-pg-use2-a1:5432/features org.orcid.persistence.togglz.db.username=orcid ################ @@ -66,6 +66,10 @@ org.orcid.core.baseUri=https://dev.orcid.org org.orcid.core.internalApiBaseUri=http://localhost:8080/orcid-internal-api org.orcid.core.pubBaseUri=https://pub.dev.orcid.org +# Used to encrypt some user data before sending it to the database +org.orcid.core.passPhraseForExternalEncryption=spAbusa3ubRase7udEpr +org.orcid.core.passPhraseForInternalEncryption=XeZa8wUkuchusp8saWaW + # Mailgun com.mailgun.alt.apiUrl=https://api.mailgun.net/v2/samples.mailgun.org/messages com.mailgun.alt.notify.apiUrl=https://api.mailgun.net/v2/samples.mailgun.org/messages