package net.curisit.securis.services; import java.security.Principal; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.security.RolesAllowed; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import net.curisit.integrity.commons.Utils; import net.curisit.securis.DefaultExceptionHandler; import net.curisit.securis.SeCurisException; import net.curisit.securis.db.License; import net.curisit.securis.db.LicenseStatus; import net.curisit.securis.db.LicenseType; import net.curisit.securis.db.Organization; import net.curisit.securis.db.Pack; import net.curisit.securis.db.PackMetadata; import net.curisit.securis.db.PackStatus; import net.curisit.securis.db.User; import net.curisit.securis.ioc.EnsureTransaction; import net.curisit.securis.security.BasicSecurityContext; import net.curisit.securis.security.Securable; import net.curisit.securis.services.exception.SeCurisServiceException; import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes; import net.curisit.securis.services.helpers.LicenseHelper; import net.curisit.securis.utils.LicUtils; import net.curisit.securis.utils.TokenHelper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * Pack resource, this service will provide methods to create, modify and delete * packs * * @author roberto */ @Path("/pack") public class PackResource { private static final Logger LOG = LogManager.getLogger(PackResource.class); @Inject TokenHelper tokenHelper; @Context EntityManager em; @Inject private LicenseHelper licenseHelper; /** * * @return the server version in format majorVersion.minorVersion */ @GET @Path("/") @Securable @Produces({ MediaType.APPLICATION_JSON }) public Response index(@Context BasicSecurityContext bsc) { LOG.info("Getting packs list "); // EntityManager em = emProvider.get(); em.clear(); TypedQuery q; if (bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) { LOG.info("Getting all packs for user: " + bsc.getUserPrincipal()); q = em.createNamedQuery("list-packs", Pack.class); } else { q = em.createNamedQuery("list-packs-by-orgs", Pack.class); if (bsc.getOrganizationsIds() == null) { Response.ok().build(); } q.setParameter("list_ids", bsc.getOrganizationsIds()); } List list = q.getResultList(); return Response.ok(list).build(); } 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 @Path("/{packId}") @Securable @Produces({ MediaType.APPLICATION_JSON }) public Response get(@PathParam("packId") Integer packId, @Context BasicSecurityContext bsc) { LOG.info("Getting pack data for id: {}: ", packId); if (packId == null || "".equals(packId)) { LOG.error("Pack ID is mandatory"); return Response.status(Status.NOT_FOUND).build(); } // EntityManager em = emProvider.get(); em.clear(); Pack pack = em.find(Pack.class, packId); if (pack == null) { LOG.error("Pack with id {} not found in DB", packId); return Response.status(Status.NOT_FOUND).build(); } if (bsc.isUserInRole(BasicSecurityContext.ROL_ADVANCE) && (bsc.getOrganizationsIds() == null || !bsc.getOrganizationsIds().contains(pack.getOrgId()))) { return generateErrorUnathorizedAccess(pack, bsc.getUserPrincipal()); } return Response.ok(pack).build(); } @POST @Path("/") @Securable @RolesAllowed(BasicSecurityContext.ROL_ADMIN) @Consumes(MediaType.APPLICATION_JSON) @Produces({ MediaType.APPLICATION_JSON }) @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"); } try { setPackOrganization(pack, pack.getOrgId(), em); } catch (SeCurisException e) { return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build(); } try { setPackLicenseType(pack, pack.getLicTypeId(), em); } catch (SeCurisException e) { return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build(); } User user = em.find(User.class, bsc.getUserPrincipal().getName()); pack.setStatus(PackStatus.CREATED); pack.setCreatedBy(user); pack.setCreationTimestamp(new Date()); em.persist(pack); Set newMD = pack.getMetadata(); if (newMD != null) { for (PackMetadata md : newMD) { md.setPack(pack); em.persist(md); } } pack.setMetadata(newMD); return Response.ok(pack).build(); } /** * Check if there is some pack with the same code * * @param code * Pack code * @param em * DB session object * @return true if code is already used, false * otherwise */ private boolean checkIfCodeExists(String code, EntityManager em) { TypedQuery 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 @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 getMdKeys(Set mds) { Set ids = new HashSet(); if (mds != null) { for (PackMetadata md : mds) { ids.add(md.getKey()); } } return ids; } @PUT @POST @Path("/{packId}") @EnsureTransaction @Securable @RolesAllowed(BasicSecurityContext.ROL_ADMIN) @Consumes(MediaType.APPLICATION_JSON) @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 { setPackOrganization(currentPack, pack.getOrgId(), em); } catch (SeCurisException e) { return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build(); } try { setPackLicenseType(currentPack, pack.getLicTypeId(), em); } catch (SeCurisException e) { return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, e.getMessage()).build(); } currentPack.setLicensePreactivation(pack.isLicensePreactivation()); currentPack.setCode(pack.getCode()); currentPack.setComments(pack.getComments()); currentPack.setNumLicenses(pack.getNumLicenses()); currentPack.setPreactivationValidPeriod(pack.getPreactivationValidPeriod()); currentPack.setRenewValidPeriod(pack.getRenewValidPeriod()); Set newMD = pack.getMetadata(); Set newMdKeys = getMdKeys(newMD); for (PackMetadata currentMd : currentPack.getMetadata()) { if (!newMdKeys.contains(currentMd.getKey())) { em.remove(currentMd); } } if (newMD != null) { Set oldMD = currentPack.getMetadata(); Set oldMdKeys = getMdKeys(newMD); for (PackMetadata md : newMD) { if (oldMdKeys.contains(md.getKey())) { em.merge(md); } else { md.setPack(currentPack); em.persist(md); } } } currentPack.setMetadata(newMD); em.merge(currentPack); return Response.ok(currentPack).build(); } @POST @Path("/{packId}/activate") @EnsureTransaction @Securable @RolesAllowed(BasicSecurityContext.ROL_ADMIN) @Consumes(MediaType.APPLICATION_JSON) @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); if (!Pack.Status.isActionValid(Pack.Action.ACTIVATION, currentPack.getStatus())) { LOG.error("Pack with id {} cannot be activaed from status {}", packId, currentPack.getStatus().name()); throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "Pack cannot be activated in status: " + currentPack.getStatus().name()); } currentPack.setStatus(PackStatus.ACTIVE); em.persist(currentPack); return Response.ok(currentPack).build(); } @POST @Path("/{packId}/putonhold") @EnsureTransaction @Securable @RolesAllowed(BasicSecurityContext.ROL_ADMIN) @Consumes(MediaType.APPLICATION_JSON) @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); if (!Pack.Status.isActionValid(Pack.Action.PUT_ONHOLD, currentPack.getStatus())) { LOG.error("Pack with id {} cannot be put on hold from status {}", packId, currentPack.getStatus().name()); throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "Pack cannot be put on hold in status: " + currentPack.getStatus().name()); } currentPack.setStatus(PackStatus.ON_HOLD); em.persist(currentPack); return Response.ok(currentPack).build(); } @POST @Path("/{packId}/cancel") @EnsureTransaction @Securable @RolesAllowed(BasicSecurityContext.ROL_ADMIN) @Consumes(MediaType.APPLICATION_JSON) @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); if (!Pack.Status.isActionValid(Pack.Action.CANCEL, currentPack.getStatus())) { LOG.error("Pack with id {} cannot cancelled from status {}", packId, currentPack.getStatus().name()); throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "Pack cannot be cancelled in status: " + currentPack.getStatus().name()); } Set licenses = currentPack.getLicenses(); for (License license : licenses) { if (license.getStatus() == LicenseStatus.ACTIVE || license.getStatus() == LicenseStatus.PRE_ACTIVE) { licenseHelper.cancelLicense(license, "Pack cancellation. " + reason, bsc, em); } } currentPack.setStatus(PackStatus.CANCELLED); em.persist(currentPack); 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); } } currentPack.setOrganization(org); } @DELETE @Path("/{packId}") @Securable @RolesAllowed(BasicSecurityContext.ROL_ADMIN) @EnsureTransaction @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 licenses = pack.getLicenses(); for (License license : licenses) { if (license.getStatus() == LicenseStatus.ACTIVE || license.getStatus() == LicenseStatus.PRE_ACTIVE) { throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "An active license cannot be deleted. License code: " + license.getCode()); } em.remove(license); } em.remove(pack); return Response.ok(Utils.createMap("success", true, "id", packId)).build(); } }