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 494d1d84..61df11d1 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java @@ -1,6 +1,7 @@ package stirling.software.SPDF.config.security; import java.io.IOException; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -66,43 +67,50 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { Object principalTest = authentication.getPrincipal(); String username = UserUtils.getUsernameFromPrincipal(principalTest); - log.info("Principal: {}", username); List allSessions = sessionPersistentRegistry.getAllSessions(username, false); + int userSessions = allSessions.size(); + HttpSession session = request.getSession(false); if (session == null) { - session = request.getSession(true); + filterChain.doFilter(request, response); + return; } + String sessionId = session.getId(); - String sessionId = request.getSession(false).getId(); - - log.info("allSessions: {} username: {}", allSessions.size(), username); - + if (allSessions.size() > 2) { + // Sortiere nach letzter Aktivität – älteste zuerst + List sortedSessions = + allSessions.stream() + .sorted(Comparator.comparing(SessionInformation::getLastRequest)) + .collect(Collectors.toList()); + int sessionsToExpire = allSessions.size() - 2; + for (int i = 0; i < sessionsToExpire; i++) { + SessionInformation oldSession = sortedSessions.get(i); + if (!sessionId.equals(oldSession.getSessionId())) { + sessionPersistentRegistry.expireSession(oldSession.getSessionId()); + oldSession.expireNow(); + log.info( + "Expired old session: {} (last request: {})", + oldSession.getSessionId(), + oldSession.getLastRequest()); + } + } + } for (SessionInformation sessionInformation : allSessions) { if (sessionId.equals(sessionInformation.getSessionId())) { - log.info("Session found: {}", sessionId); - log.info("lastRequest: {}", sessionInformation.getLastRequest()); sessionPersistentRegistry.refreshLastRequest(sessionId); - SessionInformation sessionInfo = - sessionPersistentRegistry.getSessionInformation(sessionId); - log.info("new lastRequest: {}", sessionInfo.getLastRequest()); - } else if (allSessions.size() > 2) { - sessionPersistentRegistry.expireSession(sessionId); - sessionInformation.expireNow(); - authentication.setAuthenticated(false); - SecurityContextHolder.clearContext(); - request.getSession().invalidate(); - log.info( - "Expired session: {} Date: {}", - sessionInformation.getSessionId(), - sessionInformation.getLastRequest()); - response.sendRedirect(request.getContextPath() + "/login?error=expiredSession"); - return; } } allSessions = sessionPersistentRegistry.getAllSessions(username, false); + log.info( + "username: {} || before Sessions: {} | after Sessions: {}", + username, + userSessions, + allSessions.size()); + SessionEntity sessionEntity = sessionPersistentRegistry.getSessionEntity(sessionId); if (allSessions.isEmpty() || sessionEntity.isExpired()) { diff --git a/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java b/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java index 65a9e94e..94f87b01 100644 --- a/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java +++ b/src/main/java/stirling/software/SPDF/config/security/session/SessionPersistentRegistry.java @@ -7,6 +7,7 @@ import java.util.Date; import java.util.List; import java.util.Optional; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionRegistry; @@ -24,11 +25,14 @@ import stirling.software.SPDF.model.SessionEntity; public class SessionPersistentRegistry implements SessionRegistry { private final SessionRepository sessionRepository; + private final boolean runningEE; @Value("${server.servlet.session.timeout:120s}") // TODO: Change to 30m private Duration defaultMaxInactiveInterval; - public SessionPersistentRegistry(SessionRepository sessionRepository) { + public SessionPersistentRegistry( + SessionRepository sessionRepository, @Qualifier("runningEE") boolean runningEE) { + this.runningEE = runningEE; this.sessionRepository = sessionRepository; } @@ -184,4 +188,12 @@ public class SessionPersistentRegistry implements SessionRegistry { // The first session in the list is the latest session for the given principal name return Optional.of(allSessions.get(0)); } + + // Get the maximum number of sessions + public int getMaxSessions() { + if (runningEE) { + return Integer.MAX_VALUE; + } + return 30; + } } diff --git a/src/main/java/stirling/software/SPDF/config/security/session/SessionRegistryConfig.java b/src/main/java/stirling/software/SPDF/config/security/session/SessionRegistryConfig.java index 8fa24e95..750cf740 100644 --- a/src/main/java/stirling/software/SPDF/config/security/session/SessionRegistryConfig.java +++ b/src/main/java/stirling/software/SPDF/config/security/session/SessionRegistryConfig.java @@ -1,5 +1,6 @@ package stirling.software.SPDF.config.security.session; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.session.SessionRegistryImpl; @@ -14,7 +15,7 @@ public class SessionRegistryConfig { @Bean public SessionPersistentRegistry sessionPersistentRegistry( - SessionRepository sessionRepository) { - return new SessionPersistentRegistry(sessionRepository); + SessionRepository sessionRepository, @Qualifier("runningEE") boolean runningEE) { + return new SessionPersistentRegistry(sessionRepository, runningEE); } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/UserController.java b/src/main/java/stirling/software/SPDF/controller/api/UserController.java index c676169c..c93f652e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/UserController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/UserController.java @@ -13,8 +13,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.session.SessionInformation; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -30,7 +28,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import stirling.software.SPDF.config.security.UserService; -import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal; +import stirling.software.SPDF.config.security.UserUtils; import stirling.software.SPDF.config.security.session.SessionPersistentRegistry; import stirling.software.SPDF.model.AuthenticationType; import stirling.software.SPDF.model.Role; @@ -293,19 +291,11 @@ public class UserController { if (!enabled) { // Invalidate all sessions if the user is being disabled List principals = sessionRegistry.getAllPrincipals(); - String userNameP = ""; for (Object principal : principals) { List sessionsInformation = sessionRegistry.getAllSessions(principal, false); - if (principal instanceof UserDetails detailsUser) { - userNameP = detailsUser.getUsername(); - } else if (principal instanceof OAuth2User oAuth2User) { - userNameP = oAuth2User.getName(); - } else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) { - userNameP = saml2User.name(); - } else if (principal instanceof String stringUser) { - userNameP = stringUser; - } + String userNameP = UserUtils.getUsernameFromPrincipal(principal); + if (userNameP.equalsIgnoreCase(username)) { for (SessionInformation sessionInfo : sessionsInformation) { sessionRegistry.expireSession(sessionInfo.getSessionId()); 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 1396772f..c49d1872 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java @@ -14,6 +14,7 @@ import java.util.stream.Collectors; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; +import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; @@ -205,8 +206,10 @@ public class AccountWebController { // Map to store session information and user activity status Map userSessions = new HashMap<>(); Map userLastRequest = new HashMap<>(); + Map userActiveSessions = new HashMap<>(); int activeUsers = 0; int disabledUsers = 0; + int maxSessions = sessionPersistentRegistry.getMaxSessions(); while (iterator.hasNext()) { User user = iterator.next(); if (user != null) { @@ -221,7 +224,7 @@ public class AccountWebController { // Determine the user's session status and last request time int maxInactiveInterval = sessionPersistentRegistry.getMaxInactiveInterval(); boolean hasActiveSession = false; - Date lastRequest = null; + Date lastRequest; Optional latestSession = sessionPersistentRegistry.findLatestSession(user.getUsername()); if (latestSession.isPresent()) { @@ -251,6 +254,9 @@ public class AccountWebController { if (!user.isEnabled()) { disabledUsers++; } + List sessionInformations = + sessionPersistentRegistry.getAllSessions(user.getUsername(), false); + userActiveSessions.put(user.getUsername(), sessionInformations.size()); } } // Sort users by active status and last request date @@ -316,9 +322,11 @@ public class AccountWebController { model.addAttribute("roleDetails", roleDetails); model.addAttribute("userSessions", userSessions); model.addAttribute("userLastRequest", userLastRequest); + model.addAttribute("userActiveSessions", userActiveSessions); model.addAttribute("totalUsers", allUsers.size()); model.addAttribute("activeUsers", activeUsers); model.addAttribute("disabledUsers", disabledUsers); + model.addAttribute("maxSessions", maxSessions); return "addUsers"; } diff --git a/src/main/resources/templates/addUsers.html b/src/main/resources/templates/addUsers.html index 9b6475f3..b49cb879 100644 --- a/src/main/resources/templates/addUsers.html +++ b/src/main/resources/templates/addUsers.html @@ -25,7 +25,7 @@

-
+
manage_accounts Admin User Control Settings @@ -45,6 +45,7 @@ Total Users: Active Users: Disabled Users: + Max Sessions
@@ -69,6 +70,7 @@ Roles Authenticated Last Request + Sessions Actions @@ -80,6 +82,7 @@ +