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/LicenseResource.java |  549 ++++++++++++++++++++++++++++++++----------------------
 1 files changed, 328 insertions(+), 221 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/services/LicenseResource.java b/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
index ed337ce..0885630 100644
--- a/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
@@ -1,3 +1,6 @@
+/*
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+ */
 package net.curisit.securis.services;
 
 import java.io.File;
@@ -63,34 +66,45 @@
 import net.curisit.securis.utils.LicUtils;
 
 /**
- * License resource, this service will provide methods to create, modify and
- * delete licenses
- * 
- * @author roberto <roberto.sanchez@curisit.net>
+ * LicenseResource
+ * <p>
+ * REST resource in charge of managing licenses: list, fetch, create, activate,
+ * email delivery, cancel, block/unblock, modify and delete. It relies on
+ * {@link BasicSecurityContext} to scope access (organizations/apps) and
+ * on {@link EnsureTransaction} for mutating endpoints that need a TX.
+ * <p>
+ * Key rules:
+ * <ul>
+ *   <li>Non-admin users must belong to the license's organization.</li>
+ *   <li>License creation validates code CRC, activation code and email.</li>
+ *   <li>Request payload must match Pack constraints (org/type/pack codes).</li>
+ *   <li>History is recorded for key actions (CREATE/ACTIVATE/DOWNLOAD/etc.).</li>
+ * </ul>
+ *
+ * @author roberto
+ * Last reviewed by JRA on Oct 5, 2025.
  */
 @Path("/license")
 public class LicenseResource {
 
 	private static final Logger LOG = LogManager.getLogger(LicenseResource.class);
 
-	@Inject
-	private EmailManager emailManager;
+	@Inject private EmailManager emailManager;
+	@Inject private UserHelper userHelper;
+	@Inject private LicenseHelper licenseHelper;
+	@Inject private LicenseGenerator licenseGenerator;
 
-	@Inject
-	private UserHelper userHelper;
-
-	@Inject
-	private LicenseHelper licenseHelper;
-
-	@Context
-	EntityManager em;
-
-	@Inject
-	private LicenseGenerator licenseGenerator;
+	@Context EntityManager em;
 
 	/**
-	 * 
-	 * @return the server version in format majorVersion.minorVersion
+	 * index
+	 * <p>
+	 * List all licenses for a given pack. If the caller is not admin,
+	 * verifies the pack belongs to an accessible organization.
+	 *
+	 * @param packId Pack identifier to filter licenses (required).
+	 * @param bsc    Security context to evaluate roles and scoping.
+	 * @return 200 OK with a list (possibly empty), or 401 if unauthorized.
 	 */
 	@GET
 	@Path("/")
@@ -98,31 +112,33 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response index(@QueryParam("packId") Integer packId, @Context BasicSecurityContext bsc) {
 		LOG.info("Getting licenses list ");
-
-		// EntityManager em = emProvider.get();
 		em.clear();
 
 		if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {
 			Pack pack = em.find(Pack.class, packId);
-			if (pack == null) {
-				return Response.ok().build();
-			}
+			if (pack == null) return Response.ok().build();
 			if (!bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {
 				LOG.error("Pack with id {} not accesible by user {}", pack, bsc.getUserPrincipal());
-				return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack licenses").build();
+				return Response.status(Status.UNAUTHORIZED)
+						.header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack licenses")
+						.build();
 			}
 		}
 		TypedQuery<License> q = em.createNamedQuery("list-licenses-by-pack", License.class);
 		q.setParameter("packId", packId);
 		List<License> list = q.getResultList();
-
 		return Response.ok(list).build();
 	}
 
 	/**
-	 * 
-	 * @return the server version in format majorVersion.minorVersion
-	 * @throws SeCurisServiceException
+	 * get
+	 * <p>
+	 * Fetch a single license by id, enforcing access scope for non-admin users.
+	 *
+	 * @param licId License id (required).
+	 * @param bsc   Security context.
+	 * @return 200 OK with the license.
+	 * @throws SeCurisServiceException 404 if not found, 401 if out of scope.
 	 */
 	@GET
 	@Path("/{licId}")
@@ -130,17 +146,22 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response get(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
 		LOG.info("Getting organization data for id: {}: ", licId);
-
-		// EntityManager em = emProvider.get();
 		em.clear();
 		License lic = getCurrentLicense(licId, bsc, em);
 		return Response.ok(lic).build();
 	}
 
 	/**
-	 * 
-	 * @return The license file, only of license is active
-	 * @throws SeCurisServiceException
+	 * download
+	 * <p>
+	 * Download the license file. Only allowed when the license is ACTIVE
+	 * and license data exists. Adds a DOWNLOAD entry in history.
+	 *
+	 * @param licId License id.
+	 * @param bsc   Security context.
+	 * @return 200 OK with the binary as application/octet-stream and a
+	 *         Content-Disposition header; otherwise specific error codes.
+	 * @throws SeCurisServiceException if state or data is invalid.
 	 */
 	@GET
 	@Path("/{licId}/download")
@@ -149,7 +170,6 @@
 	@EnsureTransaction
 	public Response download(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
 
-		// EntityManager em = emProvider.get();
 		License lic = getCurrentLicense(licId, bsc, em);
 
 		if (lic.getLicenseData() == null) {
@@ -166,12 +186,16 @@
 	}
 
 	/**
-	 * Activate the given license
-	 * 
-	 * @param licId
-	 * @param bsc
-	 * @return
-	 * @throws SeCurisServiceException
+	 * activate
+	 * <p>
+	 * Set license to ACTIVE provided status transition is valid, pack has
+	 * available units and request data passes validation/uniqueness.
+	 * Adds an ACTIVATE entry in history.
+	 *
+	 * @param licId License id.
+	 * @param bsc   Security context.
+	 * @return 200 OK with updated license.
+	 * @throws SeCurisServiceException if invalid transition, no availability or invalid request data.
 	 */
 	@PUT
 	@POST
@@ -182,7 +206,6 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response activate(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
 
-		// EntityManager em = emProvider.get();
 		License lic = getCurrentLicense(licId, bsc, em);
 
 		if (!License.Status.isActionValid(License.Action.ACTIVATION, lic.getStatus())) {
@@ -211,12 +234,18 @@
 	}
 
 	/**
-	 * Send license file by email to the organization
-	 * 
-	 * @param licId
-	 * @param bsc
-	 * @return
-	 * @throws SeCurisServiceException
+	 * send
+	 * <p>
+	 * Email the license file to the license owner. Builds a temporary file
+	 * using the application license filename and cleans it afterwards.
+	 * Adds a SEND entry in history.
+	 *
+	 * @param licId License id.
+	 * @param addCC whether to CC the current operator.
+	 * @param bsc   Security context.
+	 * @return 200 OK with the license (no state change).
+	 * @throws SeCurisServiceException when no license file exists or user full name is missing.
+	 * @throws SeCurisException        if JSON/signature process fails.
 	 */
 	@SuppressWarnings("deprecation")
 	@PUT
@@ -229,7 +258,6 @@
 	public Response send(@PathParam("licId") Integer licId, @DefaultValue("false") @FormParam("add_cc") Boolean addCC, @Context BasicSecurityContext bsc)
 			throws SeCurisServiceException, SeCurisException {
 
-		// EntityManager em = emProvider.get();
 		License lic = getCurrentLicense(licId, bsc, em);
 		Application app = lic.getPack().getLicenseType().getApplication();
 		File licFile = null;
@@ -259,19 +287,21 @@
 			}
 		}
 
-		// lic.setModificationTimestamp(new Date());
-		// em.merge(lic);
 		em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.SEND, "Email sent to: " + lic.getEmail()));
 		return Response.ok(lic).build();
 	}
 
 	/**
-	 * Cancel given license
-	 * 
-	 * @param licId
-	 * @param bsc
-	 * @return
-	 * @throws SeCurisServiceException
+	 * cancel
+	 * <p>
+	 * Cancel a license (requires valid state transition and a non-null reason).
+	 * Delegates to {@link LicenseHelper#cancelLicense}.
+	 *
+	 * @param licId      License id.
+	 * @param actionData DTO carrying the cancellation reason.
+	 * @param bsc        Security context.
+	 * @return 200 OK with updated license.
+	 * @throws SeCurisServiceException when state is invalid or reason is missing.
 	 */
 	@PUT
 	@POST
@@ -282,7 +312,6 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	public Response cancel(@PathParam("licId") Integer licId, CancellationLicenseActionBean actionData, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
 
-		// EntityManager em = emProvider.get();
 		License lic = getCurrentLicense(licId, bsc, em);
 
 		if (!License.Status.isActionValid(License.Action.CANCEL, lic.getStatus())) {
@@ -300,22 +329,24 @@
 	}
 
 	/**
-	 * Check if there is some pack with the same code
-	 * 
-	 * @param code
-	 *            Pack code
-	 * @param em
-	 *            DB session object
-	 * @return <code>true</code> if code is already used, <code>false</code>
-	 *         otherwise
+	 * create
+	 * <p>
+	 * Create a license. Validates:
+	 * <ul>
+	 *   <li>Unique license code and valid CRC.</li>
+	 *   <li>Activation code presence and uniqueness.</li>
+	 *   <li>Valid user email.</li>
+	 *   <li>Pack existence, ACTIVE status and scope authorization.</li>
+	 *   <li>Request data consistency and unblock status (if provided).</li>
+	 * </ul>
+	 * If request data is provided and the Pack has availability, the license is
+	 * generated and set to ACTIVE immediately.
+	 *
+	 * @param lic License payload.
+	 * @param bsc Security context.
+	 * @return 200 OK with created license.
+	 * @throws SeCurisServiceException on validation failures.
 	 */
-	private boolean checkIfCodeExists(String code, EntityManager em) {
-		TypedQuery<License> query = em.createNamedQuery("license-by-code", License.class);
-		query.setParameter("code", code);
-		int lics = query.getResultList().size();
-		return lics > 0;
-	}
-
 	@POST
 	@Path("/")
 	@Consumes(MediaType.APPLICATION_JSON)
@@ -323,8 +354,6 @@
 	@Produces({ MediaType.APPLICATION_JSON })
 	@EnsureTransaction
 	public Response create(License lic, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-		// EntityManager em = emProvider.get();
-
 		if (checkIfCodeExists(lic.getCode(), em)) {
 			throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The license code is already used in an existing license");
 		}
@@ -370,10 +399,8 @@
 			}
 
 			if (pack.getNumAvailables() > 0) {
-
 				SignedLicenseBean signedLicense = generateLicense(lic, em);
-				// If user provide a request data the license status is passed
-				// directly to ACTIVE
+				// Move directly to ACTIVE when request data is provided
 				lic.setStatus(LicenseStatus.ACTIVE);
 				try {
 					lic.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class));
@@ -404,6 +431,203 @@
 		return Response.ok(lic).build();
 	}
 
+	/**
+	 * modify
+	 * <p>
+	 * Update license basic fields (comments, fullName, email) and, when
+	 * status is CREATED and request payload changes, re-normalize/validate and
+	 * regenerate the signed license data. Adds a MODIFY history entry.
+	 *
+	 * @param lic   New values.
+	 * @param licId License id.
+	 * @param bsc   Security context.
+	 * @return 200 OK with updated license.
+	 * @throws SeCurisServiceException if validation fails.
+	 */
+	@SuppressWarnings("deprecation")
+	@PUT
+	@POST
+	@Path("/{licId}")
+	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
+	@EnsureTransaction
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Produces({ MediaType.APPLICATION_JSON })
+	public Response modify(License lic, @PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+		LOG.info("Modifying license with id: {}", licId);
+
+		License currentLicense = getCurrentLicense(licId, bsc, em);
+		currentLicense.setComments(lic.getComments());
+		currentLicense.setFullName(lic.getFullName());
+		currentLicense.setEmail(lic.getEmail());
+		if (currentLicense.getActivationCode() == null) {
+			currentLicense.setActivationCode(lic.getActivationCode());
+		}
+
+		if (currentLicense.getStatus() == LicenseStatus.CREATED && !ObjectUtils.equals(currentLicense.getReqDataHash(), lic.getReqDataHash())) {
+			if (lic.getRequestData() != null) {
+				SignedLicenseBean signedLicense = generateLicense(lic, em);
+				try {
+					// Normalize the request JSON and update signed license JSON
+					currentLicense.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class));
+					LOG.info("JSON generated for request: \n{}", currentLicense.getRequestData());
+					if (BlockedRequest.isRequestBlocked(currentLicense.getRequestData(), em)) {
+						throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be used again");
+					}
+					currentLicense.setLicenseData(JsonUtils.toJSON(signedLicense));
+				} catch (SeCurisException e) {
+					LOG.error("Error generaing license JSON", e);
+					throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
+				}
+			} else {
+				currentLicense.setRequestData(null);
+			}
+		}
+
+		currentLicense.setModificationTimestamp(new Date());
+		em.persist(currentLicense);
+		em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.MODIFY));
+
+		return Response.ok(currentLicense).build();
+	}
+
+	/**
+	 * delete
+	 * <p>
+	 * Delete the license when the current status allows it. If the license
+	 * was BLOCKED, removes the BlockedRequest entry to unblock the request.
+	 *
+	 * @param licId License id.
+	 * @param bsc   Security context.
+	 * @return 200 OK with a success payload.
+	 * @throws SeCurisServiceException if status does not allow deletion.
+	 */
+	@DELETE
+	@Path("/{licId}")
+	@EnsureTransaction
+	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
+	@Produces({ MediaType.APPLICATION_JSON })
+	public Response delete(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+		LOG.info("Deleting license with id: {}", licId);
+		License lic = getCurrentLicense(licId, bsc, em);
+
+		if (!License.Status.isActionValid(License.Action.DELETE, lic.getStatus())) {
+			LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
+			throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can not be deleted in current status: " + lic.getStatus().name());
+		}
+		if (lic.getStatus() == LicenseStatus.BLOCKED) {
+			BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
+			if (blockedReq != null) {
+				em.remove(blockedReq);
+			}
+		}
+
+		em.remove(lic);
+		return Response.ok(Utils.createMap("success", true, "id", licId)).build();
+	}
+
+	/**
+	 * block
+	 * <p>
+	 * Block the license request data (allowed only from CANCELLED state).
+	 * Persists a {@link BlockedRequest} and transitions the license to BLOCKED.
+	 *
+	 * @param licId License id.
+	 * @param bsc   Security context.
+	 * @return 200 OK with a success payload.
+	 * @throws SeCurisServiceException if state is not CANCELLED or already blocked.
+	 */
+	@POST
+	@Path("/{licId}/block")
+	@EnsureTransaction
+	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
+	@Produces({ MediaType.APPLICATION_JSON })
+	public Response block(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+		LOG.info("Blocking license with id: {}", licId);
+		License lic = getCurrentLicense(licId, bsc, em);
+
+		if (!License.Status.isActionValid(License.Action.BLOCK, lic.getStatus())) {
+			LOG.error("License can only be blocked in CANCELLED status, current: {}", lic.getStatus().name());
+			throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can only be blocked in CANCELLED status");
+		}
+		if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
+			throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is already blocked");
+		}
+		BlockedRequest blockedReq = new BlockedRequest();
+		blockedReq.setCreationTimestamp(new Date());
+		blockedReq.setBlockedBy(userHelper.getUser(bsc, em));
+		blockedReq.setRequestData(lic.getRequestData());
+
+		em.persist(blockedReq);
+		lic.setStatus(LicenseStatus.BLOCKED);
+		lic.setModificationTimestamp(new Date());
+		em.merge(lic);
+
+		em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.BLOCK));
+		return Response.ok(Utils.createMap("success", true, "id", licId)).build();
+	}
+
+	/**
+	 * unblock
+	 * <p>
+	 * Remove the block for the license request data (if present) and move
+	 * license back to CANCELLED. Adds an UNBLOCK history entry.
+	 *
+	 * @param licId License id.
+	 * @param bsc   Security context.
+	 * @return 200 OK with a success payload.
+	 * @throws SeCurisServiceException never if not blocked (returns success anyway).
+	 */
+	@POST
+	@Path("/{licId}/unblock")
+	@EnsureTransaction
+	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
+	@Produces({ MediaType.APPLICATION_JSON })
+	public Response unblock(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+		LOG.info("Unblocking license with id: {}", licId);
+		License lic = getCurrentLicense(licId, bsc, em);
+
+		if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
+			BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
+			em.remove(blockedReq);
+
+			lic.setStatus(LicenseStatus.CANCELLED);
+			lic.setModificationTimestamp(new Date());
+			em.merge(lic);
+			em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.UNBLOCK));
+		} else {
+			LOG.info("Request data for license {} is NOT blocked", licId);
+		}
+
+		return Response.ok(Utils.createMap("success", true, "id", licId)).build();
+	}
+
+	// ---------------------------------------------------------------------
+	// Helpers
+	// ---------------------------------------------------------------------
+
+	/** 
+	 * checkIfCodeExists<p>
+	 * Check if there is an existing license with the same code. 
+	 * 
+	 * @param code
+	 * @param entityManager
+	 */
+	private boolean checkIfCodeExists(String code, EntityManager em) {
+		TypedQuery<License> query = em.createNamedQuery("license-by-code", License.class);
+		query.setParameter("code", code);
+		int lics = query.getResultList().size();
+		return lics > 0;
+	}
+
+	/**
+	 * generateLicense<p>
+	 * Generate a signed license from request data and pack metadata/expiration.
+	 *
+	 * @param license License with requestData and packId populated.
+	 * @param em      Entity manager.
+	 * @return Signed license bean.
+	 * @throws SeCurisServiceException if validation/generation fails.
+	 */
 	private SignedLicenseBean generateLicense(License license, EntityManager em) throws SeCurisServiceException {
 		SignedLicenseBean sl = null;
 		Pack pack = em.find(Pack.class, license.getPackId());
@@ -419,12 +643,14 @@
 	}
 
 	/**
-	 * We check if the given request data is valid for the current Pack and has
-	 * a valid format
-	 * 
-	 * @param pack
-	 * @param requestData
-	 * @throws SeCurisServiceException
+	 * validateRequestData<p>
+	 * Validate that requestData matches the Pack and is well-formed.
+	 *
+	 * @param pack           Target pack (org/type constraints).
+	 * @param requestData    Raw JSON string with the license request.
+	 * @param activationCode Activation code from the license payload.
+	 * @return Parsed {@link RequestBean}.
+	 * @throws SeCurisServiceException on format mismatch or wrong codes.
 	 */
 	private RequestBean validateRequestData(Pack pack, String requestData, String activationCode) throws SeCurisServiceException {
 		if (requestData == null) {
@@ -456,143 +682,16 @@
 		return rb;
 	}
 
-	@SuppressWarnings("deprecation")
-	@PUT
-	@POST
-	@Path("/{licId}")
-	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
-	@EnsureTransaction
-	@Consumes(MediaType.APPLICATION_JSON)
-	@Produces({ MediaType.APPLICATION_JSON })
-	public Response modify(License lic, @PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-		LOG.info("Modifying license with id: {}", licId);
-
-		// EntityManager em = emProvider.get();
-
-		License currentLicense = getCurrentLicense(licId, bsc, em);
-		currentLicense.setComments(lic.getComments());
-		currentLicense.setFullName(lic.getFullName());
-		currentLicense.setEmail(lic.getEmail());
-		if (currentLicense.getActivationCode() == null) {
-			currentLicense.setActivationCode(lic.getActivationCode());
-		}
-
-		if (currentLicense.getStatus() == LicenseStatus.CREATED && !ObjectUtils.equals(currentLicense.getReqDataHash(), lic.getReqDataHash())) {
-			if (lic.getRequestData() != null) {
-				SignedLicenseBean signedLicense = generateLicense(lic, em);
-				try {
-					// Next 2 lines are necessary to normalize the String that
-					// contains
-					// the request.
-					currentLicense.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class));
-					LOG.info("JSON generated for request: \n{}", currentLicense.getRequestData());
-					if (BlockedRequest.isRequestBlocked(currentLicense.getRequestData(), em)) {
-						throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be used again");
-					}
-					currentLicense.setLicenseData(JsonUtils.toJSON(signedLicense));
-				} catch (SeCurisException e) {
-					LOG.error("Error generaing license JSON", e);
-					throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
-				}
-			} else {
-				// This set method could pass a null value
-				currentLicense.setRequestData(null);
-			}
-		}
-
-		currentLicense.setModificationTimestamp(new Date());
-		em.persist(currentLicense);
-		em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.MODIFY));
-
-		return Response.ok(currentLicense).build();
-	}
-
-	@DELETE
-	@Path("/{licId}")
-	@EnsureTransaction
-	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
-	@Produces({ MediaType.APPLICATION_JSON })
-	public Response delete(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-		LOG.info("Deleting license with id: {}", licId);
-		// EntityManager em = emProvider.get();
-		License lic = getCurrentLicense(licId, bsc, em);
-
-		if (!License.Status.isActionValid(License.Action.DELETE, lic.getStatus())) {
-			LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
-			throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can not be deleted in current status: " + lic.getStatus().name());
-		}
-		if (lic.getStatus() == LicenseStatus.BLOCKED) {
-			// If license is removed and it's blocked then the blocked request
-			// should be removed, that is,
-			// the license deletion will unblock the request data
-			BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
-			if (blockedReq != null) {
-				// This if is to avoid some race condition or if the request has
-				// been already removed manually
-				em.remove(blockedReq);
-			}
-		}
-
-		em.remove(lic);
-		return Response.ok(Utils.createMap("success", true, "id", licId)).build();
-	}
-
-	@POST
-	@Path("/{licId}/block")
-	@EnsureTransaction
-	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
-	@Produces({ MediaType.APPLICATION_JSON })
-	public Response block(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-		LOG.info("Blocking license with id: {}", licId);
-		// EntityManager em = emProvider.get();
-		License lic = getCurrentLicense(licId, bsc, em);
-
-		if (!License.Status.isActionValid(License.Action.BLOCK, lic.getStatus())) {
-			LOG.error("License can only be blocked in CANCELLED status, current: {}", lic.getStatus().name());
-			throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can only be blocked in CANCELLED status");
-		}
-		if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
-			throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is already blocked");
-		}
-		BlockedRequest blockedReq = new BlockedRequest();
-		blockedReq.setCreationTimestamp(new Date());
-		blockedReq.setBlockedBy(userHelper.getUser(bsc, em));
-		blockedReq.setRequestData(lic.getRequestData());
-
-		em.persist(blockedReq);
-		lic.setStatus(LicenseStatus.BLOCKED);
-		lic.setModificationTimestamp(new Date());
-		em.merge(lic);
-
-		em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.BLOCK));
-		return Response.ok(Utils.createMap("success", true, "id", licId)).build();
-	}
-
-	@POST
-	@Path("/{licId}/unblock")
-	@EnsureTransaction
-	@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
-	@Produces({ MediaType.APPLICATION_JSON })
-	public Response unblock(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-		LOG.info("Unblocking license with id: {}", licId);
-		// EntityManager em = emProvider.get();
-		License lic = getCurrentLicense(licId, bsc, em);
-
-		if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
-			BlockedRequest blockedReq = em.find(BlockedRequest.class, lic.getReqDataHash());
-			em.remove(blockedReq);
-
-			lic.setStatus(LicenseStatus.CANCELLED);
-			lic.setModificationTimestamp(new Date());
-			em.merge(lic);
-			em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.UNBLOCK));
-		} else {
-			LOG.info("Request data for license {} is NOT blocked", licId);
-		}
-
-		return Response.ok(Utils.createMap("success", true, "id", licId)).build();
-	}
-
+	/**
+	 * getCurrentLicense<p>
+	 * Load a license and verify scope for non-admin users.
+	 *
+	 * @param licId License id.
+	 * @param bsc   Security context.
+	 * @param em    Entity manager.
+	 * @return License entity.
+	 * @throws SeCurisServiceException if id is missing, not found or unauthorized.
+	 */
 	private License getCurrentLicense(Integer licId, BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
 		if (licId == null || "".equals(Integer.toString(licId))) {
 			LOG.error("License ID is mandatory");
@@ -611,6 +710,13 @@
 		return lic;
 	}
 
+	// ---------------------------------------------------------------------
+	// DTOs
+	// ---------------------------------------------------------------------
+
+	/**
+	 * DTO used to carry a cancellation reason for the cancel endpoint.
+	 */
 	@JsonAutoDetect
 	@JsonIgnoreProperties(ignoreUnknown = true)
 	static class CancellationLicenseActionBean {
@@ -618,3 +724,4 @@
 		private String reason;
 	}
 }
+

--
Gitblit v1.3.2