mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-04-19 11:11:18 +00:00
add expired by Admin
This commit is contained in:
parent
cfa71e537b
commit
09da81643c
@ -536,6 +536,9 @@ dependencies {
|
||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
|
||||
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||
// https://mvnrepository.com/artifact/org.springframework/spring-webflux
|
||||
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
||||
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
|
@ -39,14 +39,14 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
}
|
||||
String requestURI = request.getRequestURI();
|
||||
|
||||
if ("GET".equalsIgnoreCase(request.getMethod())) {
|
||||
boolean isApiRequest = requestURI.contains("/api/v1");
|
||||
|
||||
if ("GET".equalsIgnoreCase(request.getMethod()) && !isApiRequest) {
|
||||
|
||||
Principal principal = request.getUserPrincipal();
|
||||
|
||||
boolean isApiRequest = requestURI.contains("/api/v1");
|
||||
|
||||
// allowlist for public or static routes
|
||||
if (("/".equals(requestURI)
|
||||
if ("/".equals(requestURI)
|
||||
|| "/login".equals(requestURI)
|
||||
|| "/home".equals(requestURI)
|
||||
|| "/home-legacy".equals(requestURI)
|
||||
@ -61,9 +61,9 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
|| requestURI.endsWith(".js")
|
||||
|| requestURI.endsWith(".png")
|
||||
|| requestURI.endsWith(".webmanifest")
|
||||
|| requestURI.contains("/files/")) && !isApiRequest) {
|
||||
|| requestURI.contains("/files/")) {
|
||||
return true;
|
||||
} else if (principal != null && !isApiRequest) {
|
||||
} else if (principal != null) {
|
||||
if (session == null) {
|
||||
session = request.getSession(true);
|
||||
}
|
||||
@ -71,6 +71,38 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
final HttpSession finalSession = session;
|
||||
String sessionId = finalSession.getId();
|
||||
|
||||
boolean isExpiredByAdmin =
|
||||
sessionsInterface.getAllSessions().stream()
|
||||
.filter(s -> s.getSessionId().equals(finalSession.getId()))
|
||||
.anyMatch(s -> s.isExpired());
|
||||
|
||||
if (isExpiredByAdmin) {
|
||||
response.sendRedirect("/logout");
|
||||
log.info("Session expired. Logging out user {}", principal.getName());
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxApplicationSessions = sessionsInterface.getMaxApplicationSessions();
|
||||
|
||||
Collection<SessionsModelInterface> allSessions = sessionsInterface.getAllSessions();
|
||||
|
||||
long totalSessionsNonExpired =
|
||||
allSessions.stream().filter(s -> !s.isExpired()).count();
|
||||
|
||||
List<SessionsModelInterface> activeSessions =
|
||||
allSessions.stream()
|
||||
.filter(s -> !s.isExpired())
|
||||
.sorted(
|
||||
(s1, s2) ->
|
||||
Long.compare(
|
||||
s2.getLastRequest().getTime(),
|
||||
s1.getLastRequest().getTime()))
|
||||
.limit(maxApplicationSessions)
|
||||
.toList();
|
||||
|
||||
boolean hasUserActiveSession =
|
||||
activeSessions.stream().anyMatch(s -> s.getSessionId().equals(sessionId));
|
||||
|
||||
final String currentPrincipal = principal.getName();
|
||||
|
||||
long userSessions =
|
||||
@ -82,29 +114,21 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
s.getPrincipalName()))
|
||||
.count();
|
||||
|
||||
long totalSessions =
|
||||
sessionsInterface.getAllSessions().stream()
|
||||
.filter(s -> !s.isExpired())
|
||||
.count();
|
||||
|
||||
int maxUserSessions = sessionsInterface.getMaxUserSessions();
|
||||
|
||||
log.info(
|
||||
"Active sessions for {}: {} (max: {}) | Total: {} (max: {})",
|
||||
"Active sessions for {}: {} (max: {}) | Total: {} (max: {}) | Active"
|
||||
+ " sessions: {}",
|
||||
currentPrincipal,
|
||||
userSessions,
|
||||
maxUserSessions,
|
||||
totalSessions,
|
||||
sessionsInterface.getMaxApplicationSessions());
|
||||
|
||||
boolean isCurrentSessionRegistered =
|
||||
sessionsInterface.getAllSessions().stream()
|
||||
.filter(s -> !s.isExpired())
|
||||
.anyMatch(s -> s.getSessionId().equals(sessionId));
|
||||
totalSessionsNonExpired,
|
||||
maxApplicationSessions,
|
||||
hasUserActiveSession);
|
||||
|
||||
if ((userSessions >= maxUserSessions
|
||||
|| totalSessions >= sessionsInterface.getMaxApplicationSessions())
|
||||
&& !isCurrentSessionRegistered) {
|
||||
|| totalSessionsNonExpired >= maxApplicationSessions)
|
||||
&& !hasUserActiveSession) {
|
||||
response.sendError(
|
||||
HttpServletResponse.SC_UNAUTHORIZED,
|
||||
"Max sessions reached for this user. To continue on this device, please"
|
||||
@ -114,15 +138,15 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
||||
|
||||
// If session is not registered yet, register it; otherwise, update the last request
|
||||
// timestamp.
|
||||
if (!isCurrentSessionRegistered) {
|
||||
log.debug("Register session: {}", sessionId);
|
||||
if (!hasUserActiveSession) {
|
||||
log.info("Register session: {}", sessionId);
|
||||
sessionsInterface.registerSession(finalSession);
|
||||
} else {
|
||||
log.debug("Update session last request: {}", sessionId);
|
||||
log.info("Update session last request: {}", sessionId);
|
||||
sessionsInterface.updateSessionLastRequest(sessionId);
|
||||
}
|
||||
return true;
|
||||
} else if (principal == null && !isApiRequest) {
|
||||
} else if (principal == null) {
|
||||
if (session == null) {
|
||||
session = request.getSession(true);
|
||||
}
|
||||
|
@ -89,6 +89,10 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
||||
sessionPersistentRegistry.expireSession(sessionId);
|
||||
}
|
||||
|
||||
public void expireSession(String sessionId, boolean expiredByAdmin) {
|
||||
sessionPersistentRegistry.expireSession(sessionId, expiredByAdmin);
|
||||
}
|
||||
|
||||
public int getMaxInactiveInterval() {
|
||||
return (int) defaultMaxInactiveInterval.getSeconds();
|
||||
}
|
||||
@ -139,7 +143,7 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
||||
.toList()
|
||||
.size();
|
||||
boolean isAnonymousUserWithoutLogin = "anonymousUser".equals(principalName) && loginEnabled;
|
||||
log.debug(
|
||||
log.info(
|
||||
"all {} allNonExpiredSessions {} {} isAnonymousUserWithoutLogin {}",
|
||||
all,
|
||||
allNonExpiredSessions,
|
||||
@ -147,7 +151,7 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
||||
isAnonymousUserWithoutLogin);
|
||||
|
||||
if (allNonExpiredSessions >= getMaxApplicationSessions() && !isAnonymousUserWithoutLogin) {
|
||||
log.debug("Session {} Expired=TRUE", session.getId());
|
||||
log.info("Session {} Expired=TRUE", session.getId());
|
||||
sessionPersistentRegistry.expireSession(session.getId());
|
||||
sessionPersistentRegistry.removeSessionInformation(se.getSession().getId());
|
||||
// if (allNonExpiredSessions > getMaxUserSessions()) {
|
||||
@ -155,12 +159,12 @@ public class CustomHttpSessionListener implements HttpSessionListener, SessionsI
|
||||
// }
|
||||
} else if (all >= getMaxUserSessions() && !isAnonymousUserWithoutLogin) {
|
||||
enforceMaxSessionsForPrincipal(principalName);
|
||||
log.debug("Session {} Expired=TRUE", session.getId());
|
||||
log.info("Session {} Expired=TRUE", session.getId());
|
||||
} else if (isAnonymousUserWithoutLogin) {
|
||||
sessionPersistentRegistry.expireSession(session.getId());
|
||||
sessionPersistentRegistry.removeSessionInformation(se.getSession().getId());
|
||||
} else {
|
||||
log.debug("Session created: {}", session.getId());
|
||||
log.info("Session created: {}", session.getId());
|
||||
sessionPersistentRegistry.registerNewSession(se.getSession().getId(), principalName);
|
||||
}
|
||||
}
|
||||
|
@ -28,14 +28,18 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
|
||||
private final SessionRepository sessionRepository;
|
||||
private final boolean runningEE;
|
||||
private final boolean loginEnabled;
|
||||
|
||||
@Value("${server.servlet.session.timeout:30m}")
|
||||
private Duration defaultMaxInactiveInterval;
|
||||
|
||||
public SessionPersistentRegistry(
|
||||
SessionRepository sessionRepository, @Qualifier("runningEE") boolean runningEE) {
|
||||
SessionRepository sessionRepository,
|
||||
@Qualifier("runningEE") boolean runningEE,
|
||||
@Qualifier("loginEnabled") boolean loginEnabled) {
|
||||
this.runningEE = runningEE;
|
||||
this.sessionRepository = sessionRepository;
|
||||
this.loginEnabled = loginEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,7 +90,7 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
if (sessionEntity == null) {
|
||||
sessionEntity = new SessionEntity();
|
||||
sessionEntity.setSessionId(sessionId);
|
||||
log.debug("Registering new session for principal: {}", principalName);
|
||||
log.info("Registering new session for principal: {}", principalName);
|
||||
}
|
||||
sessionEntity.setPrincipalName(principalName);
|
||||
sessionEntity.setLastRequest(new Date()); // Set lastRequest to the current date
|
||||
@ -188,6 +192,17 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
public void expireSession(String sessionId, boolean expiredByAdmin) {
|
||||
Optional<SessionEntity> sessionEntityOpt = sessionRepository.findById(sessionId);
|
||||
if (sessionEntityOpt.isPresent()) {
|
||||
SessionEntity sessionEntity = sessionEntityOpt.get();
|
||||
sessionEntity.setExpired(true); // Set expired to true
|
||||
sessionEntity.setAdminExpired(expiredByAdmin);
|
||||
sessionRepository.save(sessionEntity);
|
||||
log.debug("Session expired: {}", sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all sessions as expired
|
||||
public void expireAllSessions() {
|
||||
List<SessionEntity> sessionEntities = sessionRepository.findAll();
|
||||
@ -283,9 +298,12 @@ public class SessionPersistentRegistry implements SessionRegistry {
|
||||
|
||||
// Get the maximum number of user sessions
|
||||
public int getMaxUserSessions() {
|
||||
if (runningEE) {
|
||||
return Integer.MAX_VALUE;
|
||||
if (loginEnabled) {
|
||||
if (runningEE) {
|
||||
return 3;
|
||||
}
|
||||
return Integer.MAX_VALUE; // (3)
|
||||
}
|
||||
return 3;
|
||||
return Integer.MAX_VALUE; // (10)
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ public class SessionRegistryConfig {
|
||||
|
||||
@Bean
|
||||
public SessionPersistentRegistry sessionPersistentRegistry(
|
||||
SessionRepository sessionRepository, @Qualifier("runningEE") boolean runningEE) {
|
||||
return new SessionPersistentRegistry(sessionRepository, runningEE);
|
||||
SessionRepository sessionRepository,
|
||||
@Qualifier("runningEE") boolean runningEE,
|
||||
@Qualifier("loginEnabled") boolean loginEnabled) {
|
||||
return new SessionPersistentRegistry(sessionRepository, runningEE, loginEnabled);
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ 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.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
@ -20,15 +20,18 @@ 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.CustomHttpSessionListener;
|
||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
|
||||
@RestController
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class SessionStatusController {
|
||||
|
||||
@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) {
|
||||
@ -40,6 +43,35 @@ public class SessionStatusController {
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/session/invalidate/{sessionId}")
|
||||
public String invalidateSession(
|
||||
HttpServletRequest request,
|
||||
Authentication authentication,
|
||||
@PathVariable String sessionId) {
|
||||
// ist ROLE_ADMIN oder session inhaber
|
||||
if (authentication == null || !authentication.isAuthenticated()) {
|
||||
return "redirect:/login";
|
||||
}
|
||||
Object principal = authentication.getPrincipal();
|
||||
String principalName = UserUtils.getUsernameFromPrincipal(principal);
|
||||
if (principalName == null) {
|
||||
return "redirect:/login";
|
||||
}
|
||||
boolean isAdmin =
|
||||
authentication.getAuthorities().stream()
|
||||
.anyMatch(role -> "ROLE_ADMIN".equals(role.getAuthority()));
|
||||
|
||||
boolean isOwner =
|
||||
sessionPersistentRegistry.getAllSessions(principalName, false).stream()
|
||||
.anyMatch(session -> session.getSessionId().equals(sessionId));
|
||||
if (isAdmin || isOwner) {
|
||||
customHttpSessionListener.expireSession(sessionId, isAdmin);
|
||||
return "redirect:/adminSettings?messageType=sessionInvalidated";
|
||||
} else {
|
||||
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) {
|
||||
|
@ -215,13 +215,15 @@ public class AccountWebController {
|
||||
@GetMapping("/adminSettings")
|
||||
public String showAddUserForm(
|
||||
HttpServletRequest request, Model model, Authentication authentication) {
|
||||
String currentSessionId = request.getSession().getId();
|
||||
|
||||
List<User> allUsers = userRepository.findAll();
|
||||
Iterator<User> iterator = allUsers.iterator();
|
||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
||||
// Map to store session information and user activity status
|
||||
Map<String, Boolean> userSessions = new HashMap<>();
|
||||
Map<String, Date> userLastRequest = new HashMap<>();
|
||||
Map<String, Integer> userActiveSessions = new HashMap<>();
|
||||
Map<String, List<SessionsModelInterface>> userActiveSessions = new HashMap<>();
|
||||
int activeUsers = 0;
|
||||
int disabledUsers = 0;
|
||||
int maxSessions = customHttpSessionListener.getMaxApplicationSessions();
|
||||
@ -273,7 +275,7 @@ public class AccountWebController {
|
||||
}
|
||||
List<SessionsModelInterface> sessionInformations =
|
||||
customHttpSessionListener.getAllSessions(user.getUsername(), false);
|
||||
userActiveSessions.put(user.getUsername(), sessionInformations.size());
|
||||
userActiveSessions.put(user.getUsername(), sessionInformations);
|
||||
}
|
||||
}
|
||||
// Sort users by active status and last request date
|
||||
@ -335,6 +337,7 @@ public class AccountWebController {
|
||||
}
|
||||
|
||||
model.addAttribute("users", sortedUsers);
|
||||
model.addAttribute("currentSessionId", currentSessionId);
|
||||
if (authentication != null) {
|
||||
model.addAttribute("currentUsername", authentication.getName());
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ public class SessionEntity implements Serializable, SessionsModelInterface {
|
||||
private Date lastRequest;
|
||||
private boolean expired;
|
||||
|
||||
private Boolean adminExpired = false;
|
||||
|
||||
@Override
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
|
@ -66,7 +66,7 @@
|
||||
<div class="my-4">
|
||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong>
|
||||
<span th:text="${totalUsers}"></span>
|
||||
<span th:if="${@runningProOrHigher}" th:text="'| ' + ${maxPaidUsers}"></span>
|
||||
<span th:if="${@runningProOrHigher}" th:text="' | ' + ${maxPaidUsers}"></span>
|
||||
|
||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
|
||||
<span th:text="${activeUsers}"></span>
|
||||
@ -75,7 +75,8 @@
|
||||
<span th:text="${disabledUsers}"></span>
|
||||
<th:block>
|
||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalSessions}">Total Sessions:</strong>
|
||||
<span th:text="${sessionCount}"></span><span th:text="' | ' + ${maxSessions}"></span>
|
||||
<span th:if="${@runningProOrHigher}" th:text="${sessionCount}"></span>
|
||||
<span th:text="' | ' + ${maxSessions}"></span>
|
||||
</th:block>
|
||||
</div>
|
||||
</div>
|
||||
@ -107,59 +108,101 @@
|
||||
th:text="#{adminUserSettings.authenticated}">Authenticated</th>
|
||||
<th scope="col" th:title="#{adminUserSettings.lastRequest}" class="text-overflow"
|
||||
th:text="#{adminUserSettings.lastRequest}">Last Request</th>
|
||||
<th scope="col" th:title="#{adminUserSettings.userSessions}" th:text="#{adminUserSettings.userSessions}">
|
||||
User Sessions</th>
|
||||
<th scope="col" th:title="#{adminUserSettings.actions}" th:text="#{adminUserSettings.actions}" colspan="2">
|
||||
Actions</th>
|
||||
<th scope="col" class="text-center" th:title="#{adminUserSettings.userSessions}"
|
||||
th:text="#{adminUserSettings.userSessions}">User Sessions</th>
|
||||
<th scope="col" class="text-center" th:title="#{adminUserSettings.actions}"
|
||||
th:text="#{adminUserSettings.actions}" colspan="2">Actions</th>
|
||||
<!-- <th scope="col"></th> -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="user : ${users}">
|
||||
<th scope="row" style="align-content: center;" th:text="${user.id}"></th>
|
||||
<td style="align-content: center;" th:text="${user.username}"
|
||||
th:classappend="${userSessions[user.username] ? 'active-user' : ''}"></td>
|
||||
<td style="align-content: center;" th:text="#{${user.roleName}}"></td>
|
||||
<td style="align-content: center;" th:text="${user.authenticationType}"></td>
|
||||
<td style="align-content: center;"
|
||||
th:text="${userLastRequest[user.username] != null ? #dates.format(userLastRequest[user.username], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}">
|
||||
</td>
|
||||
<th:block th:if="${@runningProOrHigher}">
|
||||
<td style="align-content: center;"
|
||||
th:text="${userActiveSessions[user.username] != null ? userActiveSessions[user.username] : 0}">
|
||||
<th:block th:each="user, iterStat : ${users}">
|
||||
<tr>
|
||||
<th scope="row" th:text="${user.id}"></th>
|
||||
<td th:text="${user.username}" th:classappend="${userSessions[user.username] ? 'active-user' : ''}"></td>
|
||||
<td th:text="#{${user.roleName}}"></td>
|
||||
<td th:text="${user.authenticationType}"></td>
|
||||
<td
|
||||
th:text="${userLastRequest[user.username] != null ? #dates.format(userLastRequest[user.username], 'yyyy-MM-dd HH:mm:ss') : 'N/A'}">
|
||||
</td>
|
||||
</th:block>
|
||||
<th:block th:if="${!@runningProOrHigher}">
|
||||
<td style="align-content: center;"
|
||||
th:text="${userActiveSessions[user.username] != null ? userActiveSessions[user.username] : 0} + '/' + ${maxUserSessions}">
|
||||
<th:block th:if="${@runningProOrHigher}">
|
||||
<td class="text-center">
|
||||
<button th:if="${@enableAlphaFunctionality}" type="button"
|
||||
th:text="${#lists.size(userActiveSessions[user.username])}" th:data-bs-toggle="'collapse'"
|
||||
th:data-bs-target="'#sessions__' + ${iterStat.index}"
|
||||
th:aria-controls="'sessions__' + ${iterStat.index}"
|
||||
th:class="${#lists.isEmpty(userActiveSessions[user.username])} ? 'btn btn-sm btn-outline-secondary disabled' : 'btn btn-sm btn-outline-secondary'"
|
||||
th:aria-disabled="${#lists.isEmpty(userActiveSessions[user.username])} ? 'true' : 'false'">
|
||||
0
|
||||
</button>
|
||||
<span th:if="${!@enableAlphaFunctionality}"
|
||||
th:text="${#lists.size(userActiveSessions[user.username])}"></span> |
|
||||
<span th:text="${maxUserSessions}"></span>
|
||||
</td>
|
||||
</th:block>
|
||||
<th:block th:if="${!@runningProOrHigher}">
|
||||
<td class="text-center" th:text="${#lists.size(userActiveSessions[user.username])}">0</td>
|
||||
</th:block>
|
||||
<td class="text-center">
|
||||
<form th:if="${user.username != currentUsername}"
|
||||
th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post"
|
||||
onsubmit="return confirmDeleteUser()">
|
||||
<button type="submit" th:title="#{adminUserSettings.deleteUser}" class="btn btn-info btn-sm">
|
||||
<span class="material-symbols-rounded">person_remove</span>
|
||||
</button>
|
||||
</form>
|
||||
<a th:if="${user.username == currentUsername}" th:title="#{adminUserSettings.editOwnProfil}"
|
||||
th:href="@{'/account'}" class="btn btn-outline-success btn-sm">
|
||||
<span class="material-symbols-rounded">edit</span>
|
||||
</a>
|
||||
</td>
|
||||
</th:block>
|
||||
<td style="align-content: center;">
|
||||
<form th:if="${user.username != currentUsername}"
|
||||
th:action="@{'/api/v1/user/admin/deleteUser/' + ${user.username}}" method="post"
|
||||
onsubmit="return confirmDeleteUser()">
|
||||
<button type="submit" th:title="#{adminUserSettings.deleteUser}" class="btn btn-info btn-sm"><span
|
||||
class="material-symbols-rounded">person_remove</span></button>
|
||||
</form>
|
||||
<a th:if="${user.username == currentUsername}" th:title="#{adminUserSettings.editOwnProfil}"
|
||||
th:href="@{'/account'}" class="btn btn-outline-success btn-sm"><span
|
||||
class="material-symbols-rounded">edit</span></a>
|
||||
</td>
|
||||
<td style="align-content: center;">
|
||||
<form th:action="@{'/api/v1/user/admin/changeUserEnabled/' + ${user.username}}" method="post"
|
||||
onsubmit="return confirmChangeUserStatus()">
|
||||
<input type="hidden" name="enabled" th:value="!${user.enabled}" />
|
||||
<button type="submit" th:if="${user.enabled}" th:title="#{adminUserSettings.enabledUser}"
|
||||
class="btn btn-success btn-sm">
|
||||
<span class="material-symbols-rounded">person</span>
|
||||
</button>
|
||||
<button type="submit" th:unless="${user.enabled}" th:title="#{adminUserSettings.disabledUser}"
|
||||
class="btn btn-danger btn-sm">
|
||||
<span class="material-symbols-rounded">person_off</span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<td>
|
||||
<form th:action="@{'/api/v1/user/admin/changeUserEnabled/' + ${user.username}}" method="post"
|
||||
onsubmit="return confirmChangeUserStatus()">
|
||||
<input type="hidden" name="enabled" th:value="!${user.enabled}" />
|
||||
<button type="submit" th:if="${user.enabled}" th:title="#{adminUserSettings.enabledUser}"
|
||||
class="btn btn-success btn-sm">
|
||||
<span class="material-symbols-rounded">person</span>
|
||||
</button>
|
||||
<button type="submit" th:unless="${user.enabled}" th:title="#{adminUserSettings.disabledUser}"
|
||||
class="btn btn-danger btn-sm">
|
||||
<span class="material-symbols-rounded">person_off</span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${@enableAlphaFunctionality}">
|
||||
<td colspan="8" class="p-0 border-0">
|
||||
<div th:id="'sessions__' + ${iterStat.index}" class="collapse">
|
||||
<table class="table table-striped table-hover table-sm mb-0">
|
||||
<tbody>
|
||||
<tr th:each="s : ${userActiveSessions[user.username]}">
|
||||
<td scope="row" colspan="4">
|
||||
<span th:text="${s.sessionId}"></span>
|
||||
<span th:if="${s.sessionId == currentSessionId}" class="text-warning ms-2"
|
||||
title="Aktuelle Sitzung">
|
||||
⚠️
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="2" th:text="${#dates.format(s.lastRequest, 'yyyy-MM-dd HH:mm:ss')}"></td>
|
||||
<td colspan="2">
|
||||
<form th:action="@{'/session/invalidate/' + ${s.sessionId}}" method="get"
|
||||
onsubmit="return confirm('Session wirklich beenden?')">
|
||||
<input type="hidden" name="_method" value="DELETE" />
|
||||
<button class="btn btn-danger btn-sm"><span
|
||||
class="material-symbols-rounded">remove_circle_outline</span></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr th:if="${#lists.isEmpty(userActiveSessions[user.username])}">
|
||||
<td colspan="3" class="text-center text-muted">Keine aktiven Sessions</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user