From 146a0fb8b0e90f9196e569152f649baf60d6cc8f Mon Sep 17 00:00:00 2001
From: Joaquín Reñé <jrene@curisit.net>
Date: Tue, 07 Oct 2025 14:52:57 +0000
Subject: [PATCH] #4410 - Comments on classes

---
 securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java |  298 +++++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 186 insertions(+), 112 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java b/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
index fd61433..2417954 100644
--- a/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
+++ b/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
@@ -1,3 +1,6 @@
+/*
+* Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+*/
 package net.curisit.securis.ioc;
 
 import java.io.IOException;
@@ -31,143 +34,214 @@
 import net.curisit.securis.utils.CacheTTL;
 import net.curisit.securis.utils.TokenHelper;
 
+/**
+* RequestsInterceptor
+* <p>
+* Authentication/authorization interceptor that:
+* <ul>
+*   <li>Loads and stores the {@link EntityManager} in the request context.</li>
+*   <li>Validates tokens for methods annotated with {@link Securable}.</li>
+*   <li>Builds a {@link BasicSecurityContext} with roles and scoped organization/application IDs.</li>
+*   <li>Manages transactions when {@code @EnsureTransaction} is present.</li>
+* </ul>
+*
+* <p><b>Cache usage:</b> Uses {@link CacheTTL} to cache roles and scope sets.
+* The new {@link CacheTTL#getSet(String, Class)} helper removes unchecked
+* conversion warnings when retrieving {@code Set<Integer>} from the cache.
+* 
+* @author JRA
+* Last reviewed by JRA on Oct 5, 2025.
+*/
 @Provider
 @Priority(Priorities.AUTHENTICATION)
 public class RequestsInterceptor implements ContainerRequestFilter, WriterInterceptor {
 
-	private static final Logger LOG = LogManager.getLogger(RequestsInterceptor.class);
+    private static final Logger LOG = LogManager.getLogger(RequestsInterceptor.class);
 
-	@Context
-	private HttpServletResponse servletResponse;
+    @Inject private CacheTTL cache;
+    @Inject private TokenHelper tokenHelper;
+    @Inject private EntityManagerProvider emProvider;
 
-	@Context
-	private HttpServletRequest servletRequest;
+    @Context private HttpServletResponse servletResponse;
+    @Context private HttpServletRequest servletRequest;
+    @Context private ResourceInfo resourceInfo;
 
-	@Context
-	private ResourceInfo resourceInfo;
+    private static final String EM_CONTEXT_PROPERTY = "curisit.entitymanager";
 
-	@Inject
-	private CacheTTL cache;
+    // -------------------------------------------------------------
+    // Request filter (authN/authZ + EM handling)
+    // -------------------------------------------------------------
 
-	@Inject
-	private TokenHelper tokenHelper;
+    /** 
+     * filter<p>
+     * Entry point before resource method invocation. 
+     * 
+     * @param requestContext
+     * @throws IOException
+     */
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        EntityManager em = emProvider.getEntityManager();
+        LOG.debug("GETTING EM: {}", em);
 
-	@Inject
-	private EntityManagerProvider emProvider;
+        // Store EntityManager for later retrieval (writer interceptor)
+        requestContext.setProperty(EM_CONTEXT_PROPERTY, em);
 
-	private static final String EM_CONTEXT_PROPERTY = "curisit.entitymanager";
+        Method method = resourceInfo.getResourceMethod();
 
-	@Override
-	public void filter(ContainerRequestContext requestContext) throws IOException {
-		EntityManager em = emProvider.getEntityManager();
-		LOG.debug("GETTING EM: {}", em);
+        if (checkSecurableMethods(requestContext, method)) {
+            if (method.isAnnotationPresent(EnsureTransaction.class)) {
+                LOG.debug("Beginning transaction");
+                em.getTransaction().begin();
+            }
+        }
+    }
 
-		// Guardamos el EntityManager en el contexto para recuperación posterior
-		requestContext.setProperty(EM_CONTEXT_PROPERTY, em);
+    /**
+    * checkSecurableMethods<p>
+    * Enforce security checks for methods annotated with {@link Securable}.
+    * Builds {@link BasicSecurityContext} when authorized.
+    *
+    * @param ctx
+    * @param method
+    * @return true if request can proceed; false when aborted
+    */
+    private boolean checkSecurableMethods(ContainerRequestContext ctx, Method method) {
+        if (!method.isAnnotationPresent(Securable.class)) return true;
 
-		Method method = resourceInfo.getResourceMethod();
+        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;
+        }
 
-		if (checkSecurableMethods(requestContext, method)) {
-			if (method.isAnnotationPresent(EnsureTransaction.class)) {
-				LOG.debug("Beginning transaction");
-				em.getTransaction().begin();
-			}
-		}
-	}
+        String username = tokenHelper.extractUserFromToken(token);
+        int roles = getUserRoles(username);
+        Securable securable = method.getAnnotation(Securable.class);
 
-	private boolean checkSecurableMethods(ContainerRequestContext ctx, Method method) {
-		if (!method.isAnnotationPresent(Securable.class)) return true;
+        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;
+        }
 
-		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;
-		}
+        BasicSecurityContext sc = new BasicSecurityContext(username, roles, servletRequest.isSecure());
+        sc.setOrganizationsIds(getUserOrganizations(username));
+        sc.setApplicationsIds(getUserApplications(username));
+        ctx.setSecurityContext(sc);
+        return true;
+    }
 
-		String username = tokenHelper.extractUserFromToken(token);
-		int roles = getUserRoles(username);
-		Securable securable = method.getAnnotation(Securable.class);
+    // -------------------------------------------------------------
+    // Cached lookups (roles/orgs/apps)
+    // -------------------------------------------------------------
 
-		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;
-		}
+    /**
+    * getUserRoles<p>
+    * Retrieve roles bitmask for the given user (cached).
+    * 
+    * @param username
+    * @return userRoles
+    */
+    private int getUserRoles(String username) {
+        if (username == null) return 0;
+        Integer cached = cache.get("roles_" + username, Integer.class);
+        if (cached != null) return cached;
 
-		BasicSecurityContext sc = new BasicSecurityContext(username, roles, servletRequest.isSecure());
-		sc.setOrganizationsIds(getUserOrganizations(username));
-		sc.setApplicationsIds(getUserApplications(username));
-		ctx.setSecurityContext(sc);
-		return true;
-	}
+        EntityManager em = emProvider.getEntityManager();
+        User user = em.find(User.class, username);
+        int roles = 0;
+        if (user != null) {
+            List<Integer> r = user.getRoles();
+            if (r != null) for (Integer role : r) roles += role;
+            cache.set("roles_" + username, roles, 3600);
+            // also warm some caches
+            cache.set("orgs_" + username, user.getOrgsIds(), 3600);
+        }
+        return roles;
+    }
 
-	private int getUserRoles(String username) {
-		if (username == null) return 0;
-		Integer cached = cache.get("roles_" + username, Integer.class);
-		if (cached != null) return cached;
+    /**
+    * getUserOrganizations<p>
+    * Retrieve organization scope for the user as a typed {@code Set<Integer>}
+    * using the cache helper that validates element types.
+    * 
+    * @param username
+    * @return userOrganizations
+    */
+    private Set<Integer> getUserOrganizations(String username) {
+        Set<Integer> cached = cache.getSet("orgs_" + 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<Integer> 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;
-	}
+        User user = emProvider.getEntityManager().find(User.class, username);
+        if (user != null) {
+            Set<Integer> result = user.getAllOrgsIds();
+            cache.set("orgs_" + username, result, 3600);
+            return result;
+        }
+        return Set.of();
+    }
 
-	private Set<Integer> getUserOrganizations(String username) {
-		Set<Integer> cached = cache.get("orgs_" + username, Set.class);
-		if (cached != null) return cached;
-		User user = emProvider.getEntityManager().find(User.class, username);
-		if (user != null) {
-			Set<Integer> result = user.getAllOrgsIds();
-			cache.set("orgs_" + username, result, 3600);
-			return result;
-		}
-		return Set.of();
-	}
+    /**
+    * getUserApplications<p>
+    * Retrieve application scope for the user as a typed {@code Set<Integer>}
+    * using the cache helper that validates element types.
+    * 
+    * @param username
+    * @return userApplications
+    */
+    private Set<Integer> getUserApplications(String username) {
+        Set<Integer> cached = cache.getSet("apps_" + username, Integer.class);
+        if (cached != null) return cached;
 
-	private Set<Integer> getUserApplications(String username) {
-		Set<Integer> cached = cache.get("apps_" + username, Set.class);
-		if (cached != null) return cached;
-		User user = emProvider.getEntityManager().find(User.class, username);
-		if (user != null) {
-			Set<Integer> result = user.getAllAppsIds();
-			cache.set("apps_" + username, result, 3600);
-			return result;
-		}
-		return Set.of();
-	}
+        User user = emProvider.getEntityManager().find(User.class, username);
+        if (user != null) {
+            Set<Integer> 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();
+    // -------------------------------------------------------------
+    // Writer interceptor (transaction finalize)
+    // -------------------------------------------------------------
 
-		EntityManager em = (EntityManager) context.getProperty(EM_CONTEXT_PROPERTY);
-		if (em == null) return;
+    /** 
+     * aroundWriteTo<p>
+     * Commit/rollback and close EM after response writing. 
+     * 
+     * @param context
+     * @throws IOException
+     * @throws WebApplicationException
+     */
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
+        context.proceed();
 
-		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);
-				}
-			}
-		}
-	}
+        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);
+                }
+            }
+        }
+    }
 }
+

--
Gitblit v1.3.2