From 35a998b934e003e870ee4cebbbf886ccd2630257 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Sun, 13 Aug 2023 22:46:18 +0100 Subject: [PATCH] Login --- .../config/UserBasedRateLimitingFilter.java | 41 +++++++++++++++---- .../SPDF/config/security/InitialSetup.java | 5 ++- .../stirling/software/SPDF/model/Role.java | 9 +++- .../resources/static/images/clipboard.svg | 4 ++ src/main/resources/templates/account.html | 19 ++++++++- src/main/resources/templates/login.html | 1 + 6 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 src/main/resources/static/images/clipboard.svg diff --git a/src/main/java/stirling/software/SPDF/config/UserBasedRateLimitingFilter.java b/src/main/java/stirling/software/SPDF/config/UserBasedRateLimitingFilter.java index 45c5feba..3ecb3ead 100644 --- a/src/main/java/stirling/software/SPDF/config/UserBasedRateLimitingFilter.java +++ b/src/main/java/stirling/software/SPDF/config/UserBasedRateLimitingFilter.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Component; @@ -23,10 +24,12 @@ import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import stirling.software.SPDF.model.Role; @Component public class UserBasedRateLimitingFilter extends OncePerRequestFilter { - private final Map buckets = new ConcurrentHashMap<>(); + private final Map apiBuckets = new ConcurrentHashMap<>(); + private final Map webBuckets = new ConcurrentHashMap<>(); @Autowired private UserDetailsService userDetailsService; @@ -39,7 +42,6 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - if (!rateLimit) { // If rateLimit is not enabled, just pass all requests without rate limiting filterChain.doFilter(request, response); @@ -47,7 +49,6 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter { } String method = request.getMethod(); - if (!"POST".equalsIgnoreCase(method)) { // If the request is not a POST, just pass it through without rate limiting filterChain.doFilter(request, response); @@ -73,7 +74,34 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter { identifier = request.getRemoteAddr(); } - Bucket userBucket = buckets.computeIfAbsent(identifier, k -> createUserBucket()); + Role userRole = getRoleFromAuthentication(SecurityContextHolder.getContext().getAuthentication()); + + if (request.getHeader("X-API-Key") != null) { + // It's an API call + processRequest(userRole.getApiCallsPerDay(), identifier, apiBuckets, request, response, filterChain); + } else { + // It's a Web UI call + processRequest(userRole.getWebCallsPerDay(), identifier, webBuckets, request, response, filterChain); + } + } + + private Role getRoleFromAuthentication(Authentication authentication) { + if (authentication != null && authentication.isAuthenticated()) { + for (GrantedAuthority authority : authentication.getAuthorities()) { + try { + return Role.fromString(authority.getAuthority()); + } catch (IllegalArgumentException ex) { + // Ignore and continue to next authority. + } + } + } + throw new IllegalStateException("User does not have a valid role."); + } + + private void processRequest(int limitPerDay, String identifier, Map buckets, + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws IOException, ServletException { + Bucket userBucket = buckets.computeIfAbsent(identifier, k -> createUserBucket(limitPerDay)); ConsumptionProbe probe = userBucket.tryConsumeAndReturnRemaining(1); if (probe.isConsumed()) { @@ -84,12 +112,11 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter { response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); response.setHeader("X-Rate-Limit-Retry-After-Seconds", String.valueOf(waitForRefill)); response.getWriter().write("Rate limit exceeded for POST requests."); - return; } } - private Bucket createUserBucket() { - Bandwidth limit = Bandwidth.classic(1000, Refill.intervally(1000, Duration.ofDays(1))); + private Bucket createUserBucket(int limitPerDay) { + Bandwidth limit = Bandwidth.classic(limitPerDay, Refill.intervally(limitPerDay, Duration.ofDays(1))); return Bucket.builder().addLimit(limit).build(); } } diff --git a/src/main/java/stirling/software/SPDF/config/security/InitialSetup.java b/src/main/java/stirling/software/SPDF/config/security/InitialSetup.java index 97ae9373..31d1f9bc 100644 --- a/src/main/java/stirling/software/SPDF/config/security/InitialSetup.java +++ b/src/main/java/stirling/software/SPDF/config/security/InitialSetup.java @@ -19,9 +19,10 @@ public class InitialSetup { String initialPassword = System.getenv("INITIAL_PASSWORD"); if(initialUsername != null && initialPassword != null) { userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId()); - } else { - userService.saveUser("admin", "password", Role.ADMIN.getRoleId()); } +// else { +// userService.saveUser("admin", "password", Role.ADMIN.getRoleId()); +// } } } } diff --git a/src/main/java/stirling/software/SPDF/model/Role.java b/src/main/java/stirling/software/SPDF/model/Role.java index fc0d1e9b..1b775de0 100644 --- a/src/main/java/stirling/software/SPDF/model/Role.java +++ b/src/main/java/stirling/software/SPDF/model/Role.java @@ -38,6 +38,13 @@ public enum Role { return webCallsPerDay; } - + public static Role fromString(String roleId) { + for (Role role : Role.values()) { + if (role.getRoleId().equalsIgnoreCase(roleId)) { + return role; + } + } + throw new IllegalArgumentException("No Role defined for id: " + roleId); + } } diff --git a/src/main/resources/static/images/clipboard.svg b/src/main/resources/static/images/clipboard.svg new file mode 100644 index 00000000..360e0894 --- /dev/null +++ b/src/main/resources/static/images/clipboard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/account.html b/src/main/resources/templates/account.html index ab8428f0..78d5bddd 100644 --- a/src/main/resources/templates/account.html +++ b/src/main/resources/templates/account.html @@ -68,21 +68,34 @@
+ +