package net.curisit.securis.ioc; import java.io.IOException; import java.lang.reflect.Method; import java.util.List; import java.util.Set; import jakarta.annotation.Priority; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.ws.rs.Priorities; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.ResourceInfo; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.ext.Provider; import jakarta.ws.rs.ext.WriterInterceptor; import jakarta.ws.rs.ext.WriterInterceptorContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import net.curisit.securis.db.User; import net.curisit.securis.security.BasicSecurityContext; import net.curisit.securis.security.Securable; import net.curisit.securis.utils.CacheTTL; import net.curisit.securis.utils.TokenHelper; @Provider @Priority(Priorities.AUTHENTICATION) public class RequestsInterceptor implements ContainerRequestFilter, WriterInterceptor { private static final Logger LOG = LogManager.getLogger(RequestsInterceptor.class); @Context private HttpServletResponse servletResponse; @Context private HttpServletRequest servletRequest; @Context private ResourceInfo resourceInfo; @Inject private CacheTTL cache; @Inject private TokenHelper tokenHelper; @Inject private EntityManagerProvider emProvider; private static final String EM_CONTEXT_PROPERTY = "curisit.entitymanager"; @Override public void filter(ContainerRequestContext requestContext) throws IOException { EntityManager em = emProvider.getEntityManager(); LOG.debug("GETTING EM: {}", em); // Guardamos el EntityManager en el contexto para recuperación posterior requestContext.setProperty(EM_CONTEXT_PROPERTY, em); Method method = resourceInfo.getResourceMethod(); if (checkSecurableMethods(requestContext, method)) { if (method.isAnnotationPresent(EnsureTransaction.class)) { LOG.debug("Beginning transaction"); em.getTransaction().begin(); } } } private boolean checkSecurableMethods(ContainerRequestContext ctx, Method method) { if (!method.isAnnotationPresent(Securable.class)) return true; String token = servletRequest.getHeader(TokenHelper.TOKEN_HEADER_PÀRAM); if (token == null || !tokenHelper.isTokenValid(token)) { LOG.warn("Access denied, invalid token"); ctx.abortWith(Response.status(Status.UNAUTHORIZED).build()); return false; } String username = tokenHelper.extractUserFromToken(token); int roles = getUserRoles(username); Securable securable = method.getAnnotation(Securable.class); if (securable.roles() != 0 && (securable.roles() & roles) == 0) { LOG.warn("User {} lacks required roles for method {}", username, method.getName()); ctx.abortWith(Response.status(Status.UNAUTHORIZED).build()); return false; } BasicSecurityContext sc = new BasicSecurityContext(username, roles, servletRequest.isSecure()); sc.setOrganizationsIds(getUserOrganizations(username)); sc.setApplicationsIds(getUserApplications(username)); ctx.setSecurityContext(sc); return true; } private int getUserRoles(String username) { if (username == null) return 0; Integer cached = cache.get("roles_" + username, Integer.class); if (cached != null) return cached; EntityManager em = emProvider.getEntityManager(); User user = em.find(User.class, username); int roles = 0; if (user != null) { List r = user.getRoles(); if (r != null) for (Integer role : r) roles += role; cache.set("roles_" + username, roles, 3600); cache.set("orgs_" + username, user.getOrgsIds(), 3600); } return roles; } private Set getUserOrganizations(String username) { Set cached = cache.get("orgs_" + username, Set.class); if (cached != null) return cached; User user = emProvider.getEntityManager().find(User.class, username); if (user != null) { Set result = user.getAllOrgsIds(); cache.set("orgs_" + username, result, 3600); return result; } return Set.of(); } private Set getUserApplications(String username) { Set cached = cache.get("apps_" + username, Set.class); if (cached != null) return cached; User user = emProvider.getEntityManager().find(User.class, username); if (user != null) { Set result = user.getAllAppsIds(); cache.set("apps_" + username, result, 3600); return result; } return Set.of(); } @Override public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { context.proceed(); EntityManager em = (EntityManager) context.getProperty(EM_CONTEXT_PROPERTY); if (em == null) return; try { if (em.getTransaction().isActive()) { if (servletResponse.getStatus() == Status.OK.getStatusCode()) { em.getTransaction().commit(); LOG.debug("Transaction committed"); } else { em.getTransaction().rollback(); LOG.debug("Transaction rolled back"); } } } finally { if (em.isOpen()) { try { em.close(); } catch (Exception e) { LOG.error("Error closing EntityManager", e); } } } } }