From ddec2c5c7b7842536d6d705c2de20f96e16c8aa8 Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Wed, 22 Oct 2014 17:38:57 +0000
Subject: [PATCH] #2021 feature - Added blocked request table and refactoring License actions

---
 securis/src/main/java/net/curisit/securis/services/LicenseResource.java |  227 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 201 insertions(+), 26 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 4032651..2930626 100644
--- a/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
@@ -1,11 +1,15 @@
 package net.curisit.securis.services;
 
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.text.MessageFormat;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.TreeMap;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Provider;
@@ -13,6 +17,8 @@
 import javax.persistence.TypedQuery;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
@@ -27,16 +33,26 @@
 
 import net.curisit.integrity.commons.JsonUtils;
 import net.curisit.integrity.commons.Utils;
+import net.curisit.integrity.exception.CurisException;
 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.License;
 import net.curisit.securis.db.LicenseHistory;
+import net.curisit.securis.db.LicenseStatus;
 import net.curisit.securis.db.Pack;
+import net.curisit.securis.db.PackMetadata;
 import net.curisit.securis.db.User;
 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.utils.EmailManager;
+import net.curisit.securis.utils.Params;
 import net.curisit.securis.utils.TokenHelper;
 
 import org.apache.commons.io.IOUtils;
@@ -60,7 +76,13 @@
     TokenHelper tokenHelper;
 
     @Inject
+    EmailManager emailManager;
+
+    @Inject
     Provider<EntityManager> emProvider;
+
+    @Inject
+    LicenseGenerator licenseGenerator;
 
     /**
      * 
@@ -138,11 +160,16 @@
             LOG.error("License with id {} is not active, so It can not downloaded", licId, bsc.getUserPrincipal());
             throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "License is not active, so It can not be downloaded");
         }
-        return Response.ok(lic.getLicenseData()).build();
+        em.persist(createLicenseHistoryAction(lic, 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 the given license
+     * 
      * @param licId
      * @param bsc
      * @return
@@ -168,8 +195,9 @@
                     + " can not be activated from the current license status");
         }
 
-        lic.setStatus(License.Status.ACTIVE);
+        lic.setStatus(LicenseStatus.ACTIVE);
         lic.setModificationTimestamp(new Date());
+
         em.persist(lic);
         User user = getUser(bsc.getUserPrincipal().getName(), em);
         em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.ACTIVATE));
@@ -178,6 +206,7 @@
 
     /**
      * Send license file by email to the organization
+     * 
      * @param licId
      * @param bsc
      * @return
@@ -192,13 +221,34 @@
     @Produces({
         MediaType.APPLICATION_JSON
     })
-    public Response send(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+    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;
+        if (lic.getLicenseData() == null) {
+            throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "There is no license file available");
+        }
 
         User user = getUser(bsc.getUserPrincipal().getName(), em);
-        // TODO: Send mail with lic file
+        try {
+            String subject = MessageFormat.format(Params.get(Params.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 = 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();
+            }
+        }
+
         lic.setModificationTimestamp(new Date());
         em.persist(lic);
         em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.SEND, "Email sent to: " + lic.getEmail()));
@@ -207,6 +257,7 @@
 
     /**
      * Cancel given license
+     * 
      * @param licId
      * @param bsc
      * @return
@@ -221,7 +272,8 @@
     @Produces({
         MediaType.APPLICATION_JSON
     })
-    public Response cancel(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
+    public Response cancel(@PathParam("licId") Integer licId, @FormParam("reason") String reason, @Context BasicSecurityContext bsc)
+            throws SeCurisServiceException {
 
         EntityManager em = emProvider.get();
         License lic = getCurrentLicense(licId, bsc, em);
@@ -232,12 +284,17 @@
                     + " can not be canceled from the current license status");
         }
 
-        lic.setStatus(License.Status.CANCELED);
+        if (reason == null && (lic.getStatus() == LicenseStatus.ACTIVE || lic.getStatus() == LicenseStatus.PRE_ACTIVE)) {
+            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");
+        }
+
+        lic.setStatus(LicenseStatus.CANCELLED);
         lic.setModificationTimestamp(new Date());
         em.persist(lic);
 
-        User user = getUser(bsc.getUserPrincipal().getName(), em);
-        em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.CANCEL));
+        em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.CANCEL, "Cancelation reason: " + reason));
         return Response.ok(lic).build();
     }
 
@@ -250,39 +307,102 @@
     })
     @Transactional
     public Response create(License lic, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
-        LOG.info("Creating new license from create()");
         EntityManager em = emProvider.get();
         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());
-                return Response.status(Status.NOT_FOUND)
-                        .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's pack not found with ID: " + lic.getPackId()).build();
-            } 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());
-                    return Response.status(Status.UNAUTHORIZED)
-                            .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized action on pack license").build();
-                }
+        }
+
+        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");
             }
         }
 
         User createdBy = getUser(bsc.getUserPrincipal().getName(), em);
 
-        // TODO: Manage status if request data is set
+        if (lic.getRequestData() != null) {
+            SignedLicenseBean signedLicense = generateLicense(lic, em);
+            // If user provide a request data the license status is passed
+            // directly to ACTIVE
+            lic.setStatus(LicenseStatus.ACTIVE);
+            try {
+                lic.setLicenseData(JsonUtils.toJSON(signedLicense));
+            } catch (CurisException e) {
+                LOG.error("Error generaing license JSON", e);
+                throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
+            }
+        } else {
+            lic.setStatus(LicenseStatus.CREATED);
+        }
         lic.setCreatedBy(createdBy);
-        lic.setStatus(License.Status.CREATED);
         lic.setCreationTimestamp(new Date());
         lic.setModificationTimestamp(lic.getCreationTimestamp());
         em.persist(lic);
         em.persist(createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.CREATE));
+        if (lic.getStatus() == LicenseStatus.ACTIVE) {
+            em.persist(createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.CREATE, "Activated on creation"));
+        }
 
         return Response.ok(lic).build();
     }
-    
 
-    
+    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());
+        try {
+            LicenseBean lb = licenseGenerator.generateLicense(rb, extractPackMetadata(pack.getMetadata()), license.getExpirationDate(),
+                    pack.getLicenseTypeCode(), license.getCode());
+            sl = new SignedLicenseBean(lb);
+        } catch (SeCurisException e) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Error generating license: " + e.toString());
+        }
+        return sl;
+    }
+
+    private Map<String, Object> extractPackMetadata(Set<PackMetadata> packMetadata) {
+        Map<String, Object> metadata = new HashMap<>();
+        for (PackMetadata md : packMetadata) {
+            metadata.put(md.getKey(), md.getValue());
+        }
+
+        return metadata;
+    }
+
+    /**
+     * We check if the given request data is valid for the current Pack and has
+     * a valid format
+     * 
+     * @param pack
+     * @param requestData
+     * @throws SeCurisServiceException
+     */
+    private RequestBean validateRequestData(Pack pack, String requestData) throws SeCurisServiceException {
+        RequestBean rb = null;
+        try {
+            rb = JsonUtils.json2object(requestData, RequestBean.class);
+        } catch (CurisException e) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data has not a valid format");
+        }
+
+        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");
+        }
+        // TODO: [rsanchez] Verify that if pack code is null we ignore it
+        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;
+    }
+
     @PUT
     @POST
     @Path("/{licId}")
@@ -302,9 +422,22 @@
         currentLicense.setCode(lic.getCode());
         currentLicense.setFullName(lic.getFullName());
         currentLicense.setEmail(lic.getEmail());
-        currentLicense.setRequestData(lic.getRequestData());
+        if (lic.getRequestData() != null) {
+            SignedLicenseBean signedLicense = generateLicense(lic, em);
+            // If user provide a request data the license status is passed
+            // directly to ACTIVE
+            lic.setStatus(LicenseStatus.ACTIVE);
+            try {
+                lic.setLicenseData(JsonUtils.toJSON(signedLicense));
+            } catch (CurisException e) {
+                LOG.error("Error generaing license JSON", e);
+                throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
+            }
+            currentLicense.setRequestData(lic.getRequestData());
+        }
         currentLicense.setModificationTimestamp(new Date());
         em.persist(currentLicense);
+        em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.MODIFY));
 
         return Response.ok(currentLicense).build();
     }
@@ -321,7 +454,29 @@
         EntityManager em = emProvider.get();
         License lic = getCurrentLicense(licId, bsc, em);
 
-        if (lic.getStatus() != License.Status.CANCELED || lic.getStatus() != License.Status.CREATED) {
+        if (lic.getStatus() != LicenseStatus.CANCELLED || lic.getStatus() != LicenseStatus.CREATED) {
+            LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
+            return Response.status(Status.FORBIDDEN)
+                    .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License can not be deleted in current status").build();
+        }
+
+        em.remove(lic);
+        return Response.ok(Utils.createMap("success", true, "id", licId)).build();
+    }
+
+    @DELETE
+    @Path("/{licId}")
+    @Transactional
+    @Securable
+    @Produces({
+        MediaType.APPLICATION_JSON
+    })
+    public Response block(@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 (lic.getStatus() != LicenseStatus.CANCELLED || lic.getStatus() != LicenseStatus.CREATED) {
             LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
             return Response.status(Status.FORBIDDEN)
                     .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License can not be deleted in current status").build();
@@ -349,6 +504,11 @@
         return lic;
     }
 
+    private User getUser(BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
+        String username = bsc.getUserPrincipal().getName();
+        return getUser(username, em);
+    }
+
     private User getUser(String username, EntityManager em) throws SeCurisServiceException {
         User user = null;
         if (username != null) {
@@ -358,6 +518,13 @@
             }
         }
         return user;
+    }
+
+    private File createTemporaryLicenseFile(License lic, String licFileName) throws IOException {
+        File f = Files.createTempDirectory("securis-server").toFile();
+        f = new File(f, licFileName);
+        IOUtils.write(lic.getLicenseData(), new FileWriter(f));
+        return f;
     }
 
     private LicenseHistory createLicenseHistoryAction(License lic, User user, String action, String comments) {
@@ -373,4 +540,12 @@
     private LicenseHistory createLicenseHistoryAction(License lic, User user, String action) {
         return createLicenseHistoryAction(lic, user, action, null);
     }
+
+    public static void main(String[] args) throws IOException {
+        File f = Files.createTempDirectory("securis-server").toFile();
+
+        LOG.info("f: {}", f);
+        f = new File(f, "config-server.lic");
+        LOG.info("f: {}", f);
+    }
 }

--
Gitblit v1.3.2