| .. | .. |
|---|
| 12 | 12 | import jakarta.enterprise.context.RequestScoped; |
|---|
| 13 | 13 | import jakarta.inject.Inject; |
|---|
| 14 | 14 | import jakarta.persistence.EntityManager; |
|---|
| 15 | | -import jakarta.persistence.PersistenceException; |
|---|
| 16 | 15 | import jakarta.persistence.TypedQuery; |
|---|
| 17 | 16 | import jakarta.servlet.http.HttpServletRequest; |
|---|
| 18 | 17 | import jakarta.ws.rs.Consumes; |
|---|
| .. | .. |
|---|
| 57 | 56 | * <p> |
|---|
| 58 | 57 | * Notes: |
|---|
| 59 | 58 | * - Uses {@link BasicSecurityContext} authorization via @Securable and @RolesAllowed. |
|---|
| 60 | | - * - Uses JPA {@link EntityManager} injected through @Context. |
|---|
| 59 | + * - Uses JPA {@link EntityManager} injected through dependency injection. |
|---|
| 61 | 60 | * - Mutating endpoints are wrapped in @EnsureTransaction to guarantee commit/rollback. |
|---|
| 62 | 61 | * - Passwords are stored as SHA-256 hashes (see {@link Utils#sha256(String)}). |
|---|
| 63 | 62 | * |
|---|
| .. | .. |
|---|
| 86 | 85 | @Inject private CacheTTL cache; |
|---|
| 87 | 86 | |
|---|
| 88 | 87 | /** JPA entity manager bound to the current request context. */ |
|---|
| 89 | | - @Context EntityManager em; |
|---|
| 88 | + @Inject EntityManager em; |
|---|
| 90 | 89 | |
|---|
| 91 | 90 | private static final Logger LOG = LogManager.getLogger(UserResource.class); |
|---|
| 92 | 91 | |
|---|
| .. | .. |
|---|
| 330 | 329 | // lastLogin can be set through API (rare), otherwise managed at login |
|---|
| 331 | 330 | currentUser.setLastLogin(user.getLastLogin()); |
|---|
| 332 | 331 | |
|---|
| 333 | | - em.persist(currentUser); |
|---|
| 332 | + em.merge(currentUser); |
|---|
| 334 | 333 | clearUserCache(currentUser.getUsername()); |
|---|
| 335 | 334 | |
|---|
| 336 | 335 | return Response.ok(currentUser).build(); |
|---|
| .. | .. |
|---|
| 402 | 401 | @POST |
|---|
| 403 | 402 | @Path("/login") |
|---|
| 404 | 403 | @Produces({ MediaType.APPLICATION_JSON }) |
|---|
| 404 | + @EnsureTransaction |
|---|
| 405 | 405 | public Response login(@FormParam("username") String username, @FormParam("password") String password, @Context HttpServletRequest request) throws SeCurisServiceException { |
|---|
| 406 | 406 | LOG.info("index session: " + request.getSession()); |
|---|
| 407 | + LOG.info("login() called. session={}", request.getSession(false)); |
|---|
| 408 | + LOG.info("login() username='{}'", username); |
|---|
| 407 | 409 | |
|---|
| 408 | | - User user = em.find(User.class, username); |
|---|
| 409 | | - if (user == null) { |
|---|
| 410 | | - LOG.error("Unknown username {} used in login service", username); |
|---|
| 410 | + if (username == null || username.trim().isEmpty()) { |
|---|
| 411 | + LOG.error("login() username is null or empty"); |
|---|
| 411 | 412 | throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials"); |
|---|
| 412 | 413 | } |
|---|
| 414 | + if (password == null || password.isEmpty()) { |
|---|
| 415 | + LOG.error("login() password is null or empty for user '{}'", username); |
|---|
| 416 | + throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials"); |
|---|
| 417 | + } |
|---|
| 418 | + |
|---|
| 419 | + User user = em.find(User.class, username); |
|---|
| 420 | + LOG.info("login() user found? {}", user != null); |
|---|
| 421 | + |
|---|
| 422 | + if (user == null) { |
|---|
| 423 | + LOG.error("Unknown username '{}' used in login service", username); |
|---|
| 424 | + throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials"); |
|---|
| 425 | + } |
|---|
| 426 | + |
|---|
| 413 | 427 | String securedPassword = Utils.sha256(password); |
|---|
| 428 | + LOG.info("login() hashed password generated? {}", securedPassword != null); |
|---|
| 414 | 429 | |
|---|
| 415 | 430 | if (securedPassword == null || !securedPassword.equals(user.getPassword())) { |
|---|
| 431 | + LOG.error("Wrong password for user '{}'", username); |
|---|
| 416 | 432 | throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Wrong credentials"); |
|---|
| 417 | 433 | } |
|---|
| 418 | 434 | |
|---|
| 419 | 435 | user.setLastLogin(new Date()); |
|---|
| 420 | | - em.getTransaction().begin(); |
|---|
| 421 | | - try { |
|---|
| 422 | | - em.persist(user); |
|---|
| 423 | | - em.getTransaction().commit(); |
|---|
| 424 | | - } catch (PersistenceException ex) { |
|---|
| 425 | | - LOG.error("Error updating last login date for user: {}", username); |
|---|
| 426 | | - LOG.error(ex); |
|---|
| 427 | | - em.getTransaction().rollback(); |
|---|
| 428 | | - } |
|---|
| 436 | + em.merge(user); |
|---|
| 429 | 437 | |
|---|
| 430 | 438 | clearUserCache(username); |
|---|
| 431 | | - String userFullName = String.format("%s %s", user.getFirstName(), user.getLastName() == null ? "" : user.getLastName()).trim(); |
|---|
| 439 | + |
|---|
| 440 | + String userFullName = String.format("%s %s", |
|---|
| 441 | + user.getFirstName(), |
|---|
| 442 | + user.getLastName() == null ? "" : user.getLastName()).trim(); |
|---|
| 443 | + |
|---|
| 432 | 444 | String tokenAuth = tokenHelper.generateToken(username); |
|---|
| 433 | | - return Response.ok(Utils.createMap("success", true, "token", tokenAuth, "username", username, "full_name", userFullName)).build(); |
|---|
| 445 | + LOG.info("login() success for user '{}'", username); |
|---|
| 446 | + |
|---|
| 447 | + return Response.ok(Utils.createMap( |
|---|
| 448 | + "success", true, |
|---|
| 449 | + "token", tokenAuth, |
|---|
| 450 | + "username", username, |
|---|
| 451 | + "full_name", userFullName)).build(); |
|---|
| 434 | 452 | } |
|---|
| 435 | 453 | |
|---|
| 436 | 454 | /** |
|---|