diff --git a/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java b/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java index 836b661eb..9ea537e23 100644 --- a/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java +++ b/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java @@ -2,6 +2,10 @@ package stirling.software.proprietary.security.config; import static stirling.software.common.util.ProviderUtils.validateProvider; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Date; @@ -10,7 +14,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; - +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; @@ -19,16 +23,6 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import io.swagger.v3.oas.annotations.tags.Tag; - -import jakarta.servlet.http.HttpServletRequest; - -import lombok.extern.slf4j.Slf4j; - import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties.Security; import stirling.software.common.model.ApplicationProperties.Security.OAUTH2; @@ -239,7 +233,8 @@ public class AccountWebController { } // Also check if user is part of the Internal team - if (user.getTeam() != null && user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) { + if (user.getTeam() != null + && user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) { shouldRemove = true; } @@ -336,6 +331,9 @@ public class AccountWebController { case "userNotFound" -> "userNotFoundMessage"; case "downgradeCurrentUser" -> "downgradeCurrentUserMessage"; case "disabledCurrentUser" -> "disabledCurrentUserMessage"; + case "cannotMoveInternalUsers" -> "team.cannotMoveInternalUsers"; + case "internalTeamNotAccessible" -> "team.internalTeamNotAccessible"; + case "invalidRole" -> "invalidRoleMessage"; default -> messageType; }; model.addAttribute("changeMessage", changeMessage); @@ -351,10 +349,16 @@ public class AccountWebController { model.addAttribute("disabledUsers", disabledUsers); // Get all teams but filter out the Internal team - List allTeams = teamRepository.findAll() - .stream() - .filter(team -> !team.getName().equals(stirling.software.proprietary.security.service.TeamService.INTERNAL_TEAM_NAME)) - .toList(); + List allTeams = + teamRepository.findAll().stream() + .filter( + team -> + !team.getName() + .equals( + stirling.software.proprietary.security + .service.TeamService + .INTERNAL_TEAM_NAME)) + .toList(); model.addAttribute("teams", allTeams); model.addAttribute("maxPaidUsers", applicationProperties.getPremium().getMaxUsers()); diff --git a/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/TeamController.java b/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/TeamController.java index 9c77af94a..57b9c7a17 100644 --- a/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/TeamController.java +++ b/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/TeamController.java @@ -1,19 +1,14 @@ package stirling.software.proprietary.security.controller.api; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.transaction.Transactional; import java.util.Optional; - +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.view.RedirectView; - -import io.swagger.v3.oas.annotations.tags.Tag; - -import jakarta.transaction.Transactional; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - import stirling.software.proprietary.model.Team; import stirling.software.proprietary.security.config.PremiumEndpoint; import stirling.software.proprietary.security.database.repository.UserRepository; @@ -36,12 +31,12 @@ public class TeamController { @PostMapping("/create") public RedirectView createTeam(@RequestParam("name") String name) { if (teamRepository.existsByNameIgnoreCase(name)) { - return new RedirectView("/adminSettings?messageType=teamExists"); + return new RedirectView("/teams?messageType=teamExists"); } Team team = new Team(); team.setName(name); teamRepository.save(team); - return new RedirectView("/adminSettings?messageType=teamCreated"); + return new RedirectView("/teams?messageType=teamCreated"); } @PreAuthorize("hasRole('ROLE_ADMIN')") @@ -50,21 +45,21 @@ public class TeamController { @RequestParam("teamId") Long teamId, @RequestParam("newName") String newName) { Optional existing = teamRepository.findById(teamId); if (existing.isEmpty()) { - return new RedirectView("/adminSettings?messageType=teamNotFound"); + return new RedirectView("/teams?messageType=teamNotFound"); } if (teamRepository.existsByNameIgnoreCase(newName)) { - return new RedirectView("/adminSettings?messageType=teamNameExists"); + return new RedirectView("/teams?messageType=teamNameExists"); } Team team = existing.get(); // Prevent renaming the Internal team if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) { - return new RedirectView("/adminSettings?messageType=internalTeamNotAccessible"); + return new RedirectView("/teams?messageType=internalTeamNotAccessible"); } team.setName(newName); teamRepository.save(team); - return new RedirectView("/adminSettings?messageType=teamRenamed"); + return new RedirectView("/teams?messageType=teamRenamed"); } @PreAuthorize("hasRole('ROLE_ADMIN')") @@ -73,35 +68,36 @@ public class TeamController { public RedirectView deleteTeam(@RequestParam("teamId") Long teamId) { Optional teamOpt = teamRepository.findById(teamId); if (teamOpt.isEmpty()) { - return new RedirectView("/adminSettings?messageType=teamNotFound"); + return new RedirectView("/teams?messageType=teamNotFound"); } Team team = teamOpt.get(); // Prevent deleting the Internal team if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) { - return new RedirectView("/adminSettings?messageType=internalTeamNotAccessible"); + return new RedirectView("/teams?messageType=internalTeamNotAccessible"); } long memberCount = userRepository.countByTeam(team); if (memberCount > 0) { - return new RedirectView("/adminSettings?messageType=teamHasUsers"); + return new RedirectView("/teams?messageType=teamHasUsers"); } teamRepository.delete(team); - return new RedirectView("/adminSettings?messageType=teamDeleted"); + return new RedirectView("/teams?messageType=teamDeleted"); } @PreAuthorize("hasRole('ROLE_ADMIN')") @PostMapping("/addUser") @Transactional public RedirectView addUserToTeam( - @RequestParam("teamId") Long teamId, - @RequestParam("userId") Long userId) { + @RequestParam("teamId") Long teamId, @RequestParam("userId") Long userId) { // Find the team - Team team = teamRepository.findById(teamId) - .orElseThrow(() -> new RuntimeException("Team not found")); + Team team = + teamRepository + .findById(teamId) + .orElseThrow(() -> new RuntimeException("Team not found")); // Prevent adding users to the Internal team if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) { @@ -109,11 +105,14 @@ public class TeamController { } // Find the user - User user = userRepository.findById(userId) - .orElseThrow(() -> new RuntimeException("User not found")); + User user = + userRepository + .findById(userId) + .orElseThrow(() -> new RuntimeException("User not found")); // Check if user is in the Internal team - prevent moving them - if (user.getTeam() != null && user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) { + if (user.getTeam() != null + && user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) { return new RedirectView("/teams/" + teamId + "?error=cannotMoveInternalUsers"); } diff --git a/proprietary/src/main/java/stirling/software/proprietary/security/controller/web/TeamWebController.java b/proprietary/src/main/java/stirling/software/proprietary/security/controller/web/TeamWebController.java index d41b2aa75..14cd747bb 100644 --- a/proprietary/src/main/java/stirling/software/proprietary/security/controller/web/TeamWebController.java +++ b/proprietary/src/main/java/stirling/software/proprietary/security/controller/web/TeamWebController.java @@ -1,20 +1,17 @@ package stirling.software.proprietary.security.controller.web; +import jakarta.servlet.http.HttpServletRequest; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; - -import org.springframework.security.access.prepost.PreAuthorize; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - import stirling.software.proprietary.model.Team; import stirling.software.proprietary.model.dto.TeamWithUserCountDTO; import stirling.software.proprietary.security.database.repository.SessionRepository; @@ -35,14 +32,15 @@ public class TeamWebController { @GetMapping @PreAuthorize("hasRole('ROLE_ADMIN')") - public String listTeams(Model model) { + public String listTeams(HttpServletRequest request, Model model) { // Get teams with user counts using a DTO projection List allTeamsWithCounts = teamRepository.findAllTeamsWithUserCount(); // Filter out the Internal team - List teamsWithCounts = allTeamsWithCounts.stream() - .filter(team -> !team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) - .toList(); + List teamsWithCounts = + allTeamsWithCounts.stream() + .filter(team -> !team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) + .toList(); // Get the latest activity for each team List teamActivities = sessionRepository.findLatestActivityByTeam(); @@ -55,6 +53,27 @@ public class TeamWebController { teamLastRequest.put(teamId, lastActivity); } + String messageType = request.getParameter("messageType"); + if (messageType != null) { + if ("teamCreated".equals(messageType)) { + model.addAttribute("addMessage", "teamCreated"); + } else if ("teamExists".equals(messageType)) { + model.addAttribute("errorMessage", "teamExists"); + } else if ("teamNotFound".equals(messageType)) { + model.addAttribute("errorMessage", "teamNotFound"); + } else if ("teamNameExists".equals(messageType)) { + model.addAttribute("errorMessage", "teamNameExists"); + } else if ("internalTeamNotAccessible".equals(messageType)) { + model.addAttribute("errorMessage", "team.internalTeamNotAccessible"); + } else if ("teamRenamed".equals(messageType)) { + model.addAttribute("changeMessage", "teamRenamed"); + } else if ("teamHasUsers".equals(messageType)) { + model.addAttribute("errorMessage", "teamHasUsers"); + } else if ("teamDeleted".equals(messageType)) { + model.addAttribute("deleteMessage", "teamDeleted"); + } + } + // Add data to the model model.addAttribute("teamsWithCounts", teamsWithCounts); model.addAttribute("teamLastRequest", teamLastRequest); @@ -64,10 +83,13 @@ public class TeamWebController { @GetMapping("/{id}") @PreAuthorize("hasRole('ROLE_ADMIN')") - public String viewTeamDetails(@PathVariable("id") Long id, Model model) { + public String viewTeamDetails( + HttpServletRequest request, @PathVariable("id") Long id, Model model) { // Get the team - Team team = teamRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Team not found")); + Team team = + teamRepository + .findById(id) + .orElseThrow(() -> new RuntimeException("Team not found")); // Prevent access to Internal team if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) { @@ -80,10 +102,19 @@ public class TeamWebController { // Get all users not in this team for the Add User to Team dropdown // Exclude users that are in the Internal team List allUsers = userRepository.findAllWithTeam(); - List availableUsers = allUsers.stream() - .filter(user -> (user.getTeam() == null || !user.getTeam().getId().equals(id)) && - (user.getTeam() == null || !user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME))) - .toList(); + List availableUsers = + allUsers.stream() + .filter( + user -> + (user.getTeam() == null + || !user.getTeam().getId().equals(id)) + && (user.getTeam() == null + || !user.getTeam() + .getName() + .equals( + TeamService + .INTERNAL_TEAM_NAME))) + .toList(); // Get the latest session for each user in the team List userSessions = sessionRepository.findLatestSessionByTeamId(id); @@ -96,6 +127,13 @@ public class TeamWebController { userLastRequest.put(username, lastRequest); } + String errorMessage = request.getParameter("error"); + if (errorMessage != null) { + if ("cannotMoveInternalUsers".equals(errorMessage)) { + model.addAttribute("errorMessage", "team.cannotMoveInternalUsers"); + } + } + model.addAttribute("team", team); model.addAttribute("teamUsers", teamUsers); model.addAttribute("availableUsers", availableUsers); diff --git a/proprietary/src/main/resources/templates/accounts/team-details.html b/proprietary/src/main/resources/templates/accounts/team-details.html index aff0c4150..3fb779bae 100644 --- a/proprietary/src/main/resources/templates/accounts/team-details.html +++ b/proprietary/src/main/resources/templates/accounts/team-details.html @@ -32,6 +32,11 @@ + +
+ Default message if not found +
+
arrow_back diff --git a/proprietary/src/main/resources/templates/accounts/teams.html b/proprietary/src/main/resources/templates/accounts/teams.html index 509c3f727..32ac70679 100644 --- a/proprietary/src/main/resources/templates/accounts/teams.html +++ b/proprietary/src/main/resources/templates/accounts/teams.html @@ -29,12 +29,29 @@
+ +
+ Default message if not found +
+ +
+ Default message if not found +
+ +
+ Default message if not found +
+ +
+ Default message if not found +
+
person_add Add New User - - + + group Manage Teams @@ -108,7 +108,7 @@ Change User's Role - + analytics Usage Statistics @@ -120,27 +120,27 @@ # - Username - Team - Roles + Username + Team + Roles Authenticated Last Request - Actions + Actions - - - + + + shield - Role + Role - - + +