Added tests

This commit is contained in:
Dario Ghunney Ware 2025-07-09 16:44:40 +01:00
parent 2fb60eee8d
commit 00db1ccf95
3 changed files with 630 additions and 0 deletions

View File

@ -0,0 +1,42 @@
package stirling.software.proprietary.security;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.AuthenticationException;
import java.io.IOException;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class JWTAuthenticationEntryPointTest {
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@Mock
private AuthenticationException authException;
@InjectMocks
private JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Test
void testCommence() throws IOException {
String errorMessage = "Authentication failed";
when(authException.getMessage()).thenReturn(errorMessage);
jwtAuthenticationEntryPoint.commence(request, response, authException);
verify(response).sendError(
HttpServletResponse.SC_UNAUTHORIZED,
"Unauthorized: " + errorMessage);
}
}

View File

@ -0,0 +1,301 @@
package stirling.software.proprietary.security.filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import stirling.software.proprietary.security.model.exception.AuthenticationFailureException;
import stirling.software.proprietary.security.service.CustomUserDetailsService;
import stirling.software.proprietary.security.service.JWTServiceInterface;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class JWTAuthenticationFilterTest {
@Mock
private JWTServiceInterface jwtService;
@Mock
private CustomUserDetailsService userDetailsService;
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@Mock
private FilterChain filterChain;
@Mock
private UserDetails userDetails;
@Mock
private SecurityContext securityContext;
@InjectMocks
private JWTAuthenticationFilter jwtAuthenticationFilter;
@Test
void testDoFilterInternalWhenJwtDisabled() throws ServletException, IOException {
when(jwtService.isJwtEnabled()).thenReturn(false);
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
verify(filterChain).doFilter(request, response);
verify(jwtService, never()).extractTokenFromRequest(any());
}
@Test
void testDoFilterInternalWhenShouldNotFilter() throws ServletException, IOException {
when(jwtService.isJwtEnabled()).thenReturn(true);
when(request.getRequestURI()).thenReturn("/login");
when(request.getMethod()).thenReturn("POST");
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
verify(filterChain).doFilter(request, response);
verify(jwtService, never()).extractTokenFromRequest(any());
}
@Test
void testDoFilterInternalWithValidToken() throws ServletException, IOException {
String token = "valid-jwt-token";
String newToken = "new-jwt-token";
String username = "testuser";
when(jwtService.isJwtEnabled()).thenReturn(true);
when(request.getRequestURI()).thenReturn("/protected");
when(request.getMethod()).thenReturn("GET");
when(jwtService.extractTokenFromRequest(request)).thenReturn(token);
when(jwtService.validateToken(token)).thenReturn(true);
when(jwtService.extractUsername(token)).thenReturn(username);
when(userDetails.getAuthorities()).thenReturn((Collection) Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
when(userDetailsService.loadUserByUsername(username)).thenReturn(userDetails);
try (MockedStatic<SecurityContextHolder> mockedSecurityContextHolder = mockStatic(SecurityContextHolder.class)) {
when(securityContext.getAuthentication()).thenReturn(null);
mockedSecurityContextHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext);
when(jwtService.generateToken(any())).thenReturn(newToken);
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
verify(jwtService).validateToken(token);
verify(jwtService).extractUsername(token);
verify(userDetailsService).loadUserByUsername(username);
verify(securityContext).setAuthentication(any(UsernamePasswordAuthenticationToken.class));
verify(jwtService).generateToken(any());
verify(jwtService).addTokenToResponse(response, newToken);
verify(filterChain).doFilter(request, response);
}
}
@Test
void testDoFilterInternalWithMissingTokenForRootPath() throws ServletException, IOException {
when(jwtService.isJwtEnabled()).thenReturn(true);
when(request.getRequestURI()).thenReturn("/");
when(request.getMethod()).thenReturn("GET");
when(jwtService.extractTokenFromRequest(request)).thenReturn(null);
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
verify(response).sendRedirect("/login");
verify(filterChain, never()).doFilter(request, response);
}
@Test
void testDoFilterInternalWithMissingTokenForNonRootPath() throws ServletException, IOException {
when(jwtService.isJwtEnabled()).thenReturn(true);
when(request.getRequestURI()).thenReturn("/protected");
when(request.getMethod()).thenReturn("GET");
when(jwtService.extractTokenFromRequest(request)).thenReturn(null);
assertThrows(AuthenticationFailureException.class, () -> {
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
});
verify(response, never()).sendRedirect(anyString());
verify(filterChain, never()).doFilter(request, response);
}
@Test
void testDoFilterInternalWithInvalidToken() throws ServletException, IOException {
String token = "invalid-jwt-token";
when(jwtService.isJwtEnabled()).thenReturn(true);
when(request.getRequestURI()).thenReturn("/protected");
when(request.getMethod()).thenReturn("GET");
when(jwtService.extractTokenFromRequest(request)).thenReturn(token);
when(jwtService.validateToken(token)).thenReturn(false);
assertThrows(AuthenticationFailureException.class, () -> {
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
});
verify(jwtService).validateToken(token);
verify(filterChain, never()).doFilter(request, response);
}
@Test
void testDoFilterInternalWithUserNotFound() throws ServletException, IOException {
String token = "valid-jwt-token";
String username = "nonexistentuser";
when(jwtService.isJwtEnabled()).thenReturn(true);
when(request.getRequestURI()).thenReturn("/protected");
when(request.getMethod()).thenReturn("GET");
when(jwtService.extractTokenFromRequest(request)).thenReturn(token);
when(jwtService.validateToken(token)).thenReturn(true);
when(jwtService.extractUsername(token)).thenReturn(username);
when(userDetailsService.loadUserByUsername(username)).thenReturn(null);
try (MockedStatic<SecurityContextHolder> mockedSecurityContextHolder = mockStatic(SecurityContextHolder.class)) {
when(securityContext.getAuthentication()).thenReturn(null);
mockedSecurityContextHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext);
assertThrows(UsernameNotFoundException.class, () -> {
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
});
verify(userDetailsService).loadUserByUsername(username);
verify(filterChain, never()).doFilter(request, response);
}
}
@Test
void testDoFilterInternalWithExistingAuthentication() throws ServletException, IOException {
String token = "valid-jwt-token";
String newToken = "new-jwt-token";
String username = "testuser";
when(jwtService.isJwtEnabled()).thenReturn(true);
when(request.getRequestURI()).thenReturn("/protected");
when(request.getMethod()).thenReturn("GET");
when(jwtService.extractTokenFromRequest(request)).thenReturn(token);
when(jwtService.validateToken(token)).thenReturn(true);
when(jwtService.extractUsername(token)).thenReturn(username);
try (MockedStatic<SecurityContextHolder> mockedSecurityContextHolder = mockStatic(SecurityContextHolder.class)) {
Authentication existingAuth = mock(Authentication.class);
when(securityContext.getAuthentication()).thenReturn(existingAuth);
mockedSecurityContextHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext);
when(jwtService.generateToken(existingAuth)).thenReturn(newToken);
jwtAuthenticationFilter.doFilterInternal(request, response, filterChain);
verify(userDetailsService, never()).loadUserByUsername(anyString());
verify(jwtService).generateToken(existingAuth);
verify(jwtService).addTokenToResponse(response, newToken);
verify(filterChain).doFilter(request, response);
}
}
@Test
void testShouldNotFilterLoginPost() {
when(request.getRequestURI()).thenReturn("/login");
when(request.getMethod()).thenReturn("POST");
assertTrue(jwtAuthenticationFilter.shouldNotFilter(request));
}
@Test
void testShouldNotFilterLoginGet() {
when(request.getRequestURI()).thenReturn("/login");
when(request.getMethod()).thenReturn("GET");
assertTrue(jwtAuthenticationFilter.shouldNotFilter(request));
}
@Test
void testShouldNotFilterPublicPaths() {
String[] publicPaths = {
"/register",
"/error",
"/images/logo.png",
"/public/file.txt",
"/css/style.css",
"/fonts/font.ttf",
"/js/script.js",
"/pdfjs/viewer.js",
"/pdfjs-legacy/viewer.js",
"/api/v1/info/status",
"/site.webmanifest",
"/favicon.ico"
};
for (String path : publicPaths) {
when(request.getRequestURI()).thenReturn(path);
when(request.getMethod()).thenReturn("GET");
assertTrue(jwtAuthenticationFilter.shouldNotFilter(request),
"Should not filter path: " + path);
}
}
@Test
void testShouldNotFilterStaticFiles() {
String[] staticFiles = {
"/some/path/file.svg",
"/another/path/image.png",
"/path/to/icon.ico"
};
for (String file : staticFiles) {
when(request.getRequestURI()).thenReturn(file);
when(request.getMethod()).thenReturn("GET");
assertTrue(jwtAuthenticationFilter.shouldNotFilter(request),
"Should not filter file: " + file);
}
}
@Test
void testShouldFilterProtectedPaths() {
String[] protectedPaths = {
"/protected",
"/api/v1/user/profile",
"/admin",
"/dashboard"
};
for (String path : protectedPaths) {
when(request.getRequestURI()).thenReturn(path);
when(request.getMethod()).thenReturn("GET");
assertFalse(jwtAuthenticationFilter.shouldNotFilter(request),
"Should filter path: " + path);
}
}
@Test
void testShouldFilterRootPath() {
when(request.getRequestURI()).thenReturn("/");
when(request.getMethod()).thenReturn("GET");
assertFalse(jwtAuthenticationFilter.shouldNotFilter(request));
}
}

View File

@ -0,0 +1,287 @@
package stirling.software.proprietary.security.service;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.SignatureException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import stirling.software.common.model.ApplicationProperties;
import java.security.KeyPair;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class JWTServiceTest {
@Mock
private ApplicationProperties.Security securityProperties;
@Mock
private ApplicationProperties.Security.JWT jwtProperties;
@Mock
private Authentication authentication;
@Mock
private UserDetails userDetails;
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
private JWTService jwtService;
@BeforeEach
void setUp() {
lenient().when(securityProperties.getJwt()).thenReturn(jwtProperties);
lenient().when(securityProperties.isJwtActive()).thenReturn(true);
lenient().when(jwtProperties.isSettingsValid()).thenReturn(true);
lenient().when(jwtProperties.getExpiration()).thenReturn(3600000L);
lenient().when(jwtProperties.getIssuer()).thenReturn("Stirling-PDF");
jwtService = new JWTService(securityProperties);
}
@Test
void testGenerateTokenWithAuthentication() {
String username = "testuser";
when(authentication.getPrincipal()).thenReturn(userDetails);
when(userDetails.getUsername()).thenReturn(username);
String token = jwtService.generateToken(authentication);
assertNotNull(token);
assertTrue(token.length() > 0);
assertEquals(username, jwtService.extractUsername(token));
}
@Test
void testGenerateTokenWithUsernameAndClaims() {
String username = "testuser";
Map<String, Object> claims = new HashMap<>();
claims.put("role", "admin");
claims.put("department", "IT");
String token = jwtService.generateToken(username, claims);
assertNotNull(token);
assertTrue(token.length() > 0);
assertEquals(username, jwtService.extractUsername(token));
Map<String, Object> extractedClaims = jwtService.extractAllClaims(token);
assertEquals("admin", extractedClaims.get("role"));
assertEquals("IT", extractedClaims.get("department"));
}
@Test
void testGenerateTokenWhenJwtDisabled() {
when(securityProperties.isJwtActive()).thenReturn(false);
assertThrows(IllegalStateException.class, () -> {
jwtService.generateToken("testuser", new HashMap<>());
});
}
@Test
void testValidateTokenSuccess() {
String token = jwtService.generateToken("testuser", new HashMap<>());
assertTrue(jwtService.validateToken(token));
}
@Test
void testValidateTokenWhenJwtDisabled() {
when(securityProperties.isJwtActive()).thenReturn(false);
assertFalse(jwtService.validateToken("any-token"));
}
@Test
void testValidateTokenWithInvalidToken() {
assertFalse(jwtService.validateToken("invalid-token"));
}
@Test
void testValidateTokenWithExpiredToken() {
// Create a token that expires immediately
when(jwtProperties.getExpiration()).thenReturn(1L);
JWTService shortLivedJwtService = new JWTService(securityProperties);
String token = shortLivedJwtService.generateToken("testuser", new HashMap<>());
// Wait a bit to ensure expiration
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
assertFalse(shortLivedJwtService.validateToken(token));
}
@Test
void testExtractUsername() {
String username = "testuser";
String token = jwtService.generateToken(username, new HashMap<>());
assertEquals(username, jwtService.extractUsername(token));
}
@Test
void testExtractAllClaims() {
String username = "testuser";
Map<String, Object> claims = new HashMap<>();
claims.put("role", "admin");
claims.put("department", "IT");
String token = jwtService.generateToken(username, claims);
Map<String, Object> extractedClaims = jwtService.extractAllClaims(token);
assertEquals("admin", extractedClaims.get("role"));
assertEquals("IT", extractedClaims.get("department"));
assertEquals(username, extractedClaims.get("sub"));
assertEquals("Stirling-PDF", extractedClaims.get("iss"));
}
@Test
void testExtractAllClaimsWhenJwtDisabled() {
when(securityProperties.isJwtActive()).thenReturn(false);
assertThrows(IllegalStateException.class, () -> {
jwtService.extractAllClaims("any-token");
});
}
@Test
void testIsTokenExpired() {
String token = jwtService.generateToken("testuser", new HashMap<>());
assertFalse(jwtService.isTokenExpired(token));
when(jwtProperties.getExpiration()).thenReturn(1L);
JWTService shortLivedJwtService = new JWTService(securityProperties);
String expiredToken = shortLivedJwtService.generateToken("testuser", new HashMap<>());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
assertThrows(ExpiredJwtException.class, () -> assertTrue(shortLivedJwtService.isTokenExpired(expiredToken)));
}
@Test
void testExtractTokenFromRequestWithAuthorizationHeader() {
String token = "test-token";
when(request.getHeader("Authorization")).thenReturn("Bearer " + token);
assertEquals(token, jwtService.extractTokenFromRequest(request));
}
@Test
void testExtractTokenFromRequestWithCookie() {
String token = "test-token";
Cookie[] cookies = {new Cookie("STIRLING_JWT", token)};
when(request.getHeader("Authorization")).thenReturn(null);
when(request.getCookies()).thenReturn(cookies);
assertEquals(token, jwtService.extractTokenFromRequest(request));
}
@Test
void testExtractTokenFromRequestWithNoCookies() {
when(request.getHeader("Authorization")).thenReturn(null);
when(request.getCookies()).thenReturn(null);
assertNull(jwtService.extractTokenFromRequest(request));
}
@Test
void testExtractTokenFromRequestWithWrongCookie() {
Cookie[] cookies = {new Cookie("OTHER_COOKIE", "value")};
when(request.getHeader("Authorization")).thenReturn(null);
when(request.getCookies()).thenReturn(cookies);
assertNull(jwtService.extractTokenFromRequest(request));
}
@Test
void testExtractTokenFromRequestWithInvalidAuthorizationHeader() {
when(request.getHeader("Authorization")).thenReturn("Basic token");
when(request.getCookies()).thenReturn(null);
assertNull(jwtService.extractTokenFromRequest(request));
}
@Test
void testAddTokenToResponse() {
String token = "test-token";
jwtService.addTokenToResponse(response, token);
verify(response).setHeader("Authorization", "Bearer " + token);
verify(response).addHeader(eq("Set-Cookie"), contains("STIRLING_JWT=" + token));
verify(response).addHeader(eq("Set-Cookie"), contains("HttpOnly"));
verify(response).addHeader(eq("Set-Cookie"), contains("Secure"));
verify(response).addHeader(eq("Set-Cookie"), contains("SameSite=Strict"));
}
@Test
void testClearTokenFromResponse() {
jwtService.clearTokenFromResponse(response);
verify(response).setHeader("Authorization", "");
verify(response).addHeader(eq("Set-Cookie"), contains("STIRLING_JWT="));
verify(response).addHeader(eq("Set-Cookie"), contains("Max-Age=0"));
}
@Test
void testIsJwtEnabledWhenEnabled() {
when(securityProperties.isJwtActive()).thenReturn(true);
when(jwtProperties.isSettingsValid()).thenReturn(true);
assertTrue(jwtService.isJwtEnabled());
}
@Test
void testIsJwtEnabledWhenDisabled() {
when(securityProperties.isJwtActive()).thenReturn(false);
assertFalse(jwtService.isJwtEnabled());
}
@Test
void testIsJwtEnabledWhenInvalidSettings() {
when(securityProperties.isJwtActive()).thenReturn(true);
when(jwtProperties.isSettingsValid()).thenReturn(false);
assertFalse(jwtService.isJwtEnabled());
}
@Test
void testIsJwtEnabledWhenJwtPropertiesNull() {
when(securityProperties.isJwtActive()).thenReturn(true);
when(securityProperties.getJwt()).thenReturn(null);
JWTService jwtServiceWithNullProps = new JWTService(securityProperties);
assertFalse(jwtServiceWithNullProps.isJwtEnabled());
}
}