diff --git a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java index 93dff07b..be425590 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java @@ -22,22 +22,31 @@ 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.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal; import stirling.software.SPDF.config.security.session.SessionPersistentRegistry; import stirling.software.SPDF.model.ApiKeyAuthenticationToken; +import stirling.software.SPDF.model.ApplicationProperties; +import stirling.software.SPDF.model.ApplicationProperties.Security; +import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2; +import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2; import stirling.software.SPDF.model.User; +@Slf4j @Component public class UserAuthenticationFilter extends OncePerRequestFilter { + private final ApplicationProperties applicationProperties; private final UserService userService; private final SessionPersistentRegistry sessionPersistentRegistry; private final boolean loginEnabledValue; public UserAuthenticationFilter( + @Lazy ApplicationProperties applicationProperties, @Lazy UserService userService, SessionPersistentRegistry sessionPersistentRegistry, @Qualifier("loginEnabled") boolean loginEnabledValue) { + this.applicationProperties = applicationProperties; this.userService = userService; this.sessionPersistentRegistry = sessionPersistentRegistry; this.loginEnabledValue = loginEnabledValue; @@ -121,33 +130,67 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { // Check if the authenticated user is disabled and invalidate their session if so if (authentication != null && authentication.isAuthenticated()) { + + Security securityProp = applicationProperties.getSecurity(); + LoginMethod loginMethod = LoginMethod.UNKNOWN; + + boolean blockRegistration = false; + + // Extract username and determine the login method Object principal = authentication.getPrincipal(); String username = null; if (principal instanceof UserDetails) { username = ((UserDetails) principal).getUsername(); + loginMethod = LoginMethod.USERDETAILS; } else if (principal instanceof OAuth2User) { username = ((OAuth2User) principal).getName(); + loginMethod = LoginMethod.OAUTH2USER; + OAUTH2 oAuth = securityProp.getOauth2(); + blockRegistration = oAuth != null && oAuth.getBlockRegistration(); } else if (principal instanceof CustomSaml2AuthenticatedPrincipal) { username = ((CustomSaml2AuthenticatedPrincipal) principal).getName(); + loginMethod = LoginMethod.SAML2USER; + SAML2 saml2 = securityProp.getSaml2(); + blockRegistration = saml2 != null && saml2.getBlockRegistration(); } else if (principal instanceof String) { username = (String) principal; + loginMethod = LoginMethod.STRINGUSER; } + // Retrieve all active sessions for the user List sessionsInformations = sessionPersistentRegistry.getAllSessions(principal, false); + // Check if the user exists, is disabled, or needs session invalidation if (username != null) { + log.debug("Validating user: {}", username); boolean isUserExists = userService.usernameExistsIgnoreCase(username); boolean isUserDisabled = userService.isUserDisabled(username); + boolean notSsoLogin = + !loginMethod.equals(LoginMethod.OAUTH2USER) + && !loginMethod.equals(LoginMethod.SAML2USER); + + // Block user registration if not allowed by configuration + if (blockRegistration && !isUserExists) { + log.warn("Blocked registration for OAuth2/SAML user: {}", username); + response.sendRedirect( + request.getContextPath() + "/logout?oauth2_admin_blocked_user=true"); + return; + } + + // Expire sessions and logout if the user does not exist or is disabled if (!isUserExists || isUserDisabled) { + log.info( + "Invalidating session for disabled or non-existent user: {}", username); for (SessionInformation sessionsInformation : sessionsInformations) { sessionsInformation.expireNow(); sessionPersistentRegistry.expireSession(sessionsInformation.getSessionId()); } } - if (!isUserExists) { + // Redirect to logout if credentials are invalid + if (!isUserExists && notSsoLogin) { response.sendRedirect(request.getContextPath() + "/logout?badcredentials=true"); return; } @@ -161,6 +204,25 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { filterChain.doFilter(request, response); } + private enum LoginMethod { + USERDETAILS("UserDetails"), + OAUTH2USER("OAuth2User"), + STRINGUSER("StringUser"), + UNKNOWN("Unknown"), + SAML2USER("Saml2User"); + + private String method; + + LoginMethod(String method) { + this.method = method; + } + + @Override + public String toString() { + return method; + } + } + @Override protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException { String uri = request.getRequestURI(); diff --git a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java index 81b46166..31c39c1f 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java @@ -163,6 +163,9 @@ public class AccountWebController { case "invalid_destination": erroroauth = "login.invalid_destination"; break; + case "relying_party_registration_not_found": + erroroauth = "login.relyingPartyRegistrationNotFound"; + break; // Valid InResponseTo was not available from the validation context, unable to // evaluate case "invalid_in_response_to": diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 73e56c9d..8caba5a1 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -561,6 +561,7 @@ login.oauth2invalidRequest=Invalid Request login.oauth2AccessDenied=Access Denied login.oauth2InvalidTokenResponse=Invalid Token Response login.oauth2InvalidIdToken=Invalid Id Token +login.relyingPartyRegistrationNotFound=No relying party registration found login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator. login.alreadyLoggedIn=You are already logged in to login.alreadyLoggedIn2=devices. Please log out of the devices and try again.