Joaquín Reñé
2025-10-07 146a0fb8b0e90f9196e569152f649baf60d6cc8f
securis/src/main/java/net/curisit/securis/services/LicenseResource.java
....@@ -1,3 +1,6 @@
1
+/*
2
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
3
+ */
14 package net.curisit.securis.services;
25
36 import java.io.File;
....@@ -63,34 +66,45 @@
6366 import net.curisit.securis.utils.LicUtils;
6467
6568 /**
66
- * License resource, this service will provide methods to create, modify and
67
- * delete licenses
68
- *
69
- * @author roberto <roberto.sanchez@curisit.net>
69
+ * LicenseResource
70
+ * <p>
71
+ * REST resource in charge of managing licenses: list, fetch, create, activate,
72
+ * email delivery, cancel, block/unblock, modify and delete. It relies on
73
+ * {@link BasicSecurityContext} to scope access (organizations/apps) and
74
+ * on {@link EnsureTransaction} for mutating endpoints that need a TX.
75
+ * <p>
76
+ * Key rules:
77
+ * <ul>
78
+ * <li>Non-admin users must belong to the license's organization.</li>
79
+ * <li>License creation validates code CRC, activation code and email.</li>
80
+ * <li>Request payload must match Pack constraints (org/type/pack codes).</li>
81
+ * <li>History is recorded for key actions (CREATE/ACTIVATE/DOWNLOAD/etc.).</li>
82
+ * </ul>
83
+ *
84
+ * @author roberto
85
+ * Last reviewed by JRA on Oct 5, 2025.
7086 */
7187 @Path("/license")
7288 public class LicenseResource {
7389
7490 private static final Logger LOG = LogManager.getLogger(LicenseResource.class);
7591
76
- @Inject
77
- private EmailManager emailManager;
92
+ @Inject private EmailManager emailManager;
93
+ @Inject private UserHelper userHelper;
94
+ @Inject private LicenseHelper licenseHelper;
95
+ @Inject private LicenseGenerator licenseGenerator;
7896
79
- @Inject
80
- private UserHelper userHelper;
81
-
82
- @Inject
83
- private LicenseHelper licenseHelper;
84
-
85
- @Context
86
- EntityManager em;
87
-
88
- @Inject
89
- private LicenseGenerator licenseGenerator;
97
+ @Context EntityManager em;
9098
9199 /**
92
- *
93
- * @return the server version in format majorVersion.minorVersion
100
+ * index
101
+ * <p>
102
+ * List all licenses for a given pack. If the caller is not admin,
103
+ * verifies the pack belongs to an accessible organization.
104
+ *
105
+ * @param packId Pack identifier to filter licenses (required).
106
+ * @param bsc Security context to evaluate roles and scoping.
107
+ * @return 200 OK with a list (possibly empty), or 401 if unauthorized.
94108 */
95109 @GET
96110 @Path("/")
....@@ -98,31 +112,33 @@
98112 @Produces({ MediaType.APPLICATION_JSON })
99113 public Response index(@QueryParam("packId") Integer packId, @Context BasicSecurityContext bsc) {
100114 LOG.info("Getting licenses list ");
101
-
102
- // EntityManager em = emProvider.get();
103115 em.clear();
104116
105117 if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {
106118 Pack pack = em.find(Pack.class, packId);
107
- if (pack == null) {
108
- return Response.ok().build();
109
- }
119
+ if (pack == null) return Response.ok().build();
110120 if (!bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {
111121 LOG.error("Pack with id {} not accesible by user {}", pack, bsc.getUserPrincipal());
112
- return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack licenses").build();
122
+ return Response.status(Status.UNAUTHORIZED)
123
+ .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack licenses")
124
+ .build();
113125 }
114126 }
115127 TypedQuery<License> q = em.createNamedQuery("list-licenses-by-pack", License.class);
116128 q.setParameter("packId", packId);
117129 List<License> list = q.getResultList();
118
-
119130 return Response.ok(list).build();
120131 }
121132
122133 /**
123
- *
124
- * @return the server version in format majorVersion.minorVersion
125
- * @throws SeCurisServiceException
134
+ * get
135
+ * <p>
136
+ * Fetch a single license by id, enforcing access scope for non-admin users.
137
+ *
138
+ * @param licId License id (required).
139
+ * @param bsc Security context.
140
+ * @return 200 OK with the license.
141
+ * @throws SeCurisServiceException 404 if not found, 401 if out of scope.
126142 */
127143 @GET
128144 @Path("/{licId}")
....@@ -130,17 +146,22 @@
130146 @Produces({ MediaType.APPLICATION_JSON })
131147 public Response get(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
132148 LOG.info("Getting organization data for id: {}: ", licId);
133
-
134
- // EntityManager em = emProvider.get();
135149 em.clear();
136150 License lic = getCurrentLicense(licId, bsc, em);
137151 return Response.ok(lic).build();
138152 }
139153
140154 /**
141
- *
142
- * @return The license file, only of license is active
143
- * @throws SeCurisServiceException
155
+ * download
156
+ * <p>
157
+ * Download the license file. Only allowed when the license is ACTIVE
158
+ * and license data exists. Adds a DOWNLOAD entry in history.
159
+ *
160
+ * @param licId License id.
161
+ * @param bsc Security context.
162
+ * @return 200 OK with the binary as application/octet-stream and a
163
+ * Content-Disposition header; otherwise specific error codes.
164
+ * @throws SeCurisServiceException if state or data is invalid.
144165 */
145166 @GET
146167 @Path("/{licId}/download")
....@@ -149,7 +170,6 @@
149170 @EnsureTransaction
150171 public Response download(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
151172
152
- // EntityManager em = emProvider.get();
153173 License lic = getCurrentLicense(licId, bsc, em);
154174
155175 if (lic.getLicenseData() == null) {
....@@ -166,12 +186,16 @@
166186 }
167187
168188 /**
169
- * Activate the given license
170
- *
171
- * @param licId
172
- * @param bsc
173
- * @return
174
- * @throws SeCurisServiceException
189
+ * activate
190
+ * <p>
191
+ * Set license to ACTIVE provided status transition is valid, pack has
192
+ * available units and request data passes validation/uniqueness.
193
+ * Adds an ACTIVATE entry in history.
194
+ *
195
+ * @param licId License id.
196
+ * @param bsc Security context.
197
+ * @return 200 OK with updated license.
198
+ * @throws SeCurisServiceException if invalid transition, no availability or invalid request data.
175199 */
176200 @PUT
177201 @POST
....@@ -182,7 +206,6 @@
182206 @Produces({ MediaType.APPLICATION_JSON })
183207 public Response activate(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
184208
185
- // EntityManager em = emProvider.get();
186209 License lic = getCurrentLicense(licId, bsc, em);
187210
188211 if (!License.Status.isActionValid(License.Action.ACTIVATION, lic.getStatus())) {
....@@ -211,12 +234,18 @@
211234 }
212235
213236 /**
214
- * Send license file by email to the organization
215
- *
216
- * @param licId
217
- * @param bsc
218
- * @return
219
- * @throws SeCurisServiceException
237
+ * send
238
+ * <p>
239
+ * Email the license file to the license owner. Builds a temporary file
240
+ * using the application license filename and cleans it afterwards.
241
+ * Adds a SEND entry in history.
242
+ *
243
+ * @param licId License id.
244
+ * @param addCC whether to CC the current operator.
245
+ * @param bsc Security context.
246
+ * @return 200 OK with the license (no state change).
247
+ * @throws SeCurisServiceException when no license file exists or user full name is missing.
248
+ * @throws SeCurisException if JSON/signature process fails.
220249 */
221250 @SuppressWarnings("deprecation")
222251 @PUT
....@@ -229,7 +258,6 @@
229258 public Response send(@PathParam("licId") Integer licId, @DefaultValue("false") @FormParam("add_cc") Boolean addCC, @Context BasicSecurityContext bsc)
230259 throws SeCurisServiceException, SeCurisException {
231260
232
- // EntityManager em = emProvider.get();
233261 License lic = getCurrentLicense(licId, bsc, em);
234262 Application app = lic.getPack().getLicenseType().getApplication();
235263 File licFile = null;
....@@ -259,19 +287,21 @@
259287 }
260288 }
261289
262
- // lic.setModificationTimestamp(new Date());
263
- // em.merge(lic);
264290 em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.SEND, "Email sent to: " + lic.getEmail()));
265291 return Response.ok(lic).build();
266292 }
267293
268294 /**
269
- * Cancel given license
270
- *
271
- * @param licId
272
- * @param bsc
273
- * @return
274
- * @throws SeCurisServiceException
295
+ * cancel
296
+ * <p>
297
+ * Cancel a license (requires valid state transition and a non-null reason).
298
+ * Delegates to {@link LicenseHelper#cancelLicense}.
299
+ *
300
+ * @param licId License id.
301
+ * @param actionData DTO carrying the cancellation reason.
302
+ * @param bsc Security context.
303
+ * @return 200 OK with updated license.
304
+ * @throws SeCurisServiceException when state is invalid or reason is missing.
275305 */
276306 @PUT
277307 @POST
....@@ -282,7 +312,6 @@
282312 @Produces({ MediaType.APPLICATION_JSON })
283313 public Response cancel(@PathParam("licId") Integer licId, CancellationLicenseActionBean actionData, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
284314
285
- // EntityManager em = emProvider.get();
286315 License lic = getCurrentLicense(licId, bsc, em);
287316
288317 if (!License.Status.isActionValid(License.Action.CANCEL, lic.getStatus())) {
....@@ -300,22 +329,24 @@
300329 }
301330
302331 /**
303
- * Check if there is some pack with the same code
304
- *
305
- * @param code
306
- * Pack code
307
- * @param em
308
- * DB session object
309
- * @return <code>true</code> if code is already used, <code>false</code>
310
- * otherwise
332
+ * create
333
+ * <p>
334
+ * Create a license. Validates:
335
+ * <ul>
336
+ * <li>Unique license code and valid CRC.</li>
337
+ * <li>Activation code presence and uniqueness.</li>
338
+ * <li>Valid user email.</li>
339
+ * <li>Pack existence, ACTIVE status and scope authorization.</li>
340
+ * <li>Request data consistency and unblock status (if provided).</li>
341
+ * </ul>
342
+ * If request data is provided and the Pack has availability, the license is
343
+ * generated and set to ACTIVE immediately.
344
+ *
345
+ * @param lic License payload.
346
+ * @param bsc Security context.
347
+ * @return 200 OK with created license.
348
+ * @throws SeCurisServiceException on validation failures.
311349 */
312
- private boolean checkIfCodeExists(String code, EntityManager em) {
313
- TypedQuery<License> query = em.createNamedQuery("license-by-code", License.class);
314
- query.setParameter("code", code);
315
- int lics = query.getResultList().size();
316
- return lics > 0;
317
- }
318
-
319350 @POST
320351 @Path("/")
321352 @Consumes(MediaType.APPLICATION_JSON)
....@@ -323,8 +354,6 @@
323354 @Produces({ MediaType.APPLICATION_JSON })
324355 @EnsureTransaction
325356 public Response create(License lic, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
326
- // EntityManager em = emProvider.get();
327
-
328357 if (checkIfCodeExists(lic.getCode(), em)) {
329358 throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The license code is already used in an existing license");
330359 }
....@@ -370,10 +399,8 @@
370399 }
371400
372401 if (pack.getNumAvailables() > 0) {
373
-
374402 SignedLicenseBean signedLicense = generateLicense(lic, em);
375
- // If user provide a request data the license status is passed
376
- // directly to ACTIVE
403
+ // Move directly to ACTIVE when request data is provided
377404 lic.setStatus(LicenseStatus.ACTIVE);
378405 try {
379406 lic.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class));
....@@ -404,6 +431,203 @@
404431 return Response.ok(lic).build();
405432 }
406433
434
+ /**
435
+ * modify
436
+ * <p>
437
+ * Update license basic fields (comments, fullName, email) and, when
438
+ * status is CREATED and request payload changes, re-normalize/validate and
439
+ * regenerate the signed license data. Adds a MODIFY history entry.
440
+ *
441
+ * @param lic New values.
442
+ * @param licId License id.
443
+ * @param bsc Security context.
444
+ * @return 200 OK with updated license.
445
+ * @throws SeCurisServiceException if validation fails.
446
+ */
447
+ @SuppressWarnings("deprecation")
448
+ @PUT
449
+ @POST
450
+ @Path("/{licId}")
451
+ @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
452
+ @EnsureTransaction
453
+ @Consumes(MediaType.APPLICATION_JSON)
454
+ @Produces({ MediaType.APPLICATION_JSON })
455
+ public Response modify(License lic, @PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
456
+ LOG.info("Modifying license with id: {}", licId);
457
+
458
+ License currentLicense = getCurrentLicense(licId, bsc, em);
459
+ currentLicense.setComments(lic.getComments());
460
+ currentLicense.setFullName(lic.getFullName());
461
+ currentLicense.setEmail(lic.getEmail());
462
+ if (currentLicense.getActivationCode() == null) {
463
+ currentLicense.setActivationCode(lic.getActivationCode());
464
+ }
465
+
466
+ if (currentLicense.getStatus() == LicenseStatus.CREATED && !ObjectUtils.equals(currentLicense.getReqDataHash(), lic.getReqDataHash())) {
467
+ if (lic.getRequestData() != null) {
468
+ SignedLicenseBean signedLicense = generateLicense(lic, em);
469
+ try {
470
+ // Normalize the request JSON and update signed license JSON
471
+ currentLicense.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class));
472
+ LOG.info("JSON generated for request: \n{}", currentLicense.getRequestData());
473
+ if (BlockedRequest.isRequestBlocked(currentLicense.getRequestData(), em)) {
474
+ throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be used again");
475
+ }
476
+ currentLicense.setLicenseData(JsonUtils.toJSON(signedLicense));
477
+ } catch (SeCurisException e) {
478
+ LOG.error("Error generaing license JSON", e);
479
+ throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
480
+ }
481
+ } else {
482
+ currentLicense.setRequestData(null);
483
+ }
484
+ }
485
+
486
+ currentLicense.setModificationTimestamp(new Date());
487
+ em.persist(currentLicense);
488
+ em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.MODIFY));
489
+
490
+ return Response.ok(currentLicense).build();
491
+ }
492
+
493
+ /**
494
+ * delete
495
+ * <p>
496
+ * Delete the license when the current status allows it. If the license
497
+ * was BLOCKED, removes the BlockedRequest entry to unblock the request.
498
+ *
499
+ * @param licId License id.
500
+ * @param bsc Security context.
501
+ * @return 200 OK with a success payload.
502
+ * @throws SeCurisServiceException if status does not allow deletion.
503
+ */
504
+ @DELETE
505
+ @Path("/{licId}")
506
+ @EnsureTransaction
507
+ @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
508
+ @Produces({ MediaType.APPLICATION_JSON })
509
+ public Response delete(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
510
+ LOG.info("Deleting license with id: {}", licId);
511
+ License lic = getCurrentLicense(licId, bsc, em);
512
+
513
+ if (!License.Status.isActionValid(License.Action.DELETE, lic.getStatus())) {
514
+ LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
515
+ throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can not be deleted in current status: " + lic.getStatus().name());
516
+ }
517
+ if (lic.getStatus() == LicenseStatus.BLOCKED) {
518
+ BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
519
+ if (blockedReq != null) {
520
+ em.remove(blockedReq);
521
+ }
522
+ }
523
+
524
+ em.remove(lic);
525
+ return Response.ok(Utils.createMap("success", true, "id", licId)).build();
526
+ }
527
+
528
+ /**
529
+ * block
530
+ * <p>
531
+ * Block the license request data (allowed only from CANCELLED state).
532
+ * Persists a {@link BlockedRequest} and transitions the license to BLOCKED.
533
+ *
534
+ * @param licId License id.
535
+ * @param bsc Security context.
536
+ * @return 200 OK with a success payload.
537
+ * @throws SeCurisServiceException if state is not CANCELLED or already blocked.
538
+ */
539
+ @POST
540
+ @Path("/{licId}/block")
541
+ @EnsureTransaction
542
+ @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
543
+ @Produces({ MediaType.APPLICATION_JSON })
544
+ public Response block(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
545
+ LOG.info("Blocking license with id: {}", licId);
546
+ License lic = getCurrentLicense(licId, bsc, em);
547
+
548
+ if (!License.Status.isActionValid(License.Action.BLOCK, lic.getStatus())) {
549
+ LOG.error("License can only be blocked in CANCELLED status, current: {}", lic.getStatus().name());
550
+ throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can only be blocked in CANCELLED status");
551
+ }
552
+ if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
553
+ throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is already blocked");
554
+ }
555
+ BlockedRequest blockedReq = new BlockedRequest();
556
+ blockedReq.setCreationTimestamp(new Date());
557
+ blockedReq.setBlockedBy(userHelper.getUser(bsc, em));
558
+ blockedReq.setRequestData(lic.getRequestData());
559
+
560
+ em.persist(blockedReq);
561
+ lic.setStatus(LicenseStatus.BLOCKED);
562
+ lic.setModificationTimestamp(new Date());
563
+ em.merge(lic);
564
+
565
+ em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.BLOCK));
566
+ return Response.ok(Utils.createMap("success", true, "id", licId)).build();
567
+ }
568
+
569
+ /**
570
+ * unblock
571
+ * <p>
572
+ * Remove the block for the license request data (if present) and move
573
+ * license back to CANCELLED. Adds an UNBLOCK history entry.
574
+ *
575
+ * @param licId License id.
576
+ * @param bsc Security context.
577
+ * @return 200 OK with a success payload.
578
+ * @throws SeCurisServiceException never if not blocked (returns success anyway).
579
+ */
580
+ @POST
581
+ @Path("/{licId}/unblock")
582
+ @EnsureTransaction
583
+ @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
584
+ @Produces({ MediaType.APPLICATION_JSON })
585
+ public Response unblock(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
586
+ LOG.info("Unblocking license with id: {}", licId);
587
+ License lic = getCurrentLicense(licId, bsc, em);
588
+
589
+ if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
590
+ BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
591
+ em.remove(blockedReq);
592
+
593
+ lic.setStatus(LicenseStatus.CANCELLED);
594
+ lic.setModificationTimestamp(new Date());
595
+ em.merge(lic);
596
+ em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.UNBLOCK));
597
+ } else {
598
+ LOG.info("Request data for license {} is NOT blocked", licId);
599
+ }
600
+
601
+ return Response.ok(Utils.createMap("success", true, "id", licId)).build();
602
+ }
603
+
604
+ // ---------------------------------------------------------------------
605
+ // Helpers
606
+ // ---------------------------------------------------------------------
607
+
608
+ /**
609
+ * checkIfCodeExists<p>
610
+ * Check if there is an existing license with the same code.
611
+ *
612
+ * @param code
613
+ * @param entityManager
614
+ */
615
+ private boolean checkIfCodeExists(String code, EntityManager em) {
616
+ TypedQuery<License> query = em.createNamedQuery("license-by-code", License.class);
617
+ query.setParameter("code", code);
618
+ int lics = query.getResultList().size();
619
+ return lics > 0;
620
+ }
621
+
622
+ /**
623
+ * generateLicense<p>
624
+ * Generate a signed license from request data and pack metadata/expiration.
625
+ *
626
+ * @param license License with requestData and packId populated.
627
+ * @param em Entity manager.
628
+ * @return Signed license bean.
629
+ * @throws SeCurisServiceException if validation/generation fails.
630
+ */
407631 private SignedLicenseBean generateLicense(License license, EntityManager em) throws SeCurisServiceException {
408632 SignedLicenseBean sl = null;
409633 Pack pack = em.find(Pack.class, license.getPackId());
....@@ -419,12 +643,14 @@
419643 }
420644
421645 /**
422
- * We check if the given request data is valid for the current Pack and has
423
- * a valid format
424
- *
425
- * @param pack
426
- * @param requestData
427
- * @throws SeCurisServiceException
646
+ * validateRequestData<p>
647
+ * Validate that requestData matches the Pack and is well-formed.
648
+ *
649
+ * @param pack Target pack (org/type constraints).
650
+ * @param requestData Raw JSON string with the license request.
651
+ * @param activationCode Activation code from the license payload.
652
+ * @return Parsed {@link RequestBean}.
653
+ * @throws SeCurisServiceException on format mismatch or wrong codes.
428654 */
429655 private RequestBean validateRequestData(Pack pack, String requestData, String activationCode) throws SeCurisServiceException {
430656 if (requestData == null) {
....@@ -456,143 +682,16 @@
456682 return rb;
457683 }
458684
459
- @SuppressWarnings("deprecation")
460
- @PUT
461
- @POST
462
- @Path("/{licId}")
463
- @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
464
- @EnsureTransaction
465
- @Consumes(MediaType.APPLICATION_JSON)
466
- @Produces({ MediaType.APPLICATION_JSON })
467
- public Response modify(License lic, @PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
468
- LOG.info("Modifying license with id: {}", licId);
469
-
470
- // EntityManager em = emProvider.get();
471
-
472
- License currentLicense = getCurrentLicense(licId, bsc, em);
473
- currentLicense.setComments(lic.getComments());
474
- currentLicense.setFullName(lic.getFullName());
475
- currentLicense.setEmail(lic.getEmail());
476
- if (currentLicense.getActivationCode() == null) {
477
- currentLicense.setActivationCode(lic.getActivationCode());
478
- }
479
-
480
- if (currentLicense.getStatus() == LicenseStatus.CREATED && !ObjectUtils.equals(currentLicense.getReqDataHash(), lic.getReqDataHash())) {
481
- if (lic.getRequestData() != null) {
482
- SignedLicenseBean signedLicense = generateLicense(lic, em);
483
- try {
484
- // Next 2 lines are necessary to normalize the String that
485
- // contains
486
- // the request.
487
- currentLicense.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class));
488
- LOG.info("JSON generated for request: \n{}", currentLicense.getRequestData());
489
- if (BlockedRequest.isRequestBlocked(currentLicense.getRequestData(), em)) {
490
- throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be used again");
491
- }
492
- currentLicense.setLicenseData(JsonUtils.toJSON(signedLicense));
493
- } catch (SeCurisException e) {
494
- LOG.error("Error generaing license JSON", e);
495
- throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
496
- }
497
- } else {
498
- // This set method could pass a null value
499
- currentLicense.setRequestData(null);
500
- }
501
- }
502
-
503
- currentLicense.setModificationTimestamp(new Date());
504
- em.persist(currentLicense);
505
- em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.MODIFY));
506
-
507
- return Response.ok(currentLicense).build();
508
- }
509
-
510
- @DELETE
511
- @Path("/{licId}")
512
- @EnsureTransaction
513
- @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
514
- @Produces({ MediaType.APPLICATION_JSON })
515
- public Response delete(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
516
- LOG.info("Deleting license with id: {}", licId);
517
- // EntityManager em = emProvider.get();
518
- License lic = getCurrentLicense(licId, bsc, em);
519
-
520
- if (!License.Status.isActionValid(License.Action.DELETE, lic.getStatus())) {
521
- LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
522
- throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can not be deleted in current status: " + lic.getStatus().name());
523
- }
524
- if (lic.getStatus() == LicenseStatus.BLOCKED) {
525
- // If license is removed and it's blocked then the blocked request
526
- // should be removed, that is,
527
- // the license deletion will unblock the request data
528
- BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
529
- if (blockedReq != null) {
530
- // This if is to avoid some race condition or if the request has
531
- // been already removed manually
532
- em.remove(blockedReq);
533
- }
534
- }
535
-
536
- em.remove(lic);
537
- return Response.ok(Utils.createMap("success", true, "id", licId)).build();
538
- }
539
-
540
- @POST
541
- @Path("/{licId}/block")
542
- @EnsureTransaction
543
- @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
544
- @Produces({ MediaType.APPLICATION_JSON })
545
- public Response block(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
546
- LOG.info("Blocking license with id: {}", licId);
547
- // EntityManager em = emProvider.get();
548
- License lic = getCurrentLicense(licId, bsc, em);
549
-
550
- if (!License.Status.isActionValid(License.Action.BLOCK, lic.getStatus())) {
551
- LOG.error("License can only be blocked in CANCELLED status, current: {}", lic.getStatus().name());
552
- throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can only be blocked in CANCELLED status");
553
- }
554
- if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
555
- throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is already blocked");
556
- }
557
- BlockedRequest blockedReq = new BlockedRequest();
558
- blockedReq.setCreationTimestamp(new Date());
559
- blockedReq.setBlockedBy(userHelper.getUser(bsc, em));
560
- blockedReq.setRequestData(lic.getRequestData());
561
-
562
- em.persist(blockedReq);
563
- lic.setStatus(LicenseStatus.BLOCKED);
564
- lic.setModificationTimestamp(new Date());
565
- em.merge(lic);
566
-
567
- em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.BLOCK));
568
- return Response.ok(Utils.createMap("success", true, "id", licId)).build();
569
- }
570
-
571
- @POST
572
- @Path("/{licId}/unblock")
573
- @EnsureTransaction
574
- @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
575
- @Produces({ MediaType.APPLICATION_JSON })
576
- public Response unblock(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
577
- LOG.info("Unblocking license with id: {}", licId);
578
- // EntityManager em = emProvider.get();
579
- License lic = getCurrentLicense(licId, bsc, em);
580
-
581
- if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
582
- BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
583
- em.remove(blockedReq);
584
-
585
- lic.setStatus(LicenseStatus.CANCELLED);
586
- lic.setModificationTimestamp(new Date());
587
- em.merge(lic);
588
- em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.UNBLOCK));
589
- } else {
590
- LOG.info("Request data for license {} is NOT blocked", licId);
591
- }
592
-
593
- return Response.ok(Utils.createMap("success", true, "id", licId)).build();
594
- }
595
-
685
+ /**
686
+ * getCurrentLicense<p>
687
+ * Load a license and verify scope for non-admin users.
688
+ *
689
+ * @param licId License id.
690
+ * @param bsc Security context.
691
+ * @param em Entity manager.
692
+ * @return License entity.
693
+ * @throws SeCurisServiceException if id is missing, not found or unauthorized.
694
+ */
596695 private License getCurrentLicense(Integer licId, BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
597696 if (licId == null || "".equals(Integer.toString(licId))) {
598697 LOG.error("License ID is mandatory");
....@@ -611,6 +710,13 @@
611710 return lic;
612711 }
613712
713
+ // ---------------------------------------------------------------------
714
+ // DTOs
715
+ // ---------------------------------------------------------------------
716
+
717
+ /**
718
+ * DTO used to carry a cancellation reason for the cancel endpoint.
719
+ */
614720 @JsonAutoDetect
615721 @JsonIgnoreProperties(ignoreUnknown = true)
616722 static class CancellationLicenseActionBean {
....@@ -618,3 +724,4 @@
618724 private String reason;
619725 }
620726 }
727
+