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/PackResource.java | 407 +++++++++++++++++++++++++++++++++++++---------------------
1 files changed, 260 insertions(+), 147 deletions(-)
diff --git a/securis/src/main/java/net/curisit/securis/services/PackResource.java b/securis/src/main/java/net/curisit/securis/services/PackResource.java
index 4623314..85b784f 100644
--- a/securis/src/main/java/net/curisit/securis/services/PackResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/PackResource.java
@@ -1,3 +1,6 @@
+/*
+ * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved.
+ */
package net.curisit.securis.services;
import java.security.Principal;
@@ -53,31 +56,35 @@
import net.curisit.securis.utils.TokenHelper;
/**
- * Pack resource, this service will provide methods to create, modify and delete
- * packs
- *
- * @author roberto <roberto.sanchez@curisit.net>
+ * PackResource
+ * <p>
+ * Manages Packs (group of licenses bound to an organization, application/type,
+ * and configuration/metadata). Provides list/filter, get, create, modify,
+ * state transitions (activate/hold/cancel) and deletion.
+ *
+ * @author JRA
+ * Last reviewed by JRA on Oct 5, 2025.
*/
@Path("/pack")
public class PackResource {
private static final Logger LOG = LogManager.getLogger(PackResource.class);
- @Inject
- TokenHelper tokenHelper;
+ @Inject TokenHelper tokenHelper;
+ @Inject MetadataHelper metadataHelper;
+ @Inject private LicenseHelper licenseHelper;
- @Inject
- MetadataHelper metadataHelper;
-
- @Context
- EntityManager em;
-
- @Inject
- private LicenseHelper licenseHelper;
+ @Context EntityManager em;
/**
- *
- * @return the server version in format majorVersion.minorVersion
+ * index
+ * <p>
+ * List packs with optional filters (organizationId, applicationId, licenseTypeId).
+ * For non-admins, results are scoped by both apps and orgs from {@link BasicSecurityContext}.
+ *
+ * @param uriInfo supplies query parameters.
+ * @param bsc security scope/roles.
+ * @return 200 OK with the list (possibly empty).
*/
@GET
@Path("/")
@@ -86,70 +93,25 @@
public Response index(@Context UriInfo uriInfo, @Context BasicSecurityContext bsc) {
LOG.info("Getting packs list ");
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
-
- // EntityManager em = emProvider.get();
em.clear();
TypedQuery<Pack> q = createQuery(queryParams, bsc);
if (q == null) {
return Response.ok().build();
}
-
List<Pack> list = q.getResultList();
-
return Response.ok(list).build();
}
- private String generateWhereFromParams(boolean addWhere, MultivaluedMap<String, String> queryParams) {
- List<String> conditions = new ArrayList<>();
- if (queryParams.containsKey("organizationId")) {
- conditions.add(String.format("pa.organization.id = %s", queryParams.getFirst("organizationId")));
- }
- if (queryParams.containsKey("applicationId")) {
- conditions.add(String.format("pa.licenseType.application.id = %s", queryParams.getFirst("applicationId")));
- }
- if (queryParams.containsKey("licenseTypeId")) {
- conditions.add(String.format("pa.licenseType.id = %s", queryParams.getFirst("licenseTypeId")));
- }
- String connector = addWhere ? " where " : " and ";
- return (conditions.isEmpty() ? "" : connector) + String.join(" and ", conditions);
- }
-
- private TypedQuery<Pack> createQuery(MultivaluedMap<String, String> queryParams, BasicSecurityContext bsc) {
- TypedQuery<Pack> q;
- String hql = "SELECT pa FROM Pack pa";
- if (bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {
- hql += generateWhereFromParams(true, queryParams);
- q = em.createQuery(hql, Pack.class);
- } else {
- if (bsc.getApplicationsIds() == null || bsc.getApplicationsIds().isEmpty()) {
- return null;
- }
- if (bsc.getOrganizationsIds() == null || bsc.getOrganizationsIds().isEmpty()) {
- hql += " where pa.licenseType.application.id in :list_ids_app ";
- } else {
- hql += " where pa.organization.id in :list_ids_org and pa.licenseType.application.id in :list_ids_app ";
- }
- hql += generateWhereFromParams(false, queryParams);
- q = em.createQuery(hql, Pack.class);
- if (hql.contains("list_ids_org")) {
- q.setParameter("list_ids_org", bsc.getOrganizationsIds());
- }
- q.setParameter("list_ids_app", bsc.getApplicationsIds());
- LOG.info("Getting packs from orgs: {} and apps: {}", bsc.getOrganizationsIds(), bsc.getApplicationsIds());
- }
-
- return q;
- }
-
- private Response generateErrorUnathorizedAccess(Pack pack, Principal user) {
- LOG.error("Pack with id {} not accesible by user {}", pack, user);
- return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack").build();
- }
-
/**
- *
- * @return the server version in format majorVersion.minorVersion
+ * get
+ * <p>
+ * Fetch a pack by id. If the caller is an ADVANCE user, validates
+ * the organization scope.
+ *
+ * @param packId pack id.
+ * @param bsc security context.
+ * @return 200 OK with entity, or 404/401 accordingly.
*/
@GET
@Path("/{packId}")
@@ -162,7 +124,6 @@
return Response.status(Status.NOT_FOUND).build();
}
- // EntityManager em = emProvider.get();
em.clear();
Pack pack = em.find(Pack.class, packId);
if (pack == null) {
@@ -175,6 +136,18 @@
return Response.ok(pack).build();
}
+ /**
+ * create
+ * <p>
+ * Create a new pack. Validates code uniqueness, sets organization and
+ * license type references, stamps creator and timestamps, and persists
+ * metadata entries.
+ *
+ * @param pack payload.
+ * @param bsc security context (for createdBy).
+ * @return 200 OK with created pack or 404 when references not found.
+ * @throws SeCurisServiceException on duplicated code.
+ */
@POST
@Path("/")
@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@@ -184,7 +157,6 @@
@EnsureTransaction
public Response create(Pack pack, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
LOG.info("Creating new pack");
- // EntityManager em = emProvider.get();
if (checkIfCodeExists(pack.getCode(), em)) {
throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The pack code is already used in an existing pack");
@@ -221,67 +193,16 @@
}
/**
- * 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
+ * modify
+ * <p>
+ * Update a pack basic fields and reconcile metadata (remove/merge/persist).
+ * If metadata keys changed, marks dependent licenses metadata as obsolete via
+ * {@link MetadataHelper#markObsoleteMetadata}.
+ *
+ * @param pack payload values.
+ * @param packId target id.
+ * @return 200 OK with updated pack or 404 on ref errors.
*/
- private boolean checkIfCodeExists(String code, EntityManager em) {
- TypedQuery<Pack> query = em.createNamedQuery("pack-by-code", Pack.class);
- query.setParameter("code", code);
- int packs = query.getResultList().size();
- return packs > 0;
- }
-
- /**
- *
- * @return The next available code suffix in pack for license code
- * @throws SeCurisServiceException
- */
- @GET
- @Path("/{packId}/next_license_code")
- @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
- @Produces({ MediaType.TEXT_PLAIN })
- public Response getCodeSuffix(@PathParam("packId") Integer packId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
- // EntityManager em = emProvider.get();
-
- if (packId == null) {
- throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The pack code is mandatory");
- }
- Integer codeSuffix = licenseHelper.getNextCodeSuffix(packId, em);
- Pack pack = em.find(Pack.class, packId);
- ;
-
- String licCode = LicUtils.getLicenseCode(pack.getCode(), codeSuffix);
- return Response.ok(licCode).build();
- }
-
- private void setPackLicenseType(Pack pack, Integer licTypeId, EntityManager em) throws SeCurisException {
- LicenseType lt = null;
- if (licTypeId != null) {
- lt = em.find(LicenseType.class, pack.getLicTypeId());
- if (lt == null) {
- LOG.error("Pack license type with id {} not found in DB", licTypeId);
- throw new SeCurisException("Pack license type not found with ID: " + licTypeId);
- }
- }
- pack.setLicenseType(lt);
- }
-
- private Set<String> getMdKeys(Set<PackMetadata> mds) {
- Set<String> ids = new HashSet<String>();
- if (mds != null) {
- for (PackMetadata md : mds) {
- ids.add(md.getKey());
- }
- }
- return ids;
- }
-
@PUT
@POST
@Path("/{packId}")
@@ -292,7 +213,6 @@
@Produces({ MediaType.APPLICATION_JSON })
public Response modify(Pack pack, @PathParam("packId") Integer packId) {
LOG.info("Modifying pack with id: {}", packId);
- // EntityManager em = emProvider.get();
Pack currentPack = em.find(Pack.class, packId);
try {
@@ -348,6 +268,15 @@
return Response.ok(currentPack).build();
}
+ /**
+ * activate
+ * <p>
+ * Move a pack to ACTIVE (only from allowed states).
+ *
+ * @param packId target pack id.
+ * @return 200 OK with updated pack or error when invalid transition.
+ * @throws SeCurisServiceException when invalid state transition.
+ */
@POST
@Path("/{packId}/activate")
@EnsureTransaction
@@ -357,7 +286,6 @@
@Produces({ MediaType.APPLICATION_JSON })
public Response activate(@PathParam("packId") Integer packId) throws SeCurisServiceException {
LOG.info("Activating pack with id: {}", packId);
- // EntityManager em = emProvider.get();
Pack currentPack = em.find(Pack.class, packId);
@@ -372,8 +300,17 @@
return Response.ok(currentPack).build();
}
+ /**
+ * onhold
+ * <p>
+ * Put a pack ON_HOLD from allowed states.
+ *
+ * @param packId id.
+ * @return 200 OK with updated pack or error on invalid state.
+ * @throws SeCurisServiceException on invalid state.
+ */
@POST
- @Path("/{packId}/putonhold")
+ @Path("/{packId}/putonhold}")
@EnsureTransaction
@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@RolesAllowed(BasicSecurityContext.ROL_ADMIN)
@@ -381,7 +318,6 @@
@Produces({ MediaType.APPLICATION_JSON })
public Response onhold(@PathParam("packId") Integer packId) throws SeCurisServiceException {
LOG.info("Putting On hold pack with id: {}", packId);
- // EntityManager em = emProvider.get();
Pack currentPack = em.find(Pack.class, packId);
@@ -396,6 +332,18 @@
return Response.ok(currentPack).build();
}
+ /**
+ * cancel
+ * <p>
+ * Cancel a pack. Cascades cancel to ACTIVE/PRE_ACTIVE licenses in the pack
+ * via {@link LicenseHelper#cancelLicense}.
+ *
+ * @param packId id.
+ * @param reason cancellation reason.
+ * @param bsc actor for history entries.
+ * @return 200 OK with updated pack.
+ * @throws SeCurisServiceException on invalid state.
+ */
@POST
@Path("/{packId}/cancel")
@EnsureTransaction
@@ -405,7 +353,6 @@
@Produces({ MediaType.APPLICATION_JSON })
public Response cancel(@PathParam("packId") Integer packId, @FormParam("reason") String reason, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
LOG.info("Cancelling pack with id: {}", packId);
- // EntityManager em = emProvider.get();
Pack currentPack = em.find(Pack.class, packId);
@@ -426,18 +373,41 @@
return Response.ok(currentPack).build();
}
- private void setPackOrganization(Pack currentPack, Integer orgId, EntityManager em) throws SeCurisException {
- Organization org = null;
- if (orgId != null) {
- org = em.find(Organization.class, orgId);
- if (org == null) {
- LOG.error("Organization pack with id {} not found in DB", orgId);
- throw new SeCurisException("Pack organization not found with ID: " + orgId);
- }
+ /**
+ * getCodeSuffix
+ * <p>
+ * Compute the next available license code for a pack, by asking the helper
+ * for the next numeric suffix and composing with {@link LicUtils}.
+ *
+ * @param packId id.
+ * @param bsc (unused) security context.
+ * @return 200 OK with the full code text.
+ * @throws SeCurisServiceException if packId missing.
+ */
+ @GET
+ @Path("/{packId}/next_license_code")
+ @Securable(roles = Rol.ADMIN | Rol.ADVANCE)
+ @Produces({ MediaType.TEXT_PLAIN })
+ public Response getCodeSuffix(@PathParam("packId") Integer packId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+ if (packId == null) {
+ throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The pack code is mandatory");
}
- currentPack.setOrganization(org);
+ Integer codeSuffix = licenseHelper.getNextCodeSuffix(packId, em);
+ Pack pack = em.find(Pack.class, packId);
+ String licCode = LicUtils.getLicenseCode(pack.getCode(), codeSuffix);
+ return Response.ok(licCode).build();
}
+ /**
+ * delete
+ * <p>
+ * Delete a pack after ensuring there are no ACTIVE/PRE_ACTIVE licenses.
+ * Removes remaining licenses then the pack itself.
+ *
+ * @param packId String id.
+ * @return 200 OK with success map, 404 if missing, or 409 if active license exists.
+ * @throws SeCurisServiceException on constraint errors.
+ */
@DELETE
@Path("/{packId}")
@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@@ -446,13 +416,11 @@
@Produces({ MediaType.APPLICATION_JSON })
public Response delete(@PathParam("packId") String packId) throws SeCurisServiceException {
LOG.info("Deleting pack with id: {}", packId);
- // EntityManager em = emProvider.get();
Pack pack = em.find(Pack.class, Integer.parseInt(packId));
if (pack == null) {
LOG.error("Pack with id {} can not be deleted, It was not found in DB", packId);
return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Pack was not found, ID: " + packId).build();
}
- // Pack metadata is removed in cascade automatically.
Set<License> licenses = pack.getLicenses();
for (License license : licenses) {
@@ -466,4 +434,149 @@
return Response.ok(Utils.createMap("success", true, "id", packId)).build();
}
+ // ---------------------------------------------------------------------
+ // Helpers
+ // ---------------------------------------------------------------------
+
+ /**
+ * generateWhereFromParams<p>
+ * Generate where clause to include to a query
+ *
+ * @param addWhere
+ * @param queryParams
+ * @return whereClause
+ */
+ private String generateWhereFromParams(boolean addWhere, MultivaluedMap<String, String> queryParams) {
+ List<String> conditions = new ArrayList<>();
+ if (queryParams.containsKey("organizationId")) {
+ conditions.add(String.format("pa.organization.id = %s", queryParams.getFirst("organizationId")));
+ }
+ if (queryParams.containsKey("applicationId")) {
+ conditions.add(String.format("pa.licenseType.application.id = %s", queryParams.getFirst("applicationId")));
+ }
+ if (queryParams.containsKey("licenseTypeId")) {
+ conditions.add(String.format("pa.licenseType.id = %s", queryParams.getFirst("licenseTypeId")));
+ }
+ String connector = addWhere ? " where " : " and ";
+ return (conditions.isEmpty() ? "" : connector) + String.join(" and ", conditions);
+ }
+
+ /**
+ * createQuery<p>
+ * Build a typed query considering role-based scopes and filters.
+ *
+ * @param queryParams
+ * @param basicSecurityContext
+ */
+ private TypedQuery<Pack> createQuery(MultivaluedMap<String, String> queryParams, BasicSecurityContext bsc) {
+ TypedQuery<Pack> q;
+ String hql = "SELECT pa FROM Pack pa";
+ if (bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {
+ hql += generateWhereFromParams(true, queryParams);
+ q = em.createQuery(hql, Pack.class);
+ } else {
+ if (bsc.getApplicationsIds() == null || bsc.getApplicationsIds().isEmpty()) {
+ return null;
+ }
+ if (bsc.getOrganizationsIds() == null || bsc.getOrganizationsIds().isEmpty()) {
+ hql += " where pa.licenseType.application.id in :list_ids_app ";
+ } else {
+ hql += " where pa.organization.id in :list_ids_org and pa.licenseType.application.id in :list_ids_app ";
+ }
+ hql += generateWhereFromParams(false, queryParams);
+ q = em.createQuery(hql, Pack.class);
+ if (hql.contains("list_ids_org")) {
+ q.setParameter("list_ids_org", bsc.getOrganizationsIds());
+ }
+ q.setParameter("list_ids_app", bsc.getApplicationsIds());
+ LOG.info("Getting packs from orgs: {} and apps: {}", bsc.getOrganizationsIds(), bsc.getApplicationsIds());
+ }
+ return q;
+ }
+
+ /**
+ * generateErrorUnathorizedAccess<p>
+ * Convenience 401 generator with log.
+ *
+ * @param pack
+ * @param user
+ */
+ private Response generateErrorUnathorizedAccess(Pack pack, Principal user) {
+ LOG.error("Pack with id {} not accesible by user {}", pack, user);
+ return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack").build();
+ }
+
+ /**
+ * setPackLicenseType<p>
+ * Set the pack type
+ *
+ * @param pack
+ * @param licTypeId
+ * @param em
+ * @throws SeCurisException
+ */
+ private void setPackLicenseType(Pack pack, Integer licTypeId, EntityManager em) throws SeCurisException {
+ LicenseType lt = null;
+ if (licTypeId != null) {
+ lt = em.find(LicenseType.class, pack.getLicTypeId());
+ if (lt == null) {
+ LOG.error("Pack license type with id {} not found in DB", licTypeId);
+ throw new SeCurisException("Pack license type not found with ID: " + licTypeId);
+ }
+ }
+ pack.setLicenseType(lt);
+ }
+
+ /**
+ * getMdKeys<p>
+ * Get the MD keys
+ *
+ * @param mds
+ * @return mdKeys
+ */
+ private Set<String> getMdKeys(Set<PackMetadata> mds) {
+ Set<String> ids = new HashSet<String>();
+ if (mds != null) {
+ for (PackMetadata md : mds) {
+ ids.add(md.getKey());
+ }
+ }
+ return ids;
+ }
+
+ /**
+ * checkIfCodeExists<p>
+ * Check if the code already exist
+ *
+ * @param code
+ * @param em
+ * @return codeExist
+ */
+ private boolean checkIfCodeExists(String code, EntityManager em) {
+ TypedQuery<Pack> query = em.createNamedQuery("pack-by-code", Pack.class);
+ query.setParameter("code", code);
+ int packs = query.getResultList().size();
+ return packs > 0;
+ }
+
+ /**
+ * setPackOrganization<p>
+ * Set the organization of the pack
+ *
+ * @param currentPack
+ * @param orgId
+ * @param em
+ * @throws SeCurisException
+ */
+ private void setPackOrganization(Pack currentPack, Integer orgId, EntityManager em) throws SeCurisException {
+ Organization org = null;
+ if (orgId != null) {
+ org = em.find(Organization.class, orgId);
+ if (org == null) {
+ LOG.error("Organization pack with id {} not found in DB", orgId);
+ throw new SeCurisException("Pack organization not found with ID: " + orgId);
+ }
+ }
+ currentPack.setOrganization(org);
+ }
}
--
Gitblit v1.3.2