This commit is contained in:
Anthony Stirling 2023-12-29 20:48:21 +00:00
parent 610ff22abe
commit 1b2df20fdd
12 changed files with 79 additions and 37 deletions

View file

@ -12,6 +12,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import stirling.software.SPDF.utils.RequestUriUtils;
@Component @Component
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@ -28,7 +29,7 @@ public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthent
// Get the saved request // Get the saved request
HttpSession session = request.getSession(false); HttpSession session = request.getSession(false);
SavedRequest savedRequest = session != null ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") : null; SavedRequest savedRequest = session != null ? (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST") : null;
if (savedRequest != null && !isStaticResource(savedRequest)) { if (savedRequest != null && !RequestUriUtils.isStaticResource(savedRequest.getRedirectUrl())) {
// Redirect to the original destination // Redirect to the original destination
super.onAuthenticationSuccess(request, response, authentication); super.onAuthenticationSuccess(request, response, authentication);
} else { } else {
@ -38,15 +39,6 @@ public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthent
//super.onAuthenticationSuccess(request, response, authentication); //super.onAuthenticationSuccess(request, response, authentication);
} }
private boolean isStaticResource(SavedRequest savedRequest) {
String requestURI = savedRequest.getRedirectUrl();
return requestURI.startsWith("/css/")
|| requestURI.startsWith("/js/")
|| requestURI.startsWith("/images/")
|| requestURI.startsWith("/public/")
|| requestURI.startsWith("/pdfjs/")
|| requestURI.endsWith(".svg");
}
} }

View file

@ -15,6 +15,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import stirling.software.SPDF.model.User; import stirling.software.SPDF.model.User;
import stirling.software.SPDF.utils.RequestUriUtils;
@Component @Component
public class FirstLoginFilter extends OncePerRequestFilter { public class FirstLoginFilter extends OncePerRequestFilter {
@ -28,11 +29,7 @@ public class FirstLoginFilter extends OncePerRequestFilter {
String method = request.getMethod(); String method = request.getMethod();
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
// Check if the request is for static resources // Check if the request is for static resources
boolean isStaticResource = requestURI.startsWith("/css/") boolean isStaticResource = RequestUriUtils.isStaticResource(requestURI);
|| requestURI.startsWith("/js/")
|| requestURI.startsWith("/images/")
|| requestURI.startsWith("/public/")
|| requestURI.endsWith(".svg");
// If it's a static resource, just continue the filter chain and skip the logic below // If it's a static resource, just continue the filter chain and skip the logic below
if (isStaticResource) { if (isStaticResource) {

View file

@ -9,6 +9,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import stirling.software.SPDF.utils.RequestUriUtils;
public class IPRateLimitingFilter implements Filter { public class IPRateLimitingFilter implements Filter {
@ -29,12 +30,7 @@ public class IPRateLimitingFilter implements Filter {
String method = httpRequest.getMethod(); String method = httpRequest.getMethod();
String requestURI = httpRequest.getRequestURI(); String requestURI = httpRequest.getRequestURI();
// Check if the request is for static resources // Check if the request is for static resources
boolean isStaticResource = requestURI.startsWith("/css/") boolean isStaticResource = RequestUriUtils.isStaticResource(requestURI);
|| requestURI.startsWith("/js/")
|| requestURI.startsWith("/images/")
|| requestURI.startsWith("/public/")
|| requestURI.startsWith("/pdfjs/")
|| requestURI.endsWith(".svg");
// If it's a static resource, just continue the filter chain and skip the logic below // If it's a static resource, just continue the filter chain and skip the logic below
if (isStaticResource) { if (isStaticResource) {

View file

@ -2,15 +2,30 @@ package stirling.software.SPDF.config.security;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.AttemptCounter; import stirling.software.SPDF.model.AttemptCounter;
@Service @Service
public class LoginAttemptService { public class LoginAttemptService {
private final int MAX_ATTEMPTS = 10;
private final long ATTEMPT_INCREMENT_TIME = TimeUnit.MINUTES.toMillis(1); @Autowired
ApplicationProperties applicationProperties;
private int MAX_ATTEMPTS;
private long ATTEMPT_INCREMENT_TIME;
@PostConstruct
public void init() {
MAX_ATTEMPTS = applicationProperties.getSecurity().getLoginAttemptCount();
ATTEMPT_INCREMENT_TIME = TimeUnit.MINUTES.toMillis(applicationProperties.getSecurity().getLoginResetTimeMinutes());
}
private final ConcurrentHashMap<String, AttemptCounter> attemptsCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, AttemptCounter> attemptsCache = new ConcurrentHashMap<>();
public void loginSucceeded(String key) { public void loginSucceeded(String key) {

View file

@ -11,7 +11,7 @@ public class RateLimitResetScheduler {
this.rateLimitingFilter = rateLimitingFilter; this.rateLimitingFilter = rateLimitingFilter;
} }
@Scheduled(cron = "0 0 0 * * MON") // At 00:00 every Monday @Scheduled(cron = "0 0 0 * * MON") // At 00:00 every Monday TODO: configurable
public void resetRateLimit() { public void resetRateLimit() {
rateLimitingFilter.resetRequestCounts(); rateLimitingFilter.resetRequestCounts();
} }

View file

@ -28,7 +28,12 @@ public class ApiDocService {
private String getApiDocsUrl() { private String getApiDocsUrl() {
String contextPath = servletContext.getContextPath(); String contextPath = servletContext.getContextPath();
return "http://localhost:8080" + contextPath + "/v1/api-docs"; String port = System.getProperty("local.server.port");
if(port == null || port.length() == 0) {
port="8080";
}
return "http://localhost:"+ port + contextPath + "/v1/api-docs";
} }

View file

@ -109,7 +109,7 @@ public class PipelineDirectoryProcessor {
private PipelineConfig readAndParseJson(Path jsonFile) throws IOException { private PipelineConfig readAndParseJson(Path jsonFile) throws IOException {
String jsonString = new String(Files.readAllBytes(jsonFile), StandardCharsets.UTF_8); String jsonString = new String(Files.readAllBytes(jsonFile), StandardCharsets.UTF_8);
logger.info("Reading JSON file: {}", jsonFile); logger.debug("Reading JSON file: {}", jsonFile);
return objectMapper.readValue(jsonString, PipelineConfig.class); return objectMapper.readValue(jsonString, PipelineConfig.class);
} }
@ -118,7 +118,7 @@ public class PipelineDirectoryProcessor {
validateOperation(operation); validateOperation(operation);
File[] files = collectFilesForProcessing(dir, jsonFile, operation); File[] files = collectFilesForProcessing(dir, jsonFile, operation);
if(files == null || files.length == 0) { if(files == null || files.length == 0) {
logger.info("No files detected for {} ", dir); logger.debug("No files detected for {} ", dir);
return; return;
} }
List<File> filesToProcess = prepareFilesForProcessing(files, processingDir); List<File> filesToProcess = prepareFilesForProcessing(files, processingDir);

View file

@ -238,7 +238,7 @@ public class PipelineProcessor {
for (File file : files) { for (File file : files) {
Path path = Paths.get(file.getAbsolutePath()); Path path = Paths.get(file.getAbsolutePath());
System.out.println("Reading file: " + path); // debug statement logger.info("Reading file: " + path); // debug statement
if (Files.exists(path)) { if (Files.exists(path)) {
Resource fileResource = new ByteArrayResource(Files.readAllBytes(path)) { Resource fileResource = new ByteArrayResource(Files.readAllBytes(path)) {
@ -249,7 +249,7 @@ public class PipelineProcessor {
}; };
outputFiles.add(fileResource); outputFiles.add(fileResource);
} else { } else {
System.out.println("File not found: " + path); // debug statement logger.info("File not found: " + path);
} }
} }
logger.info("Files successfully loaded. Starting processing..."); logger.info("Files successfully loaded. Starting processing...");

View file

@ -106,6 +106,25 @@ public class ApplicationProperties {
private Boolean enableLogin; private Boolean enableLogin;
private Boolean csrfDisabled; private Boolean csrfDisabled;
private InitialLogin initialLogin; private InitialLogin initialLogin;
private int loginAttemptCount;
private long loginResetTimeMinutes;
public int getLoginAttemptCount() {
return loginAttemptCount;
}
public void setLoginAttemptCount(int loginAttemptCount) {
this.loginAttemptCount = loginAttemptCount;
}
public long getLoginResetTimeMinutes() {
return loginResetTimeMinutes;
}
public void setLoginResetTimeMinutes(long loginResetTimeMinutes) {
this.loginResetTimeMinutes = loginResetTimeMinutes;
}
public InitialLogin getInitialLogin() { public InitialLogin getInitialLogin() {
return initialLogin != null ? initialLogin : new InitialLogin(); return initialLogin != null ? initialLogin : new InitialLogin();

View file

@ -1,27 +1,27 @@
package stirling.software.SPDF.model; package stirling.software.SPDF.model;
public class AttemptCounter { public class AttemptCounter {
private int attemptCount; private int attemptCount;
private long firstAttemptTime; private long lastAttemptTime;
public AttemptCounter() { public AttemptCounter() {
this.attemptCount = 1; this.attemptCount = 1;
this.firstAttemptTime = System.currentTimeMillis(); this.lastAttemptTime = System.currentTimeMillis();
} }
public void increment() { public void increment() {
this.attemptCount++; this.attemptCount++;
this.firstAttemptTime = System.currentTimeMillis(); this.lastAttemptTime = System.currentTimeMillis();
} }
public int getAttemptCount() { public int getAttemptCount() {
return attemptCount; return attemptCount;
} }
public long getFirstAttemptTime() { public long getlastAttemptTime() {
return firstAttemptTime; return lastAttemptTime;
} }
public boolean shouldReset(long ATTEMPT_INCREMENT_TIME) { public boolean shouldReset(long ATTEMPT_INCREMENT_TIME) {
return System.currentTimeMillis() - firstAttemptTime > ATTEMPT_INCREMENT_TIME; return System.currentTimeMillis() - lastAttemptTime > ATTEMPT_INCREMENT_TIME;
} }
} }

View file

@ -0,0 +1,16 @@
package stirling.software.SPDF.utils;
public class RequestUriUtils {
public static boolean isStaticResource(String requestURI) {
return requestURI.startsWith("/css/")
|| requestURI.startsWith("/js/")
|| requestURI.startsWith("/images/")
|| requestURI.startsWith("/public/")
|| requestURI.startsWith("/pdfjs/")
|| requestURI.endsWith(".svg");
}
}

View file

@ -5,7 +5,9 @@
security: security:
enableLogin: false # set to 'true' to enable login enableLogin: false # set to 'true' to enable login
csrfDisabled: true csrfDisabled: true
loginAttemptCount: 5 # lock user account after 5 tries
loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts
system: system:
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc) defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow