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