| .. | .. |
|---|
| 1 | 1 | package net.curisit.securis.services; |
|---|
| 2 | 2 | |
|---|
| 3 | | -import java.io.File; |
|---|
| 4 | 3 | import java.io.IOException; |
|---|
| 5 | 4 | import java.util.Date; |
|---|
| 6 | 5 | import java.util.List; |
|---|
| 7 | | -import java.util.Map; |
|---|
| 8 | | -import java.util.TreeMap; |
|---|
| 9 | 6 | |
|---|
| 10 | 7 | import javax.inject.Inject; |
|---|
| 11 | 8 | import javax.inject.Provider; |
|---|
| .. | .. |
|---|
| 19 | 16 | import javax.ws.rs.core.MediaType; |
|---|
| 20 | 17 | import javax.ws.rs.core.Response; |
|---|
| 21 | 18 | |
|---|
| 22 | | -import net.curisit.integrity.commons.JsonUtils; |
|---|
| 23 | 19 | import net.curisit.securis.LicenseGenerator; |
|---|
| 24 | 20 | import net.curisit.securis.LicenseManager; |
|---|
| 25 | 21 | import net.curisit.securis.SeCurisException; |
|---|
| .. | .. |
|---|
| 27 | 23 | import net.curisit.securis.beans.RequestBean; |
|---|
| 28 | 24 | import net.curisit.securis.beans.SignedLicenseBean; |
|---|
| 29 | 25 | import net.curisit.securis.beans.StatusBean; |
|---|
| 26 | +import net.curisit.securis.db.BlockedRequest; |
|---|
| 30 | 27 | import net.curisit.securis.db.License; |
|---|
| 28 | +import net.curisit.securis.db.LicenseHistory; |
|---|
| 29 | +import net.curisit.securis.db.LicenseStatus; |
|---|
| 30 | +import net.curisit.securis.db.Pack; |
|---|
| 31 | +import net.curisit.securis.db.User; |
|---|
| 31 | 32 | import net.curisit.securis.security.BasicSecurityContext; |
|---|
| 32 | 33 | import net.curisit.securis.security.Securable; |
|---|
| 33 | 34 | import net.curisit.securis.services.exception.SeCurisServiceException; |
|---|
| 34 | 35 | import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes; |
|---|
| 36 | +import net.curisit.securis.services.helpers.LicenseHelper; |
|---|
| 37 | +import net.curisit.securis.utils.JsonUtils; |
|---|
| 38 | +import net.curisit.securis.utils.LicUtils; |
|---|
| 39 | +import net.curisit.securis.utils.SignatureHelper; |
|---|
| 35 | 40 | import net.curisit.securis.utils.TokenHelper; |
|---|
| 36 | 41 | |
|---|
| 37 | | -import org.apache.commons.io.IOUtils; |
|---|
| 38 | 42 | import org.apache.commons.lang.time.DateUtils; |
|---|
| 39 | 43 | import org.apache.logging.log4j.LogManager; |
|---|
| 40 | 44 | import org.apache.logging.log4j.Logger; |
|---|
| .. | .. |
|---|
| 57 | 61 | TokenHelper tokenHelper; |
|---|
| 58 | 62 | |
|---|
| 59 | 63 | @Inject |
|---|
| 64 | + private LicenseHelper licenseHelper; |
|---|
| 65 | + |
|---|
| 66 | + @Inject |
|---|
| 60 | 67 | Provider<EntityManager> emProvider; |
|---|
| 61 | 68 | |
|---|
| 62 | 69 | @Inject |
|---|
| 63 | 70 | LicenseGenerator licenseGenerator; |
|---|
| 71 | + |
|---|
| 72 | + private static final String CLIENT_USERNAME = "_client"; |
|---|
| 64 | 73 | |
|---|
| 65 | 74 | public ApiResource() { |
|---|
| 66 | 75 | } |
|---|
| .. | .. |
|---|
| 116 | 125 | SeCurisException { |
|---|
| 117 | 126 | LOG.info("Request to get license: {}", request); |
|---|
| 118 | 127 | |
|---|
| 119 | | - Map<String, Object> metadata = getLicenseMetadata(request); |
|---|
| 120 | | - License licDB = getLicenseData(request); |
|---|
| 128 | + SignedLicenseBean lic = createLicense(request, emProvider.get(), false); |
|---|
| 121 | 129 | |
|---|
| 122 | | - Date expirationDate = licDB.getExpirationDate(); |
|---|
| 123 | | - String licenseTypeCode = licDB.getPack().getLicenseType().getCode(); |
|---|
| 124 | | - String licenseCode = licDB.getCode(); |
|---|
| 125 | | - LicenseBean lic = licenseGenerator.generateLicense(request, metadata, expirationDate, licenseTypeCode, licenseCode); |
|---|
| 126 | | - SignedLicenseBean signedLic = new SignedLicenseBean(lic); |
|---|
| 127 | | - return Response.ok(signedLic).build(); |
|---|
| 130 | + return Response.ok(lic).build(); |
|---|
| 128 | 131 | } |
|---|
| 129 | 132 | |
|---|
| 130 | 133 | /** |
|---|
| .. | .. |
|---|
| 186 | 189 | throw new SeCurisServiceException(ErrorCodes.LICENSE_NOT_READY_FOR_RENEW, "The license is still valid, not ready for renew"); |
|---|
| 187 | 190 | } |
|---|
| 188 | 191 | |
|---|
| 189 | | - Map<String, Object> metadata = getLicenseMetadata(previousLic); |
|---|
| 190 | | - License licDB = getLicenseData(previousLic); |
|---|
| 192 | + SignedLicenseBean lic = createLicense(previousLic, emProvider.get(), true); |
|---|
| 191 | 193 | |
|---|
| 192 | | - Date expirationDate = licDB.getExpirationDate(); |
|---|
| 193 | | - String licenseTypeCode = licDB.getPack().getLicenseType().getCode(); |
|---|
| 194 | | - String licenseCode = licDB.getCode(); |
|---|
| 195 | | - LicenseBean lic = licenseGenerator.generateLicense(previousLic, metadata, expirationDate, licenseTypeCode, licenseCode); |
|---|
| 196 | 194 | return Response.ok(lic).build(); |
|---|
| 195 | + } |
|---|
| 196 | + |
|---|
| 197 | + /** |
|---|
| 198 | + * License validation on server side, in this case we validate that the |
|---|
| 199 | + * current licenses has not been cancelled. |
|---|
| 200 | + * |
|---|
| 201 | + * @param currentLic |
|---|
| 202 | + * @param bsc |
|---|
| 203 | + * @return |
|---|
| 204 | + * @throws IOException |
|---|
| 205 | + * @throws SeCurisServiceException |
|---|
| 206 | + * @throws SeCurisException |
|---|
| 207 | + */ |
|---|
| 208 | + @POST |
|---|
| 209 | + @Path("/validate") |
|---|
| 210 | + @Consumes(MediaType.APPLICATION_JSON) |
|---|
| 211 | + // TODO: Enable this: @Securable |
|---|
| 212 | + @Produces({ |
|---|
| 213 | + MediaType.APPLICATION_JSON |
|---|
| 214 | + }) |
|---|
| 215 | + @Transactional |
|---|
| 216 | + public Response validate(LicenseBean currentLic, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException { |
|---|
| 217 | + LOG.info("Validate license: {}", currentLic); |
|---|
| 218 | + EntityManager em = emProvider.get(); |
|---|
| 219 | + try { |
|---|
| 220 | + SignatureHelper.getInstance().validateSignature(currentLic); |
|---|
| 221 | + } catch (SeCurisException ex) { |
|---|
| 222 | + throw new SeCurisServiceException(ErrorCodes.LICENSE_DATA_IS_NOT_VALID, "The license signature is not valid"); |
|---|
| 223 | + } |
|---|
| 224 | + licenseHelper.assertLicenseStatusIsActive(currentLic, em); |
|---|
| 225 | + |
|---|
| 226 | + return Response.ok(currentLic).build(); |
|---|
| 197 | 227 | } |
|---|
| 198 | 228 | |
|---|
| 199 | 229 | /** |
|---|
| .. | .. |
|---|
| 236 | 266 | return createFromRequest(lic, bsc); |
|---|
| 237 | 267 | } |
|---|
| 238 | 268 | |
|---|
| 239 | | - private License getLicenseData(RequestBean req) throws SeCurisException { |
|---|
| 240 | | - // TODO: The dummy expiration date is temporal, this info should be read |
|---|
| 241 | | - // from DB |
|---|
| 242 | | - License lic = new License(); |
|---|
| 243 | | - // TODO: IMPLEMENT LICENSE GENERATION |
|---|
| 244 | | - |
|---|
| 245 | | - // lic.setExpirationDate(new Date(new Date().getTime() + (1000L * 3600 * |
|---|
| 246 | | - // 24 * 30))); |
|---|
| 247 | | - // lic.setCode(req.getPackCode() + "-LIC-INTERNAL"); |
|---|
| 248 | | - // LicenseType lt = new LicenseType(); |
|---|
| 249 | | - // lt.setCode("TYPE-" + req.getAppCode()); |
|---|
| 250 | | - // Pack pack = new Pack(); |
|---|
| 251 | | - // pack.setLicenseType(lt); |
|---|
| 252 | | - // lic.setPack(pack); |
|---|
| 253 | | - return lic; |
|---|
| 254 | | - } |
|---|
| 255 | | - |
|---|
| 256 | 269 | /** |
|---|
| 257 | | - * Extract the corresponding metadata for the Request license given |
|---|
| 270 | + * Creates a new signed license from request data or from previous license |
|---|
| 271 | + * if It's a renew |
|---|
| 258 | 272 | * |
|---|
| 259 | 273 | * @param req |
|---|
| 274 | + * @param em |
|---|
| 275 | + * @param renew |
|---|
| 260 | 276 | * @return |
|---|
| 261 | | - * @throws SeCurisException |
|---|
| 277 | + * @throws SeCurisServiceException |
|---|
| 262 | 278 | */ |
|---|
| 263 | | - @SuppressWarnings("unchecked") |
|---|
| 264 | | - private Map<String, Object> getLicenseMetadata(RequestBean req) throws SeCurisException { |
|---|
| 265 | | - // TODO: The dummy metadata file is temporal, this info should be read |
|---|
| 266 | | - // from DB |
|---|
| 267 | | - File dummyMetadata = new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "dummy_metadata.json"); |
|---|
| 268 | | - Map<String, Object> metadata = null; |
|---|
| 269 | | - try { |
|---|
| 270 | | - String metadataJson = IOUtils.toString(dummyMetadata.toURI()); |
|---|
| 271 | | - metadata = (Map<String, Object>) JsonUtils.json2map(metadataJson).get(req.getLicenseTypeCode()); |
|---|
| 272 | | - if (metadata == null) { |
|---|
| 273 | | - throw new SeCurisException("App code in request is unknown: " + req.getLicenseTypeCode()); |
|---|
| 279 | + private SignedLicenseBean createLicense(RequestBean req, EntityManager em, boolean renew) throws SeCurisServiceException { |
|---|
| 280 | + LicenseBean previousLicenseBean = null; |
|---|
| 281 | + License lic = null; |
|---|
| 282 | + if (renew) { |
|---|
| 283 | + previousLicenseBean = (LicenseBean) req; |
|---|
| 284 | + lic = License.findLicenseByCode(previousLicenseBean.getLicenseCode(), em); |
|---|
| 285 | + if (lic.getStatus() != LicenseStatus.ACTIVE && lic.getStatus() != LicenseStatus.PRE_ACTIVE) { |
|---|
| 286 | + throw new SeCurisServiceException(ErrorCodes.INVALID_DATA, "The current license has been cancelled"); |
|---|
| 274 | 287 | } |
|---|
| 275 | | - metadata = new TreeMap<>(metadata); |
|---|
| 276 | | - } catch (IOException e) { |
|---|
| 277 | | - LOG.error("Error reading dummy metadata file", e); |
|---|
| 278 | | - throw new SeCurisException("Error reading dummy metadata file"); |
|---|
| 288 | + } else { |
|---|
| 289 | + lic = new License(); |
|---|
| 279 | 290 | } |
|---|
| 280 | 291 | |
|---|
| 281 | | - return metadata; |
|---|
| 292 | + if (!renew) { |
|---|
| 293 | + License existingLicense = License.findLicenseByRequestData(lic.getRequestData(), em); |
|---|
| 294 | + if (existingLicense != null) { |
|---|
| 295 | + throw new SeCurisServiceException(ErrorCodes.DUPLICATED_REQUEST_DATA, "There is already an active license for current request data"); |
|---|
| 296 | + } |
|---|
| 297 | + } |
|---|
| 298 | + Pack pack = em.createNamedQuery("pack-by-code", Pack.class).setParameter("code", req.getPackCode()).getSingleResult(); |
|---|
| 299 | + |
|---|
| 300 | + if (!renew && pack.getNumAvailables() <= 0) { |
|---|
| 301 | + throw new SeCurisServiceException(ErrorCodes.NO_AVAILABLE_LICENSES, "The current pack has no licenses availables"); |
|---|
| 302 | + } |
|---|
| 303 | + SignedLicenseBean signedLicense; |
|---|
| 304 | + try { |
|---|
| 305 | + String licCode; |
|---|
| 306 | + if (renew) { |
|---|
| 307 | + licCode = previousLicenseBean.getLicenseCode(); |
|---|
| 308 | + } else { |
|---|
| 309 | + licCode = LicUtils.getLicenseCode(pack.getCode(), licenseHelper.getNextCodeSuffix(pack.getId(), em)); |
|---|
| 310 | + } |
|---|
| 311 | + Date expirationDate = licenseHelper.getExpirationDateFromPack(pack, !renew); |
|---|
| 312 | + |
|---|
| 313 | + LicenseBean lb = licenseGenerator.generateLicense(req, licenseHelper.extractPackMetadata(pack.getMetadata()), expirationDate, licCode, |
|---|
| 314 | + pack.getAppName()); |
|---|
| 315 | + signedLicense = new SignedLicenseBean(lb); |
|---|
| 316 | + } catch (SeCurisException e) { |
|---|
| 317 | + throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Error generating license: " + e.toString()); |
|---|
| 318 | + } |
|---|
| 319 | + try { |
|---|
| 320 | + lic.setRequestData(JsonUtils.toJSON(signedLicense, RequestBean.class)); |
|---|
| 321 | + if (BlockedRequest.isRequestBlocked(lic.getRequestData(), em)) { |
|---|
| 322 | + throw new SeCurisServiceException(ErrorCodes.BLOCKED_REQUEST_DATA, "Given request data is blocked and cannot be activated"); |
|---|
| 323 | + } |
|---|
| 324 | + lic.setLicenseData(JsonUtils.toJSON(signedLicense)); |
|---|
| 325 | + } catch (SeCurisException e) { |
|---|
| 326 | + LOG.error("Error generating license JSON", e); |
|---|
| 327 | + throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generating license JSON"); |
|---|
| 328 | + } |
|---|
| 329 | + |
|---|
| 330 | + lic.setModificationTimestamp(new Date()); |
|---|
| 331 | + lic.setExpirationDate(signedLicense.getExpirationDate()); |
|---|
| 332 | + User user = em.find(User.class, CLIENT_USERNAME); |
|---|
| 333 | + if (!renew) { |
|---|
| 334 | + |
|---|
| 335 | + lic.setPack(pack); |
|---|
| 336 | + lic.setCreatedBy(user); |
|---|
| 337 | + lic.setCreationTimestamp(new Date()); |
|---|
| 338 | + lic.setStatus(LicenseStatus.PRE_ACTIVE); |
|---|
| 339 | + lic.setCode(signedLicense.getLicenseCode()); |
|---|
| 340 | + lic.setCodeSuffix(LicUtils.getLicenseCodeSuffix(signedLicense.getLicenseCode())); |
|---|
| 341 | + em.persist(lic); |
|---|
| 342 | + em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.CREATE)); |
|---|
| 343 | + if (lic.getStatus() == LicenseStatus.ACTIVE) { |
|---|
| 344 | + em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.PRE_ACTIVATE, "Pre-activated on creation")); |
|---|
| 345 | + } |
|---|
| 346 | + } else { |
|---|
| 347 | + lic.setStatus(LicenseStatus.ACTIVE); |
|---|
| 348 | + em.merge(lic); |
|---|
| 349 | + em.persist(licenseHelper.createLicenseHistoryAction(lic, user, LicenseHistory.Actions.RENEW)); |
|---|
| 350 | + } |
|---|
| 351 | + |
|---|
| 352 | + return signedLicense; |
|---|
| 282 | 353 | } |
|---|
| 283 | 354 | |
|---|
| 284 | 355 | } |
|---|