Joaquín Reñé
2025-10-07 146a0fb8b0e90f9196e569152f649baf60d6cc8f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
 */
package net.curisit.securis.services;
import java.net.URI;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.UriBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.curisit.integrity.AppVersion;
import net.curisit.integrity.commons.Utils;
import net.curisit.securis.ioc.EnsureTransaction;
import net.curisit.securis.security.Securable;
import net.curisit.securis.utils.TokenHelper;
/**
 * BasicServices
 * <p>
 * Minimal public endpoints for service liveness, version info and token checks.
 * Also provides entry routing to SPA (admin/login/licenses) via /index.jsp.
 *
 * Security:
 * <ul>
 *   <li>/check requires a valid bearer token (via {@link Securable}).</li>
 *   <li>/logout just logs intention; token invalidation is outside this class.</li>
 * </ul>
 *
 * Author: roberto &lt;roberto.sanchez@curisit.net&gt;
 * Last reviewed by JRA on Oct 5, 2025.
 */
@Path("/")
@ApplicationScoped
public class BasicServices {
    private static final Logger LOG = LogManager.getLogger(BasicServices.class);
    @Inject TokenHelper tokenHelper;
    @Context EntityManager em;
    @Inject  public BasicServices() {}
    /** 
     * info<p>
     * Simple liveness text endpoint. 
     * 
     * @param request
     * @return response
     */
    @GET
    @Path("/info")
    @Produces({ MediaType.TEXT_PLAIN })
    public Response info(@Context HttpServletRequest request) {
        return Response.ok().entity("License server running OK. Date: " + new Date()).build();
    }
    /** 
     * version<p>
     * Returns semantic app version as JSON. 
     * 
     * @param request
     * @return version
     */
    @GET
    @Path("/version")
    @Produces({ MediaType.APPLICATION_JSON })
    public Map<String, String> version(@Context HttpServletRequest request) {
        Map<String, String> resp = new HashMap<>();
        resp.put("version", AppVersion.getInstance().getCompleteVersion());
        return resp;
    }
    /**
     * init<p>
     * Redirects SPA modules to the main index page.
     * 
     * @param module
     * @param request
     * @return response
     */
    @GET
    @Path("/{module:(admin)|(login)|(licenses)}")
    @Produces({ MediaType.TEXT_HTML })
    public Response init(@PathParam("module") String module, @Context HttpServletRequest request) {
        LOG.info("App index main.html");
        URI uri = UriBuilder.fromUri("/index.jsp").build();
        return Response.seeOther(uri).build();
    }
    /**
     * check<p>
     * Validates a token (from header or query param).
     *
     * @param token X-Token header
     * @param token2 token query param fallback
     * @return 200 with user/date if valid, 401/403 otherwise
     */
    @GET
    @Securable()
    @Path("/check")
    @Produces({ MediaType.APPLICATION_JSON })
    @EnsureTransaction
    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();
        }
        boolean valid = tokenHelper.isTokenValid(token);
        if (!valid) {
            return Response.status(Status.UNAUTHORIZED).build();
        }
        String user = tokenHelper.extractUserFromToken(token);
        Date date = tokenHelper.extractDateCreationFromToken(token);
        return Response.ok(Utils.createMap("valid", true, "user", user, "date", date)).build();
    }
    /**
     * logout<p>
     * Logs logout event. (Token invalidation is handled elsewhere.)
     * 
     * @param token
     * @return response
     */
    @GET
    @POST
    @Path("/logout")
    @Produces({ MediaType.APPLICATION_JSON })
    public Response logout(@HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {
        if (token == null) {
            Response.status(Status.BAD_REQUEST).build();
        }
        String user = tokenHelper.extractUserFromToken(token);
        LOG.info("User {} has logged out", user);
        return Response.ok().build();
    }
}