From 94c288b4f8d353c44b64e40c0863c7fce6782293 Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Thu, 24 Sep 2015 17:26:14 +0000
Subject: [PATCH] #2756 fix - chnaged API to allow activation by code and other UI changes

---
 securis/src/main/java/net/curisit/securis/services/ApiResource.java           |  219 +++++++++++++++++++++---------
 securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java        |    6 
 securis/src/main/java/net/curisit/securis/db/Application.java                 |    9 +
 securis/src/main/webapp/js/catalogs.json                                      |    6 
 securis/src/main/java/net/curisit/securis/services/BasicServices.java         |    6 
 securis/src/main/java/net/curisit/securis/services/ApplicationResource.java   |    1 
 securis/src/main/java/net/curisit/securis/db/License.java                     |   33 ++++
 securis/src/main/webapp/licenses.html                                         |   38 +++-
 securis/pom.xml                                                               |    4 
 securis/src/main/java/net/curisit/securis/services/helpers/LicenseHelper.java |    3 
 securis/src/main/java/net/curisit/securis/utils/TokenHelper.java              |   22 ++
 securis/src/main/java/net/curisit/securis/services/LicenseResource.java       |   12 +
 securis/src/main/webapp/js/licenses.js                                        |   20 ++
 securis/src/main/resources/db/schema.sql                                      |    2 
 14 files changed, 286 insertions(+), 95 deletions(-)

diff --git a/securis/pom.xml b/securis/pom.xml
index 74efe79..d6a4847 100644
--- a/securis/pom.xml
+++ b/securis/pom.xml
@@ -3,7 +3,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>net.curisit</groupId>
 	<artifactId>securis-server</artifactId>
-	<version>1.0.2</version>
+	<version>1.1.1</version>
 	<name>SeCuris</name>
 	<description>CurisTEC Server Licenses</description>
 	<dependencies>
@@ -51,7 +51,7 @@
 		<dependency>
 			<groupId>net.curisit</groupId>
 			<artifactId>securis-client</artifactId>
-			<version>1.0.6-SNAPSHOT</version>
+			<version>1.1.0-SNAPSHOT</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
diff --git a/securis/src/main/java/net/curisit/securis/db/Application.java b/securis/src/main/java/net/curisit/securis/db/Application.java
index 6bf6ba8..f7f546d 100644
--- a/securis/src/main/java/net/curisit/securis/db/Application.java
+++ b/securis/src/main/java/net/curisit/securis/db/Application.java
@@ -46,6 +46,7 @@
     @GeneratedValue
     private Integer id;
 
+    private String code;
     private String name;
     private String description;
 
@@ -139,4 +140,12 @@
     public void setLicenseTypes(Set<LicenseType> licenseTypes) {
         this.licenseTypes = licenseTypes;
     }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
 }
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 6c0ec5b..fcaec12 100644
--- a/securis/src/main/java/net/curisit/securis/db/License.java
+++ b/securis/src/main/java/net/curisit/securis/db/License.java
@@ -54,6 +54,7 @@
 @JsonIgnoreProperties(ignoreUnknown = true)
 @NamedQueries({
         @NamedQuery(name = "license-by-code", query = "SELECT l FROM License l where l.code = :code"),
+        @NamedQuery(name = "license-by-activation-code", query = "SELECT l FROM License l where l.activationCode = :activationCode"),
         @NamedQuery(name = "last-code-suffix-used-in-pack", query = "SELECT max(l.codeSuffix) FROM License l where l.pack.id = :packId"),
         @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"),
@@ -72,6 +73,10 @@
     private int id;
 
     private String code;
+
+    @Column(name = "activation_code")
+    @JsonProperty("activation_code")
+    private String activationCode;
 
     @Column(name = "code_suffix")
     @JsonProperty("code_suffix")
@@ -397,6 +402,26 @@
         }
     }
 
+    /**
+     * Return licenses with status: REquested, ACtive, Pre-Active for a given
+     * request data
+     * 
+     * @param requestData
+     * @param em
+     * @return
+     * @throws SeCurisServiceException
+     */
+    public static License findLicenseByActivationCode(String activationCode, EntityManager em) throws SeCurisServiceException {
+        TypedQuery<License> query = em.createNamedQuery("license-by-activation-code", License.class);
+        query.setParameter("activationCode", activationCode);
+        try {
+            return query.getSingleResult();
+        } catch (NoResultException e) {
+            // There is no license for request data
+            return null;
+        }
+    }
+
     public static License findActiveLicenseByRequestData(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));
@@ -435,4 +460,12 @@
         this.codeSuffix = codeSuffix;
     }
 
+    public String getActivationCode() {
+        return activationCode;
+    }
+
+    public void setActivationCode(String activationCode) {
+        this.activationCode = activationCode;
+    }
+
 }
diff --git a/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java b/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
index 6a74d4d..63f42b5 100644
--- a/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
+++ b/securis/src/main/java/net/curisit/securis/ioc/RequestsInterceptor.java
@@ -143,8 +143,10 @@
             if (user != null) {
                 userRoles = 0;
                 List<Integer> roles = user.getRoles();
-                for (Integer rol : roles) {
-                    userRoles += rol;
+                if (roles != null) {
+                    for (Integer rol : roles) {
+                        userRoles += rol;
+                    }
                 }
                 // We store user roles in cache only for one hour
                 cache.set("roles_" + username, userRoles, 3600);
diff --git a/securis/src/main/java/net/curisit/securis/services/ApiResource.java b/securis/src/main/java/net/curisit/securis/services/ApiResource.java
index 743be97..20fe504 100644
--- a/securis/src/main/java/net/curisit/securis/services/ApiResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/ApiResource.java
@@ -68,7 +68,7 @@
     @Inject
     LicenseGenerator licenseGenerator;
 
-    private static final String CLIENT_USERNAME = "_client";
+    public static final String API_CLIENT_USERNAME = "_client";
 
     public ApiResource() {
     }
@@ -115,7 +115,7 @@
     @POST
     @Path("/request")
     @Consumes(MediaType.APPLICATION_JSON)
-    // TODO: Enable this: @Securable
+    @Securable
     @Produces({
         MediaType.APPLICATION_JSON
     })
@@ -123,7 +123,7 @@
     public Response createFromRequest(RequestBean request, @HeaderParam(LicenseManager.HEADER_LICENSE_NAME_OR_REFERENCE) String nameOrReference,
             @HeaderParam(LicenseManager.HEADER_LICENSE_EMAIL) String userEmail) throws IOException, SeCurisServiceException, SeCurisException {
         LOG.info("Request to get license: {}", request);
-        SignedLicenseBean lic = createLicense(request, em, false, nameOrReference, userEmail);
+        SignedLicenseBean lic = createLicense(request, em, nameOrReference, userEmail);
 
         return Response.ok(lic).build();
     }
@@ -151,6 +151,8 @@
             @HeaderParam(LicenseManager.HEADER_LICENSE_NAME_OR_REFERENCE) String nameOrReference,
             @HeaderParam(LicenseManager.HEADER_LICENSE_EMAIL) String userEmail) throws IOException, SeCurisServiceException, SeCurisException {
         RequestBean req = new RequestBean();
+        req.setAppCode(mpfdi.getFormDataPart("appCode", String.class, null));
+        req.setActivationCode(mpfdi.getFormDataPart("activationCode", String.class, null));
         req.setPackCode(mpfdi.getFormDataPart("packCode", String.class, null));
         req.setLicenseTypeCode(mpfdi.getFormDataPart("licenseTypeCode", String.class, null));
         req.setCustomerCode(mpfdi.getFormDataPart("customerCode", String.class, null));
@@ -175,7 +177,7 @@
     @POST
     @Path("/renew")
     @Consumes(MediaType.APPLICATION_JSON)
-    // TODO: Enable this: @Securable
+    @Securable
     @Produces({
         MediaType.APPLICATION_JSON
     })
@@ -214,7 +216,7 @@
     @POST
     @Path("/validate")
     @Consumes(MediaType.APPLICATION_JSON)
-    // TODO: Enable this: @Securable
+    @Securable
     @Produces({
         MediaType.APPLICATION_JSON
     })
@@ -226,7 +228,6 @@
             throw new SeCurisServiceException(ErrorCodes.LICENSE_IS_EXPIRED, "The license is expired");
         }
 
-        // EntityManager em = emProvider.get();
         try {
             SignatureHelper.getInstance().validateSignature(currentLic);
         } catch (SeCurisException ex) {
@@ -261,6 +262,8 @@
             SeCurisServiceException, SeCurisException {
         LicenseBean lic = new LicenseBean();
 
+        lic.setAppCode(mpfdi.getFormDataPart("appCode", String.class, null));
+        lic.setActivationCode(mpfdi.getFormDataPart("activationName", String.class, null));
         lic.setAppName(mpfdi.getFormDataPart("appName", String.class, null));
         lic.setArch(mpfdi.getFormDataPart("arch", String.class, null));
         lic.setCrcLogo(mpfdi.getFormDataPart("crcLogo", String.class, null));
@@ -279,7 +282,7 @@
     }
 
     private SignedLicenseBean renewLicense(RequestBean req, EntityManager em) throws SeCurisServiceException {
-        return createLicense(req, em, true, null, null);
+        return renewLicense(req, em);
     }
 
     /**
@@ -292,16 +295,38 @@
      * @return
      * @throws SeCurisServiceException
      */
-    private SignedLicenseBean createLicense(RequestBean req, EntityManager em, boolean renew, String nameOrReference, String email)
-            throws SeCurisServiceException {
-        LicenseBean previousLicenseBean = null;
+    private SignedLicenseBean createLicense(RequestBean req, EntityManager em, String nameOrReference, String email) throws SeCurisServiceException {
         License lic = null;
-        if (renew) {
-            previousLicenseBean = (LicenseBean) req;
-            lic = License.findLicenseByCode(previousLicenseBean.getLicenseCode(), em);
-            if (lic.getStatus() != LicenseStatus.ACTIVE && lic.getStatus() != LicenseStatus.PRE_ACTIVE) {
-                throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The current license has been cancelled");
+
+        if (req.getActivationCode() != null) {
+            lic = License.findLicenseByActivationCode(req.getActivationCode(), em);
+            if (lic == null) {
+                throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The given activation code is invalid: " + req.getActivationCode());
             }
+            if (lic.getStatus() == LicenseStatus.ACTIVE) {
+                RequestBean initialRequest;
+                try {
+                    initialRequest = JsonUtils.json2object(lic.getRequestData(), RequestBean.class);
+                    if (!req.match(initialRequest)) {
+                        throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "There is already an active license for given activation code: "
+                                + req.getActivationCode());
+                    } else {
+                        return JsonUtils.json2object(lic.getLicenseData(), SignedLicenseBean.class);
+                    }
+                } catch (SeCurisException e) {
+                    LOG.error("Error getting existing license", e);
+                    throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Original request is wrong");
+                }
+            } else {
+                if (req.getAppCode() != null && !req.getAppCode().equals(lic.getPack().getLicenseType().getApplication().getCode())) {
+                    LOG.error("Activation code {} belongs to app: {} but was sent by: {}", req.getActivationCode(), lic.getPack().getLicenseType()
+                            .getApplication().getCode(), req.getAppCode());
+                    throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The given activation code belongs to a different application: "
+                            + req.getActivationCode());
+                }
+            }
+            // We validate if the HW is the same, otherwise an error is
+            // thrown
         } else {
             try {
                 lic = License.findValidLicenseByRequestData(JsonUtils.toJSON(req), em);
@@ -321,40 +346,126 @@
                 lic = new License();
             }
         }
+
         Pack pack;
-        try {
-            pack = em.createNamedQuery("pack-by-code", Pack.class).setParameter("code", req.getPackCode()).getSingleResult();
-        } catch (NoResultException e) {
-            throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "No pack found for code: " + req.getPackCode());
-        }
+        if (lic.getActivationCode() == null) {
+            try {
+                pack = em.createNamedQuery("pack-by-code", Pack.class).setParameter("code", req.getPackCode()).getSingleResult();
+            } catch (NoResultException e) {
+                throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "No pack found for code: " + req.getPackCode());
+            }
 
-        if (!renew && pack.getNumAvailables() <= 0) {
-            throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "The current pack has no licenses availables");
-        }
-        if (!renew && lic.getStatus() == LicenseStatus.REQUESTED && !pack.isLicensePreactivation()) {
-            throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "Current pack doesn't allow license preactivation");
-        }
+            if (pack.getNumAvailables() <= 0) {
+                throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "The current pack has no licenses availables");
+            }
+            if (lic.getStatus() == LicenseStatus.REQUESTED && !pack.isLicensePreactivation()) {
+                throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "Current pack doesn't allow license preactivation");
+            }
 
-        if (!req.getCustomerCode().equals(pack.getOrganization().getCode())) {
-            throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Customer code is not valid: " + req.getCustomerCode());
-        }
+            if (!req.getCustomerCode().equals(pack.getOrganization().getCode())) {
+                throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Customer code is not valid: " + req.getCustomerCode());
+            }
 
-        if (!req.getLicenseTypeCode().equals(pack.getLicenseTypeCode())) {
-            throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "License type code is not valid: " + req.getLicenseTypeCode());
+            if (!req.getLicenseTypeCode().equals(pack.getLicenseTypeCode())) {
+                throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "License type code is not valid: "
+                        + req.getLicenseTypeCode());
+            }
+        } else {
+            pack = lic.getPack();
         }
-
         SignedLicenseBean signedLicense;
         try {
             String licCode;
-            if (renew || lic.getStatus() == LicenseStatus.REQUESTED) {
-                licCode = lic.getCode();
-            } else {
+            if (lic.getCode() == null) {
                 licCode = LicUtils.getLicenseCode(pack.getCode(), licenseHelper.getNextCodeSuffix(pack.getId(), em));
+            } else {
+                licCode = lic.getCode();
             }
-            Date expirationDate = licenseHelper.getExpirationDateFromPack(pack, !renew);
+            Date expirationDate = licenseHelper.getExpirationDateFromPack(pack, lic.getActivationCode() == null);
 
             LicenseBean lb = licenseGenerator.generateLicense(req, licenseHelper.extractPackMetadata(pack.getMetadata()), expirationDate, licCode,
                     pack.getAppName());
+            signedLicense = new SignedLicenseBean(lb);
+        } catch (SeCurisException e) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Error generating license: " + e.toString());
+        }
+        try {
+            lic.setRequestData(JsonUtils.toJSON(req));
+            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");
+        }
+
+        lic.setModificationTimestamp(new Date());
+        lic.setExpirationDate(signedLicense.getExpirationDate());
+        User user = em.find(User.class, API_CLIENT_USERNAME);
+        if (lic.getStatus() != LicenseStatus.REQUESTED) {
+            lic.setPack(pack);
+            lic.setCreatedBy(user);
+            lic.setCreationTimestamp(new Date());
+            if (lic.getActivationCode() != null) {
+                lic.setStatus(LicenseStatus.ACTIVE);
+            } else {
+                lic.setStatus(pack.isLicensePreactivation() ? LicenseStatus.PRE_ACTIVE : LicenseStatus.REQUESTED);
+            }
+            lic.setCode(signedLicense.getLicenseCode());
+            lic.setCodeSuffix(LicUtils.getLicenseCodeSuffix(signedLicense.getLicenseCode()));
+            if (lic.getEmail() == null || "".equals(lic.getEmail())) {
+                lic.setEmail(email);
+            }
+            if (lic.getFullName() == null || "".equals(lic.getFullName())) {
+                lic.setFullName(nameOrReference);
+            }
+            em.persist(lic);
+            em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.CREATE));
+            if (lic.getActivationCode() != null) {
+                em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.ACTIVATE, "Activated by code on creation"));
+            } else {
+                if (pack.isLicensePreactivation()) {
+                    em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.PRE_ACTIVATE, "Pre-activated on creation"));
+                } else {
+                    LOG.warn("License ({}) created, but the pack doesn't allow preactivation", lic.getCode());
+                    throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "Current pack doesn't allow license preactivation");
+                }
+            }
+        } else {
+            lic.setStatus(LicenseStatus.PRE_ACTIVE);
+            em.merge(lic);
+            em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.PRE_ACTIVATE, "Pre-activated after request"));
+        }
+
+        return signedLicense;
+    }
+
+    /**
+     * Creates a new signed license from request data or from previous license
+     * if It's a renew
+     * 
+     * @param req
+     * @param em
+     * @param renew
+     * @return
+     * @throws SeCurisServiceException
+     */
+    private SignedLicenseBean renewLicense(LicenseBean previousLicenseBean, EntityManager em) throws SeCurisServiceException {
+
+        License lic = License.findLicenseByCode(previousLicenseBean.getLicenseCode(), em);
+        if (lic.getStatus() != LicenseStatus.ACTIVE && lic.getStatus() != LicenseStatus.PRE_ACTIVE) {
+            throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The current license has been cancelled");
+        }
+
+        Pack pack = lic.getPack();
+        SignedLicenseBean signedLicense;
+        try {
+            String licCode = lic.getCode();
+            Date expirationDate = licenseHelper.getExpirationDateFromPack(pack, false);
+
+            LicenseBean lb = licenseGenerator.generateLicense(previousLicenseBean, licenseHelper.extractPackMetadata(pack.getMetadata()),
+                    expirationDate, licCode, pack.getAppName());
             signedLicense = new SignedLicenseBean(lb);
         } catch (SeCurisException e) {
             throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Error generating license: " + e.toString());
@@ -372,37 +483,11 @@
 
         lic.setModificationTimestamp(new Date());
         lic.setExpirationDate(signedLicense.getExpirationDate());
-        User user = em.find(User.class, CLIENT_USERNAME);
-        if (!renew && lic.getStatus() != LicenseStatus.REQUESTED) {
-            lic.setPack(pack);
-            lic.setCreatedBy(user);
-            lic.setCreationTimestamp(new Date());
-            if (pack.isLicensePreactivation()) {
-                lic.setStatus(LicenseStatus.PRE_ACTIVE);
-            } else {
-                lic.setStatus(LicenseStatus.REQUESTED);
-            }
-            lic.setCode(signedLicense.getLicenseCode());
-            lic.setCodeSuffix(LicUtils.getLicenseCodeSuffix(signedLicense.getLicenseCode()));
-            lic.setEmail(email);
-            lic.setFullName(nameOrReference);
-            em.persist(lic);
-            em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.CREATE));
-            if (pack.isLicensePreactivation()) {
-                em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.PRE_ACTIVATE, "Pre-activated on creation"));
-            } else {
-                LOG.warn("License ({}) created, but the pack doesn't allow preactivation", lic.getCode());
-                throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "Current pack doesn't allow license preactivation");
-            }
-        } else {
-            lic.setStatus(renew ? LicenseStatus.ACTIVE : LicenseStatus.PRE_ACTIVE);
-            em.merge(lic);
-            if (renew) {
-                em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.RENEW));
-            } else {
-                em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.PRE_ACTIVATE, "Pre-activated after request"));
-            }
-        }
+        User user = em.find(User.class, API_CLIENT_USERNAME);
+
+        lic.setStatus(LicenseStatus.ACTIVE);
+        em.merge(lic);
+        em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.RENEW));
 
         return signedLicense;
     }
diff --git a/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java b/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java
index e9b5551..5ce62d6 100644
--- a/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/ApplicationResource.java
@@ -161,6 +161,7 @@
             return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Application not found with ID: " + appid)
                     .build();
         }
+        currentapp.setCode(app.getCode());
         currentapp.setName(app.getName());
         currentapp.setLicenseFilename(app.getLicenseFilename());
         currentapp.setDescription(app.getDescription());
diff --git a/securis/src/main/java/net/curisit/securis/services/BasicServices.java b/securis/src/main/java/net/curisit/securis/services/BasicServices.java
index 1b7a494..d1bba29 100644
--- a/securis/src/main/java/net/curisit/securis/services/BasicServices.java
+++ b/securis/src/main/java/net/curisit/securis/services/BasicServices.java
@@ -73,14 +73,8 @@
         MediaType.APPLICATION_JSON
     })
     public Response login(@FormParam("username") String user, @FormParam("password") String password, @Context HttpServletRequest request) {
-        LOG.info("index session: " + request.getSession());
-        LOG.info("user: {}, pass: {}", user, password);
         LOG.info("is user in role: {} == {} ? ", "advance", request.isUserInRole("advance"));
 
-        if ("no".equals(password)) {
-            // TODO: Code to text exception handling
-            return Response.status(Status.UNAUTHORIZED).build();
-        }
         String tokenAuth = tokenHelper.generateToken(user);
         return Response.ok(Utils.createMap("success", true, "token", tokenAuth)).build();
     }
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 3abb26f..fc57a2f 100644
--- a/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
+++ b/securis/src/main/java/net/curisit/securis/services/LicenseResource.java
@@ -209,7 +209,7 @@
 
         License existingLicense = License.findActiveLicenseByRequestData(lic.getRequestData(), em);
         if (existingLicense != null && existingLicense.getStatus() == LicenseStatus.ACTIVE) {
-            throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "The pack has not available licenses");
+            throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "An active license already exists for the given request data");
         }
 
         lic.setStatus(LicenseStatus.ACTIVE);
@@ -347,9 +347,19 @@
         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) {
diff --git a/securis/src/main/java/net/curisit/securis/services/helpers/LicenseHelper.java b/securis/src/main/java/net/curisit/securis/services/helpers/LicenseHelper.java
index 039eb03..512957b 100644
--- a/securis/src/main/java/net/curisit/securis/services/helpers/LicenseHelper.java
+++ b/securis/src/main/java/net/curisit/securis/services/helpers/LicenseHelper.java
@@ -125,6 +125,9 @@
         if (isPreActivation) {
             validPeriod = pack.getPreactivationValidPeriod() * MS_PER_DAY;
         } else {
+            if (pack.getRenewValidPeriod() <= 0) {
+                return pack.getEndValidDate();
+            }
             validPeriod = pack.getRenewValidPeriod() * MS_PER_DAY;
         }
         Date expirationDate = new Date(new Date().getTime() + validPeriod);
diff --git a/securis/src/main/java/net/curisit/securis/utils/TokenHelper.java b/securis/src/main/java/net/curisit/securis/utils/TokenHelper.java
index 824dc51..72289c8 100644
--- a/securis/src/main/java/net/curisit/securis/utils/TokenHelper.java
+++ b/securis/src/main/java/net/curisit/securis/utils/TokenHelper.java
@@ -11,6 +11,7 @@
 import javax.inject.Inject;
 
 import net.curisit.integrity.commons.Utils;
+import net.curisit.securis.services.ApiResource;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
@@ -42,8 +43,12 @@
      * @return
      */
     public String generateToken(String user) {
+
+        return generateToken(user, new Date());
+    }
+
+    public String generateToken(String user, Date date) {
         try {
-            Date date = new Date();
             String secret = generateSecret(user, date);
             StringBuffer sb = new StringBuffer();
             sb.append(secret);
@@ -58,7 +63,6 @@
             LOG.error("Error generating SHA-256 hash", e);
         }
         return null;
-
     }
 
     private String generateSecret(String user, Date date) throws UnsupportedEncodingException, NoSuchAlgorithmException {
@@ -90,9 +94,11 @@
             String secret = parts[0];
             String user = parts[1];
             Date date = Utils.toDateFromIso(parts[2]);
-            if (new Date().after(new Date(date.getTime() + VALID_TOKEN_PERIOD * 60 * 60 * 1000))) {
-                return false;
-            }
+            if (date.getTime() > 0 || !user.equals(ApiResource.API_CLIENT_USERNAME)) {
+                if (new Date().after(new Date(date.getTime() + VALID_TOKEN_PERIOD * 60 * 60 * 1000))) {
+                    return false;
+                }
+            } // else: It's a securis-client API call
             String newSecret = generateSecret(user, date);
             return newSecret.equals(secret);
         } catch (IOException e) {
@@ -136,4 +142,10 @@
         return null;
     }
 
+    public static void main(String[] args) {
+        // client token:
+        // OTk3ODRiMzY5NzQ5MWI5NmYyZGQyODRiYjY2ZTU2YzdmMTZjYzM3YTY3N2ExM2M3ODI2MjU5ZTMzOTIyYjUzNSBfY2xpZW50IDE5NzAtMDEtMDFUMDA6NTk6NTkuOTk5KzAxMDA=
+        // OTk3ODRiMzY5NzQ5MWI5NmYyZGQyODRiYjY2ZTU2YzdmMTZjYzM3YTY3N2ExM2M3ODI2MjU5ZTMzOTIyYjUzNSBfY2xpZW50IDE5NzAtMDEtMDFUMDA6NTk6NTkuOTk5KzAxMDA=
+        System.out.print("client token: " + new TokenHelper().generateToken("_client", new Date(-1)));
+    }
 }
diff --git a/securis/src/main/resources/db/schema.sql b/securis/src/main/resources/db/schema.sql
index 9dd74ed..61e8b7d 100644
--- a/securis/src/main/resources/db/schema.sql
+++ b/securis/src/main/resources/db/schema.sql
@@ -23,6 +23,7 @@
 drop table IF EXISTS application;
 CREATE TABLE IF NOT EXISTS application (
   id INT NOT NULL auto_increment,
+  code VARCHAR(4) NOT NULL ,
   name VARCHAR(45) NOT NULL ,
   license_filename VARCHAR(100) NOT NULL ,
   description VARCHAR(500) NULL ,
@@ -105,6 +106,7 @@
 CREATE TABLE IF NOT EXISTS license (
   id INT NOT NULL auto_increment,
   code VARCHAR(100) NOT NULL ,
+  activation_code VARCHAR(100) NULL ,
   code_suffix INT NULL ,
   request_data VARCHAR(1024) NULL ,
   request_data_hash VARCHAR(64) NULL ,
diff --git a/securis/src/main/webapp/js/catalogs.json b/securis/src/main/webapp/js/catalogs.json
index ccb1948..77bf78a 100644
--- a/securis/src/main/webapp/js/catalogs.json
+++ b/securis/src/main/webapp/js/catalogs.json
@@ -10,6 +10,12 @@
 		"autogenerate" : true,
 		"readOnly" : true
 	}, {
+		"name" : "code",
+		"display" : "Code",
+		"type" : "string",
+		"maxlength" : 4,
+		"mandatory" : true
+	}, {
 		"name" : "name",
 		"display" : "Name",
 		"type" : "string",
diff --git a/securis/src/main/webapp/js/licenses.js b/securis/src/main/webapp/js/licenses.js
index 712f7e8..fc4faed 100644
--- a/securis/src/main/webapp/js/licenses.js
+++ b/securis/src/main/webapp/js/licenses.js
@@ -724,7 +724,10 @@
 	                                		email: true
 	                                	}
 	                                	$scope.maxlength = {
+	                                		activation_code: 36,
 	                                		code: 50,
+	                                		full_name: 100,
+	                                		email: 100,
 	                                		request_data: 500,
 	                                		comments: 1024
 	                                	}
@@ -787,7 +790,8 @@
 	                                		$scope.isNew = true;
 	                                		$scope.showForm = true;
 	                                		$scope.license = {
-	                                				pack_id: $scope.currentPack.id
+	                                				pack_id: $scope.currentPack.id,
+	                                				activation_code: $scope.createUUID()
 	                                		}
 	                                		Packs.nextliccode($scope.currentPack.id, function(data) {
 	                    						console.log('New code: ' + data);
@@ -797,12 +801,26 @@
 	                                			$('#licenseForm * #email').focus();
 	                                		}, 0);
 	                                	}
+	                                	$scope.createUUID = function () {
+	                                	    // http://www.ietf.org/rfc/rfc4122.txt
+	                                	    var s = new Array(36);
+	                                	    var hexDigits = "0123456789abcdef";
+	                                	    for (var i = 0; i < 36; i++) {
+	                                	        s[i] = hexDigits.substr(Math.random() * 0x10 | 0, 1);
+	                                	    }
+	                                	    s[14] = "4";  // bits 12-15 of the time_hi_and_version field to 0010
+	                                	    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);  // bits 6-7 of the clock_seq_hi_and_reserved to 01
+	                                	    s[8] = s[13] = s[18] = s[23] = "-";
 
+	                                	    var uuid = s.join("");
+	                                	    return uuid;
+	                                	}
 	                                	$scope.editLicense = function(selectedlicense) {
 	                                		$scope.isNew = false;
 	                                		$scope.showForm = true;
 	                                		$scope.license = selectedlicense;
 	                                		$scope.license.status_name = Licenses.getStatusName($scope.license.status); 
+	                                		$scope.license.activation_code = $scope.license.activation_code || $scope.createUUID();
 
 	                                		setTimeout(function() {
 	                                			$('#licenseForm * #code').focus();
diff --git a/securis/src/main/webapp/licenses.html b/securis/src/main/webapp/licenses.html
index db2eb06..f8394b7 100644
--- a/securis/src/main/webapp/licenses.html
+++ b/securis/src/main/webapp/licenses.html
@@ -56,7 +56,7 @@
 							ng-show="packForm.code.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
 								ng-show="packForm.code.$error.maxlength"
-								ng-bind="maxlengthErrorMsg('Code', maxlength.code)"></span> <span
+								ng-bind="maxLengthErrorMsg('Code', maxlength.code)"></span> <span
 								ng-show="packForm.code.$error.required"
 								ng-bind="mandatoryFieldErrorMsg('Code')"></span>
 						</div>
@@ -103,7 +103,7 @@
 							ng-show="packForm.num_licenses.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
 								ng-show="packForm.num_licenses.$error.maxlength"
-								ng-bind="maxlengthErrorMsg('Num. Licenses', maxlength.num_licenses)"></span>
+								ng-bind="maxLengthErrorMsg('Num. Licenses', maxlength.num_licenses)"></span>
 							<span ng-show="packForm.num_licenses.$error.required"
 								ng-bind="mandatoryFieldErrorMsg('Num. Licenses')"></span>
 						</div>
@@ -190,7 +190,7 @@
 						i18n>Period for renew (days)</label>
 					<div class="col-md-8">
 						<input type="number" id="renew_valid_period" name="renew_valid_period"
-						    min="1" class="form-control" ng-model="pack.renew_valid_period"
+						    min="0" class="form-control" ng-model="pack.renew_valid_period"
 							ng-required="true" />
 						<div class="alert inline-alert alert-warning"
 							ng-show="packForm.renew_valid_period.$invalid">
@@ -215,7 +215,7 @@
 							ng-show="packForm.comments.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
 								ng-show="packForm.comments.$error.maxlength"
-								ng-bind="maxlengthErrorMsg('Comments', maxlength.comments)"></span>
+								ng-bind="maxLengthErrorMsg('Comments', maxlength.comments)"></span>
 							<span ng-show="packForm.comments.$error.required"
 								ng-bind="mandatoryFieldErrorMsg('comments')"></span>
 						</div>
@@ -415,9 +415,25 @@
 							ng-show="licenseForm.code.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
 								ng-show="licenseForm.code.$error.maxlength"
-								ng-bind="maxlengthErrorMsg('Code', maxlength.code)"></span> <span
+								ng-bind="maxLengthErrorMsg('Code', maxlength.code)"></span> <span
 								ng-show="licenseForm.code.$error.required"
 								ng-bind="mandatoryFieldErrorMsg('Code')"></span>
+						</div>
+					</div>
+				</div>
+				<div class="form-group">
+					<label class="col-md-3 control-label" for="activation_code" i18n>Activation code</label>
+					<div class="col-md-8">
+						<input type="string" id="activation_code" name="activation_code" placeholder=""
+							class="form-control" ng-model="license.activation_code" readonly
+							ng-required="mandatory.activation_code" ng-maxlength="{{maxlength.activation_code}}" />
+						<div class="alert inline-alert alert-warning"
+							ng-show="licenseForm.activation_code.$invalid">
+							<span class="glyphicon glyphicon-warning-sign"></span> <span
+								ng-show="licenseForm.activation_code.$error.maxlength"
+								ng-bind="maxLengthErrorMsg('Activation code', maxlength.activation_code)"></span> <span
+								ng-show="licenseForm.activation_code.$error.required"
+								ng-bind="mandatoryFieldErrorMsg('Activation code')"></span>
 						</div>
 					</div>
 				</div>
@@ -442,12 +458,12 @@
 					<div class="col-md-8">
 						<input type="string" id="full_name" name="full_name"
 							placeholder="" class="form-control" ng-model="license.full_name"
-							ng-required="mandatory.full_name" />
+							ng-required="mandatory.full_name" ng-maxlength="{{maxlength.full_name}}"  />
 						<div class="alert inline-alert alert-warning"
 							ng-show="licenseForm.full_name.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
 								ng-show="licenseForm.full_name.$error.maxlength"
-								ng-bind="maxlengthErrorMsg('User full name', maxlength.full_name)"></span>
+								ng-bind="maxLengthErrorMsg('User full name', maxlength.full_name)"></span>
 							<span ng-show="licenseForm.full_name.$error.required"
 								ng-bind="mandatoryFieldErrorMsg('User full name')"></span>
 						</div>
@@ -460,14 +476,14 @@
 					<div class="col-md-8">
 						<input type="email" id="email" name="email" placeholder=""
 							class="form-control" ng-model="license.email"
-							ng-required="mandatory.email" />
+							ng-required="mandatory.email" ng-maxlength="{{maxlength.email}}"   />
 						<div class="alert inline-alert alert-warning"
 							ng-show="licenseForm.email.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
 								ng-show="licenseForm.email.$error.email"
 								ng-bind="'Please, write a valid email address'"></span> <span
 								ng-show="licenseForm.email.$error.maxlength"
-								ng-bind="maxlengthErrorMsg('User email', maxlength.email)"></span>
+								ng-bind="maxLengthErrorMsg('User email', maxlength.email)"></span>
 							<span ng-show="licenseForm.email.$error.required"
 								ng-bind="mandatoryFieldErrorMsg('User email')"></span>
 						</div>
@@ -490,7 +506,7 @@
 							<span class="glyphicon glyphicon-warning-sign">
 								<span
 									ng-show="licenseForm.request_data.$error.maxlength"
-									ng-bind="maxlengthErrorMsg('Request data', maxlength.request_data)"></span>
+									ng-bind="maxLengthErrorMsg('Request data', maxlength.request_data)"></span>
 								<span ng-show="licenseForm.request_data.$error.required"
 									ng-bind="mandatoryFieldErrorMsg('Request data')"></span>
 							</span> 
@@ -514,7 +530,7 @@
 							ng-show="licenseForm.comments.$invalid">
 							<span class="glyphicon glyphicon-warning-sign"></span> <span
 								ng-show="licenseForm.comments.$error.maxlength"
-								ng-bind="maxlengthErrorMsg('Comments', maxlength.comments)"></span>
+								ng-bind="maxLengthErrorMsg('Comments', maxlength.comments)"></span>
 							<span ng-show="licenseForm.comments.$error.required"
 								ng-bind="mandatoryFieldErrorMsg('comments')"></span>
 						</div>

--
Gitblit v1.3.2