any more change

This commit is contained in:
Ludy87 2025-03-30 23:12:04 +02:00
parent f33d8b0f23
commit a16b6478a7
No known key found for this signature in database
GPG Key ID: 92696155E0220F94
7 changed files with 101 additions and 47 deletions

View File

@ -80,11 +80,13 @@ public class EndpointInterceptor implements HandlerInterceptor {
.filter(s -> !s.isExpired())
.count();
log.debug(
int maxUserSessions = sessionsInterface.getMaxUserSessions();
log.info(
"Active sessions for {}: {} (max: {}) | Total: {} (max: {})",
currentPrincipal,
userSessions,
sessionsInterface.getMaxUserSessions(),
maxUserSessions,
totalSessions,
sessionsInterface.getMaxApplicationSessions());
@ -93,7 +95,7 @@ public class EndpointInterceptor implements HandlerInterceptor {
.filter(s -> !s.isExpired())
.anyMatch(s -> s.getSessionId().equals(sessionId));
if ((userSessions >= sessionsInterface.getMaxUserSessions()
if ((userSessions >= maxUserSessions
|| totalSessions >= sessionsInterface.getMaxApplicationSessions())
&& !isCurrentSessionRegistered) {
response.sendError(
@ -129,8 +131,14 @@ public class EndpointInterceptor implements HandlerInterceptor {
.filter(s -> !s.isExpired())
.anyMatch(s -> s.getSessionId().equals(sessionId));
if (totalSessions >= sessionsInterface.getMaxApplicationSessions()
&& !isCurrentSessionRegistered) {
int maxApplicationSessions = sessionsInterface.getMaxApplicationSessions();
log.info(
"Active sessions for anonymous: Total: {} (max: {})",
totalSessions,
maxApplicationSessions);
if (totalSessions >= maxApplicationSessions && !isCurrentSessionRegistered) {
response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
"Max sessions reached for this user. To continue on this device, please"

View File

@ -167,11 +167,6 @@ public class AnonymusSessionRegistry implements HttpSessionListener, SessionsInt
}
}
@Override
public int getMaxApplicationSessions() {
return getMaxUserSessions();
}
@Override
public void removeSession(HttpSession session) {
AnonymusSessionInfo sessionsInfo = (AnonymusSessionInfo) sessions.get(session.getId());
@ -179,4 +174,14 @@ public class AnonymusSessionRegistry implements HttpSessionListener, SessionsInt
session.invalidate();
sessions.remove(session.getId());
}
@Override
public int getMaxApplicationSessions() {
return 5;
}
@Override
public int getMaxUsers() {
return 1;
}
}

View File

@ -21,6 +21,10 @@ public interface SessionsInterface {
}
default int getMaxApplicationSessions() {
return 10 * getMaxUserSessions();
return getMaxUserSessions() * 3;
}
default int getMaxUsers() {
return 10;
}
}

View File

@ -8,6 +8,7 @@ import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Qualifier;
@ -27,12 +28,14 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.SessionsInterface;
import stirling.software.SPDF.config.interfaces.SessionsModelInterface;
import stirling.software.SPDF.config.security.UserUtils;
import stirling.software.SPDF.model.ApplicationProperties;
@Component
@Slf4j
public class CustomHttpSessionListener implements HttpSessionListener, SessionsInterface {
private final SessionPersistentRegistry sessionPersistentRegistry;
private final ApplicationProperties applicationProperties;
private final boolean loginEnabled;
private final boolean runningEE;
@ -42,11 +45,13 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
public CustomHttpSessionListener(
SessionPersistentRegistry sessionPersistentRegistry,
@Qualifier("loginEnabled") boolean loginEnabled,
@Qualifier("runningEE") boolean runningEE) {
@Qualifier("runningEE") boolean runningEE,
ApplicationProperties applicationProperties) {
super();
this.sessionPersistentRegistry = sessionPersistentRegistry;
this.loginEnabled = loginEnabled;
this.runningEE = runningEE;
this.applicationProperties = applicationProperties;
}
@Override
@ -56,6 +61,14 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
.toList();
}
public List<SessionsModelInterface> getAllSessions(Object principalName, boolean expired) {
return sessionPersistentRegistry.getAllSessions().stream()
.filter(s -> s.getPrincipalName().equals(principalName))
.filter(s -> expired == s.isExpired())
.sorted(Comparator.comparing(SessionsModelInterface::getLastRequest))
.collect(Collectors.toList());
}
@Override
public Collection<SessionsModelInterface> getAllSessions() {
return new ArrayList<>(sessionPersistentRegistry.getAllSessions());
@ -66,6 +79,20 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
sessionPersistentRegistry.refreshLastRequest(sessionId);
}
public Optional<SessionsModelInterface> findLatestSession(String principalName) {
return getAllSessions(principalName, false).stream()
.filter(s -> s.getPrincipalName().equals(principalName))
.max(Comparator.comparing(SessionsModelInterface::getLastRequest));
}
public void expireSession(String sessionId) {
sessionPersistentRegistry.expireSession(sessionId);
}
public int getMaxInactiveInterval() {
return (int) defaultMaxInactiveInterval.getSeconds();
}
@Override
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
@ -207,21 +234,33 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
log.debug("Session {} expired=TRUE", session.getId());
}
// Get the maximum number of sessions
// Get the maximum number of application sessions
@Override
public int getMaxApplicationSessions() {
if (runningEE) {
return Integer.MAX_VALUE;
}
return getMaxUserSessions() * 10;
return getMaxUsers() * getMaxUserSessions();
}
// Get the maximum number of user sessions
@Override
public int getMaxUserSessions() {
if (runningEE) {
return Integer.MAX_VALUE;
if (loginEnabled) {
return 3;
}
return 3;
return 10;
}
// Get the maximum number of user sessions
@Override
public int getMaxUsers() {
if (loginEnabled) {
if (runningEE) {
int maxUsers = applicationProperties.getPremium().getMaxUsers();
if (maxUsers > 0) {
return maxUsers;
}
}
return 50;
}
return 1;
}
}

View File

@ -18,6 +18,7 @@ import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.SessionsInterface;
import stirling.software.SPDF.config.security.UserUtils;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
@ -26,6 +27,7 @@ import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
public class SessionStatusController {
@Autowired private SessionPersistentRegistry sessionPersistentRegistry;
@Autowired private SessionsInterface sessionInterface;
// Returns the current session ID or 401 if no session exists
@GetMapping("/session")
@ -59,7 +61,7 @@ public class SessionStatusController {
.anyMatch(sessionEntity -> !sessionEntity.isExpired());
int userSessions = allSessions.size();
int maxUserSessions = sessionPersistentRegistry.getMaxUserSessions();
int maxUserSessions = sessionInterface.getMaxUserSessions();
// Check if the current session is valid or expired based on the session registry
if (userSessions >= maxUserSessions && !isActivSession) {

View File

@ -14,7 +14,6 @@ import java.util.Optional;
import org.springframework.beans.factory.annotation.Qualifier;
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;
@ -30,8 +29,9 @@ import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.SessionsModelInterface;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.config.security.session.CustomHttpSessionListener;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.ApplicationProperties.Security;
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
@ -54,7 +54,7 @@ public class AccountWebController {
public static final String OAUTH_2_AUTHORIZATION = "/oauth2/authorization/";
private final ApplicationProperties applicationProperties;
private final SessionPersistentRegistry sessionPersistentRegistry;
private final CustomHttpSessionListener customHttpSessionListener;
// Assuming you have a repository for user operations
private final UserRepository userRepository;
private final boolean loginEnabledValue;
@ -62,15 +62,15 @@ public class AccountWebController {
public AccountWebController(
ApplicationProperties applicationProperties,
SessionPersistentRegistry sessionPersistentRegistry,
UserRepository userRepository,
@Qualifier("loginEnabled") boolean loginEnabledValue,
@Qualifier("runningEE") boolean runningEE) {
@Qualifier("runningEE") boolean runningEE,
CustomHttpSessionListener customHttpSessionListener) {
this.applicationProperties = applicationProperties;
this.sessionPersistentRegistry = sessionPersistentRegistry;
this.userRepository = userRepository;
this.loginEnabledValue = loginEnabledValue;
this.runningEE = runningEE;
this.customHttpSessionListener = customHttpSessionListener;
}
@GetMapping("/login")
@ -224,9 +224,9 @@ public class AccountWebController {
Map<String, Integer> userActiveSessions = new HashMap<>();
int activeUsers = 0;
int disabledUsers = 0;
int maxSessions = sessionPersistentRegistry.getMaxSessions();
int maxUserSessions = sessionPersistentRegistry.getMaxUserSessions();
int sessionCount = sessionPersistentRegistry.getAllSessionsNotExpired().size();
int maxSessions = customHttpSessionListener.getMaxApplicationSessions();
int maxUserSessions = customHttpSessionListener.getMaxUserSessions();
int sessionCount = customHttpSessionListener.getAllNonExpiredSessions().size();
while (iterator.hasNext()) {
User user = iterator.next();
if (user != null) {
@ -239,13 +239,13 @@ public class AccountWebController {
}
}
// Determine the user's session status and last request time
int maxInactiveInterval = sessionPersistentRegistry.getMaxInactiveInterval();
int maxInactiveInterval = customHttpSessionListener.getMaxInactiveInterval();
boolean hasActiveSession = false;
Date lastRequest;
Optional<SessionEntity> latestSession =
sessionPersistentRegistry.findLatestSession(user.getUsername());
Optional<SessionsModelInterface> latestSession =
customHttpSessionListener.findLatestSession(user.getUsername());
if (latestSession.isPresent()) {
SessionEntity sessionEntity = latestSession.get();
SessionEntity sessionEntity = (SessionEntity) latestSession.get();
Date lastAccessedTime = sessionEntity.getLastRequest();
Instant now = Instant.now();
// Calculate session expiration and update session status accordingly
@ -254,7 +254,7 @@ public class AccountWebController {
.toInstant()
.plus(maxInactiveInterval, ChronoUnit.SECONDS);
if (now.isAfter(expirationTime)) {
sessionPersistentRegistry.expireSession(sessionEntity.getSessionId());
customHttpSessionListener.expireSession(sessionEntity.getSessionId());
} else {
hasActiveSession = !sessionEntity.isExpired();
}
@ -271,8 +271,8 @@ public class AccountWebController {
if (!user.isEnabled()) {
disabledUsers++;
}
List<SessionInformation> sessionInformations =
sessionPersistentRegistry.getAllSessions(user.getUsername(), false);
List<SessionsModelInterface> sessionInformations =
customHttpSessionListener.getAllSessions(user.getUsername(), false);
userActiveSessions.put(user.getUsername(), sessionInformations.size());
}
}
@ -348,7 +348,7 @@ public class AccountWebController {
model.addAttribute("maxSessions", maxSessions);
model.addAttribute("maxUserSessions", maxUserSessions);
model.addAttribute("sessionCount", sessionCount);
model.addAttribute("maxPaidUsers", applicationProperties.getPremium().getMaxUsers());
model.addAttribute("maxPaidUsers", customHttpSessionListener.getMaxUsers());
return "adminSettings";
}

View File

@ -37,10 +37,10 @@
<!-- User Settings Title -->
<div
style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
<a href="#" th:data-bs-toggle="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : 'modal'"
<a href="#" th:data-bs-toggle="${totalUsers >= maxPaidUsers} ? null : 'modal'"
th:data-bs-target="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : '#addUserModal'"
th:class="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
th:title="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
th:class="${totalUsers >= maxPaidUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
th:title="${totalUsers >= maxPaidUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
<span class="material-symbols-rounded">person_add</span>
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
</a>
@ -73,13 +73,9 @@
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.disabledUsers}">Disabled Users:</strong>
<span th:text="${disabledUsers}"></span>
<th:block th:if="${@runningProOrHigher}">
<th:block>
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total Sessions:</strong>
<span th:text="${sessionCount}"></span>
</th:block>
<th:block th:if="${!@runningProOrHigher}">
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total Sessions:</strong>
<span th:text="${sessionCount}"></span><span th:if="${!@runningEE}" th:text="' | ' + ${maxSessions}"></span>
<span th:text="${sessionCount}"></span><span th:text="' | ' + ${maxSessions}"></span>
</th:block>
</div>
</div>