mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-04-19 11:11:18 +00:00
clean up and more
This commit is contained in:
parent
e76e83c427
commit
4ed49d0c15
@ -151,10 +151,10 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
if ((userSessions >= maxUserSessions
|
||||
|| totalSessionsNonExpired >= maxApplicationSessions)
|
||||
&& !hasUserActiveSession) {
|
||||
response.sendError(
|
||||
HttpServletResponse.SC_UNAUTHORIZED,
|
||||
log.info(
|
||||
"Max sessions reached for this user. To continue on this device, please"
|
||||
+ " close your session in another browser.");
|
||||
response.sendError(HttpServletResponse.SC_EXPECTATION_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -203,10 +203,10 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
|
||||
if (totalSessions >= maxApplicationSessions && !hasUserActiveSession) {
|
||||
sessionsInterface.removeSession(finalSession);
|
||||
response.sendError(
|
||||
HttpServletResponse.SC_UNAUTHORIZED,
|
||||
log.info(
|
||||
"Max sessions reached for this user. To continue on this device, please"
|
||||
+ " close your session in another browser.");
|
||||
response.sendError(HttpServletResponse.SC_EXPECTATION_FAILED);
|
||||
return false;
|
||||
}
|
||||
if (!hasUserActiveSession) {
|
||||
|
@ -193,8 +193,15 @@ public class AnonymusSessionListener implements HttpSessionListener, SessionsInt
|
||||
@Override
|
||||
public void removeSession(HttpSession session) {
|
||||
AnonymusSessionInfo sessionsInfo = (AnonymusSessionInfo) sessions.get(session.getId());
|
||||
sessionsInfo.setExpired(true);
|
||||
session.invalidate();
|
||||
if (sessionsInfo != null) {
|
||||
sessionsInfo.setExpired(true);
|
||||
}
|
||||
try {
|
||||
session.invalidate();
|
||||
} catch (IllegalStateException e) {
|
||||
log.debug("Session {} already invalidated", session.getId());
|
||||
}
|
||||
sessions.remove(session.getId());
|
||||
sessions.remove(session.getId());
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
package stirling.software.SPDF.config.anonymus.session;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
@ -12,72 +13,44 @@ import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.interfaces.SessionsModelInterface;
|
||||
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class AnonymusSessionStatusController {
|
||||
|
||||
@Autowired private AnonymusSessionListener sessionRegistry;
|
||||
|
||||
@GetMapping("/session/status")
|
||||
public ResponseEntity<String> getSessionStatus(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) {
|
||||
// No session found
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("No session found");
|
||||
}
|
||||
|
||||
boolean isActiveSession =
|
||||
sessionRegistry.getAllSessions().stream()
|
||||
.filter(s -> s.getSessionId().equals(session.getId()))
|
||||
.anyMatch(s -> !s.isExpired());
|
||||
|
||||
long sessionCount =
|
||||
sessionRegistry.getAllSessions().stream().filter(s -> !s.isExpired()).count();
|
||||
|
||||
long userSessions = sessionCount;
|
||||
int maxUserSessions = sessionRegistry.getMaxUserSessions();
|
||||
|
||||
// Session invalid or expired
|
||||
if (userSessions >= maxUserSessions && !isActiveSession) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body("Session invalid or expired");
|
||||
}
|
||||
// Valid session
|
||||
else if (session.getId() != null && isActiveSession) {
|
||||
return ResponseEntity.ok("Valid session: " + session.getId());
|
||||
}
|
||||
// Fallback message with session count
|
||||
else {
|
||||
return ResponseEntity.ok("User has " + userSessions + " sessions");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/session/expire")
|
||||
public String expireSession(HttpServletRequest request) {
|
||||
@GetMapping("/userSession")
|
||||
public String getUserSessions(HttpServletRequest request, Model model) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
// Invalidate current session
|
||||
sessionRegistry.expireFirstSession(session.getId());
|
||||
log.info("Session invalidated: {}", session.getId());
|
||||
// return ResponseEntity.ok("Session invalidated");
|
||||
} else {
|
||||
log.info("No session to invalidate");
|
||||
// return ResponseEntity.ok("No session to invalidate");
|
||||
|
||||
boolean isSessionValid =
|
||||
sessionRegistry.getAllNonExpiredSessions().stream()
|
||||
.allMatch(
|
||||
sessionEntity ->
|
||||
sessionEntity.getSessionId().equals(session.getId()));
|
||||
|
||||
// Get all sessions for the user
|
||||
List<SessionsModelInterface> sessionList =
|
||||
sessionRegistry.getAllNonExpiredSessions().stream()
|
||||
.filter(
|
||||
sessionEntity ->
|
||||
!sessionEntity.getSessionId().equals(session.getId()))
|
||||
.toList();
|
||||
|
||||
model.addAttribute("sessionList", sessionList);
|
||||
return "userSession";
|
||||
}
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
@GetMapping("/session/expire/all")
|
||||
public ResponseEntity<String> expireAllSessions() {
|
||||
// Invalidate all sessions
|
||||
sessionRegistry.expireAllSessions();
|
||||
return ResponseEntity.ok("All sessions invalidated");
|
||||
}
|
||||
|
||||
@GetMapping("/session/expire/{username}")
|
||||
public ResponseEntity<String> expireAllSessionsByUsername(@PathVariable String username) {
|
||||
// Invalidate all sessions for specific user
|
||||
sessionRegistry.expireAllSessionsByUsername(username);
|
||||
return ResponseEntity.ok("All sessions invalidated for user: " + username);
|
||||
@GetMapping("/userSession/invalidate/{sessionId}")
|
||||
public String invalidateUserSession(
|
||||
HttpServletRequest request, @PathVariable String sessionId) {
|
||||
sessionRegistry.expireSession(sessionId);
|
||||
sessionRegistry.registerSession(request.getSession(false));
|
||||
return "redirect:/userSession";
|
||||
}
|
||||
}
|
||||
|
@ -142,25 +142,25 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
||||
.filter(s -> !s.isExpired() && s.getPrincipalName().equals(principalName))
|
||||
.toList()
|
||||
.size();
|
||||
boolean isAnonymousUserWithoutLogin = "anonymousUser".equals(principalName) && loginEnabled;
|
||||
boolean isAnonymousUserWithLogin = "anonymousUser".equals(principalName) && loginEnabled;
|
||||
log.info(
|
||||
"all {} allNonExpiredSessions {} {} isAnonymousUserWithoutLogin {}",
|
||||
"all {} allNonExpiredSessions {} {} isAnonymousUserWithLogin {}",
|
||||
all,
|
||||
allNonExpiredSessions,
|
||||
getMaxUserSessions(),
|
||||
isAnonymousUserWithoutLogin);
|
||||
isAnonymousUserWithLogin);
|
||||
|
||||
if (allNonExpiredSessions >= getMaxApplicationSessions() && !isAnonymousUserWithoutLogin) {
|
||||
if (allNonExpiredSessions >= getMaxApplicationSessions() && !isAnonymousUserWithLogin) {
|
||||
log.info("Session {} Expired=TRUE", session.getId());
|
||||
sessionPersistentRegistry.expireSession(session.getId());
|
||||
sessionPersistentRegistry.removeSessionInformation(se.getSession().getId());
|
||||
// if (allNonExpiredSessions > getMaxUserSessions()) {
|
||||
// enforceMaxSessionsForPrincipal(principalName);
|
||||
// }
|
||||
} else if (all >= getMaxUserSessions() && !isAnonymousUserWithoutLogin) {
|
||||
} else if (all >= getMaxUserSessions() && !isAnonymousUserWithLogin) {
|
||||
enforceMaxSessionsForPrincipal(principalName);
|
||||
log.info("Session {} Expired=TRUE", session.getId());
|
||||
} else if (isAnonymousUserWithoutLogin) {
|
||||
} else if (isAnonymousUserWithLogin) {
|
||||
sessionPersistentRegistry.expireSession(session.getId());
|
||||
sessionPersistentRegistry.removeSessionInformation(se.getSession().getId());
|
||||
} else {
|
||||
|
@ -3,11 +3,8 @@ package stirling.software.SPDF.config.anonymus.session;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
@ -29,35 +26,32 @@ import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
@Slf4j
|
||||
public class SessionStatusController {
|
||||
|
||||
@Qualifier("loginEnabled")
|
||||
private boolean loginEnabled;
|
||||
|
||||
@Autowired private SessionPersistentRegistry sessionPersistentRegistry;
|
||||
@Autowired private SessionsInterface sessionInterface;
|
||||
|
||||
@Autowired private CustomHttpSessionListener customHttpSessionListener;
|
||||
|
||||
// Returns the current session ID or 401 if no session exists
|
||||
@GetMapping("/session")
|
||||
public ResponseEntity<String> getSession(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session == null) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("No session found");
|
||||
} else {
|
||||
return ResponseEntity.ok(session.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// list all sessions from authentication user, return String redirect userSession.html
|
||||
@GetMapping("/userSession")
|
||||
public String getUserSessions(
|
||||
HttpServletRequest request, Model model, Authentication authentication) {
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
if ((authentication == null || !authentication.isAuthenticated()) && loginEnabled) {
|
||||
return "redirect:/login";
|
||||
}
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
Object principal = authentication.getPrincipal();
|
||||
String principalName = UserUtils.getUsernameFromPrincipal(principal);
|
||||
if (principalName == null) {
|
||||
return "redirect:/login";
|
||||
String principalName = null;
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
Object principal = authentication.getPrincipal();
|
||||
principalName = UserUtils.getUsernameFromPrincipal(principal);
|
||||
if (principalName == null) {
|
||||
return "redirect:/login";
|
||||
}
|
||||
} else {
|
||||
principalName = "anonymousUser";
|
||||
}
|
||||
|
||||
boolean isSessionValid =
|
||||
@ -139,81 +133,4 @@ public class SessionStatusController {
|
||||
return "redirect:/login";
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the session is active and valid according to user session limits
|
||||
@GetMapping("/session/status")
|
||||
public ResponseEntity<String> getSessionStatus(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
Object principalTest = authentication.getPrincipal();
|
||||
String username = UserUtils.getUsernameFromPrincipal(principalTest);
|
||||
|
||||
List<SessionInformation> allSessions =
|
||||
sessionPersistentRegistry.getAllSessions(username, false);
|
||||
|
||||
boolean isActivSession =
|
||||
sessionPersistentRegistry.getAllSessions().stream()
|
||||
.filter(
|
||||
sessionEntity ->
|
||||
session.getId().equals(sessionEntity.getSessionId()))
|
||||
.anyMatch(sessionEntity -> !sessionEntity.isExpired());
|
||||
|
||||
int userSessions = allSessions.size();
|
||||
int maxUserSessions = sessionInterface.getMaxUserSessions();
|
||||
|
||||
// Check if the current session is valid or expired based on the session registry
|
||||
if (userSessions >= maxUserSessions && !isActivSession) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body("Session invalid or expired");
|
||||
} else if (session.getId() != null && isActivSession) {
|
||||
return ResponseEntity.ok("Valid session: " + session.getId());
|
||||
} else {
|
||||
return ResponseEntity.ok(
|
||||
"User: " + username + " has " + userSessions + " sessions");
|
||||
}
|
||||
} else {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body("Session invalid or expired");
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidates the current session
|
||||
@GetMapping("/session/expire")
|
||||
public ResponseEntity<String> expireSession(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null) {
|
||||
sessionPersistentRegistry.expireSession(session.getId());
|
||||
return ResponseEntity.ok("Session invalidated");
|
||||
} else {
|
||||
return ResponseEntity.ok("No session to invalidate");
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidates all sessions
|
||||
@GetMapping("/session/expire/all")
|
||||
public ResponseEntity<String> expireAllSessions() {
|
||||
log.debug("Expire all sessions");
|
||||
sessionPersistentRegistry.expireAllSessions();
|
||||
return ResponseEntity.ok("All sessions invalidated");
|
||||
}
|
||||
|
||||
// Invalidates all sessions for a specific user, only if requested by the same user
|
||||
@GetMapping("/session/expire/{username}")
|
||||
public ResponseEntity<String> expireAllSessionsByUsername(@PathVariable String username) {
|
||||
SecurityContext cxt = SecurityContextHolder.getContext();
|
||||
Authentication auth = cxt.getAuthentication();
|
||||
if (auth != null && auth.isAuthenticated()) {
|
||||
Object principal = auth.getPrincipal();
|
||||
String principalName = UserUtils.getUsernameFromPrincipal(principal);
|
||||
if (principalName.equals(username)) {
|
||||
sessionPersistentRegistry.expireAllSessionsByUsername(username);
|
||||
return ResponseEntity.ok("All sessions invalidated for user: " + username);
|
||||
} else {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");
|
||||
}
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized");
|
||||
}
|
||||
}
|
||||
|
@ -287,6 +287,14 @@ database.notSupported=Diese Funktion ist für deine Datenbankverbindung nicht ve
|
||||
session.expired=Ihre Sitzung ist abgelaufen. Bitte laden Sie die Seite neu und versuchen Sie es erneut.
|
||||
session.refreshPage=Seite aktualisieren
|
||||
|
||||
#################
|
||||
# USER SESSION #
|
||||
#################
|
||||
userSession.title=Benutzersitzungen
|
||||
userSession.header=Benutzersitzungen
|
||||
userSession.maxUserSession=Wenn die maximale Anzahl Sitzungen für diesen Benutzer erreicht ist, können Sie hier andere Anmeldungen beenden, um auf diesem Gerät fortzufahren.
|
||||
userSession.lastRequest=Letzte Aufrufe
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
|
@ -287,6 +287,14 @@ database.notSupported=This function is not available for your database connectio
|
||||
session.expired=Your session has expired. Please refresh the page and try again.
|
||||
session.refreshPage=Refresh Page
|
||||
|
||||
#################
|
||||
# USER SESSION #
|
||||
#################
|
||||
userSession.title=User Sessions
|
||||
userSession.header=User Sessions
|
||||
userSession.maxUserSession=If the maximum number of sessions for this user is reached, you can end other logins here to continue on this device.
|
||||
userSession.lastRequest=last Request
|
||||
|
||||
#############
|
||||
# HOME-PAGE #
|
||||
#############
|
||||
@ -1428,7 +1436,7 @@ cookieBanner.preferencesModal.description.2=Stirling PDF cannot—and will never
|
||||
cookieBanner.preferencesModal.description.3=Your privacy and trust are at the core of what we do.
|
||||
cookieBanner.preferencesModal.necessary.title.1=Strictly Necessary Cookies
|
||||
cookieBanner.preferencesModal.necessary.title.2=Always Enabled
|
||||
cookieBanner.preferencesModal.necessary.description=These cookies are essential for the website to function properly. They enable core features like setting your privacy preferences, logging in, and filling out forms—which is why they can’t be turned off.
|
||||
cookieBanner.preferencesModal.necessary.description=These cookies are essential for the website to function properly. They enable core features like setting your privacy preferences, logging in, and filling out forms—which is why they can’t be turned off.
|
||||
cookieBanner.preferencesModal.analytics.title=Analytics
|
||||
cookieBanner.preferencesModal.analytics.description=These cookies help us understand how our tools are being used, so we can focus on building the features our community values most. Rest assured—Stirling PDF cannot and will never track the content of the documents you work with.
|
||||
|
||||
|
@ -15,8 +15,7 @@
|
||||
<p class="lead" th:if="${param.status == '404'}" th:text="#{error.404.1}"></p>
|
||||
<p class="lead" th:unless="${param.status == '404'}" th:text="#{error.404.2}"></p>
|
||||
<p class="lead" th:if="${status == 417}">
|
||||
<a th:href="@{'/userSession'}" th:text="#{session.maxUserSession}">Max sessions reached for this user. To continue on this device, please close your
|
||||
session in another browser.</a>
|
||||
<a th:href="@{'/userSession'}" th:text="#{userSession.maxUserSession}">Max sessions reached for this user.</a>
|
||||
</p>
|
||||
<br>
|
||||
<h2 th:text="#{error.needHelp}"></h2>
|
||||
|
@ -3,7 +3,7 @@
|
||||
xmlns:th="https://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{session.title}, header=#{session.header})}"></th:block>
|
||||
<th:block th:insert="~{fragments/common :: head(title=#{userSession.title}, header=#{userSession.header})}"></th:block>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -16,21 +16,21 @@
|
||||
<div class="col-md-9 bg-card">
|
||||
<div class="tool-header">
|
||||
<span class="material-symbols-rounded tool-header-icon organize">key</span>
|
||||
<span class="tool-header-text" th:text="#{session.user}">User Session</span>
|
||||
<span class="tool-header-text" th:text="#{userSession.title}">User Session</span>
|
||||
</div>
|
||||
<div class="bg-card mt-3 mb-3">
|
||||
<span th:text="#{session.maxUserSession}">Max sessions reached for this user. To continue on this device, please close your session in another browser.</span>
|
||||
<table class="table table-striped table-hover mb-0">
|
||||
<span th:text="#{userSession.maxUserSession}">Max sessions reached for this user.</span>
|
||||
<table th:unless="${#lists.isEmpty(sessionList)}" class="table table-striped table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" th:text="#{session.sessionId}">Session ID</th>
|
||||
<th scope="col" th:text="#{session.lastRequest}">last Request</th>
|
||||
<th scope="col" th:text="#{session.invalidate}">Logout</th>
|
||||
<!-- <th scope="col" th:text="#{session.sessionId}">Session ID</th> -->
|
||||
<th scope="col" th:text="#{userSession.lastRequest}">last Request</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="userSession : ${sessionList}">
|
||||
<td th:text="${userSession.getSessionId}"></td>
|
||||
<!-- <td th:text="${userSession.getSessionId}"></td> -->
|
||||
<td th:text="${userSession.getLastRequest}"></td>
|
||||
<td><a th:href="@{/userSession/invalidate/{id}(id=${userSession.getSessionId})}"
|
||||
class="btn btn-sm btn-danger">
|
||||
|
Loading…
x
Reference in New Issue
Block a user