From c4d513ca26fe80946a5d90264de5d8e13e4ea974 Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Thu, 23 Oct 2014 17:21:24 +0000
Subject: [PATCH] #2021 feature - Added pack actions in server and in frontend

---
 securis/src/main/resources/META-INF/persistence.xml                     |    2 
 securis/src/main/java/net/curisit/securis/db/BlockedRequest.java        |    3 
 securis/src/main/resources/static/js/licenses.js                        |   71 +++++++
 securis/src/main/java/net/curisit/securis/db/Pack.java                  |   37 ++++
 securis/src/main/java/net/curisit/securis/services/PackResource.java    |   95 +++++++++
 securis/src/main/java/net/curisit/securis/db/License.java               |   64 ++++--
 securis/src/main/java/net/curisit/securis/SeCurisServer.java            |   59 +++---
 securis/pom.xml                                                         |   10 
 securis/src/main/resources/static/licenses.html                         |   20 +-
 securis/src/main/java/net/curisit/securis/db/Organization.java          |   10 
 securis/src/main/java/net/curisit/securis/db/LicenseHistory.java        |    1 
 securis/src/main/java/net/curisit/securis/services/LicenseResource.java |  115 ++++++++++--
 securis/src/main/java/net/curisit/securis/ioc/SecurisModule.java        |   16 -
 securis/src/main/java/net/curisit/securis/utils/EmailManager.java       |    2 
 securis/src/main/resources/db/schema.sql                                |    6 
 15 files changed, 379 insertions(+), 132 deletions(-)

diff --git a/securis/pom.xml b/securis/pom.xml
index 1f5b23b..d505b56 100644
--- a/securis/pom.xml
+++ b/securis/pom.xml
@@ -28,11 +28,6 @@
   	</dependency>
   	<dependency>
   		<groupId>org.jboss.resteasy</groupId>
-  		<artifactId>resteasy-jackson-provider</artifactId>
-  		<version>3.0.9.Final</version>
-  	</dependency>
-  	<dependency>
-  		<groupId>org.jboss.resteasy</groupId>
   		<artifactId>resteasy-multipart-provider</artifactId>
   		<version>3.0.9.Final</version>
   	</dependency>
@@ -76,6 +71,11 @@
   		<artifactId>httpmime</artifactId>
   		<version>4.4-beta1</version>
   	</dependency>
+  	<dependency>
+  		<groupId>org.jboss.resteasy</groupId>
+  		<artifactId>resteasy-jackson2-provider</artifactId>
+  		<version>3.0.9.Final</version>
+  	</dependency>
   </dependencies>
 	  <build>
 		<plugins>
diff --git a/securis/src/main/java/net/curisit/securis/SeCurisServer.java b/securis/src/main/java/net/curisit/securis/SeCurisServer.java
index 8b28615..23db35f 100644
--- a/securis/src/main/java/net/curisit/securis/SeCurisServer.java
+++ b/securis/src/main/java/net/curisit/securis/SeCurisServer.java
@@ -52,7 +52,7 @@
     private static final Logger CONSOLE = LogManager.getLogger("console");
 
     private static final String PID_FILE = System.getProperty("user.home") + "/.SeCuris/securis-server.pid";
-    
+
     private static Server server;
     private static Injector injector = null;
 
@@ -64,19 +64,15 @@
         CONSOLE.info("Execute SeCuris server using:");
         CONSOLE.info("    $ ./securis-server.sh {start|stop}");
     }
-    
-    private void testMail() {
-        
-    }
-    
+
     public static void main(String[] args) throws Exception {
-        String command; 
+        String command;
         if (args.length > 0) {
             command = args[0].toLowerCase();
         } else {
             command = "start";
         }
-        
+
         switch (command) {
         case "start":
             startServer();
@@ -90,7 +86,7 @@
             System.exit(-1);
         }
     }
-    
+
     private static void stopServer() {
         if (!new File(PID_FILE).exists()) {
             CONSOLE.error("SeCuris server is NOT running or PID file is missing");
@@ -105,7 +101,7 @@
             LOG.error("Error getting SeCuris server process PID from file: {}", PID_FILE);
         }
     }
-    
+
     private static void startServer() {
 
         if (new File(PID_FILE).exists()) {
@@ -116,20 +112,22 @@
             }
             System.exit(-2);
         }
-        
+
         SecurisModule securisModule = new SecurisModule();
         JpaPersistModule jpaPersistModule = new JpaPersistModule("localdb");
         Properties props = new Properties();
         props.put("javax.persistence.jdbc.password", securisModule.getPassword());
         props.put("javax.persistence.jdbc.url", securisModule.getUrl(securisModule.getAppDir()));
-        //LOG.info("BD Url: {} {}", securisModule.getUrl(securisModule.getAppDir()), securisModule.getPassword());
+        // LOG.info("BD Url: {} {}",
+        // securisModule.getUrl(securisModule.getAppDir()),
+        // securisModule.getPassword());
         jpaPersistModule.properties(props);
 
         injector = Guice.createInjector(securisModule, new RequestsModule(), jpaPersistModule);
 
         try {
             startServer(injector.getInstance(Key.get(URI.class, Names.named("base-uri"))));
-            
+
         } catch (SeCurisException e) {
             CONSOLE.error("Error launching the SeCuris server, {}", e);
         }
@@ -153,14 +151,14 @@
 
         QueuedThreadPool threadPool = new QueuedThreadPool();
         threadPool.setMaxThreads(50);
-        
+
         server = new Server();
-        
+
         ServerConnector httpConnector = new ServerConnector(server);
         httpConnector.setPort(Config.getInt(Config.KEYS.SERVER_PORT, 9080));
         httpConnector.setHost(Config.get(Config.KEYS.SERVER_HOSTNAME, "0.0.0.0"));
         server.addConnector(httpConnector);
-        
+
         ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
         context.setContextPath("/");
         context.addEventListener(injector.getInstance(GuiceResteasyBootstrapServletContextListener.class));
@@ -191,33 +189,33 @@
         contexts.setHandlers(new Handler[] {
                 staticResources, context
         });
-        
+
         HttpConfiguration http_config = new HttpConfiguration();
         http_config.setSecureScheme("https");
         http_config.setSecurePort(Config.getInt(Config.KEYS.SERVER_SSL_PORT, 9443));
         http_config.setOutputBufferSize(32768);
         http_config.setSendServerVersion(true);
         http_config.setSendDateHeader(false);
-        
-        
+
         HttpConfiguration https_config = new HttpConfiguration(http_config);
         https_config.addCustomizer(new SecureRequestCustomizer());
-        
+
         SslContextFactory sslContextFactory = new SslContextFactory();
         sslContextFactory.setKeyStorePath(Config.get(Config.KEYS.KEYSTORE_PATH));
         sslContextFactory.setKeyStoreType(Config.get(Config.KEYS.KEYSTORE_TYPE, "JKS"));
         sslContextFactory.setKeyStorePassword(Config.get(Config.KEYS.KEYSTORE_PASSWORD, ""));
-        //sslContextFactory.setCertAlias("1");
-//        sslContextFactory.setKeyManagerPassword("curist3c");
-//        sslContextFactory.setTrustStorePath("/Users/rob/.ssh/keys/keystore");
-//        sslContextFactory.setTrustStorePassword("curist3c");
+        // sslContextFactory.setCertAlias("1");
+        // sslContextFactory.setKeyManagerPassword("curist3c");
+        // sslContextFactory.setTrustStorePath("/Users/rob/.ssh/keys/keystore");
+        // sslContextFactory.setTrustStorePassword("curist3c");
         sslContextFactory.checkKeyStore();
         sslContextFactory.setNeedClientAuth(false);
-        
-        ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory,HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(https_config));
+
+        ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+                new HttpConnectionFactory(https_config));
         sslConnector.setPort(Config.getInt(Config.KEYS.SERVER_SSL_PORT, 9443));
         sslConnector.setHost(Config.get(Config.KEYS.SERVER_HOSTNAME, "0.0.0.0"));
-        server.addConnector( sslConnector );
+        server.addConnector(sslConnector);
 
         server.setHandler(context);
         server.setStopAtShutdown(true);
@@ -233,9 +231,10 @@
         }
 
     }
-    
-    static class ServerStoppedListener extends AbstractLifeCycleListener  {
-        @Override public void lifeCycleStopped(LifeCycle event) {
+
+    static class ServerStoppedListener extends AbstractLifeCycleListener {
+        @Override
+        public void lifeCycleStopped(LifeCycle event) {
             if (new File(PID_FILE).exists())
                 new File(PID_FILE).delete();
         }
diff --git a/securis/src/main/java/net/curisit/securis/db/BlockedRequest.java b/securis/src/main/java/net/curisit/securis/db/BlockedRequest.java
index 51eac7e..b0b2826 100644
--- a/securis/src/main/java/net/curisit/securis/db/BlockedRequest.java
+++ b/securis/src/main/java/net/curisit/securis/db/BlockedRequest.java
@@ -89,8 +89,7 @@
     }
 
     public static String generateHash(String reqData) {
-        String hash = reqData != null ? Utils.sha256(reqData) : null;
-        return hash;
+        return (reqData != null ? Utils.sha256(reqData) : null);
     }
 
     public static boolean isRequestBlocked(String requestData, EntityManager em) {
diff --git a/securis/src/main/java/net/curisit/securis/db/License.java b/securis/src/main/java/net/curisit/securis/db/License.java
index e8ce296..037ccf5 100644
--- a/securis/src/main/java/net/curisit/securis/db/License.java
+++ b/securis/src/main/java/net/curisit/securis/db/License.java
@@ -16,11 +16,17 @@
 import javax.persistence.ManyToOne;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
+import javax.persistence.NonUniqueResultException;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.persistence.TypedQuery;
 
 import net.curisit.integrity.commons.Utils;
+import net.curisit.securis.services.exception.SeCurisServiceException;
+import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -39,11 +45,13 @@
 @JsonIgnoreProperties(ignoreUnknown = true)
 @NamedQueries({
         @NamedQuery(name = "list-licenses-by-pack", query = "SELECT l FROM License l where l.pack.id = :packId"),
+        @NamedQuery(name = "list-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash"),
         @NamedQuery(name = "list-active-licenses-by-req-data", query = "SELECT l FROM License l where l.reqDataHash = :hash and l.status in ('AC', 'PA')")
 })
 public class License implements Serializable {
+    private static final long serialVersionUID = 2700310404904877227L;
 
-    private static final long serialVersionUID = 1L;
+    private static final Logger LOG = LogManager.getLogger(License.class);
 
     @Id
     @GeneratedValue
@@ -63,8 +71,8 @@
 
     @JsonIgnore
     @ManyToOne
-    @JoinColumn(name = "canceled_by")
-    private User canceledBy;
+    @JoinColumn(name = "cancelled_by")
+    private User cancelledBy;
 
     private LicenseStatus status;
 
@@ -78,6 +86,10 @@
     @JsonProperty("request_data")
     private String requestData;
 
+    /**
+     * request data hash is automatically set when we use
+     * {@link License#setRequestData(String)} method
+     */
     @Column(name = "request_data_hash")
     @JsonIgnore
     private String reqDataHash;
@@ -158,18 +170,18 @@
         }
     }
 
-    @JsonProperty("canceled_by_id")
-    public String getCanceledById() {
-        return canceledBy == null ? null : canceledBy.getUsername();
+    @JsonProperty("cancelled_by_id")
+    public String getCancelledById() {
+        return cancelledBy == null ? null : cancelledBy.getUsername();
     }
 
-    @JsonProperty("canceled_by_id")
-    public void setCanceledById(String username) {
+    @JsonProperty("cancelled_by_id")
+    public void setCancelledById(String username) {
         if (username == null) {
-            canceledBy = null;
+            cancelledBy = null;
         } else {
-            canceledBy = new User();
-            canceledBy.setUsername(username);
+            cancelledBy = new User();
+            cancelledBy.setUsername(username);
         }
     }
 
@@ -229,12 +241,12 @@
         this.id = id;
     }
 
-    public User getCanceledBy() {
-        return canceledBy;
+    public User getCancelledBy() {
+        return cancelledBy;
     }
 
-    public void setCanceledBy(User canceledBy) {
-        this.canceledBy = canceledBy;
+    public void setCancelledBy(User cancelledBy) {
+        this.cancelledBy = cancelledBy;
     }
 
     public Date getLastAccessTimestamp() {
@@ -251,6 +263,7 @@
 
     public void setRequestData(String requestData) {
         this.requestData = requestData;
+        this.reqDataHash = BlockedRequest.generateHash(this.requestData);
     }
 
     public String getLicenseData() {
@@ -283,6 +296,10 @@
 
     public void setExpirationDate(Date expirationDate) {
         this.expirationDate = expirationDate;
+    }
+
+    public String getReqDataHash() {
+        return reqDataHash;
     }
 
     public static class Action {
@@ -323,17 +340,16 @@
         }
     }
 
-    public License findLicenseByRequestData(String requestData, EntityManager em) {
+    public static License findLicenseByRequestData(String requestData, EntityManager em) throws SeCurisServiceException {
         TypedQuery<License> query = em.createNamedQuery("list-active-licenses-by-req-data", License.class);
         query.setParameter("hash", BlockedRequest.generateHash(requestData));
-        return null;
+        try {
+            return query.getSingleResult();
+        } catch (NonUniqueResultException e) {
+            LOG.error("There are more than 1 active license for request data: {}\nHash: {}", requestData, BlockedRequest.generateHash(requestData));
+            throw new SeCurisServiceException(ErrorCodes.DUPLICATED_REQUEST_DATA, "There are more than 1 active license for request data hash: "
+                    + BlockedRequest.generateHash(requestData));
+        }
     }
 
-    public String getReqDataHash() {
-        return reqDataHash;
-    }
-
-    public void setReqDataHash(String reqDataHash) {
-        this.reqDataHash = reqDataHash;
-    }
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java b/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java
index 2c25dc5..3666c03 100644
--- a/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java
+++ b/securis/src/main/java/net/curisit/securis/db/LicenseHistory.java
@@ -116,6 +116,7 @@
         public static final String ACTIVATE = "activate";
         public static final String CANCEL = "cancel";
         public static final String BLOCK = "block";
+        public static final String UNBLOCK = "unblock";
         public static final String DELETE = "delete";
     }
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/Organization.java b/securis/src/main/java/net/curisit/securis/db/Organization.java
index 90e403d..56a42a9 100644
--- a/securis/src/main/java/net/curisit/securis/db/Organization.java
+++ b/securis/src/main/java/net/curisit/securis/db/Organization.java
@@ -167,10 +167,12 @@
     @JsonProperty("users_ids")
     public void setUsersIds(List<String> usersIds) {
         users = new ArrayList<>();
-        for (String userid : usersIds) {
-            User u = new User();
-            u.setUsername(userid);
-            users.add(u);
+        if (usersIds != null) {
+            for (String userid : usersIds) {
+                User u = new User();
+                u.setUsername(userid);
+                users.add(u);
+            }
         }
     }
 
diff --git a/securis/src/main/java/net/curisit/securis/db/Pack.java b/securis/src/main/java/net/curisit/securis/db/Pack.java
index ff3a93e..65d8478 100644
--- a/securis/src/main/java/net/curisit/securis/db/Pack.java
+++ b/securis/src/main/java/net/curisit/securis/db/Pack.java
@@ -1,7 +1,10 @@
 package net.curisit.securis.db;
 
 import java.io.Serializable;
+import java.util.Arrays;
 import java.util.Date;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.persistence.CascadeType;
@@ -16,6 +19,8 @@
 import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
+
+import net.curisit.integrity.commons.Utils;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -321,4 +326,36 @@
 
         return (id == null ? 0 : id.hashCode());
     }
+
+    public static class Action {
+        public static final int CREATE = 1;
+        public static final int ACTIVATION = 2;
+        public static final int PUT_ONHOLD = 3;
+        public static final int CANCEL = 4;
+        public static final int DELETE = 5;
+    }
+
+    public static class Status {
+
+        private static final Map<Integer, List<Integer>> transitions = Utils.createMap( //
+                Action.ACTIVATION, Arrays.asList(PackStatus.CREATED, PackStatus.ON_HOLD, PackStatus.EXPIRED), //
+                Action.PUT_ONHOLD, Arrays.asList(PackStatus.ACTIVE), //
+                Action.CANCEL, Arrays.asList(PackStatus.ACTIVE, PackStatus.ON_HOLD, PackStatus.EXPIRED), //
+                Action.DELETE, Arrays.asList(PackStatus.CANCELLED, PackStatus.CREATED) //
+                );
+
+        /**
+         * It checks if a given action is valid for the License, passing the
+         * action and the current license status
+         * 
+         * @param oldStatus
+         * @param newStatus
+         * @return
+         */
+        public static boolean isActionValid(Integer action, PackStatus currentStatus) {
+            List<Integer> validStatuses = transitions.get(currentStatus);
+
+            return validStatuses != null && validStatuses.contains(currentStatus);
+        }
+    }
 }
diff --git a/securis/src/main/java/net/curisit/securis/ioc/SecurisModule.java b/securis/src/main/java/net/curisit/securis/ioc/SecurisModule.java
index 4e2abc6..737da7c 100644
--- a/securis/src/main/java/net/curisit/securis/ioc/SecurisModule.java
+++ b/securis/src/main/java/net/curisit/securis/ioc/SecurisModule.java
@@ -23,7 +23,7 @@
 public class SecurisModule extends AbstractModule {
 
     private static final int DEFAULT_PORT = 9997;
-    private static final String PROPERTIES_FILE_NAME = "/server.properties";
+    private static final String PROPERTIES_FILE_NAME = "/securis-server.properties";
 
     private static final Logger LOG = LogManager.getLogger(SecurisModule.class);
 
@@ -127,20 +127,6 @@
     @Singleton
     public String getHashLogo() {
         return "1b42616809d4cd8ccf109e3c30d0ab25067f160b30b7354a08ddd563de0096ba";
-    }
-
-    @Named("license-req-file-name")
-    @Provides
-    @Singleton
-    public String getLicenseReqFileName() {
-        return "license.req";
-    }
-
-    @Named("license-file-name")
-    @Provides
-    @Singleton
-    public String getLicenseFileName() {
-        return "license.lic";
     }
 
     @Provides
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 93192c7..351500f 100644
--- a/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
@@ -45,6 +45,7 @@
 import net.curisit.securis.db.LicenseStatus;
 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.security.BasicSecurityContext;
 import net.curisit.securis.security.Securable;
@@ -56,6 +57,7 @@
 import net.curisit.securis.utils.TokenHelper;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -158,7 +160,7 @@
         }
         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(Status.FORBIDDEN.getStatusCode(), "License is not active, so It can not be downloaded");
+            throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License is not active, so It can not be downloaded");
         }
         em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.DOWNLOAD));
         return Response
@@ -193,6 +195,15 @@
             LOG.error("License with id {} can not be activated from current license status", licId);
             throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "License with id " + licId
                     + " can not be activated from the current license status");
+        }
+        validateRequestData(lic.getPack(), lic.getRequestData());
+
+        License existingLicense = License.findLicenseByRequestData(lic.getRequestData(), em);
+        if (existingLicense != null) {
+            return Response.status(DefaultExceptionHandler.DEFAULT_APP_ERROR_STATUS_CODE)
+                    .header(DefaultExceptionHandler.ERROR_CODE_MESSAGE_HEADER, ErrorCodes.DUPLICATED_REQUEST_DATA)
+                    .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "There is already an active license for current request data")
+                    .type(MediaType.APPLICATION_JSON).entity(existingLicense).build();
         }
 
         lic.setStatus(LicenseStatus.ACTIVE);
@@ -291,10 +302,11 @@
         }
 
         lic.setStatus(LicenseStatus.CANCELLED);
+        lic.setCancelledById(bsc.getUserPrincipal().getName());
         lic.setModificationTimestamp(new Date());
         em.persist(lic);
 
-        em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.CANCEL, "Cancelation reason: " + reason));
+        em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.CANCEL, "Cancellation reason: " + reason));
         return Response.ok(lic).build();
     }
 
@@ -321,11 +333,22 @@
                 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 = getUser(bsc.getUserPrincipal().getName(), em);
 
         if (lic.getRequestData() != null) {
+            License existingLicense = License.findLicenseByRequestData(lic.getRequestData(), em);
+            if (existingLicense != null) {
+                return Response.status(DefaultExceptionHandler.DEFAULT_APP_ERROR_STATUS_CODE)
+                        .header(DefaultExceptionHandler.ERROR_CODE_MESSAGE_HEADER, ErrorCodes.DUPLICATED_REQUEST_DATA)
+                        .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "There is already an active license for current request data")
+                        .type(MediaType.APPLICATION_JSON).entity(existingLicense).build();
+            }
             SignedLicenseBean signedLicense = generateLicense(lic, em);
             // If user provide a request data the license status is passed
             // directly to ACTIVE
@@ -338,7 +361,7 @@
                 lic.setLicenseData(JsonUtils.toJSON(signedLicense));
             } catch (SeCurisException e) {
                 LOG.error("Error generaing license JSON", e);
-                throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
+                throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generating license JSON");
             }
         } else {
             lic.setStatus(LicenseStatus.CREATED);
@@ -387,6 +410,9 @@
      * @throws SeCurisServiceException
      */
     private RequestBean validateRequestData(Pack pack, String requestData) 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);
@@ -422,27 +448,32 @@
         EntityManager em = emProvider.get();
 
         License currentLicense = getCurrentLicense(licId, bsc, em);
-
-        currentLicense.setCode(lic.getCode());
+        currentLicense.setComments(lic.getComments());
         currentLicense.setFullName(lic.getFullName());
         currentLicense.setEmail(lic.getEmail());
-        if (lic.getRequestData() != null && currentLicense.getStatus() == LicenseStatus.CREATED) {
-            SignedLicenseBean signedLicense = generateLicense(lic, em);
-            lic.setStatus(LicenseStatus.ACTIVE);
-            try {
-                // Next line is necessary to normalize the String that contains
-                // the request.
-                lic.setRequestData(JsonUtils.toJSON((RequestBean) signedLicense));
-                if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
-                    throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be activate");
+
+        if (currentLicense.getStatus() == LicenseStatus.CREATED && !ObjectUtils.equals(currentLicense.getReqDataHash(), lic.getReqDataHash())) {
+            if (lic.getRequestData() != null) {
+                SignedLicenseBean signedLicense = generateLicense(lic, em);
+                try {
+                    // Next line is necessary to normalize the String that
+                    // contains
+                    // the request.
+                    lic.setRequestData(JsonUtils.toJSON((RequestBean) signedLicense));
+                    if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) {
+                        throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be used again");
+                    }
+                    lic.setLicenseData(JsonUtils.toJSON(signedLicense));
+                } catch (SeCurisException e) {
+                    LOG.error("Error generaing license JSON", e);
+                    throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
                 }
-                lic.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.setRequestData(lic.getRequestData());
         }
+
         currentLicense.setModificationTimestamp(new Date());
         em.persist(currentLicense);
         em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.MODIFY));
@@ -462,17 +493,30 @@
         EntityManager em = emProvider.get();
         License lic = getCurrentLicense(licId, bsc, em);
 
-        if (lic.getStatus() != LicenseStatus.CANCELLED || lic.getStatus() != LicenseStatus.CREATED) {
+        if (License.Status.isActionValid(License.Action.DELETE, lic.getStatus())) {
             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();
+            throw new SeCurisServiceException(ErrorCodes.WRONG_STATUS, "License can not be deleted in current status: " + lic.getStatus().name());
+        }
+        if (lic.getStatus() == LicenseStatus.CANCELLED) {
+            // 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
+            TypedQuery<License> query = em.createNamedQuery("list-licenses-by-req-data", License.class);
+            query.setParameter("hash", lic.getReqDataHash());
+            List<License> list = query.getResultList();
+            if (list == null || list.size() == 0) {
+                BlockedRequest br = em.find(BlockedRequest.class, lic.getReqDataHash());
+                if (br != null) {
+                    em.remove(br);
+                }
+            }
         }
 
         em.remove(lic);
         return Response.ok(Utils.createMap("success", true, "id", licId)).build();
     }
 
-    @DELETE
+    @POST
     @Path("/{licId}/block")
     @Transactional
     @Securable
@@ -484,7 +528,7 @@
         EntityManager em = emProvider.get();
         License lic = getCurrentLicense(licId, bsc, em);
 
-        if (lic.getStatus() != LicenseStatus.CANCELLED) {
+        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");
         }
@@ -502,6 +546,29 @@
         return Response.ok(Utils.createMap("success", true, "id", licId)).build();
     }
 
+    @POST
+    @Path("/{licId}/unblock")
+    @Transactional
+    @Securable
+    @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);
+            em.persist(createLicenseHistoryAction(lic, 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();
+    }
+
     private License getCurrentLicense(Integer licId, BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
         if (licId == null || "".equals(licId)) {
             LOG.error("License ID is mandatory");
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 9205945..e876cb5 100644
--- a/securis/src/main/java/net/curisit/securis/services/PackResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/PackResource.java
@@ -30,9 +30,12 @@
 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.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.TokenHelper;
 
 import org.apache.logging.log4j.LogManager;
@@ -154,11 +157,12 @@
 
         User user = em.find(User.class, bsc.getUserPrincipal().getName());
 
+        pack.setStatus(PackStatus.CREATED);
         pack.setCreatedBy(user);
         pack.setCreationTimestamp(new Date());
         em.persist(pack);
-        Set<PackMetadata> newMD = pack.getMetadata(); 
-        
+        Set<PackMetadata> newMD = pack.getMetadata();
+
         if (newMD != null) {
             for (PackMetadata md : newMD) {
                 md.setPack(pack);
@@ -215,12 +219,13 @@
 
         em.persist(currentPack);
 
-        Set<PackMetadata> newMD = pack.getMetadata(); 
+        Set<PackMetadata> newMD = pack.getMetadata();
         for (PackMetadata currentMd : currentPack.getMetadata()) {
-            if (newMD == null || !newMD.contains(currentMd));
-                em.remove(currentMd);
+            if (newMD == null || !newMD.contains(currentMd))
+                ;
+            em.remove(currentMd);
         }
-        
+
         if (newMD != null) {
             for (PackMetadata md : newMD) {
                 md.setPack(currentPack);
@@ -231,6 +236,84 @@
         return Response.ok(pack).build();
     }
 
+    @POST
+    @Path("/{packId}/activate")
+    @Transactional
+    @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")
+    @Transactional
+    @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")
+    @Transactional
+    @Securable
+    @RolesAllowed(BasicSecurityContext.ROL_ADMIN)
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces({
+        MediaType.APPLICATION_JSON
+    })
+    public Response cancel(@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.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());
+        }
+
+        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) {
diff --git a/securis/src/main/java/net/curisit/securis/utils/EmailManager.java b/securis/src/main/java/net/curisit/securis/utils/EmailManager.java
index 41c768e..d1d5f0e 100644
--- a/securis/src/main/java/net/curisit/securis/utils/EmailManager.java
+++ b/securis/src/main/java/net/curisit/securis/utils/EmailManager.java
@@ -55,7 +55,7 @@
      * 
      * @throws SeCurisException
      */
-    private EmailManager() throws SeCurisException {
+    public EmailManager() throws SeCurisException {
         String domain = Params.get(Params.KEYS.MAILGUN_DOMAIN);
         if (domain == null) {
             throw new SeCurisException("Please, add '" + Params.KEYS.MAILGUN_DOMAIN + "' parameter to config file");
diff --git a/securis/src/main/resources/META-INF/persistence.xml b/securis/src/main/resources/META-INF/persistence.xml
index 2dedfc9..5b8e9ce 100644
--- a/securis/src/main/resources/META-INF/persistence.xml
+++ b/securis/src/main/resources/META-INF/persistence.xml
@@ -11,7 +11,7 @@
 
 		<properties>
 			<property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
-			<property name="javax.persistence.jdbc.url" value="jdbc:h2:/tmp/.CurisIntegrity/db/curissecuris" />
+<!-- 			<property name="javax.persistence.jdbc.url" value="jdbc:h2:~/.SeCuris/db/securis" />  -->
 			<property name="javax.persistence.jdbc.user" value="curis" />
 			<property name="javax.persistence.jdbc.password" value="53curi5" />
 			<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
diff --git a/securis/src/main/resources/db/schema.sql b/securis/src/main/resources/db/schema.sql
index 1d4b2d9..0f448ea 100644
--- a/securis/src/main/resources/db/schema.sql
+++ b/securis/src/main/resources/db/schema.sql
@@ -8,7 +8,7 @@
 drop table IF EXISTS user;
 CREATE  TABLE IF NOT EXISTS user (
   username VARCHAR(45) NOT NULL ,
-  password VARCHAR(100) NULL ,
+  password VARCHAR(256) NULL ,
   roles INT NOT NULL default 0,
   first_name VARCHAR(100) NULL ,
   last_name VARCHAR(100) NULL ,
@@ -113,12 +113,12 @@
   modification_timestamp DATETIME NULL ,  
   last_access_timestamp DATETIME NULL ,  
   expiration_date DATETIME NULL ,  
-  canceled_by varchar(45) NULL ,  
+  cancelled_by varchar(45) NULL ,  
   created_by varchar(45) NULL ,  
   status VARCHAR(2) NOT NULL default 'CR',  
   PRIMARY KEY (id));
 
-create index if not exists lic_hash_req_idx on license(hash_request_data);  
+create index if not exists lic_hash_req_idx on license(request_data_hash);  
 create index if not exists lic_pack_idx on license(pack_id);  
   
 drop table IF EXISTS license_history;
diff --git a/securis/src/main/resources/static/js/licenses.js b/securis/src/main/resources/static/js/licenses.js
index 09a33bd..a7d5761 100644
--- a/securis/src/main/resources/static/js/licenses.js
+++ b/securis/src/main/resources/static/js/licenses.js
@@ -101,11 +101,20 @@
 			'$store',
 			'$L',
 			function($scope, $http, $resource, toaster, Catalogs, $store, $L) {
-				var packResource = $resource('/pack/:packId', {
-					packId : '@id'
-				});
+				var packResource = $resource('/pack/:packId/:action', 
+					{
+						packId : '@id',
+						action : '@action'
+					},
+					{
+                        activate: {
+                            method: "POST",
+                            params: {action: "activate"}
+                        }
+                    }
+				);
 				var PACK_STATUS = [
-				                   {id: 'PE', label: $L.get('Pending')},
+				                   {id: 'CR', label: $L.get('Created')},
 				                   {id: 'AC', label: $L.get('Active')},
 				                   {id: 'OH', label: $L.get('On Hold')},
 				                   {id: 'EX', label: $L.get('Expired')},
@@ -165,13 +174,31 @@
 				    	_savePackData();
 				    }
 				}
+
+				/**
+				 * Execute an action over the pack, activation, onhold, cancellation
+				 */
+				$scope.execute = function(action) {
+					console.log('Action: '+ action +' on pack: ' + $scope.pack.id);
+					var _success = function() {
+					    if (!$scope.isNew) $scope.showForm = false;
+						$scope.packs = packResource.query();
+						toaster.pop('success', Catalogs.getName(), $L.get("Pack '{0}' {1} successfully", $scope.pack.code, $L.get("activated")));
+					}
+					var _error =  function(error) {
+                        console.log(error);
+						toaster.pop('error', Catalogs.getName(), $L.get("Error {0} pack '{1}'. Reason: {2}", $L.get("activating"), $scope.pack.code, $L.get(error.headers('X-SECURIS-ERROR'))));
+					}
+					packResource.activate({id: $scope.pack.id}, _success, _error);
+				}
 				
+
 				$scope.newPack = function() {
 					$scope.isNew = true;
 					$scope.showForm = true;
 					$scope.pack = {
                             license_preactivation: true,
-                            status: 'PE',
+                            status: 'CR',
                             num_licenses: 1,
                             license_type_id: null,
                             organization_id: null  //!$scope.refs.organization_id || !$scope.refs.organization_id.length ? null : $scope.refs.organization_id[0].id
@@ -190,7 +217,13 @@
                     if (!(selectedPack.end_valid_date instanceof Date)) {
                     	selectedPack.end_valid_date = new Date(selectedPack.end_valid_date);
                     }
+                    
                     $scope.pack = selectedPack;
+
+                    //$scope.pack.organization_name = $scope.getLabelFromId('organization_id', $scope.pack.organization_id); 
+                    $scope.pack.license_type_name = $scope.getLabelFromId('license_type_id', $scope.pack.license_type_id); 
+                    $scope.pack.status_name = $scope.getLabelFromId('pack_status', $scope.pack.status); 
+                    
                     setTimeout(function() {
                         $('#code').focus();
                     }, 0);
@@ -223,6 +256,16 @@
 					$scope.$parent.currentPack = pack;
 					$store.put('currentPack', pack);
 					$scope.$parent.$broadcast('pack_changed', pack);
+				}
+				
+				$scope.getLabelFromId = function(field, myid) {
+					var label = null;
+					$scope.refs[field].forEach(function (elem) {
+						if (elem.id === myid) {
+							label = elem.label;
+						}
+					});
+					return label;
 				}
 				
 				$scope.createMetadataRow = function() {
@@ -264,6 +307,7 @@
 	                                   function($scope, $http, $resource, toaster, $store, $L) {
 	                                       $scope.$on('pack_changed', function(evt, message) {
                                                $scope.licenses = licenseResource.query({packId: $scope.currentPack.id});
+                                               $scope.creationAvailable = $scope.currentPack.status == 'AC';
 	                                           if ($scope.showForm) {
 	                                               if ($scope.isNew) {
 	                                                   $scope.license.pack_id = $scope.currentPack.id
@@ -296,7 +340,8 @@
 	                                           }
 	                                       });
 	                                       $scope.mandatory = {
-	                                               code: true
+	                                               code: true,
+	                                               email: true
 	                                       }
 	                                       $scope.maxlength = {
 	                                               code: 50,
@@ -356,6 +401,20 @@
 	                                               });
 	                                               return;
 	                                           }
+	                                           if (!$scope.creationAvailable) {
+	                                               BootstrapDialog.show({
+	                                                   title: $L.get('Pack not active'),
+	                                                   type: BootstrapDialog.TYPE_WARNING,
+	                                                   message: $L.get('Current pack is not active, so licenses cannot be created'),
+	                                                   buttons: [{
+	                                                       label: 'OK',
+	                                                       action: function(dialog) {
+	                                                           dialog.close();
+	                                                       }
+	                                                   }]
+	                                               });
+	                                               return;
+	                                           }
 	                                               
 	                                           $scope.isNew = true;
 	                                           $scope.showForm = true;
diff --git a/securis/src/main/resources/static/licenses.html b/securis/src/main/resources/static/licenses.html
index 2389f82..8f3367a 100644
--- a/securis/src/main/resources/static/licenses.html
+++ b/securis/src/main/resources/static/licenses.html
@@ -110,11 +110,7 @@
 				<div class="form-group" ng-if="!isNew">
 					<label class="col-md-3 control-label" for="status" i18n>Status</label>
 					<div class="col-md-8">
-						<select class="form-control" id="status"
-							ng-required="mandatory.status"
-							ng-model="pack.status"
-							ng-options="o.id as o.label for o in refs.pack_status">
-						</select>
+						<p class="form-control-static" ng-bind="pack.status_name"></p>
 						<div class="alert inline-alert alert-warning"
 							ng-show="packForm.status.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
@@ -128,13 +124,13 @@
 					<label class="col-md-3 control-label" for="license_type_id" i18n>License
 						type</label>
 					<div class="col-md-8">
-						<select class="form-control" id="license_type_id"
+						<select ng-if="isNew" class="form-control" id="license_type_id"
 						    ng-change="updateMetadata()"
 							ng-required="mandatory.license_type_id"
 							ng-model="pack.license_type_id"
 							ng-options="o.id as o.label for o in refs.license_type_id">
-
 						</select>
+						<p ng-if="!isNew" class="form-control-static" ng-bind="pack.license_type_name"></p>
 						<div class="alert inline-alert alert-warning"
 							ng-show="packForm.license_type_id.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
@@ -147,10 +143,12 @@
 				<div class="form-group">
 					<label class="col-md-3 control-label" for="organization_id" i18n>Organization</label>
 					<div class="col-md-8">
-						<select class="form-control" ng-required="field.mandatory"
+						<select ng-if="isNew" class="form-control" 
 							ng-model="pack.organization_id"
+							ng-required="mandatory.organization_id"
 							ng-options="o.id as o.label for o in refs.organization_id">
 						</select>
+						<p ng-if="!isNew" class="form-control-static" ng-bind="pack.organization_name"></p>
 						<div class="alert inline-alert alert-warning"
 							ng-show="packForm.organization_id.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
@@ -232,12 +230,12 @@
 						<button id="save" type="submit" class="btn btn-primary">
 							<span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
 						</button>
-						<button id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+						<button ng-if="!isNew" id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
 							<span i18n class="glyphicon glyphicon-align-justify"></span> Actions
 							<span class="caret"></span>
 						</button>
 						<ul class="dropdown-menu" role="menu">
-						    <li><a href="#">Activate</a></li>
+						    <li><a href="#" ng-click="execute('activate')">Activate</a></li>
 						    <li><a href="#">On hold</a></li>
 						    <li class="divider"></li>
 						    <li><a href="#">Invalidate</a></li>
@@ -545,7 +543,7 @@
 						<button id="save" type="submit" class="btn btn-primary">
 							<span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
 						</button>
-						<button id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+						<button ng-if="!isNew" id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
 							<span i18n class="glyphicon glyphicon-align-justify"></span> Actions
 							<span class="caret"></span>
 						</button>

--
Gitblit v1.3.2