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/services/UserResource.java | 643 ++++++++++++++++++++++++++++++++++++----------------------
1 files changed, 396 insertions(+), 247 deletions(-)
diff --git a/securis/src/main/java/net/curisit/securis/services/UserResource.java b/securis/src/main/java/net/curisit/securis/services/UserResource.java
index d5a9690..7c4681a 100644
--- a/securis/src/main/java/net/curisit/securis/services/UserResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/UserResource.java
@@ -1,3 +1,6 @@
+/*
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+ */
package net.curisit.securis.services;
import java.util.Date;
@@ -47,291 +50,437 @@
import net.curisit.securis.utils.TokenHelper;
/**
- * User resource
- *
+ * UserResource
+ * <p>
+ * REST resource that manages users (CRUD + authentication helpers).
+ * All endpoints are guarded and ADMIN-only unless otherwise stated.
+ * <p>
+ * Notes:
+ * - Uses {@link BasicSecurityContext} authorization via @Securable and @RolesAllowed.
+ * - Uses JPA {@link EntityManager} injected through @Context.
+ * - Mutating endpoints are wrapped in @EnsureTransaction to guarantee commit/rollback.
+ * - Passwords are stored as SHA-256 hashes (see {@link Utils#sha256(String)}).
+ *
+ * Endpoints:
+ * GET /user/ -> list users
+ * GET /user/{uid} -> get user by username
+ * POST /user/ -> create user (idempotent: upsert semantics)
+ * PUT /user/{uid} -> update user (creates if not exists)
+ * POST /user/login -> password authentication; returns token and basic identity
+ * POST /user/check -> validates a token and returns token metadata
+ * GET /user/logout -> invalidates HTTP session (non-token based)
+ *
+ * Thread-safety: RequestScoped. No shared mutable state.
+ *
* @author roberto <roberto.sanchez@curisit.net>
+ * Last reviewed by JRA on Oct 5, 2025.
*/
@Path("/user")
@RequestScoped
public class UserResource {
- @Inject
- TokenHelper tokenHelper;
+ /** Token encoder/decoder & validator. */
+ @Inject TokenHelper tokenHelper;
- @Inject
- private CacheTTL cache;
+ /** Small cache to invalidate role/org derived data after user mutations. */
+ @Inject private CacheTTL cache;
- @Context
- EntityManager em;
+ /** JPA entity manager bound to the current request context. */
+ @Context EntityManager em;
- private static final Logger LOG = LogManager.getLogger(UserResource.class);
+ private static final Logger LOG = LogManager.getLogger(UserResource.class);
- public UserResource() {
- }
+ /**
+ * UserResource
+ * Default constructor for CDI.
+ */
+ public UserResource() {
+ }
- /**
- *
- * @return the server version in format majorVersion.minorVersion
- */
- @GET
- @Path("/")
- @Produces({ MediaType.APPLICATION_JSON })
- @Securable(roles = Rol.ADMIN)
- @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
- public Response index() {
- LOG.info("Getting users list ");
+ // ---------------------------------------------------------------------
+ // Read operations
+ // ---------------------------------------------------------------------
- // EntityManager em = emProvider.get();
- em.clear();
- TypedQuery<User> q = em.createNamedQuery("list-users", User.class);
+ /**
+ * index
+ * <p>
+ * List all users.
+ *
+ * Security: ADMIN only.
+ *
+ * @return 200 OK with JSON array of {@link User}, or 200 OK with empty list.
+ */
+ @GET
+ @Path("/")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Securable(roles = Rol.ADMIN)
+ @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+ public Response index() {
+ LOG.info("Getting users list ");
- List<User> list = q.getResultList();
+ em.clear();
+ TypedQuery<User> q = em.createNamedQuery("list-users", User.class);
+ List<User> list = q.getResultList();
- return Response.ok(list).build();
- }
+ return Response.ok(list).build();
+ }
- /**
- *
- * @return The user
- */
- @GET
- @Path("/{uid}")
- @Produces({ MediaType.APPLICATION_JSON })
- @Securable(roles = Rol.ADMIN)
- @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
- public Response get(@PathParam("uid") String uid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
- LOG.info("Getting user data for id: {}: ", uid);
- if (uid == null || "".equals(uid)) {
- LOG.error("User ID is mandatory");
- return Response.status(Status.NOT_FOUND).build();
- }
+ /**
+ * get
+ * <p>
+ * Retrieve a single user by username.
+ *
+ * Security: ADMIN only.
+ *
+ * @param uid Username (primary key).
+ * @param token Optional token header (unused here, enforced by filters).
+ * @return 200 OK with user payload or 404 if not found/invalid uid.
+ */
+ @GET
+ @Path("/{uid}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Securable(roles = Rol.ADMIN)
+ @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+ public Response get(@PathParam("uid") String uid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
+ LOG.info("Getting user data for id: {}: ", uid);
+ if (uid == null || "".equals(uid)) {
+ LOG.error("User ID is mandatory");
+ return Response.status(Status.NOT_FOUND).build();
+ }
- // EntityManager em = emProvider.get();
- em.clear();
- User lt = em.find(User.class, uid);
- if (lt == null) {
- LOG.error("User with id {} not found in DB", uid);
- return Response.status(Status.NOT_FOUND).build();
- }
- return Response.ok(lt).build();
- }
+ em.clear();
+ User lt = em.find(User.class, uid);
+ if (lt == null) {
+ LOG.error("User with id {} not found in DB", uid);
+ return Response.status(Status.NOT_FOUND).build();
+ }
+ return Response.ok(lt).build();
+ }
- @POST
- @Path("/")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces({ MediaType.APPLICATION_JSON })
- @EnsureTransaction
- @Securable(roles = Rol.ADMIN)
- @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
- public Response create(User user, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
- LOG.info("Creating new user");
- // EntityManager em = emProvider.get();
- User currentUser = em.find(User.class, user.getUsername());
- if (currentUser != null) {
- LOG.info("User with id {} was found in DB, we'll try to modify it", user.getUsername());
- return modify(user, user.getUsername(), token);
- }
+ // ---------------------------------------------------------------------
+ // Create / Update / Delete
+ // ---------------------------------------------------------------------
- try {
- this.setUserOrgs(user, user.getOrgsIds(), em);
- } catch (SeCurisException e) {
- return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
- }
- try {
- this.setUserApps(user, user.getAppsIds(), em);
- } catch (SeCurisException e) {
- return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
- }
- if (user.getPassword() != null && !"".equals(user.getPassword())) {
- user.setPassword(Utils.sha256(user.getPassword()));
- } else {
- return Response.status(DefaultExceptionHandler.DEFAULT_APP_ERROR_STATUS_CODE).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "User password is mandatory")
- .build();
- }
- user.setModificationTimestamp(new Date());
- user.setLastLogin(null);
- user.setCreationTimestamp(new Date());
- em.persist(user);
+ /**
+ * create
+ * <p>
+ * Create a new user. If the username already exists, delegates to {@link #modify(User, String, String)}
+ * to behave like an upsert.
+ *
+ * Security: ADMIN only.
+ * Transaction: yes (via @EnsureTransaction).
+ *
+ * @param user Incoming user payload. Password must be non-empty (plain text).
+ * Password is SHA-256 hashed before persist.
+ * @param token Security token header (unused here; enforced by filters).
+ * @return 200 OK with created/updated user; 4xx on validation errors.
+ */
+ @POST
+ @Path("/")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces({ MediaType.APPLICATION_JSON })
+ @EnsureTransaction
+ @Securable(roles = Rol.ADMIN)
+ @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+ public Response create(User user, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
+ LOG.info("Creating new user");
- return Response.ok(user).build();
- }
+ User currentUser = em.find(User.class, user.getUsername());
+ if (currentUser != null) {
+ LOG.info("User with id {} was found in DB, we'll try to modify it", user.getUsername());
+ return modify(user, user.getUsername(), token);
+ }
- private void setUserOrgs(User user, Set<Integer> orgsIds, EntityManager em) throws SeCurisException {
- Set<Organization> orgs = null;
- if (orgsIds != null && !orgsIds.isEmpty()) {
- orgs = new HashSet<>();
- for (Integer orgId : orgsIds) {
- Organization o = em.find(Organization.class, orgId);
- if (o == null) {
- LOG.error("User organization with id {} not found in DB", orgId);
- throw new SeCurisException("User's organization not found with ID: " + orgId);
- }
- orgs.add(o);
- }
- }
+ try {
+ this.setUserOrgs(user, user.getOrgsIds(), em);
+ } catch (SeCurisException e) {
+ return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
+ }
+ try {
+ this.setUserApps(user, user.getAppsIds(), em);
+ } catch (SeCurisException e) {
+ return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
+ }
- user.setOrganizations(orgs);
+ // Password must be provided on create
+ if (user.getPassword() != null && !"".equals(user.getPassword())) {
+ user.setPassword(Utils.sha256(user.getPassword()));
+ } else {
+ return Response.status(DefaultExceptionHandler.DEFAULT_APP_ERROR_STATUS_CODE)
+ .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "User password is mandatory")
+ .build();
+ }
- }
+ user.setModificationTimestamp(new Date());
+ user.setLastLogin(null);
+ user.setCreationTimestamp(new Date());
+ em.persist(user);
- private void setUserApps(User user, Set<Integer> appsIds, EntityManager em) throws SeCurisException {
- Set<Application> apps = null;
- if (appsIds != null && !appsIds.isEmpty()) {
- apps = new HashSet<>();
- for (Integer appId : appsIds) {
- Application o = em.find(Application.class, appId);
- if (o == null) {
- LOG.error("User application with id {} not found in DB", appId);
- throw new SeCurisException("User's application not found with ID: " + appId);
- }
- apps.add(o);
- }
- }
+ return Response.ok(user).build();
+ }
- user.setApplications(apps);
- }
+ /**
+ * setUserOrgs
+ * <p>
+ * Resolve and set the organizations for a user from a set of IDs.
+ * Validates each id exists in DB.
+ *
+ * @param user Target user entity.
+ * @param orgsIds Organization ids to assign (nullable/empty allowed).
+ * @param em EntityManager.
+ * @throws SeCurisException if any of the referenced organizations does not exist.
+ */
+ private void setUserOrgs(User user, Set<Integer> orgsIds, EntityManager em) throws SeCurisException {
+ Set<Organization> orgs = null;
+ if (orgsIds != null && !orgsIds.isEmpty()) {
+ orgs = new HashSet<>();
+ for (Integer orgId : orgsIds) {
+ Organization o = em.find(Organization.class, orgId);
+ if (o == null) {
+ LOG.error("User organization with id {} not found in DB", orgId);
+ throw new SeCurisException("User's organization not found with ID: " + orgId);
+ }
+ orgs.add(o);
+ }
+ }
+ user.setOrganizations(orgs);
+ }
- @PUT
- @POST
- @Path("/{uid}")
- @EnsureTransaction
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces({ MediaType.APPLICATION_JSON })
- @Securable(roles = Rol.ADMIN)
- @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
- public Response modify(User user, @PathParam("uid") String uid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
- LOG.info("Modifying user with id: {}", uid);
- // EntityManager em = emProvider.get();
- User currentUser = em.find(User.class, uid);
- if (currentUser == null) {
- LOG.info("User with id {} not found in DB, we'll try to create it", uid);
- return create(user, token);
- }
+ /**
+ * setUserApps
+ * <p>
+ * Resolve and set the applications for a user from a set of IDs.
+ * Validates each id exists in DB.
+ *
+ * @param user Target user entity.
+ * @param appsIds Application ids to assign (nullable/empty allowed).
+ * @param em EntityManager.
+ * @throws SeCurisException if any of the referenced applications does not exist.
+ */
+ private void setUserApps(User user, Set<Integer> appsIds, EntityManager em) throws SeCurisException {
+ Set<Application> apps = null;
+ if (appsIds != null && !appsIds.isEmpty()) {
+ apps = new HashSet<>();
+ for (Integer appId : appsIds) {
+ Application o = em.find(Application.class, appId);
+ if (o == null) {
+ LOG.error("User application with id {} not found in DB", appId);
+ throw new SeCurisException("User's application not found with ID: " + appId);
+ }
+ apps.add(o);
+ }
+ }
+ user.setApplications(apps);
+ }
- try {
- this.setUserOrgs(currentUser, user.getOrgsIds(), em);
- } catch (SeCurisException e) {
- return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
- }
- try {
- this.setUserApps(currentUser, user.getAppsIds(), em);
- } catch (SeCurisException e) {
- return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
- }
- currentUser.setFirstName(user.getFirstName());
- currentUser.setLastName(user.getLastName());
- currentUser.setRoles(user.getRoles());
- currentUser.setLang(user.getLang());
- currentUser.setModificationTimestamp(new Date());
- if (user.getPassword() != null && !"".equals(user.getPassword())) {
- currentUser.setPassword(Utils.sha256(user.getPassword()));
- } else {
- // Password has not been modified
- // return
- }
+ /**
+ * modify
+ * <p>
+ * Update an existing user. If the user does not exist, delegates to {@link #create(User, String)}.
+ * Password is updated only if a non-empty password is provided.
+ * Organizations & applications are fully replaced with the given ids.
+ *
+ * Security: ADMIN only.
+ * Transaction: yes (via @EnsureTransaction).
+ *
+ * @param user Incoming user payload.
+ * @param uid Username (path param) to update.
+ * @param token Security token header (unused here).
+ * @return 200 OK with updated user; 404 if reference entities are missing.
+ */
+ @PUT
+ @POST
+ @Path("/{uid}")
+ @EnsureTransaction
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Securable(roles = Rol.ADMIN)
+ @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+ public Response modify(User user, @PathParam("uid") String uid, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
+ LOG.info("Modifying user with id: {}", uid);
- currentUser.setLastLogin(user.getLastLogin());
+ User currentUser = em.find(User.class, uid);
+ if (currentUser == null) {
+ LOG.info("User with id {} not found in DB, we'll try to create it", uid);
+ return create(user, token);
+ }
- em.persist(currentUser);
- clearUserCache(currentUser.getUsername());
+ try {
+ this.setUserOrgs(currentUser, user.getOrgsIds(), em);
+ } catch (SeCurisException e) {
+ return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
+ }
+ try {
+ this.setUserApps(currentUser, user.getAppsIds(), em);
+ } catch (SeCurisException e) {
+ return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build();
+ }
- return Response.ok(currentUser).build();
- }
+ currentUser.setFirstName(user.getFirstName());
+ currentUser.setLastName(user.getLastName());
+ currentUser.setRoles(user.getRoles());
+ currentUser.setLang(user.getLang());
+ currentUser.setModificationTimestamp(new Date());
- @DELETE
- @Path("/{uid}")
- @EnsureTransaction
- @Produces({ MediaType.APPLICATION_JSON })
- @Securable(roles = Rol.ADMIN)
- @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
- public Response delete(@PathParam("uid") String uid, @Context HttpServletRequest request) {
- LOG.info("Deleting app with id: {}", uid);
- // EntityManager em = emProvider.get();
- User user = em.find(User.class, uid);
- if (user == null) {
- LOG.error("User with id {} can not be deleted, It was not found in DB", uid);
- return Response.status(Status.NOT_FOUND).build();
- }
+ // Optional password update
+ if (user.getPassword() != null && !"".equals(user.getPassword())) {
+ currentUser.setPassword(Utils.sha256(user.getPassword()));
+ }
- em.remove(user);
- clearUserCache(user.getUsername());
- return Response.ok(Utils.createMap("success", true, "id", uid)).build();
- }
+ // lastLogin can be set through API (rare), otherwise managed at login
+ currentUser.setLastLogin(user.getLastLogin());
- private void clearUserCache(String username) {
- cache.remove("roles_" + username);
- cache.remove("orgs_" + username);
- }
+ em.persist(currentUser);
+ clearUserCache(currentUser.getUsername());
- @POST
- @Path("/login")
- @Produces({ MediaType.APPLICATION_JSON })
- public Response login(@FormParam("username") String username, @FormParam("password") String password, @Context HttpServletRequest request) throws SeCurisServiceException {
- LOG.info("index session: " + request.getSession());
+ return Response.ok(currentUser).build();
+ }
- // EntityManager em = emProvider.get();
- User user = em.find(User.class, username);
- if (user == null) {
- LOG.error("Unknown username {} used in login service", username);
- throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials");
- }
- String securedPassword = Utils.sha256(password);
+ /**
+ * delete
+ * <p>
+ * Delete a user by username.
+ *
+ * Security: ADMIN only.
+ * Transaction: yes (via @EnsureTransaction).
+ *
+ * @param uid Username to delete.
+ * @param request Http servlet request (unused).
+ * @return 200 OK on success; 404 if user does not exist.
+ */
+ @DELETE
+ @Path("/{uid}")
+ @EnsureTransaction
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Securable(roles = Rol.ADMIN)
+ @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+ public Response delete(@PathParam("uid") String uid, @Context HttpServletRequest request) {
+ LOG.info("Deleting app with id: {}", uid);
- if (securedPassword == null || !securedPassword.equals(user.getPassword())) {
- throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials");
- }
- user.setLastLogin(new Date());
- em.getTransaction().begin();
- try {
- em.persist(user);
- em.getTransaction().commit();
- } catch (PersistenceException ex) {
- LOG.error("Error updating last login date for user: {}", username);
- LOG.error(ex);
- em.getTransaction().rollback();
- }
- clearUserCache(username);
- String userFullName = String.format("%s %s", user.getFirstName(), user.getLastName() == null ? "" : user.getLastName()).trim();
- String tokenAuth = tokenHelper.generateToken(username);
- return Response.ok(Utils.createMap("success", true, "token", tokenAuth, "username", username, "full_name", userFullName)).build();
- }
+ User user = em.find(User.class, uid);
+ if (user == null) {
+ LOG.error("User with id {} can not be deleted, It was not found in DB", uid);
+ return Response.status(Status.NOT_FOUND).build();
+ }
- /**
- * Check if current token is valid
- *
- * @param user
- * @param password
- * @param request
- * @return
- */
- @POST
- @Path("/check")
- @Produces({ MediaType.APPLICATION_JSON })
- public Response check(@HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token, @QueryParam("token") String token2) {
- if (token == null) {
- token = token2;
- }
- if (token == null) {
- return Response.status(Status.FORBIDDEN).build();
- }
+ em.remove(user);
+ clearUserCache(user.getUsername());
+ return Response.ok(Utils.createMap("success", true, "id", uid)).build();
+ }
- LOG.info("Token : " + token);
- String user = tokenHelper.extractUserFromToken(token);
- LOG.info("Token user: " + user);
- Date date = tokenHelper.extractDateCreationFromToken(token);
- LOG.info("Token date: " + date);
- boolean valid = tokenHelper.isTokenValid(token);
+ /**
+ * clearUserCache
+ * <p>
+ * Helper to invalidate cached role/org projections after changes.
+ *
+ * @param username The user whose cache entries must be cleared.
+ */
+ private void clearUserCache(String username) {
+ cache.remove("roles_" + username);
+ cache.remove("orgs_" + username);
+ }
- LOG.info("Is Token valid: " + valid);
+ // ---------------------------------------------------------------------
+ // Auth helpers
+ // ---------------------------------------------------------------------
- return Response.ok(Utils.createMap("valid", true, "user", user, "date", date, "token", token)).build();
- }
+ /**
+ * login
+ * <p>
+ * Validates username & password against stored SHA-256 hash. On success,
+ * updates lastLogin timestamp, clears cache and returns an auth token.
+ *
+ * Token format: Base64("<secret> <user> <ISO8601-date>")
+ * where secret = SHA-256(seed + user + date).
+ *
+ * @param username Plain username.
+ * @param password Plain password (SHA-256 will be computed server-side).
+ * @param request Http request, used to log underlying session (not required for token flow).
+ * @return 200 OK with {token, username, full_name}; 401 on invalid credentials.
+ * @throws SeCurisServiceException if user is missing or password mismatch.
+ */
+ @POST
+ @Path("/login")
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response login(@FormParam("username") String username, @FormParam("password") String password, @Context HttpServletRequest request) throws SeCurisServiceException {
+ LOG.info("index session: " + request.getSession());
- @GET
- @Path("/logout")
- @Produces({ MediaType.APPLICATION_JSON })
- public Response logout(@Context HttpServletRequest request) {
- request.getSession().invalidate();
- return Response.ok().build();
- }
+ User user = em.find(User.class, username);
+ if (user == null) {
+ LOG.error("Unknown username {} used in login service", username);
+ throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials");
+ }
+ String securedPassword = Utils.sha256(password);
+
+ if (securedPassword == null || !securedPassword.equals(user.getPassword())) {
+ throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials");
+ }
+
+ user.setLastLogin(new Date());
+ em.getTransaction().begin();
+ try {
+ em.persist(user);
+ em.getTransaction().commit();
+ } catch (PersistenceException ex) {
+ LOG.error("Error updating last login date for user: {}", username);
+ LOG.error(ex);
+ em.getTransaction().rollback();
+ }
+
+ clearUserCache(username);
+ String userFullName = String.format("%s %s", user.getFirstName(), user.getLastName() == null ? "" : user.getLastName()).trim();
+ String tokenAuth = tokenHelper.generateToken(username);
+ return Response.ok(Utils.createMap("success", true, "token", tokenAuth, "username", username, "full_name", userFullName)).build();
+ }
+
+ /**
+ * check
+ * <p>
+ * Validates a token and echoes token claims (user, creation date, token string).
+ * Accepts header or query param for convenience.
+ *
+ * @param token Token in header {@link TokenHelper#TOKEN_HEADER_PÀRAM}, may be null.
+ * @param token2 Token in query param 'token', used if header is null.
+ * @return 200 OK with {valid, user, date, token} or 403 if token missing.
+ */
+ @POST
+ @Path("/check")
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response check(@HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token, @QueryParam("token") String token2) {
+ if (token == null) {
+ token = token2;
+ }
+ if (token == null) {
+ return Response.status(Status.FORBIDDEN).build();
+ }
+
+ LOG.info("Token : " + token);
+ String user = tokenHelper.extractUserFromToken(token);
+ LOG.info("Token user: " + user);
+ Date date = tokenHelper.extractDateCreationFromToken(token);
+ LOG.info("Token date: " + date);
+ boolean valid = tokenHelper.isTokenValid(token);
+
+ LOG.info("Is Token valid: " + valid);
+
+ return Response.ok(Utils.createMap("valid", true, "user", user, "date", date, "token", token)).build();
+ }
+
+ /**
+ * logout
+ * <p>
+ * Invalidates the HTTP session (useful if the UI also tracks session).
+ * Note: token-based auth is stateless; tokens are not revoked here.
+ *
+ * @param request HttpServletRequest to invalidate session.
+ * @return 200 OK always.
+ */
+ @GET
+ @Path("/logout")
+ @Produces({ MediaType.APPLICATION_JSON })
+ public Response logout(@Context HttpServletRequest request) {
+ request.getSession().invalidate();
+ return Response.ok().build();
+ }
}
+
--
Gitblit v1.3.2