Joaquín Reñé
2025-10-07 146a0fb8b0e90f9196e569152f649baf60d6cc8f
securis/src/main/java/net/curisit/securis/services/helpers/LicenseHelper.java
....@@ -1,3 +1,6 @@
1
+/*
2
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
3
+ */
14 package net.curisit.securis.services.helpers;
25
36 import java.io.File;
....@@ -30,128 +33,192 @@
3033 import net.curisit.securis.services.exception.SeCurisServiceException;
3134 import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes;
3235
36
+/**
37
+ * LicenseHelper
38
+ * <p>
39
+ * Stateless utility component for license lifecycle operations and helpers:
40
+ * - cancelation with history auditing
41
+ * - license resolution and validity checks
42
+ * - license file generation in a temp directory
43
+ * - metadata extraction and expiration date computation from packs
44
+ * - sequential code suffix allocation per pack
45
+ *
46
+ * Thread-safety: ApplicationScoped, stateless.
47
+ *
48
+ * @author JRA
49
+ * Last reviewed by JRA on Oct 5, 2025.
50
+ */
3351 @ApplicationScoped
3452 public class LicenseHelper {
3553
36
- @SuppressWarnings("unused")
37
- private static final Logger LOG = LogManager.getLogger(LicenseHelper.class);
38
- private static final long MS_PER_DAY = 24L * 3600L * 1000L;
54
+ @SuppressWarnings("unused")
55
+ private static final Logger LOG = LogManager.getLogger(LicenseHelper.class);
3956
40
- @Inject
41
- private UserHelper userHelper;
57
+ /** Milliseconds per day (used to derive expiration dates). */
58
+ private static final long MS_PER_DAY = 24L * 3600L * 1000L;
4259
43
- /**
44
- * Cancel the license
45
- *
46
- * @param lic
47
- * @param em
48
- */
49
- public void cancelLicense(License lic, String reason, BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
50
- lic.setStatus(LicenseStatus.CANCELLED);
51
- lic.setCancelledById(bsc.getUserPrincipal().getName());
52
- lic.setModificationTimestamp(new Date());
53
- em.persist(lic);
60
+ @Inject private UserHelper userHelper;
5461
55
- em.persist(createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.CANCEL, "Cancellation reason: " + reason));
56
- }
62
+ /**
63
+ * cancelLicense
64
+ * <p>
65
+ * Transitions a license to CANCELLED, records who canceled it and why,
66
+ * and appends a {@link LicenseHistory} entry.
67
+ *
68
+ * @param lic Target license (managed).
69
+ * @param reason Human-readable cancellation reason (auditable).
70
+ * @param bsc Current security context (used to identify the user).
71
+ * @param em Entity manager to persist changes.
72
+ * @throws SeCurisServiceException never thrown here, declared for symmetry with callers.
73
+ */
74
+ public void cancelLicense(License lic, String reason, BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
75
+ lic.setStatus(LicenseStatus.CANCELLED);
76
+ lic.setCancelledById(bsc.getUserPrincipal().getName());
77
+ lic.setModificationTimestamp(new Date());
78
+ em.persist(lic);
5779
58
- /**
59
- * Validates that the passed license exists and is still valid
60
- *
61
- * @param licBean
62
- * @param em
63
- * @return The License instance in DB
64
- * @throws SeCurisServiceException
65
- */
66
- public License getActiveLicenseFromDB(LicenseBean licBean, EntityManager em) throws SeCurisServiceException {
67
- License lic = License.findLicenseByCode(licBean.getLicenseCode(), em);
68
- if (lic == null) {
69
- throw new SeCurisServiceException(ErrorCodes.LICENSE_DATA_IS_NOT_VALID, "Current license code doesn't exist");
70
- }
71
- if (lic.getStatus() != LicenseStatus.ACTIVE && lic.getStatus() != LicenseStatus.PRE_ACTIVE) {
72
- throw new SeCurisServiceException(ErrorCodes.LICENSE_DATA_IS_NOT_VALID, "Current license in not active");
73
- }
74
- return lic;
75
- }
80
+ em.persist(createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.CANCEL, "Cancellation reason: " + reason));
81
+ }
7682
77
- public LicenseHistory createLicenseHistoryAction(License lic, User user, String action, String comments) {
78
- LicenseHistory lh = new LicenseHistory();
79
- lh.setLicense(lic);
80
- lh.setUser(user);
81
- lh.setCreationTimestamp(new Date());
82
- lh.setAction(action);
83
- lh.setComments(comments);
84
- return lh;
85
- }
83
+ /**
84
+ * getActiveLicenseFromDB
85
+ * <p>
86
+ * Resolve license by code and verify that it's ACTIVE or PRE_ACTIVE.
87
+ *
88
+ * @param licBean License bean containing the code to check.
89
+ * @param em EntityManager for DB access.
90
+ * @return The managed {@link License} instance.
91
+ * @throws SeCurisServiceException if code not found or license not in an active-ish state.
92
+ */
93
+ public License getActiveLicenseFromDB(LicenseBean licBean, EntityManager em) throws SeCurisServiceException {
94
+ License lic = License.findLicenseByCode(licBean.getLicenseCode(), em);
95
+ if (lic == null) {
96
+ throw new SeCurisServiceException(ErrorCodes.LICENSE_DATA_IS_NOT_VALID, "Current license code doesn't exist");
97
+ }
98
+ if (lic.getStatus() != LicenseStatus.ACTIVE && lic.getStatus() != LicenseStatus.PRE_ACTIVE) {
99
+ throw new SeCurisServiceException(ErrorCodes.LICENSE_DATA_IS_NOT_VALID, "Current license in not active");
100
+ }
101
+ return lic;
102
+ }
86103
87
- public LicenseHistory createLicenseHistoryAction(License lic, User user, String action) {
88
- return createLicenseHistoryAction(lic, user, action, null);
89
- }
104
+ /**
105
+ * createLicenseHistoryAction
106
+ * <p>
107
+ * Helper to build a {@link LicenseHistory} entry.
108
+ *
109
+ * @param lic License affected.
110
+ * @param user User performing the action.
111
+ * @param action Action code (see {@link LicenseHistory.Actions}).
112
+ * @param comments Optional comments, can be null.
113
+ * @return transient {@link LicenseHistory} ready to persist.
114
+ */
115
+ public LicenseHistory createLicenseHistoryAction(License lic, User user, String action, String comments) {
116
+ LicenseHistory lh = new LicenseHistory();
117
+ lh.setLicense(lic);
118
+ lh.setUser(user);
119
+ lh.setCreationTimestamp(new Date());
120
+ lh.setAction(action);
121
+ lh.setComments(comments);
122
+ return lh;
123
+ }
90124
91
- /**
92
- * Create a license file in a temporary directory
93
- *
94
- * @param lic
95
- * @param licFileName
96
- * @return
97
- * @throws IOException
98
- */
99
- public File createTemporaryLicenseFile(License lic, String licFileName) throws IOException {
100
- File f = Files.createTempDirectory("securis-server").toFile();
101
- f = new File(f, licFileName);
102
- FileUtils.writeStringToFile(f, lic.getLicenseData(), StandardCharsets.UTF_8);
103
- return f;
104
- }
125
+ /**
126
+ * createLicenseHistoryAction
127
+ * <p>
128
+ * Overload without comments.
129
+ *
130
+ * @param lic License affected.
131
+ * @param user User performing the action.
132
+ * @param action Action code.
133
+ * @return transient {@link LicenseHistory}.
134
+ */
135
+ public LicenseHistory createLicenseHistoryAction(License lic, User user, String action) {
136
+ return createLicenseHistoryAction(lic, user, action, null);
137
+ }
105138
106
- public Map<String, Object> extractPackMetadata(Set<PackMetadata> packMetadata) {
107
- Map<String, Object> metadata = new HashMap<>();
108
- for (PackMetadata md : packMetadata) {
109
- metadata.put(md.getKey(), md.getValue());
110
- }
139
+ /**
140
+ * createTemporaryLicenseFile
141
+ * <p>
142
+ * Materializes the license payload into a temporary file for emailing/download.
143
+ * The file is created under a unique temporary directory.
144
+ *
145
+ * Caller is responsible for deleting the file and its parent directory.
146
+ *
147
+ * @param lic License whose JSON/XML/text payload is in {@code getLicenseData()}.
148
+ * @param licFileName Desired file name (e.g. "license.lic").
149
+ * @return A {@link File} pointing to the newly created file.
150
+ * @throws IOException If the temporary directory or file cannot be created/written.
151
+ */
152
+ public File createTemporaryLicenseFile(License lic, String licFileName) throws IOException {
153
+ File f = Files.createTempDirectory("securis-server").toFile();
154
+ f = new File(f, licFileName);
155
+ FileUtils.writeStringToFile(f, lic.getLicenseData(), StandardCharsets.UTF_8);
156
+ return f;
157
+ }
111158
112
- return metadata;
113
- }
159
+ /**
160
+ * extractPackMetadata
161
+ * <p>
162
+ * Converts pack metadata set to a map for license generation.
163
+ *
164
+ * @param packMetadata Set of {@link PackMetadata}.
165
+ * @return Map with keys/values copied from metadata entries.
166
+ */
167
+ public Map<String, Object> extractPackMetadata(Set<PackMetadata> packMetadata) {
168
+ Map<String, Object> metadata = new HashMap<>();
169
+ for (PackMetadata md : packMetadata) {
170
+ metadata.put(md.getKey(), md.getValue());
171
+ }
172
+ return metadata;
173
+ }
114174
115
- /**
116
- * If the action is a renew the expiration date is got form pack end valid
117
- * date, if the action is a pre-activation the expiration date is calculated
118
- * using the pack default valid period
119
- *
120
- * @param pack
121
- * @param isPreActivation
122
- * @return
123
- */
124
- public Date getExpirationDateFromPack(Pack pack, boolean isPreActivation) {
125
- Long validPeriod;
126
- if (pack.getEndValidDate().before(new Date())) {
127
- throw new CurisRuntimeException("Pack end valid period is reached, no new licenses can be activated.");
128
- }
129
- if (isPreActivation) {
130
- validPeriod = pack.getPreactivationValidPeriod() * MS_PER_DAY;
131
- } else {
132
- if (pack.getRenewValidPeriod() <= 0) {
133
- return pack.getEndValidDate();
134
- }
135
- long renewPeriod = pack.getRenewValidPeriod() * MS_PER_DAY;
136
- long expirationPeriod = pack.getEndValidDate().getTime() - new Date().getTime();
137
- validPeriod = renewPeriod < expirationPeriod ? renewPeriod : expirationPeriod;
138
- }
139
- Date expirationDate = new Date(new Date().getTime() + validPeriod);
140
- return expirationDate;
141
- }
175
+ /**
176
+ * getExpirationDateFromPack
177
+ * <p>
178
+ * Computes license expiration date depending on action type:
179
+ * - Pre-activation: {@code preactivationValidPeriod} days from now.
180
+ * - Renew/Activation: min(renewValidPeriod days, pack end date - now).
181
+ * Fails fast if pack end date is already in the past.
182
+ *
183
+ * @param pack Pack with policy data.
184
+ * @param isPreActivation Whether the operation is a pre-activation.
185
+ * @return Calculated expiration {@link Date}.
186
+ * @throws CurisRuntimeException if the pack's end date is in the past.
187
+ */
188
+ public Date getExpirationDateFromPack(Pack pack, boolean isPreActivation) {
189
+ Long validPeriod;
190
+ if (pack.getEndValidDate().before(new Date())) {
191
+ throw new CurisRuntimeException("Pack end valid period is reached, no new licenses can be activated.");
192
+ }
193
+ if (isPreActivation) {
194
+ validPeriod = pack.getPreactivationValidPeriod() * MS_PER_DAY;
195
+ } else {
196
+ if (pack.getRenewValidPeriod() <= 0) {
197
+ return pack.getEndValidDate();
198
+ }
199
+ long renewPeriod = pack.getRenewValidPeriod() * MS_PER_DAY;
200
+ long expirationPeriod = pack.getEndValidDate().getTime() - new Date().getTime();
201
+ validPeriod = renewPeriod < expirationPeriod ? renewPeriod : expirationPeriod;
202
+ }
203
+ Date expirationDate = new Date(new Date().getTime() + validPeriod);
204
+ return expirationDate;
205
+ }
142206
143
- /**
144
- * Get the next free code suffis for a given Pack
145
- *
146
- * @param packId
147
- * @param em
148
- * @return
149
- */
150
- public int getNextCodeSuffix(int packId, EntityManager em) {
151
- TypedQuery<Integer> query = em.createNamedQuery("last-code-suffix-used-in-pack", Integer.class);
152
- query.setParameter("packId", packId);
153
- Integer lastCodeSuffix = query.getSingleResult();
154
- return lastCodeSuffix == null ? 1 : lastCodeSuffix + 1;
155
- }
156
-
207
+ /**
208
+ * getNextCodeSuffix
209
+ * <p>
210
+ * Retrieves the last used code suffix for a given pack and returns the next one.
211
+ * If none found, returns 1.
212
+ *
213
+ * @param packId Pack identifier.
214
+ * @param em EntityManager to query the DB.
215
+ * @return Next sequential suffix (>= 1).
216
+ */
217
+ public int getNextCodeSuffix(int packId, EntityManager em) {
218
+ TypedQuery<Integer> query = em.createNamedQuery("last-code-suffix-used-in-pack", Integer.class);
219
+ query.setParameter("packId", packId);
220
+ Integer lastCodeSuffix = query.getSingleResult();
221
+ return lastCodeSuffix == null ? 1 : lastCodeSuffix + 1;
222
+ }
157223 }
224
+