Joaquín Reñé
2025-05-27 89b1c533d1b48b8b339b9c74a59c2ce73e6431af
securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
....@@ -5,30 +5,25 @@
55 import java.util.List;
66 import java.util.Set;
77
8
-import javax.annotation.Priority;
9
-import javax.inject.Inject;
10
-import javax.persistence.EntityManager;
11
-import javax.servlet.http.HttpServletRequest;
12
-import javax.servlet.http.HttpServletResponse;
13
-import javax.ws.rs.Priorities;
14
-import javax.ws.rs.WebApplicationException;
15
-import javax.ws.rs.container.ContainerRequestContext;
16
-import javax.ws.rs.container.ContainerRequestFilter;
17
-import javax.ws.rs.core.Context;
18
-import javax.ws.rs.core.Response;
19
-import javax.ws.rs.core.Response.Status;
20
-import javax.ws.rs.ext.Provider;
21
-import javax.ws.rs.ext.WriterInterceptor;
22
-import javax.ws.rs.ext.WriterInterceptorContext;
8
+import jakarta.annotation.Priority;
9
+import jakarta.inject.Inject;
10
+import jakarta.persistence.EntityManager;
11
+import jakarta.servlet.http.HttpServletRequest;
12
+import jakarta.servlet.http.HttpServletResponse;
13
+import jakarta.ws.rs.Priorities;
14
+import jakarta.ws.rs.WebApplicationException;
15
+import jakarta.ws.rs.container.ContainerRequestContext;
16
+import jakarta.ws.rs.container.ContainerRequestFilter;
17
+import jakarta.ws.rs.container.ResourceInfo;
18
+import jakarta.ws.rs.core.Context;
19
+import jakarta.ws.rs.core.Response;
20
+import jakarta.ws.rs.core.Response.Status;
21
+import jakarta.ws.rs.ext.Provider;
22
+import jakarta.ws.rs.ext.WriterInterceptor;
23
+import jakarta.ws.rs.ext.WriterInterceptorContext;
2324
2425 import org.apache.logging.log4j.LogManager;
2526 import org.apache.logging.log4j.Logger;
26
-import org.jboss.resteasy.core.Dispatcher;
27
-import org.jboss.resteasy.core.ResourceMethodInvoker;
28
-import org.jboss.resteasy.core.ServerResponse;
29
-import org.jboss.resteasy.spi.Failure;
30
-import org.jboss.resteasy.spi.HttpRequest;
31
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
3227
3328 import net.curisit.securis.db.User;
3429 import net.curisit.securis.security.BasicSecurityContext;
....@@ -39,6 +34,7 @@
3934 @Provider
4035 @Priority(Priorities.AUTHENTICATION)
4136 public class RequestsInterceptor implements ContainerRequestFilter, WriterInterceptor {
37
+
4238 private static final Logger LOG = LogManager.getLogger(RequestsInterceptor.class);
4339
4440 @Context
....@@ -47,171 +43,131 @@
4743 @Context
4844 private HttpServletRequest servletRequest;
4945
46
+ @Context
47
+ private ResourceInfo resourceInfo;
48
+
5049 @Inject
5150 private CacheTTL cache;
5251
5352 @Inject
5453 private TokenHelper tokenHelper;
5554
56
- @Context
57
- private Dispatcher dispatcher;
58
-
5955 @Inject
6056 private EntityManagerProvider emProvider;
6157
58
+ private static final String EM_CONTEXT_PROPERTY = "curisit.entitymanager";
59
+
6260 @Override
63
- public void filter(ContainerRequestContext containerRequestContext) throws IOException {
61
+ public void filter(ContainerRequestContext requestContext) throws IOException {
6462 EntityManager em = emProvider.getEntityManager();
6563 LOG.debug("GETTING EM: {}", em);
6664
67
- ResteasyProviderFactory.pushContext(EntityManager.class, em);
65
+ // Guardamos el EntityManager en el contexto para recuperación posterior
66
+ requestContext.setProperty(EM_CONTEXT_PROPERTY, em);
6867
69
- ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) containerRequestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
70
- Method method = methodInvoker.getMethod();
68
+ Method method = resourceInfo.getResourceMethod();
7169
72
- LOG.debug("Stored in context, em: {}, {}", em, method.toGenericString());
73
-
74
- boolean next = checkSecurableMethods(containerRequestContext, method);
75
- if (next) {
76
- prepareTransaction(containerRequestContext, method, em);
70
+ if (checkSecurableMethods(requestContext, method)) {
71
+ if (method.isAnnotationPresent(EnsureTransaction.class)) {
72
+ LOG.debug("Beginning transaction");
73
+ em.getTransaction().begin();
74
+ }
7775 }
7876 }
7977
80
- private void prepareTransaction(ContainerRequestContext containerRequestContext, Method method, EntityManager em) {
78
+ private boolean checkSecurableMethods(ContainerRequestContext ctx, Method method) {
79
+ if (!method.isAnnotationPresent(Securable.class)) return true;
8180
82
- if (method.isAnnotationPresent(EnsureTransaction.class)) {
83
- LOG.debug("Beginning a new transaction");
84
- em.getTransaction().begin();
85
- }
86
- }
87
-
88
- private boolean checkSecurableMethods(ContainerRequestContext containerRequestContext, Method method) {
89
- if (!method.isAnnotationPresent(Securable.class)) {
90
- return true;
91
- }
9281 String token = servletRequest.getHeader(TokenHelper.TOKEN_HEADER_PÀRAM);
9382 if (token == null || !tokenHelper.isTokenValid(token)) {
94
- LOG.warn("Access denied, Token not valid: {} for method: {}::{}", token, method.getDeclaringClass(), method.getName());
95
- containerRequestContext.abortWith(Response.status(Status.UNAUTHORIZED).build());
83
+ LOG.warn("Access denied, invalid token");
84
+ ctx.abortWith(Response.status(Status.UNAUTHORIZED).build());
9685 return false;
97
- } else {
98
- Securable securable = method.getAnnotation(Securable.class);
99
- // If roles == 0 we only need to validate the token
100
- String username = tokenHelper.extractUserFromToken(token);
101
- int userRoles = getUserRoles(username);
102
- if (securable.roles() != 0 && (securable.roles() & userRoles) == 0) {
103
- LOG.warn("Method {} requires roles: {}, but user {} hasn't got them", method.getName(), securable.roles(), username);
104
- containerRequestContext.abortWith(Response.status(Status.UNAUTHORIZED).build());
105
- return false;
106
- }
107
- Set<Integer> orgs = getUserOrganizations(username);
108
- Set<Integer> apps = getUserApplications(username);
109
-
110
- BasicSecurityContext scw = new BasicSecurityContext(username, userRoles, servletRequest.isSecure());
111
- scw.setOrganizationsIds(orgs);
112
- scw.setApplicationsIds(apps);
113
- containerRequestContext.setSecurityContext(scw);
114
- // Next line provide injection in resource methods
115
- ResteasyProviderFactory.pushContext(BasicSecurityContext.class, scw);
116
- LOG.debug("Added custom SecurityContext for user {}, orgs: {}", username, orgs);
11786 }
87
+
88
+ String username = tokenHelper.extractUserFromToken(token);
89
+ int roles = getUserRoles(username);
90
+ Securable securable = method.getAnnotation(Securable.class);
91
+
92
+ if (securable.roles() != 0 && (securable.roles() & roles) == 0) {
93
+ LOG.warn("User {} lacks required roles for method {}", username, method.getName());
94
+ ctx.abortWith(Response.status(Status.UNAUTHORIZED).build());
95
+ return false;
96
+ }
97
+
98
+ BasicSecurityContext sc = new BasicSecurityContext(username, roles, servletRequest.isSecure());
99
+ sc.setOrganizationsIds(getUserOrganizations(username));
100
+ sc.setApplicationsIds(getUserApplications(username));
101
+ ctx.setSecurityContext(sc);
118102 return true;
119
-
120
- }
121
-
122
- private Set<Integer> getUserOrganizations(String username) {
123
- @SuppressWarnings("unchecked")
124
- Set<Integer> userOrgs = cache.get("orgs_" + username, Set.class);
125
- if (userOrgs == null) {
126
- EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
127
-
128
- // Theorically this shouldn't be never null, but just in case...
129
- User user = em.find(User.class, username);
130
- if (user != null) {
131
- userOrgs = user.getAllOrgsIds();
132
- // We store user orgs in cache only for one hour
133
- cache.set("orgs_" + username, userOrgs, 3600);
134
- }
135
- }
136
-
137
- return userOrgs;
138
- }
139
-
140
- private Set<Integer> getUserApplications(String username) {
141
- @SuppressWarnings("unchecked")
142
- Set<Integer> userApps = cache.get("apps_" + username, Set.class);
143
- if (userApps == null) {
144
- EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
145
-
146
- // Theorically this shouldn't be never null, but just in case...
147
- User user = em.find(User.class, username);
148
- if (user != null) {
149
- userApps = user.getAllAppsIds();
150
- // We store user orgs in cache only for one hour
151
- cache.set("apps_" + username, userApps, 3600);
152
- }
153
- }
154
-
155
- return userApps;
156103 }
157104
158105 private int getUserRoles(String username) {
159
- if (username == null) {
160
- return 0;
161
- }
162
- Integer userRoles = cache.get("roles_" + username, Integer.class);
163
- if (userRoles == null) {
164
- EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
106
+ if (username == null) return 0;
107
+ Integer cached = cache.get("roles_" + username, Integer.class);
108
+ if (cached != null) return cached;
165109
166
- User user = em.find(User.class, username);
167
- if (user != null) {
168
- userRoles = 0;
169
- List<Integer> roles = user.getRoles();
170
- if (roles != null) {
171
- for (Integer rol : roles) {
172
- userRoles += rol;
173
- }
174
- }
175
- // We store user roles in cache only for one hour
176
- cache.set("roles_" + username, userRoles, 3600);
177
- cache.set("orgs_" + username, user.getOrgsIds(), 3600);
178
- }
110
+ EntityManager em = emProvider.getEntityManager();
111
+ User user = em.find(User.class, username);
112
+ int roles = 0;
113
+ if (user != null) {
114
+ List<Integer> r = user.getRoles();
115
+ if (r != null) for (Integer role : r) roles += role;
116
+ cache.set("roles_" + username, roles, 3600);
117
+ cache.set("orgs_" + username, user.getOrgsIds(), 3600);
179118 }
180
- return userRoles == null ? 0 : userRoles.intValue();
119
+ return roles;
181120 }
182121
183
- // @Override
184
- public ServerResponse preProcess(HttpRequest request, ResourceMethodInvoker method) throws Failure, WebApplicationException {
185
- return null;
122
+ private Set<Integer> getUserOrganizations(String username) {
123
+ Set<Integer> cached = cache.get("orgs_" + username, Set.class);
124
+ if (cached != null) return cached;
125
+ User user = emProvider.getEntityManager().find(User.class, username);
126
+ if (user != null) {
127
+ Set<Integer> result = user.getAllOrgsIds();
128
+ cache.set("orgs_" + username, result, 3600);
129
+ return result;
130
+ }
131
+ return Set.of();
132
+ }
133
+
134
+ private Set<Integer> getUserApplications(String username) {
135
+ Set<Integer> cached = cache.get("apps_" + username, Set.class);
136
+ if (cached != null) return cached;
137
+ User user = emProvider.getEntityManager().find(User.class, username);
138
+ if (user != null) {
139
+ Set<Integer> result = user.getAllAppsIds();
140
+ cache.set("apps_" + username, result, 3600);
141
+ return result;
142
+ }
143
+ return Set.of();
186144 }
187145
188146 @Override
189147 public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
190148 context.proceed();
191
- EntityManager em = ResteasyProviderFactory.getContextData(EntityManager.class);
149
+
150
+ EntityManager em = (EntityManager) context.getProperty(EM_CONTEXT_PROPERTY);
151
+ if (em == null) return;
152
+
192153 try {
193
- if (em != null && em.getTransaction().isActive()) {
154
+ if (em.getTransaction().isActive()) {
194155 if (servletResponse.getStatus() == Status.OK.getStatusCode()) {
195156 em.getTransaction().commit();
196
- LOG.debug("COMMIT");
157
+ LOG.debug("Transaction committed");
197158 } else {
198
- // This code is never executed if there is an error the
199
- // filter chain is broken
200159 em.getTransaction().rollback();
201
- LOG.debug("ROLLBACK");
160
+ LOG.debug("Transaction rolled back");
202161 }
203162 }
204163 } finally {
205164 if (em.isOpen()) {
206
- LOG.debug("CLOSING EM: {}, trans: {}", em, em.isJoinedToTransaction());
207165 try {
208166 em.close();
209
- } catch (Exception ex) {
210
- ex.printStackTrace();
211
- LOG.error("Error closing EM: {}, {}", em, ex);
167
+ } catch (Exception e) {
168
+ LOG.error("Error closing EntityManager", e);
212169 }
213170 }
214171 }
215172 }
216
-
217173 }