|
|
@@ -0,0 +1,183 @@
|
|
|
+package com.danielbohry.authservice.service.auth;
|
|
|
+
|
|
|
+import jakarta.servlet.FilterChain;
|
|
|
+import jakarta.servlet.ServletException;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.mockito.InjectMocks;
|
|
|
+import org.mockito.Mock;
|
|
|
+import org.mockito.MockitoAnnotations;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.PrintWriter;
|
|
|
+import java.io.StringWriter;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
+
|
|
|
+class RateLimitingFilterTest {
|
|
|
+
|
|
|
+ @InjectMocks
|
|
|
+ private RateLimitingFilter rateLimitingFilter;
|
|
|
+
|
|
|
+ @Mock
|
|
|
+ private HttpServletRequest request;
|
|
|
+
|
|
|
+ @Mock
|
|
|
+ private HttpServletResponse response;
|
|
|
+
|
|
|
+ @Mock
|
|
|
+ private FilterChain filterChain;
|
|
|
+
|
|
|
+ private StringWriter stringWriter;
|
|
|
+ private PrintWriter printWriter;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ MockitoAnnotations.openMocks(this);
|
|
|
+ stringWriter = new StringWriter();
|
|
|
+ printWriter = new PrintWriter(stringWriter);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldNotApplyRateLimitingToNonForgotPasswordEndpoints() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/register");
|
|
|
+ when(request.getMethod()).thenReturn("POST");
|
|
|
+
|
|
|
+ // when
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain).doFilter(request, response);
|
|
|
+ verify(response, never()).setStatus(anyInt());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldNotApplyRateLimitingToGetRequests() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/forgot-password");
|
|
|
+ when(request.getMethod()).thenReturn("GET");
|
|
|
+
|
|
|
+ // when
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain).doFilter(request, response);
|
|
|
+ verify(response, never()).setStatus(anyInt());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldNotApplyRateLimitingWhenUsernameIsNull() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/forgot-password");
|
|
|
+ when(request.getMethod()).thenReturn("POST");
|
|
|
+ when(request.getParameter("username")).thenReturn(null);
|
|
|
+
|
|
|
+ // when
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain).doFilter(request, response);
|
|
|
+ verify(response, never()).setStatus(anyInt());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldNotApplyRateLimitingWhenUsernameIsEmpty() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/forgot-password");
|
|
|
+ when(request.getMethod()).thenReturn("POST");
|
|
|
+ when(request.getParameter("username")).thenReturn("");
|
|
|
+
|
|
|
+ // when
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain).doFilter(request, response);
|
|
|
+ verify(response, never()).setStatus(anyInt());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldAllowFirstRequest() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/forgot-password");
|
|
|
+ when(request.getMethod()).thenReturn("POST");
|
|
|
+ when(request.getParameter("username")).thenReturn("testuser");
|
|
|
+
|
|
|
+ // when
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain).doFilter(request, response);
|
|
|
+ verify(response, never()).setStatus(anyInt());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldAllowSecondRequest() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/forgot-password");
|
|
|
+ when(request.getMethod()).thenReturn("POST");
|
|
|
+ when(request.getParameter("username")).thenReturn("testuser2");
|
|
|
+
|
|
|
+ // when - first request
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // when - second request
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain, times(2)).doFilter(request, response);
|
|
|
+ verify(response, never()).setStatus(anyInt());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldBlockThirdRequest() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/forgot-password");
|
|
|
+ when(request.getMethod()).thenReturn("POST");
|
|
|
+ when(request.getParameter("username")).thenReturn("testuser3");
|
|
|
+ when(response.getWriter()).thenReturn(printWriter);
|
|
|
+
|
|
|
+ // when - first and second requests (should be allowed)
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // when - third request (should be blocked)
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain, times(2)).doFilter(request, response);
|
|
|
+ verify(response).setStatus(429);
|
|
|
+ verify(response).setContentType("application/json");
|
|
|
+
|
|
|
+ String responseContent = stringWriter.toString();
|
|
|
+ assertTrue(responseContent.contains("Too many requests"));
|
|
|
+ assertTrue(responseContent.contains("Maximum 2 requests per minute allowed"));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void shouldNotAffectDifferentUsernames() throws ServletException, IOException {
|
|
|
+ // given
|
|
|
+ when(request.getRequestURI()).thenReturn("/api/forgot-password");
|
|
|
+ when(request.getMethod()).thenReturn("POST");
|
|
|
+ when(response.getWriter()).thenReturn(printWriter);
|
|
|
+
|
|
|
+ // when - make 2 requests for user1
|
|
|
+ when(request.getParameter("username")).thenReturn("user1");
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // when - make third request for user1 (should be blocked)
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // when - make first request for user2 (should be allowed)
|
|
|
+ when(request.getParameter("username")).thenReturn("user2");
|
|
|
+ rateLimitingFilter.doFilterInternal(request, response, filterChain);
|
|
|
+
|
|
|
+ // then
|
|
|
+ verify(filterChain, times(3)).doFilter(request, response);
|
|
|
+ verify(response, times(1)).setStatus(429);
|
|
|
+ }
|
|
|
+}
|