/* * Copyright @ 2013 CurisTEC, S.A.S. All Rights Reserved. */ package net.curisit.securis.services; import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.Date; import java.util.List; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import net.curisit.integrity.commons.Utils; import net.curisit.securis.DefaultExceptionHandler; import net.curisit.securis.LicenseGenerator; import net.curisit.securis.SeCurisException; import net.curisit.securis.beans.LicenseBean; import net.curisit.securis.beans.RequestBean; import net.curisit.securis.beans.SignedLicenseBean; import net.curisit.securis.db.Application; import net.curisit.securis.db.BlockedRequest; import net.curisit.securis.db.License; import net.curisit.securis.db.LicenseHistory; import net.curisit.securis.db.LicenseStatus; import net.curisit.securis.db.Pack; import net.curisit.securis.db.PackStatus; import net.curisit.securis.db.User; import net.curisit.securis.db.User.Rol; 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.services.helpers.UserHelper; import net.curisit.securis.utils.Config; import net.curisit.securis.utils.EmailManager; import net.curisit.securis.utils.JsonUtils; import net.curisit.securis.utils.LicUtils; /** * LicenseResource *
* 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. *
* Key rules: *
* 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("/")
@Securable
@Produces({ MediaType.APPLICATION_JSON })
public Response index(@QueryParam("packId") Integer packId, @Context BasicSecurityContext bsc) {
LOG.info("Getting licenses list ");
em.clear();
if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {
Pack pack = em.find(Pack.class, packId);
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();
}
}
TypedQuery
* 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}")
@Securable
@Produces({ MediaType.APPLICATION_JSON })
public Response get(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
LOG.info("Getting organization data for id: {}: ", licId);
em.clear();
License lic = getCurrentLicense(licId, bsc, em);
return Response.ok(lic).build();
}
/**
* download
*
* 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")
@Securable
@Produces({ MediaType.APPLICATION_OCTET_STREAM })
@EnsureTransaction
public Response download(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
License lic = getCurrentLicense(licId, bsc, em);
if (lic.getLicenseData() == null) {
LOG.error("License with id {} has not license file generated", licId, bsc.getUserPrincipal());
throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "License has not contain data to generate license file");
}
if (!License.Status.isActionValid(License.Action.DOWNLOAD, lic.getStatus())) {
LOG.error("License with id {} is not active, so It can not downloaded", licId, bsc.getUserPrincipal());
throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License is not active, so It can not be downloaded");
}
em.persist(licenseHelper.createLicenseHistoryAction(lic, userHelper.getUser(bsc, em), LicenseHistory.Actions.DOWNLOAD));
return Response.ok(lic.getLicenseData())
.header("Content-Disposition", String.format("attachment; filename=\"%s\"", lic.getPack().getLicenseType().getApplication().getLicenseFilename())).build();
}
/**
* activate
*
* 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
@Path("/{licId}/activate")
@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@EnsureTransaction
@Consumes(MediaType.APPLICATION_JSON)
@Produces({ MediaType.APPLICATION_JSON })
public Response activate(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
License lic = getCurrentLicense(licId, bsc, em);
if (!License.Status.isActionValid(License.Action.ACTIVATION, lic.getStatus())) {
LOG.error("License with id {} can not be activated from current license status", licId);
throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "License with id " + licId + " can not be activated from the current license status");
}
if (lic.getPack().getNumAvailables() == 0) {
throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "The pack has not available licenses");
}
validateRequestData(lic.getPack(), lic.getRequestData(), lic.getActivationCode());
License existingLicense = License.findActiveLicenseByRequestData(lic.getRequestData(), em);
if (existingLicense != null && existingLicense.getStatus() == LicenseStatus.ACTIVE) {
throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "An active license already exists for the given request data");
}
lic.setStatus(LicenseStatus.ACTIVE);
lic.setModificationTimestamp(new Date());
em.persist(lic);
User user = userHelper.getUser(bsc.getUserPrincipal().getName(), em);
em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.ACTIVATE));
return Response.ok(lic).build();
}
/**
* send
*
* 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
@POST
@Path("/{licId}/send")
@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@EnsureTransaction
@Consumes(MediaType.APPLICATION_JSON)
@Produces({ MediaType.APPLICATION_JSON })
public Response send(@PathParam("licId") Integer licId, @DefaultValue("false") @FormParam("add_cc") Boolean addCC, @Context BasicSecurityContext bsc)
throws SeCurisServiceException, SeCurisException {
License lic = getCurrentLicense(licId, bsc, em);
Application app = lic.getPack().getLicenseType().getApplication();
File licFile = null;
if (lic.getLicenseData() == null) {
throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "There is no license file available");
}
if (lic.getFullName() == null) {
throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "Please add an user name in license data to send it the license file");
}
User user = userHelper.getUser(bsc.getUserPrincipal().getName(), em);
try {
String subject = MessageFormat.format(Config.get(Config.KEYS.EMAIL_LIC_DEFAULT_SUBJECT), lic.getPack().getAppName());
String email_tpl = IOUtils.toString(this.getClass().getResourceAsStream("/lic_email.template.en"));
String body = MessageFormat.format(email_tpl, lic.getFullName(), app.getName());
licFile = licenseHelper.createTemporaryLicenseFile(lic, app.getLicenseFilename());
emailManager.sendEmail(subject, body, lic.getEmail(), addCC ? user.getEmail() : null, licFile);
} catch (IOException e) {
LOG.error("Error creating temporary license file", e);
throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "There is no license file available");
} finally {
if (licFile != null) {
licFile.delete();
licFile.getParentFile().delete();
}
}
em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.SEND, "Email sent to: " + lic.getEmail()));
return Response.ok(lic).build();
}
/**
* cancel
*
* 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
@Path("/{licId}/cancel")
@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@EnsureTransaction
@Consumes(MediaType.APPLICATION_JSON)
@Produces({ MediaType.APPLICATION_JSON })
public Response cancel(@PathParam("licId") Integer licId, CancellationLicenseActionBean actionData, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
License lic = getCurrentLicense(licId, bsc, em);
if (!License.Status.isActionValid(License.Action.CANCEL, lic.getStatus())) {
LOG.error("License with id {} can not be canceled from current license status", licId);
throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "License with id " + licId + " can not be canceled from the current license status");
}
if (actionData.reason == null) {
LOG.error("To cancel an active License we need a reason, lic ID: {}, user: {}", lic.getId(), bsc.getUserPrincipal().getName());
throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "Active license with id " + licId + " can not be canceled without a reason");
}
licenseHelper.cancelLicense(lic, actionData.reason, bsc, em);
return Response.ok(lic).build();
}
/**
* create
*
* Create a license. Validates:
*
* 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
*
* 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
*
* 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
*
* 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
* Check if there is an existing license with the same code.
*
* @param code
* @param entityManager
*/
private boolean checkIfCodeExists(String code, EntityManager em) {
TypedQuery
* 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());
RequestBean rb = validateRequestData(pack, license.getRequestData(), license.getActivationCode());
try {
LicenseBean lb = licenseGenerator.generateLicense(rb, licenseHelper.extractPackMetadata(pack.getMetadata()), licenseHelper.getExpirationDateFromPack(pack, false),
license.getCode(), pack.getAppName());
sl = new SignedLicenseBean(lb);
} catch (SeCurisException e) {
throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Error generating license: " + e.toString());
}
return sl;
}
/**
* validateRequestData
* 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) {
throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA, "Request data is empty");
}
RequestBean rb = null;
try {
rb = JsonUtils.json2object(requestData, RequestBean.class);
} catch (SeCurisException e) {
throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data has not a valid format");
}
if (rb.getActivationCode() != null && activationCode != null) {
if (!rb.getActivationCode().equals(activationCode)) {
throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Activation code mismatch");
}
} else {
if (!rb.getCustomerCode().equals(pack.getOrganization().getCode())) {
throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong Organization code");
}
if (!rb.getLicenseTypeCode().equals(pack.getLicenseTypeCode())) {
throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong License type code");
}
if (rb.getPackCode() != null && !rb.getPackCode().equals(pack.getCode())) {
throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong Pack code");
}
}
return rb;
}
/**
* getCurrentLicense
* 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");
throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "Missing license ID");
}
License lic = em.find(License.class, licId);
if (lic == null) {
LOG.error("License with id {} not found in DB", licId);
throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "License not found for ID: " + licId);
}
if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN) && !bsc.getOrganizationsIds().contains(lic.getPack().getOrganization().getId())) {
LOG.error("License with id {} is not accesible by user {}", licId, bsc.getUserPrincipal());
throw new SeCurisServiceException(Status.UNAUTHORIZED.getStatusCode(), "Unathorized access to license data");
}
return lic;
}
// ---------------------------------------------------------------------
// DTOs
// ---------------------------------------------------------------------
/**
* DTO used to carry a cancellation reason for the cancel endpoint.
*/
@JsonAutoDetect
@JsonIgnoreProperties(ignoreUnknown = true)
static class CancellationLicenseActionBean {
@JsonProperty
private String reason;
}
}
*
* 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.
*/
@POST
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@Securable(roles = Rol.ADMIN | Rol.ADVANCE)
@Produces({ MediaType.APPLICATION_JSON })
@EnsureTransaction
public Response create(License lic, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
if (checkIfCodeExists(lic.getCode(), em)) {
throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The license code is already used in an existing license");
}
if (lic.getActivationCode() == null) {
throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The activation code is mandatory");
}
License existingLic = License.findLicenseByActivationCode(lic.getActivationCode(), em);
if (existingLic != null) {
throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The activation code is already used in: " + existingLic.getCode());
}
if (!LicUtils.checkValidLicenseCodeCrc(lic.getCode())) {
throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The license code is not valid");
}
if (!Utils.isValidEmail(lic.getEmail())) {
throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The user email should be a valid email");
}
Pack pack = null;
if (lic.getPackId() != null) {
pack = em.find(Pack.class, lic.getPackId());
}
if (pack == null) {
LOG.error("License pack with id {} not found in DB", lic.getPackId());
throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "License's pack not found with ID: " + lic.getPackId());
} else {
if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN) && !bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {
LOG.error("License for pack with id {} can not be created by user {}", pack.getId(), bsc.getUserPrincipal());
throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Unathorized action on pack license");
}
if (pack.getStatus() != PackStatus.ACTIVE) {
LOG.error("Current pack, {}, is not active so licenses cannot be created", pack.getId());
throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "Current pack is not active so licenses cannot be created");
}
}
User createdBy = userHelper.getUser(bsc.getUserPrincipal().getName(), em);
if (lic.getRequestData() != null) {
License existingLicense = License.findActiveLicenseByRequestData(lic.getRequestData(), em);
if (existingLicense != null) {
throw new SeCurisServiceException(ErrorCodes.DUPLICATED_REQUEST_DATA, "There is already an active license for current request data");
}
if (pack.getNumAvailables() > 0) {
SignedLicenseBean signedLicense = generateLicense(lic, em);
// Move directly to ACTIVE when request data is provided
lic.setStatus(LicenseStatus.ACTIVE);
try {
lic.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class));
if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be activated");
}
lic.setLicenseData(JsonUtils.toJSON(signedLicense));
} catch (SeCurisException e) {
LOG.error("Error generating license JSON", e);
throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generating license JSON");
}
}
} else {
lic.setStatus(LicenseStatus.CREATED);
}
lic.setCodeSuffix(LicUtils.getLicenseCodeSuffix(lic.getCode()));
lic.setCreatedBy(createdBy);
lic.setCreationTimestamp(new Date());
lic.setModificationTimestamp(lic.getCreationTimestamp());
lic.setMetadataObsolete(false);
em.persist(lic);
em.persist(licenseHelper.createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.CREATE));
if (lic.getStatus() == LicenseStatus.ACTIVE) {
em.persist(licenseHelper.createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.ACTIVATE, "Activated on creation"));
}
return Response.ok(lic).build();
}
/**
* modify
*