package com.imprimelibros.erp.users; import java.util.ArrayList; import java.util.List; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.security.core.userdetails.UsernameNotFoundException; import com.imprimelibros.erp.config.Sanitizer; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; @Controller public class ImpersonationController { private static final String PREVIOUS_ADMIN_ROLE = "ROLE_PREVIOUS_ADMINISTRATOR"; private static final String SESSION_ATTR = "IMPERSONATOR_AUTH"; private final UserService userService; private final Sanitizer sanitizer; public ImpersonationController(UserService userService, Sanitizer sanitizer) { this.userService = userService; this.sanitizer = sanitizer; } @PostMapping("/impersonate") @PreAuthorize("hasRole('ADMIN') or hasRole('SUPERADMIN')") public String impersonate( @RequestParam("username") String username, Authentication authentication, HttpServletRequest request) { if (authentication == null) { return "redirect:/login"; } if (hasRole(authentication, PREVIOUS_ADMIN_ROLE)) { return "redirect:/"; } String normalized = sanitizer.plain(username); if (normalized == null || normalized.isBlank()) { return "redirect:/users"; } normalized = normalized.trim().toLowerCase(); if (authentication.getName() != null && authentication.getName().equalsIgnoreCase(normalized)) { return "redirect:/users"; } UserDetails target; try { target = userService.loadUserByUsername(normalized); } catch (UsernameNotFoundException ex) { throw new AccessDeniedException("No autorizado"); } boolean currentIsSuperAdmin = hasRole(authentication, "ROLE_SUPERADMIN"); boolean targetIsSuperAdmin = target.getAuthorities().stream() .anyMatch(a -> "ROLE_SUPERADMIN".equals(a.getAuthority())); if (targetIsSuperAdmin && !currentIsSuperAdmin) { throw new AccessDeniedException("No autorizado"); } HttpSession session = request.getSession(true); if (session.getAttribute(SESSION_ATTR) == null) { session.setAttribute(SESSION_ATTR, authentication); } List authorities = new ArrayList<>(target.getAuthorities()); authorities.add(new SimpleGrantedAuthority(PREVIOUS_ADMIN_ROLE)); UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken( target, target.getPassword(), authorities); newAuth.setDetails(authentication.getDetails()); SecurityContextHolder.getContext().setAuthentication(newAuth); return "redirect:/"; } @PostMapping("/impersonate/exit") @PreAuthorize("hasRole('PREVIOUS_ADMINISTRATOR')") public String exit(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { Object previous = session.getAttribute(SESSION_ATTR); if (previous instanceof Authentication previousAuth) { SecurityContextHolder.getContext().setAuthentication(previousAuth); } else { SecurityContextHolder.clearContext(); } session.removeAttribute(SESSION_ATTR); } return "redirect:/"; } private static boolean hasRole(Authentication auth, String role) { return auth != null && auth.getAuthorities().stream() .anyMatch(a -> role.equals(a.getAuthority())); } }