mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-02-10 21:09:12 +00:00
impersonation implementado
This commit is contained in:
@ -149,6 +149,10 @@ public class SecurityConfig {
|
||||
"/pagos/redsys/**"
|
||||
)
|
||||
.permitAll()
|
||||
.requestMatchers("/impersonate/exit")
|
||||
.hasRole("PREVIOUS_ADMINISTRATOR")
|
||||
.requestMatchers("/impersonate")
|
||||
.hasAnyRole("SUPERADMIN", "ADMIN")
|
||||
.requestMatchers("/users/**").hasAnyRole("SUPERADMIN", "ADMIN")
|
||||
.anyRequest().authenticated())
|
||||
|
||||
|
||||
@ -0,0 +1,115 @@
|
||||
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<GrantedAuthority> 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()));
|
||||
}
|
||||
}
|
||||
@ -81,6 +81,9 @@ public class UserController {
|
||||
"usuarios.delete.button",
|
||||
"app.yes",
|
||||
"app.cancelar",
|
||||
"usuarios.impersonate.title",
|
||||
"usuarios.impersonate.text",
|
||||
"usuarios.impersonate.button",
|
||||
"usuarios.delete.ok.title",
|
||||
"usuarios.delete.ok.text");
|
||||
|
||||
@ -132,26 +135,36 @@ public class UserController {
|
||||
.collect(Collectors.joining(" ")))
|
||||
.add("actions", (user) -> {
|
||||
|
||||
boolean isSuperAdmin = authentication.getAuthorities().stream()
|
||||
boolean isSuperAdmin = authentication != null && authentication.getAuthorities().stream()
|
||||
.anyMatch(a -> a.getAuthority().equals("ROLE_SUPERADMIN"));
|
||||
|
||||
if (!isSuperAdmin) {
|
||||
return "<div class=\"hstack gap-3 flex-wrap\">\n" +
|
||||
" <a href=\"javascript:void(0);\" data-id=\"" + user.getId()
|
||||
+ "\" class=\"link-success btn-edit-user fs-15\"><i class=\"ri-edit-2-line\"></i></a>\n"
|
||||
+
|
||||
" </div>";
|
||||
} else {
|
||||
// Admin editando otro admin o usuario normal: puede editarse y eliminarse
|
||||
return "<div class=\"hstack gap-3 flex-wrap\">\n" +
|
||||
" <a href=\"javascript:void(0);\" data-id=\"" + user.getId()
|
||||
+ "\" class=\"link-success btn-edit-user fs-15\"><i class=\"ri-edit-2-line\"></i></a>\n"
|
||||
+
|
||||
" <a href=\"javascript:void(0);\" data-id=\"" + user.getId()
|
||||
+ "\" class=\"link-danger btn-delete-user fs-15\"><i class=\"user-delete ri-delete-bin-line\"></i></a>\n"
|
||||
+
|
||||
" </div>";
|
||||
boolean isSelf = authentication != null
|
||||
&& authentication.getName() != null
|
||||
&& authentication.getName().equalsIgnoreCase(user.getUserName());
|
||||
|
||||
boolean targetIsSuperAdmin = user.getRoles().stream()
|
||||
.anyMatch(r -> "SUPERADMIN".equalsIgnoreCase(r.getName()));
|
||||
|
||||
StringBuilder actions = new StringBuilder();
|
||||
actions.append("<div class=\"hstack gap-3 flex-wrap\">");
|
||||
actions.append("<a href=\"javascript:void(0);\" data-id=\"")
|
||||
.append(user.getId())
|
||||
.append("\" class=\"link-success btn-edit-user fs-15\"><i class=\"ri-edit-2-line\"></i></a>");
|
||||
|
||||
if (!isSelf && (isSuperAdmin || !targetIsSuperAdmin)) {
|
||||
actions.append("<a href=\"javascript:void(0);\" data-username=\"")
|
||||
.append(user.getUserName())
|
||||
.append("\" class=\"link-info btn-impersonate-user fs-15\"><i class=\"ri-user-shared-line\"></i></a>");
|
||||
}
|
||||
|
||||
if (isSuperAdmin) {
|
||||
actions.append("<a href=\"javascript:void(0);\" data-id=\"")
|
||||
.append(user.getId())
|
||||
.append("\" class=\"link-danger btn-delete-user fs-15\"><i class=\"user-delete ri-delete-bin-line\"></i></a>");
|
||||
}
|
||||
|
||||
actions.append("</div>");
|
||||
return actions.toString();
|
||||
})
|
||||
.where(base)
|
||||
// Filtros custom:
|
||||
|
||||
Reference in New Issue
Block a user