mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-02 18:45:21 +00:00
Updated test
This commit is contained in:
parent
622a0f91aa
commit
eb28ca086a
@ -0,0 +1,213 @@
|
|||||||
|
package stirling.software.proprietary.security.filter;
|
||||||
|
|
||||||
|
import static stirling.software.proprietary.security.model.AuthenticationType.*;
|
||||||
|
import static stirling.software.proprietary.security.model.AuthenticationType.SAML2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
|
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||||
|
import stirling.software.proprietary.security.model.AuthenticationType;
|
||||||
|
import stirling.software.proprietary.security.model.exception.AuthenticationFailureException;
|
||||||
|
import stirling.software.proprietary.security.service.CustomUserDetailsService;
|
||||||
|
import stirling.software.proprietary.security.service.JwtServiceInterface;
|
||||||
|
import stirling.software.proprietary.security.service.UserService;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final JwtServiceInterface jwtService;
|
||||||
|
private final UserService userService;
|
||||||
|
private final CustomUserDetailsService userDetailsService;
|
||||||
|
private final AuthenticationEntryPoint authenticationEntryPoint;
|
||||||
|
private final ApplicationProperties.Security securityProperties;
|
||||||
|
|
||||||
|
public JwtAuthenticationFilter(
|
||||||
|
JwtServiceInterface jwtService,
|
||||||
|
UserService userService,
|
||||||
|
CustomUserDetailsService userDetailsService,
|
||||||
|
AuthenticationEntryPoint authenticationEntryPoint,
|
||||||
|
ApplicationProperties.Security securityProperties) {
|
||||||
|
this.jwtService = jwtService;
|
||||||
|
this.userService = userService;
|
||||||
|
this.userDetailsService = userDetailsService;
|
||||||
|
this.authenticationEntryPoint = authenticationEntryPoint;
|
||||||
|
this.securityProperties = securityProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(
|
||||||
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
if (!jwtService.isJwtEnabled()) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (shouldNotFilter(request)) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String jwtToken = jwtService.extractTokenFromRequest(request);
|
||||||
|
|
||||||
|
if (jwtToken == null) {
|
||||||
|
// If they are unauthenticated and navigating to '/', redirect to '/login' instead of
|
||||||
|
// sending a 401
|
||||||
|
if ("/".equals(request.getRequestURI())
|
||||||
|
&& "GET".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
response.sendRedirect("/login");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleAuthenticationFailure(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
new AuthenticationFailureException("JWT is missing from the request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
jwtService.validateToken(jwtToken);
|
||||||
|
} catch (AuthenticationFailureException e) {
|
||||||
|
handleAuthenticationFailure(request, response, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> claims = jwtService.extractAllClaims(jwtToken);
|
||||||
|
String tokenUsername = claims.get("sub").toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Authentication authentication = createAuthentication(request, claims);
|
||||||
|
String jwt = jwtService.generateToken(authentication, claims);
|
||||||
|
|
||||||
|
jwtService.addTokenToResponse(response, jwt);
|
||||||
|
} catch (SQLException | UnsupportedProviderException e) {
|
||||||
|
log.error("Error processing user authentication for user: {}", tokenUsername, e);
|
||||||
|
handleAuthenticationFailure(
|
||||||
|
request,
|
||||||
|
response,
|
||||||
|
new AuthenticationFailureException("Error processing user authentication", e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Authentication createAuthentication(
|
||||||
|
HttpServletRequest request, Map<String, Object> claims)
|
||||||
|
throws SQLException, UnsupportedProviderException {
|
||||||
|
String username = claims.get("sub").toString();
|
||||||
|
|
||||||
|
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||||
|
processUserAuthenticationType(claims, username);
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||||
|
|
||||||
|
if (userDetails != null) {
|
||||||
|
UsernamePasswordAuthenticationToken authToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(
|
||||||
|
userDetails, null, userDetails.getAuthorities());
|
||||||
|
|
||||||
|
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||||
|
|
||||||
|
log.debug("JWT authentication successful for user: {}", username);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new UsernameNotFoundException("User not found: " + username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processUserAuthenticationType(Map<String, Object> claims, String username)
|
||||||
|
throws SQLException, UnsupportedProviderException {
|
||||||
|
AuthenticationType authenticationType =
|
||||||
|
AuthenticationType.valueOf(claims.getOrDefault("authType", WEB).toString());
|
||||||
|
log.debug("Processing {} login for {} user", authenticationType, username);
|
||||||
|
|
||||||
|
switch (authenticationType) {
|
||||||
|
case OAUTH2 -> {
|
||||||
|
ApplicationProperties.Security.OAUTH2 oauth2Properties =
|
||||||
|
securityProperties.getOauth2();
|
||||||
|
userService.processSSOPostLogin(
|
||||||
|
username, oauth2Properties.getAutoCreateUser(), OAUTH2);
|
||||||
|
}
|
||||||
|
case SAML2 -> {
|
||||||
|
ApplicationProperties.Security.SAML2 saml2Properties =
|
||||||
|
securityProperties.getSaml2();
|
||||||
|
userService.processSSOPostLogin(
|
||||||
|
username, saml2Properties.getAutoCreateUser(), SAML2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
String method = request.getMethod();
|
||||||
|
|
||||||
|
// Skip JWT processing for logout requests to prevent token refresh during logout
|
||||||
|
if ("/logout".equals(uri)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow login POST requests to be processed
|
||||||
|
if ("/login".equals(uri) && "POST".equalsIgnoreCase(method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] permitAllPatterns = {
|
||||||
|
"/login",
|
||||||
|
"/register",
|
||||||
|
"/error",
|
||||||
|
"/images/",
|
||||||
|
"/public/",
|
||||||
|
"/css/",
|
||||||
|
"/fonts/",
|
||||||
|
"/js/",
|
||||||
|
"/pdfjs/",
|
||||||
|
"/pdfjs-legacy/",
|
||||||
|
"/api/v1/info/status",
|
||||||
|
"/site.webmanifest",
|
||||||
|
"/favicon"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String pattern : permitAllPatterns) {
|
||||||
|
if (uri.startsWith(pattern)
|
||||||
|
|| uri.endsWith(".svg")
|
||||||
|
|| uri.endsWith(".png")
|
||||||
|
|| uri.endsWith(".ico")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleAuthenticationFailure(
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
AuthenticationException authException)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
authenticationEntryPoint.commence(request, response, authException);
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,7 @@ package stirling.software.proprietary.security.model;
|
|||||||
|
|
||||||
public enum AuthenticationType {
|
public enum AuthenticationType {
|
||||||
WEB,
|
WEB,
|
||||||
SSO,
|
@Deprecated(since = "1.0.2") SSO,
|
||||||
// TODO: Worth making a distinction between OAuth2 and SAML2?
|
|
||||||
OAUTH2,
|
OAUTH2,
|
||||||
SAML2
|
SAML2
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@ class JwtSaml2AuthenticationRequestRepositoryTest {
|
|||||||
String samlRequest = "testSamlRequest";
|
String samlRequest = "testSamlRequest";
|
||||||
String relyingPartyRegistrationId = "stirling-pdf";
|
String relyingPartyRegistrationId = "stirling-pdf";
|
||||||
|
|
||||||
|
when(jwtService.isJwtEnabled()).thenReturn(true);
|
||||||
when(authRequest.getRelayState()).thenReturn(relayState);
|
when(authRequest.getRelayState()).thenReturn(relayState);
|
||||||
when(authRequest.getId()).thenReturn(id);
|
when(authRequest.getId()).thenReturn(id);
|
||||||
when(authRequest.getAuthenticationRequestUri()).thenReturn(authnRequestUri);
|
when(authRequest.getAuthenticationRequestUri()).thenReturn(authnRequestUri);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user