rsanchez
2014-10-22 ddec2c5c7b7842536d6d705c2de20f96e16c8aa8
#2021 feature - Added blocked request table and refactoring License
actions
3 files added
15 files modified
changed files
securis/pom.xml patch | view | blame | history
securis/src/main/java/net/curisit/securis/LicenseGenerator.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/SeCurisServer.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/BlockedRequest.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/License.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/LicenseHistory.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/LicenseStatus.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/Pack.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/PackStatus.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/db/User.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/services/ApiResource.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/services/LicenseResource.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java patch | view | blame | history
securis/src/main/java/net/curisit/securis/utils/EmailManager.java patch | view | blame | history
securis/src/main/resources/db/schema.sql patch | view | blame | history
securis/src/main/resources/securis-server.properties patch | view | blame | history
securis/src/main/resources/static/js/catalogs.json patch | view | blame | history
securis/src/main/resources/static/licenses.html patch | view | blame | history
securis/pom.xml
....@@ -59,13 +59,23 @@
5959 <dependency>
6060 <groupId>net.curisit</groupId>
6161 <artifactId>securis-client</artifactId>
62
- <version>0.9.8-SNAPSHOT</version>
62
+ <version>1.0.1-SNAPSHOT</version>
6363 </dependency>
6464 <dependency>
6565 <groupId>org.hibernate</groupId>
6666 <artifactId>hibernate-c3p0</artifactId>
6767 <version>4.3.6.Final</version>
6868 </dependency>
69
+ <dependency>
70
+ <groupId>org.apache.httpcomponents</groupId>
71
+ <artifactId>httpclient</artifactId>
72
+ <version>4.4-beta1</version>
73
+ </dependency>
74
+ <dependency>
75
+ <groupId>org.apache.httpcomponents</groupId>
76
+ <artifactId>httpmime</artifactId>
77
+ <version>4.4-beta1</version>
78
+ </dependency>
6979 </dependencies>
7080 <build>
7181 <plugins>
securis/src/main/java/net/curisit/securis/LicenseGenerator.java
....@@ -11,7 +11,6 @@
1111 import java.security.Signature;
1212 import java.security.SignatureException;
1313 import java.security.spec.InvalidKeySpecException;
14
-import java.text.MessageFormat;
1514 import java.util.Date;
1615 import java.util.Map;
1716 import java.util.TreeMap;
....@@ -57,13 +56,11 @@
5756 * @return
5857 * @throws SeCurisException
5958 */
60
- public LicenseBean generateLicense(RequestBean req, Map<String, Object> metadata, Date expirationDate, String licenseType, String licenseCode)
59
+ public LicenseBean generateLicense(RequestBean req, Map<String, Object> metadata, Date expirationDate, String licenseCode, String appName)
6160 throws SeCurisException {
62
- LOG.info(MessageFormat.format("Generating license: MAC: {0}, Customer code: {1}, AppCode: {2}", req.getMacAddresses(), req.getCustomerCode(),
63
- req.getAppCode()));
61
+ LOG.debug("Generating license: MAC: {}, Customer code: {}, AppName: {}", req.getMacAddresses(), req.getCustomerCode(), appName);
6462 LicenseBean license = new LicenseBean(req);
65
- license.setLicenseType(licenseType);
66
- license.setLicenseCode(licenseCode);
63
+ license.setAppName(appName);
6764 license.setExpirationDate(expirationDate);
6865 license.setMetadata(metadata);
6966 sign(license);
....@@ -82,7 +79,7 @@
8279 SignedLicenseBean signedLic = new SignedLicenseBean(license);
8380 byte[] json;
8481 try {
85
- json = JsonUtils.toJSON(signedLic, true).getBytes("utf-8");
82
+ json = JsonUtils.toPrettyJSON(signedLic).getBytes("utf-8");
8683 Files.write(Paths.get(file.toURI()), json, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
8784 } catch (UnsupportedEncodingException e) {
8885 LOG.error("Error creating json doc from license: " + license, e);
....@@ -97,7 +94,6 @@
9794 }
9895
9996 /**
100
- * TODO: This method should be removed from client code.
10197 *
10298 * @param licBean
10399 * @return
....@@ -113,7 +109,8 @@
113109 Signature signature;
114110 try {
115111 signature = Signature.getInstance(SignatureHelper.SIGNATURE_GENERATION_ALGORITHM);
116
- signature.initSign(sh.generatePrivateKey(new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "keys" + File.separator + "securis_private_key.pkcs8")));
112
+ signature.initSign(sh.generatePrivateKey(new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "keys"
113
+ + File.separator + "securis_private_key.pkcs8")));
117114
118115 sh.prepareSignature(signature, licBean);
119116
....@@ -147,7 +144,7 @@
147144 metadata.put("timeThreshold", 0);
148145 metadata.put("datasetPrefix", "BP");
149146 metadata.put("extendedMode", true);
150
-
147
+
151148 Date expirationDate = new Date(new Date().getTime() + (1000L * 3600 * 24 * 365 * 10));
152149 LicenseBean lic = LicenseGenerator.getInstance().generateLicense(req, metadata, expirationDate, "CI-01", "LIC-CURISTEC-0001");
153150 LicenseGenerator.getInstance().save(lic, new File("/Users/cproberto/Desktop/AxelLicCI.lic"));
....@@ -156,4 +153,3 @@
156153
157154 }
158155 }
159
-
securis/src/main/java/net/curisit/securis/SeCurisServer.java
....@@ -65,6 +65,10 @@
6565 CONSOLE.info(" $ ./securis-server.sh {start|stop}");
6666 }
6767
68
+ private void testMail() {
69
+
70
+ }
71
+
6872 public static void main(String[] args) throws Exception {
6973 String command;
7074 if (args.length > 0) {
securis/src/main/java/net/curisit/securis/db/BlockedRequest.java
....@@ -0,0 +1,85 @@
1
+package net.curisit.securis.db;
2
+
3
+import java.io.Serializable;
4
+import java.util.Date;
5
+
6
+import javax.persistence.Column;
7
+import javax.persistence.Entity;
8
+import javax.persistence.Id;
9
+import javax.persistence.JoinColumn;
10
+import javax.persistence.ManyToOne;
11
+import javax.persistence.Table;
12
+
13
+import org.codehaus.jackson.annotate.JsonAutoDetect;
14
+import org.codehaus.jackson.annotate.JsonIgnore;
15
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
16
+import org.codehaus.jackson.annotate.JsonProperty;
17
+import org.codehaus.jackson.map.annotate.JsonSerialize;
18
+
19
+/**
20
+ * Entity implementation class for Entity: pack
21
+ *
22
+ */
23
+@JsonAutoDetect
24
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
25
+@Entity
26
+@Table(name = "blocked_request")
27
+@JsonIgnoreProperties(ignoreUnknown = true)
28
+public class BlockedRequest implements Serializable {
29
+
30
+ private static final long serialVersionUID = 1L;
31
+
32
+ @Id
33
+ private String hash;
34
+
35
+ @Column(name = "request_data")
36
+ @JsonProperty("request_data")
37
+ private String requestData;
38
+
39
+ @Column(name = "creation_timestamp")
40
+ @JsonProperty("creation_timestamp")
41
+ private Date creationTimestamp;
42
+
43
+ @JsonIgnore
44
+ @ManyToOne
45
+ @JoinColumn(name = "blocked_by")
46
+ private User blockedBy;
47
+
48
+ public Date getCreationTimestamp() {
49
+ return creationTimestamp;
50
+ }
51
+
52
+ public void setCreationTimestamp(Date creationTimestamp) {
53
+ this.creationTimestamp = creationTimestamp;
54
+ }
55
+
56
+ @Override
57
+ public boolean equals(Object obj) {
58
+ if (!(obj instanceof BlockedRequest))
59
+ return false;
60
+ BlockedRequest other = (BlockedRequest) obj;
61
+ return hash.equals(other.hash);
62
+ }
63
+
64
+ @Override
65
+ public int hashCode() {
66
+
67
+ return (hash == null ? 0 : hash.hashCode());
68
+ }
69
+
70
+ public String getRequestData() {
71
+ return requestData;
72
+ }
73
+
74
+ public void setRequestData(String requestData) {
75
+ this.requestData = requestData;
76
+ }
77
+
78
+ public User getBlockedBy() {
79
+ return blockedBy;
80
+ }
81
+
82
+ public void setBlockedBy(User blockedBy) {
83
+ this.blockedBy = blockedBy;
84
+ }
85
+}
securis/src/main/java/net/curisit/securis/db/License.java
....@@ -63,7 +63,7 @@
6363 @JoinColumn(name = "canceled_by")
6464 private User canceledBy;
6565
66
- private int status;
66
+ private LicenseStatus status;
6767
6868 @Column(name = "full_name")
6969 @JsonProperty("full_name")
....@@ -186,11 +186,11 @@
186186 }
187187 }
188188
189
- public int getStatus() {
189
+ public LicenseStatus getStatus() {
190190 return status;
191191 }
192192
193
- public void setStatus(int status) {
193
+ public void setStatus(LicenseStatus status) {
194194 this.status = status;
195195 }
196196
....@@ -289,21 +289,14 @@
289289 }
290290
291291 public static class Status {
292
- public static final int CREATED = 1;
293
- public static final int REQUESTED = 2;
294
- public static final int PREACTIVE = 3;
295
- public static final int ACTIVE = 4;
296
- public static final int EXPIRED = 5;
297
- public static final int CANCELED = 6;
298
- public static final int DELETED = 7;
299292
300293 private static final Map<Integer, List<Integer>> transitions = Utils.createMap( //
301
- Action.REQUEST, Arrays.asList(CREATED, REQUESTED), //
302
- Action.ACTIVATION, Arrays.asList(REQUESTED, PREACTIVE, EXPIRED), //
303
- Action.SEND, Arrays.asList(ACTIVE, PREACTIVE), //
304
- Action.DOWNLOAD, Arrays.asList(ACTIVE, PREACTIVE), //
305
- Action.CANCEL, Arrays.asList(ACTIVE, PREACTIVE, REQUESTED, EXPIRED), //
306
- Action.DELETE, Arrays.asList(CANCELED, CREATED) //
294
+ Action.REQUEST, Arrays.asList(LicenseStatus.CREATED, LicenseStatus.REQUESTED), //
295
+ Action.ACTIVATION, Arrays.asList(LicenseStatus.REQUESTED, LicenseStatus.PRE_ACTIVE, LicenseStatus.EXPIRED), //
296
+ Action.SEND, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE), //
297
+ Action.DOWNLOAD, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE), //
298
+ Action.CANCEL, Arrays.asList(LicenseStatus.ACTIVE, LicenseStatus.PRE_ACTIVE, LicenseStatus.REQUESTED, LicenseStatus.EXPIRED), //
299
+ Action.DELETE, Arrays.asList(LicenseStatus.CANCELLED, LicenseStatus.CREATED) //
307300
308301 );
309302
....@@ -315,7 +308,7 @@
315308 * @param newStatus
316309 * @return
317310 */
318
- public static boolean isActionValid(Integer action, Integer currentStatus) {
311
+ public static boolean isActionValid(Integer action, LicenseStatus currentStatus) {
319312 List<Integer> validStatuses = transitions.get(currentStatus);
320313
321314 return validStatuses != null && validStatuses.contains(currentStatus);
securis/src/main/java/net/curisit/securis/db/LicenseHistory.java
....@@ -111,8 +111,10 @@
111111 public static final String ADD_REQUEST = "request";
112112 public static final String SEND = "send";
113113 public static final String MODIFY = "modify";
114
+ public static final String DOWNLOAD = "download";
114115 public static final String ACTIVATE = "activate";
115116 public static final String CANCEL = "cancel";
117
+ public static final String BLOCK = "block";
116118 public static final String DELETE = "delete";
117119 }
118120 }
securis/src/main/java/net/curisit/securis/db/LicenseStatus.java
....@@ -0,0 +1,40 @@
1
+package net.curisit.securis.db;
2
+
3
+import org.codehaus.jackson.annotate.JsonCreator;
4
+import org.codehaus.jackson.annotate.JsonValue;
5
+
6
+/**
7
+ * Contains the possible license statuses. For further details:
8
+ * https://redmine.curistec.com/projects/securis/wiki/LicensesServerManagement
9
+ *
10
+ * @author rob
11
+ */
12
+public enum LicenseStatus {
13
+ CREATED("CR"), REQUESTED("RE"), ACTIVE("AC"), PRE_ACTIVE("PA"), EXPIRED("EX"), CANCELLED("CA");
14
+
15
+ private final String code;
16
+
17
+ LicenseStatus(String code) {
18
+ this.code = code;
19
+ }
20
+
21
+ public String getCode() {
22
+ return code;
23
+ }
24
+
25
+ @JsonCreator
26
+ public static LicenseStatus valueFromCode(String code) {
27
+ for (LicenseStatus ps : LicenseStatus.values()) {
28
+ if (ps.code.equals(code)) {
29
+ return ps;
30
+ }
31
+ }
32
+ return null;
33
+ }
34
+
35
+ @JsonValue
36
+ public String getName() {
37
+
38
+ return this.code;
39
+ }
40
+}
securis/src/main/java/net/curisit/securis/db/Pack.java
....@@ -74,7 +74,7 @@
7474 @Column(name = "num_licenses")
7575 @JsonProperty("num_licenses")
7676 private int numLicenses;
77
-
77
+
7878 @Column(name = "init_valid_date")
7979 @JsonProperty("init_valid_date")
8080 private Date initValidDate;
....@@ -155,7 +155,7 @@
155155 }
156156 int num = 0;
157157 for (License lic : licenses) {
158
- if (lic.getStatus() == License.Status.ACTIVE) {
158
+ if (lic.getStatus() == LicenseStatus.ACTIVE || lic.getStatus() == LicenseStatus.PRE_ACTIVE) {
159159 num++;
160160 }
161161 }
....@@ -176,7 +176,7 @@
176176 }
177177 int num = 0;
178178 for (License lic : licenses) {
179
- if (lic.getStatus() != License.Status.CANCELED) {
179
+ if (lic.getStatus() != LicenseStatus.CANCELLED) {
180180 num++;
181181 }
182182 }
....@@ -190,7 +190,7 @@
190190 */
191191 @JsonProperty("num_available")
192192 public int getNumAvailables() {
193
- return numLicenses - getNumCreations();
193
+ return numLicenses - getNumActivations();
194194 }
195195
196196 @JsonProperty("organization_name")
....@@ -250,11 +250,12 @@
250250
251251 @JsonProperty("created_by_name")
252252 public String getCreatedByname() {
253
- return createdBy == null ? null : String.format("%s %s (%s)", createdBy.getFirstName(), createdBy.getLastName() != null ? createdBy.getLastName() : "", createdBy.getUsername());
253
+ return createdBy == null ? null : String.format("%s %s (%s)", createdBy.getFirstName(),
254
+ createdBy.getLastName() != null ? createdBy.getLastName() : "", createdBy.getUsername());
254255 }
255256
256257 @JsonProperty("licensetype_code")
257
- public String getLicenseTypcode() {
258
+ public String getLicenseTypeCode() {
258259 return licenseType == null ? null : licenseType.getCode();
259260 }
260261
....@@ -310,13 +311,13 @@
310311 public boolean equals(Object obj) {
311312 if (!(obj instanceof Pack))
312313 return false;
313
- Pack other = (Pack)obj;
314
+ Pack other = (Pack) obj;
314315 return id.equals(other.id);
315316 }
316
-
317
+
317318 @Override
318319 public int hashCode() {
319
-
320
+
320321 return (id == null ? 0 : id.hashCode());
321322 }
322323 }
securis/src/main/java/net/curisit/securis/db/PackStatus.java
....@@ -3,17 +3,25 @@
33 import org.codehaus.jackson.annotate.JsonCreator;
44 import org.codehaus.jackson.annotate.JsonValue;
55
6
+/**
7
+ * Contains the possible pack statuses. For further details:
8
+ * https://redmine.curistec.com/projects/securis/wiki/LicensesServerManagement
9
+ *
10
+ * @author rob
11
+ */
612 public enum PackStatus {
7
- PENDING("PE"), ACTIVE("AC"), ON_HOLD("OH"), EXPIRED("EX"), CANCELLED("CA");
8
-
13
+ CREATED("CR"), ACTIVE("AC"), ON_HOLD("OH"), EXPIRED("EX"), CANCELLED("CA");
14
+
915 private final String code;
10
- PackStatus(String code ) {
16
+
17
+ PackStatus(String code) {
1118 this.code = code;
1219 }
20
+
1321 public String getCode() {
1422 return code;
1523 }
16
-
24
+
1725 @JsonCreator
1826 public static PackStatus valueFromCode(String code) {
1927 for (PackStatus ps : PackStatus.values()) {
....@@ -26,7 +34,7 @@
2634
2735 @JsonValue
2836 public String getName() {
29
-
37
+
3038 return this.code;
3139 }
3240 }
securis/src/main/java/net/curisit/securis/db/User.java
....@@ -67,6 +67,8 @@
6767
6868 private String lang;
6969
70
+ private String email;
71
+
7072 @JsonIgnore
7173 @ManyToMany
7274 @JoinTable(name = "user_organization", //
....@@ -223,6 +225,14 @@
223225 }
224226 }
225227
228
+ public String getEmail() {
229
+ return email;
230
+ }
231
+
232
+ public void setEmail(String email) {
233
+ this.email = email;
234
+ }
235
+
226236 public static class Rol {
227237 public static final int ADVANCE = 0x01;
228238 public static final int ADMIN = 0x02;
securis/src/main/java/net/curisit/securis/services/ApiResource.java
....@@ -28,8 +28,6 @@
2828 import net.curisit.securis.beans.SignedLicenseBean;
2929 import net.curisit.securis.beans.StatusBean;
3030 import net.curisit.securis.db.License;
31
-import net.curisit.securis.db.LicenseType;
32
-import net.curisit.securis.db.Pack;
3331 import net.curisit.securis.security.BasicSecurityContext;
3432 import net.curisit.securis.security.Securable;
3533 import net.curisit.securis.services.exception.SeCurisServiceException;
....@@ -96,9 +94,10 @@
9694 return Response.ok(status).build();
9795 }
9896
99
-
10097 /**
101
- * Request a new license file based in a RequestBean object sent as parameter
98
+ * Request a new license file based in a RequestBean object sent as
99
+ * parameter
100
+ *
102101 * @param mpfdi
103102 * @param bsc
104103 * @return
....@@ -108,17 +107,18 @@
108107 @POST
109108 @Path("/request")
110109 @Consumes(MediaType.APPLICATION_JSON)
111
- //TODO: Enable this: @Securable
110
+ // TODO: Enable this: @Securable
112111 @Produces({
113112 MediaType.APPLICATION_JSON
114113 })
115114 @Transactional
116
- public Response createFromRequest(RequestBean request, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
115
+ public Response createFromRequest(RequestBean request, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException,
116
+ SeCurisException {
117117 LOG.info("Request to get license: {}", request);
118118
119119 Map<String, Object> metadata = getLicenseMetadata(request);
120120 License licDB = getLicenseData(request);
121
-
121
+
122122 Date expirationDate = licDB.getExpirationDate();
123123 String licenseTypeCode = licDB.getPack().getLicenseType().getCode();
124124 String licenseCode = licDB.getCode();
....@@ -126,9 +126,10 @@
126126 SignedLicenseBean signedLic = new SignedLicenseBean(lic);
127127 return Response.ok(signedLic).build();
128128 }
129
-
129
+
130130 /**
131131 * Returns a License file in JSON format from an uploaded Request file
132
+ *
132133 * @param mpfdi
133134 * @param bsc
134135 * @return
....@@ -145,12 +146,14 @@
145146 })
146147 @Transactional
147148 @SuppressWarnings("unchecked")
148
- public Response createFromRequestFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
149
+ public Response createFromRequestFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException,
150
+ SeCurisServiceException, SeCurisException {
149151 RequestBean req = new RequestBean();
150
- req.setAppCode(mpfdi.getFormDataPart("appCode", String.class, null));
152
+ req.setPackCode(mpfdi.getFormDataPart("packCode", String.class, null));
153
+ req.setLicenseTypeCode(mpfdi.getFormDataPart("licenseTypeCode", String.class, null));
154
+ req.setCustomerCode(mpfdi.getFormDataPart("customerCode", String.class, null));
151155 req.setArch(mpfdi.getFormDataPart("arch", String.class, null));
152156 req.setCrcLogo(mpfdi.getFormDataPart("crcLogo", String.class, null));
153
- req.setCustomerCode(mpfdi.getFormDataPart("customerCode", String.class, null));
154157 req.setMacAddresses(mpfdi.getFormDataPart("macAddresses", List.class, null));
155158 req.setOsName(mpfdi.getFormDataPart("osName", String.class, null));
156159
....@@ -170,12 +173,13 @@
170173 @POST
171174 @Path("/renew")
172175 @Consumes(MediaType.APPLICATION_JSON)
173
- //TODO: Enable this: @Securable
176
+ // TODO: Enable this: @Securable
174177 @Produces({
175178 MediaType.APPLICATION_JSON
176179 })
177180 @Transactional
178
- public Response renewFromPreviousLicense(LicenseBean previousLic, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
181
+ public Response renewFromPreviousLicense(LicenseBean previousLic, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException,
182
+ SeCurisException {
179183 LOG.info("Renew license: {}", previousLic);
180184
181185 if (previousLic.getExpirationDate().after(DateUtils.addMonths(new Date(), 1))) {
....@@ -184,16 +188,17 @@
184188
185189 Map<String, Object> metadata = getLicenseMetadata(previousLic);
186190 License licDB = getLicenseData(previousLic);
187
-
191
+
188192 Date expirationDate = licDB.getExpirationDate();
189193 String licenseTypeCode = licDB.getPack().getLicenseType().getCode();
190194 String licenseCode = licDB.getCode();
191195 LicenseBean lic = licenseGenerator.generateLicense(previousLic, metadata, expirationDate, licenseTypeCode, licenseCode);
192196 return Response.ok(lic).build();
193197 }
194
-
198
+
195199 /**
196200 * Returns a new License file in JSON format based in a previous license
201
+ *
197202 * @param mpfdi
198203 * @param bsc
199204 * @return
....@@ -210,12 +215,15 @@
210215 })
211216 @Transactional
212217 @SuppressWarnings("unchecked")
213
- public Response renewFromLicenseFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException, SeCurisServiceException, SeCurisException {
218
+ public Response renewFromLicenseFile(MultipartFormDataInput mpfdi, @Context BasicSecurityContext bsc) throws IOException,
219
+ SeCurisServiceException, SeCurisException {
214220 LicenseBean lic = new LicenseBean();
215
- // TODO: Add more license parameters
216
- lic.setAppCode(mpfdi.getFormDataPart("appCode", String.class, null));
221
+
222
+ lic.setAppName(mpfdi.getFormDataPart("appName", String.class, null));
217223 lic.setArch(mpfdi.getFormDataPart("arch", String.class, null));
218224 lic.setCrcLogo(mpfdi.getFormDataPart("crcLogo", String.class, null));
225
+ lic.setPackCode(mpfdi.getFormDataPart("packCode", String.class, null));
226
+ lic.setLicenseTypeCode(mpfdi.getFormDataPart("licenseCode", String.class, null));
219227 lic.setCustomerCode(mpfdi.getFormDataPart("customerCode", String.class, null));
220228 lic.setMacAddresses(mpfdi.getFormDataPart("macAddresses", List.class, null));
221229 lic.setOsName(mpfdi.getFormDataPart("osName", String.class, null));
....@@ -227,46 +235,50 @@
227235
228236 return createFromRequest(lic, bsc);
229237 }
230
-
231
-
232
-
238
+
233239 private License getLicenseData(RequestBean req) throws SeCurisException {
234
- // TODO: The dummy expiration date is temporal, this info should be read from DB
240
+ // TODO: The dummy expiration date is temporal, this info should be read
241
+ // from DB
235242 License lic = new License();
236
- lic.setExpirationDate(new Date(new Date().getTime() + (1000L * 3600 * 24 * 30 )));
237
- lic.setCode(req.getAppCode() + "-LIC-INTERNAL");
238
- LicenseType lt = new LicenseType();
239
- lt.setCode("TYPE-" + req.getAppCode());
240
- Pack pack = new Pack();
241
- pack.setLicenseType(lt);
242
- lic.setPack(pack);
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);
243253 return lic;
244254 }
245
-
255
+
246256 /**
247257 * Extract the corresponding metadata for the Request license given
258
+ *
248259 * @param req
249260 * @return
250261 * @throws SeCurisException
251262 */
252263 @SuppressWarnings("unchecked")
253264 private Map<String, Object> getLicenseMetadata(RequestBean req) throws SeCurisException {
254
- // TODO: The dummy metadata file is temporal, this info should be read from DB
255
- File dummyMetadata = new File(System.getProperty("user.home") + File.separator + ".SeCuris" + File.separator + "dummy_metadata.json");
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");
256268 Map<String, Object> metadata = null;
257269 try {
258270 String metadataJson = IOUtils.toString(dummyMetadata.toURI());
259
- metadata = (Map<String, Object>)JsonUtils.json2map(metadataJson).get(req.getAppCode());
260
- if (metadata == null)
261
- throw new SeCurisException("App code in request is unknown: " + req.getAppCode());
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());
274
+ }
262275 metadata = new TreeMap<>(metadata);
263276 } catch (IOException e) {
264277 LOG.error("Error reading dummy metadata file", e);
265278 throw new SeCurisException("Error reading dummy metadata file");
266279 }
267
-
280
+
268281 return metadata;
269282 }
270283
271
-
272284 }
securis/src/main/java/net/curisit/securis/services/LicenseResource.java
....@@ -1,11 +1,15 @@
11 package net.curisit.securis.services;
22
33 import java.io.File;
4
+import java.io.FileWriter;
45 import java.io.IOException;
6
+import java.nio.file.Files;
7
+import java.text.MessageFormat;
58 import java.util.Date;
9
+import java.util.HashMap;
610 import java.util.List;
711 import java.util.Map;
8
-import java.util.TreeMap;
12
+import java.util.Set;
913
1014 import javax.inject.Inject;
1115 import javax.inject.Provider;
....@@ -13,6 +17,8 @@
1317 import javax.persistence.TypedQuery;
1418 import javax.ws.rs.Consumes;
1519 import javax.ws.rs.DELETE;
20
+import javax.ws.rs.DefaultValue;
21
+import javax.ws.rs.FormParam;
1622 import javax.ws.rs.GET;
1723 import javax.ws.rs.POST;
1824 import javax.ws.rs.PUT;
....@@ -27,16 +33,26 @@
2733
2834 import net.curisit.integrity.commons.JsonUtils;
2935 import net.curisit.integrity.commons.Utils;
36
+import net.curisit.integrity.exception.CurisException;
3037 import net.curisit.securis.DefaultExceptionHandler;
38
+import net.curisit.securis.LicenseGenerator;
3139 import net.curisit.securis.SeCurisException;
40
+import net.curisit.securis.beans.LicenseBean;
3241 import net.curisit.securis.beans.RequestBean;
42
+import net.curisit.securis.beans.SignedLicenseBean;
43
+import net.curisit.securis.db.Application;
3344 import net.curisit.securis.db.License;
3445 import net.curisit.securis.db.LicenseHistory;
46
+import net.curisit.securis.db.LicenseStatus;
3547 import net.curisit.securis.db.Pack;
48
+import net.curisit.securis.db.PackMetadata;
3649 import net.curisit.securis.db.User;
3750 import net.curisit.securis.security.BasicSecurityContext;
3851 import net.curisit.securis.security.Securable;
3952 import net.curisit.securis.services.exception.SeCurisServiceException;
53
+import net.curisit.securis.services.exception.SeCurisServiceException.ErrorCodes;
54
+import net.curisit.securis.utils.EmailManager;
55
+import net.curisit.securis.utils.Params;
4056 import net.curisit.securis.utils.TokenHelper;
4157
4258 import org.apache.commons.io.IOUtils;
....@@ -60,7 +76,13 @@
6076 TokenHelper tokenHelper;
6177
6278 @Inject
79
+ EmailManager emailManager;
80
+
81
+ @Inject
6382 Provider<EntityManager> emProvider;
83
+
84
+ @Inject
85
+ LicenseGenerator licenseGenerator;
6486
6587 /**
6688 *
....@@ -138,11 +160,16 @@
138160 LOG.error("License with id {} is not active, so It can not downloaded", licId, bsc.getUserPrincipal());
139161 throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "License is not active, so It can not be downloaded");
140162 }
141
- return Response.ok(lic.getLicenseData()).build();
163
+ em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.DOWNLOAD));
164
+ return Response
165
+ .ok(lic.getLicenseData())
166
+ .header("Content-Disposition",
167
+ String.format("attachment; filename=\"%s\"", lic.getPack().getLicenseType().getApplication().getLicenseFilename())).build();
142168 }
143169
144170 /**
145171 * Activate the given license
172
+ *
146173 * @param licId
147174 * @param bsc
148175 * @return
....@@ -168,8 +195,9 @@
168195 + " can not be activated from the current license status");
169196 }
170197
171
- lic.setStatus(License.Status.ACTIVE);
198
+ lic.setStatus(LicenseStatus.ACTIVE);
172199 lic.setModificationTimestamp(new Date());
200
+
173201 em.persist(lic);
174202 User user = getUser(bsc.getUserPrincipal().getName(), em);
175203 em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.ACTIVATE));
....@@ -178,6 +206,7 @@
178206
179207 /**
180208 * Send license file by email to the organization
209
+ *
181210 * @param licId
182211 * @param bsc
183212 * @return
....@@ -192,13 +221,34 @@
192221 @Produces({
193222 MediaType.APPLICATION_JSON
194223 })
195
- public Response send(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
224
+ public Response send(@PathParam("licId") Integer licId, @DefaultValue("false") @FormParam("add_cc") Boolean addCC,
225
+ @Context BasicSecurityContext bsc) throws SeCurisServiceException, SeCurisException {
196226
197227 EntityManager em = emProvider.get();
198228 License lic = getCurrentLicense(licId, bsc, em);
229
+ Application app = lic.getPack().getLicenseType().getApplication();
230
+ File licFile = null;
231
+ if (lic.getLicenseData() == null) {
232
+ throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "There is no license file available");
233
+ }
199234
200235 User user = getUser(bsc.getUserPrincipal().getName(), em);
201
- // TODO: Send mail with lic file
236
+ try {
237
+ String subject = MessageFormat.format(Params.get(Params.KEYS.EMAIL_LIC_DEFAULT_SUBJECT), lic.getPack().getAppName());
238
+ String email_tpl = IOUtils.toString(this.getClass().getResourceAsStream("/lic_email_template.en"));
239
+ String body = MessageFormat.format(email_tpl, lic.getFullName(), app.getName());
240
+ licFile = createTemporaryLicenseFile(lic, app.getLicenseFilename());
241
+
242
+ emailManager.sendEmail(subject, body, lic.getEmail(), addCC ? user.getEmail() : null, licFile);
243
+ } catch (IOException e) {
244
+ LOG.error("Error creating temporary license file", e);
245
+ throw new SeCurisServiceException(Status.NOT_FOUND.getStatusCode(), "There is no license file available");
246
+ } finally {
247
+ if (licFile != null) {
248
+ licFile.delete();
249
+ }
250
+ }
251
+
202252 lic.setModificationTimestamp(new Date());
203253 em.persist(lic);
204254 em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.SEND, "Email sent to: " + lic.getEmail()));
....@@ -207,6 +257,7 @@
207257
208258 /**
209259 * Cancel given license
260
+ *
210261 * @param licId
211262 * @param bsc
212263 * @return
....@@ -221,7 +272,8 @@
221272 @Produces({
222273 MediaType.APPLICATION_JSON
223274 })
224
- public Response cancel(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
275
+ public Response cancel(@PathParam("licId") Integer licId, @FormParam("reason") String reason, @Context BasicSecurityContext bsc)
276
+ throws SeCurisServiceException {
225277
226278 EntityManager em = emProvider.get();
227279 License lic = getCurrentLicense(licId, bsc, em);
....@@ -232,12 +284,17 @@
232284 + " can not be canceled from the current license status");
233285 }
234286
235
- lic.setStatus(License.Status.CANCELED);
287
+ if (reason == null && (lic.getStatus() == LicenseStatus.ACTIVE || lic.getStatus() == LicenseStatus.PRE_ACTIVE)) {
288
+ LOG.error("To cancel an active License we need a reason, lic ID: {}, user: {}", lic.getId(), bsc.getUserPrincipal().getName());
289
+ throw new SeCurisServiceException(Status.FORBIDDEN.getStatusCode(), "Active license with id " + licId
290
+ + " can not be canceled without a reason");
291
+ }
292
+
293
+ lic.setStatus(LicenseStatus.CANCELLED);
236294 lic.setModificationTimestamp(new Date());
237295 em.persist(lic);
238296
239
- User user = getUser(bsc.getUserPrincipal().getName(), em);
240
- em.persist(createLicenseHistoryAction(lic, user, LicenseHistory.Actions.CANCEL));
297
+ em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.CANCEL, "Cancelation reason: " + reason));
241298 return Response.ok(lic).build();
242299 }
243300
....@@ -250,39 +307,102 @@
250307 })
251308 @Transactional
252309 public Response create(License lic, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
253
- LOG.info("Creating new license from create()");
254310 EntityManager em = emProvider.get();
255311 Pack pack = null;
256312 if (lic.getPackId() != null) {
257313 pack = em.find(Pack.class, lic.getPackId());
258
- if (pack == null) {
259
- LOG.error("License pack with id {} not found in DB", lic.getPackId());
260
- return Response.status(Status.NOT_FOUND)
261
- .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's pack not found with ID: " + lic.getPackId()).build();
262
- } else {
263
- if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN) && !bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {
264
- LOG.error("License for pack with id {} can not be created by user {}", pack.getId(), bsc.getUserPrincipal());
265
- return Response.status(Status.UNAUTHORIZED)
266
- .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized action on pack license").build();
267
- }
314
+ }
315
+
316
+ if (pack == null) {
317
+ LOG.error("License pack with id {} not found in DB", lic.getPackId());
318
+ throw new SeCurisServiceException(ErrorCodes.NOT_FOUND, "License's pack not found with ID: " + lic.getPackId());
319
+ } else {
320
+ if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN) && !bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {
321
+ LOG.error("License for pack with id {} can not be created by user {}", pack.getId(), bsc.getUserPrincipal());
322
+ throw new SeCurisServiceException(ErrorCodes.UNAUTHORIZED_ACCESS, "Unathorized action on pack license");
268323 }
269324 }
270325
271326 User createdBy = getUser(bsc.getUserPrincipal().getName(), em);
272327
273
- // TODO: Manage status if request data is set
328
+ if (lic.getRequestData() != null) {
329
+ SignedLicenseBean signedLicense = generateLicense(lic, em);
330
+ // If user provide a request data the license status is passed
331
+ // directly to ACTIVE
332
+ lic.setStatus(LicenseStatus.ACTIVE);
333
+ try {
334
+ lic.setLicenseData(JsonUtils.toJSON(signedLicense));
335
+ } catch (CurisException e) {
336
+ LOG.error("Error generaing license JSON", e);
337
+ throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
338
+ }
339
+ } else {
340
+ lic.setStatus(LicenseStatus.CREATED);
341
+ }
274342 lic.setCreatedBy(createdBy);
275
- lic.setStatus(License.Status.CREATED);
276343 lic.setCreationTimestamp(new Date());
277344 lic.setModificationTimestamp(lic.getCreationTimestamp());
278345 em.persist(lic);
279346 em.persist(createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.CREATE));
347
+ if (lic.getStatus() == LicenseStatus.ACTIVE) {
348
+ em.persist(createLicenseHistoryAction(lic, createdBy, LicenseHistory.Actions.CREATE, "Activated on creation"));
349
+ }
280350
281351 return Response.ok(lic).build();
282352 }
283
-
284353
285
-
354
+ private SignedLicenseBean generateLicense(License license, EntityManager em) throws SeCurisServiceException {
355
+ SignedLicenseBean sl = null;
356
+ Pack pack = em.find(Pack.class, license.getPackId());
357
+ RequestBean rb = validateRequestData(pack, license.getRequestData());
358
+ try {
359
+ LicenseBean lb = licenseGenerator.generateLicense(rb, extractPackMetadata(pack.getMetadata()), license.getExpirationDate(),
360
+ pack.getLicenseTypeCode(), license.getCode());
361
+ sl = new SignedLicenseBean(lb);
362
+ } catch (SeCurisException e) {
363
+ throw new SeCurisServiceException(ErrorCodes.INVALID_LICENSE_REQUEST_DATA, "Error generating license: " + e.toString());
364
+ }
365
+ return sl;
366
+ }
367
+
368
+ private Map<String, Object> extractPackMetadata(Set<PackMetadata> packMetadata) {
369
+ Map<String, Object> metadata = new HashMap<>();
370
+ for (PackMetadata md : packMetadata) {
371
+ metadata.put(md.getKey(), md.getValue());
372
+ }
373
+
374
+ return metadata;
375
+ }
376
+
377
+ /**
378
+ * We check if the given request data is valid for the current Pack and has
379
+ * a valid format
380
+ *
381
+ * @param pack
382
+ * @param requestData
383
+ * @throws SeCurisServiceException
384
+ */
385
+ private RequestBean validateRequestData(Pack pack, String requestData) throws SeCurisServiceException {
386
+ RequestBean rb = null;
387
+ try {
388
+ rb = JsonUtils.json2object(requestData, RequestBean.class);
389
+ } catch (CurisException e) {
390
+ throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data has not a valid format");
391
+ }
392
+
393
+ if (!rb.getCustomerCode().equals(pack.getOrganization().getCode())) {
394
+ throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong Organization code");
395
+ }
396
+ if (!rb.getLicenseTypeCode().equals(pack.getLicenseTypeCode())) {
397
+ throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong License type code");
398
+ }
399
+ // TODO: [rsanchez] Verify that if pack code is null we ignore it
400
+ if (rb.getPackCode() != null && !rb.getPackCode().equals(pack.getCode())) {
401
+ throw new SeCurisServiceException(ErrorCodes.INVALID_REQUEST_DATA_FORMAT, "Request data not valid, wrong Pack code");
402
+ }
403
+ return rb;
404
+ }
405
+
286406 @PUT
287407 @POST
288408 @Path("/{licId}")
....@@ -302,9 +422,22 @@
302422 currentLicense.setCode(lic.getCode());
303423 currentLicense.setFullName(lic.getFullName());
304424 currentLicense.setEmail(lic.getEmail());
305
- currentLicense.setRequestData(lic.getRequestData());
425
+ if (lic.getRequestData() != null) {
426
+ SignedLicenseBean signedLicense = generateLicense(lic, em);
427
+ // If user provide a request data the license status is passed
428
+ // directly to ACTIVE
429
+ lic.setStatus(LicenseStatus.ACTIVE);
430
+ try {
431
+ lic.setLicenseData(JsonUtils.toJSON(signedLicense));
432
+ } catch (CurisException e) {
433
+ LOG.error("Error generaing license JSON", e);
434
+ throw new SeCurisServiceException(ErrorCodes.INVALID_FORMAT, "Error generaing license JSON");
435
+ }
436
+ currentLicense.setRequestData(lic.getRequestData());
437
+ }
306438 currentLicense.setModificationTimestamp(new Date());
307439 em.persist(currentLicense);
440
+ em.persist(createLicenseHistoryAction(lic, getUser(bsc, em), LicenseHistory.Actions.MODIFY));
308441
309442 return Response.ok(currentLicense).build();
310443 }
....@@ -321,7 +454,29 @@
321454 EntityManager em = emProvider.get();
322455 License lic = getCurrentLicense(licId, bsc, em);
323456
324
- if (lic.getStatus() != License.Status.CANCELED || lic.getStatus() != License.Status.CREATED) {
457
+ if (lic.getStatus() != LicenseStatus.CANCELLED || lic.getStatus() != LicenseStatus.CREATED) {
458
+ LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
459
+ return Response.status(Status.FORBIDDEN)
460
+ .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License can not be deleted in current status").build();
461
+ }
462
+
463
+ em.remove(lic);
464
+ return Response.ok(Utils.createMap("success", true, "id", licId)).build();
465
+ }
466
+
467
+ @DELETE
468
+ @Path("/{licId}")
469
+ @Transactional
470
+ @Securable
471
+ @Produces({
472
+ MediaType.APPLICATION_JSON
473
+ })
474
+ public Response block(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) throws SeCurisServiceException {
475
+ LOG.info("Deleting license with id: {}", licId);
476
+ EntityManager em = emProvider.get();
477
+ License lic = getCurrentLicense(licId, bsc, em);
478
+
479
+ if (lic.getStatus() != LicenseStatus.CANCELLED || lic.getStatus() != LicenseStatus.CREATED) {
325480 LOG.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());
326481 return Response.status(Status.FORBIDDEN)
327482 .header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License can not be deleted in current status").build();
....@@ -349,6 +504,11 @@
349504 return lic;
350505 }
351506
507
+ private User getUser(BasicSecurityContext bsc, EntityManager em) throws SeCurisServiceException {
508
+ String username = bsc.getUserPrincipal().getName();
509
+ return getUser(username, em);
510
+ }
511
+
352512 private User getUser(String username, EntityManager em) throws SeCurisServiceException {
353513 User user = null;
354514 if (username != null) {
....@@ -358,6 +518,13 @@
358518 }
359519 }
360520 return user;
521
+ }
522
+
523
+ private File createTemporaryLicenseFile(License lic, String licFileName) throws IOException {
524
+ File f = Files.createTempDirectory("securis-server").toFile();
525
+ f = new File(f, licFileName);
526
+ IOUtils.write(lic.getLicenseData(), new FileWriter(f));
527
+ return f;
361528 }
362529
363530 private LicenseHistory createLicenseHistoryAction(License lic, User user, String action, String comments) {
....@@ -373,4 +540,12 @@
373540 private LicenseHistory createLicenseHistoryAction(License lic, User user, String action) {
374541 return createLicenseHistoryAction(lic, user, action, null);
375542 }
543
+
544
+ public static void main(String[] args) throws IOException {
545
+ File f = Files.createTempDirectory("securis-server").toFile();
546
+
547
+ LOG.info("f: {}", f);
548
+ f = new File(f, "config-server.lic");
549
+ LOG.info("f: {}", f);
550
+ }
376551 }
securis/src/main/java/net/curisit/securis/services/exception/SeCurisServiceException.java
....@@ -4,15 +4,20 @@
44
55 public class SeCurisServiceException extends CurisException {
66
7
- private int status = 0;
7
+ private int errorCode = 0;
88
9
- public SeCurisServiceException(int status, String msg) {
9
+ public SeCurisServiceException(int errorCode, String msg) {
1010 super(msg);
11
- this.status = status;
11
+ this.errorCode = errorCode;
12
+ }
13
+
14
+ public SeCurisServiceException(String msg) {
15
+ super(msg);
16
+ this.errorCode = ErrorCodes.UNEXPECTED_ERROR;
1217 }
1318
1419 public int getStatus() {
15
- return status;
20
+ return errorCode;
1621 }
1722
1823 /**
....@@ -24,8 +29,13 @@
2429 public static int UNEXPECTED_ERROR = 1000;
2530 public static int INVALID_CREDENTIALS = 1001;
2631 public static int UNAUTHORIZED_ACCESS = 1002;
27
-
32
+ public static int NOT_FOUND = 1003;
33
+ public static int INVALID_FORMAT = 1004;
34
+
2835 public static int INVALID_LICENSE_REQUEST_DATA = 1100;
2936 public static int LICENSE_NOT_READY_FOR_RENEW = 1101;
37
+
38
+ public static int INVALID_REQUEST_DATA = 1201;
39
+ public static int INVALID_REQUEST_DATA_FORMAT = 1202;
3040 }
3141 }
securis/src/main/java/net/curisit/securis/utils/EmailManager.java
....@@ -0,0 +1,196 @@
1
+package net.curisit.securis.utils;
2
+
3
+import java.io.File;
4
+import java.io.IOException;
5
+import java.io.UnsupportedEncodingException;
6
+import java.nio.charset.Charset;
7
+import java.security.KeyManagementException;
8
+import java.security.KeyStore;
9
+import java.security.KeyStoreException;
10
+import java.security.NoSuchAlgorithmException;
11
+import java.security.cert.CertificateException;
12
+import java.security.cert.X509Certificate;
13
+import java.util.Map;
14
+import java.util.concurrent.Executor;
15
+import java.util.concurrent.Executors;
16
+
17
+import javax.inject.Singleton;
18
+
19
+import net.curisit.securis.SeCurisException;
20
+
21
+import org.apache.commons.io.IOUtils;
22
+import org.apache.http.HttpResponse;
23
+import org.apache.http.auth.AuthScope;
24
+import org.apache.http.auth.UsernamePasswordCredentials;
25
+import org.apache.http.client.CredentialsProvider;
26
+import org.apache.http.client.methods.HttpPost;
27
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
28
+import org.apache.http.entity.ContentType;
29
+import org.apache.http.entity.mime.HttpMultipartMode;
30
+import org.apache.http.entity.mime.MultipartEntityBuilder;
31
+import org.apache.http.entity.mime.content.FileBody;
32
+import org.apache.http.impl.client.BasicCredentialsProvider;
33
+import org.apache.http.impl.client.CloseableHttpClient;
34
+import org.apache.http.impl.client.HttpClientBuilder;
35
+import org.apache.http.ssl.SSLContextBuilder;
36
+import org.apache.http.ssl.TrustStrategy;
37
+import org.apache.logging.log4j.LogManager;
38
+import org.apache.logging.log4j.Logger;
39
+
40
+/**
41
+ * Component that send emails using Mailgun API:
42
+ * http://documentation.mailgun.com/user_manual.html#sending-messages
43
+ *
44
+ * @author roberto <roberto.sanchez@curisit.net>
45
+ */
46
+@Singleton
47
+public class EmailManager {
48
+
49
+ private static final Logger LOG = LogManager.getLogger(EmailManager.class);
50
+
51
+ private final String serverUrl;
52
+ private final CloseableHttpClient httpClient;
53
+
54
+ /**
55
+ *
56
+ * @throws SeCurisException
57
+ */
58
+ private EmailManager() throws SeCurisException {
59
+ String domain = Params.get(Params.KEYS.MAILGUN_DOMAIN);
60
+ if (domain == null) {
61
+ throw new SeCurisException("Please, add '" + Params.KEYS.MAILGUN_DOMAIN + "' parameter to config file");
62
+ }
63
+ serverUrl = String.format("https://api.mailgun.net/v2/%s/messages", domain);
64
+ httpClient = createHttpClient();
65
+
66
+ }
67
+
68
+ private CloseableHttpClient createHttpClient() throws SeCurisException {
69
+ SSLContextBuilder builder = new SSLContextBuilder();
70
+ SSLConnectionSocketFactory sslsf = null;
71
+ try {
72
+ builder.loadTrustMaterial((KeyStore) null, new TrustStrategy() {
73
+ @Override
74
+ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
75
+ return true;
76
+ }
77
+ });
78
+ sslsf = new SSLConnectionSocketFactory(builder.build());
79
+ } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e1) {
80
+ LOG.error(e1);
81
+ throw new SeCurisException("Error creating SSL socket factory");
82
+ }
83
+ CredentialsProvider provider = new BasicCredentialsProvider();
84
+ UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("api", Params.get(Params.KEYS.MAILGUN_API_KEY));
85
+ provider.setCredentials(AuthScope.ANY, credentials);
86
+
87
+ return HttpClientBuilder.create().setDefaultCredentialsProvider(provider).setSSLSocketFactory(sslsf).build();
88
+ }
89
+
90
+ /**
91
+ * Basic method to send emails in text mode with attachment. The method is
92
+ * synchronous, It waits until server responses.
93
+ *
94
+ * @param subject
95
+ * @param body
96
+ * @param to
97
+ * @param file
98
+ * @throws SeCurisException
99
+ * @throws UnsupportedEncodingException
100
+ */
101
+ public void sendEmail(String subject, String body, String to, String cc, File file) throws SeCurisException, UnsupportedEncodingException {
102
+ HttpPost postRequest = new HttpPost(serverUrl);
103
+
104
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
105
+
106
+ builder.setCharset(Charset.forName("utf-8"));
107
+ builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
108
+ builder.addTextBody("from", Params.get(Params.KEYS.EMAIL_FROM_ADDRESS));
109
+ builder.addTextBody("to", to);
110
+ if (cc != null)
111
+ builder.addTextBody("cc", cc);
112
+ builder.addTextBody("subject", subject, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), Charset.forName("utf-8")));
113
+ builder.addTextBody("text", body, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), Charset.forName("utf-8")));
114
+ if (file != null)
115
+ builder.addPart("attachment", new FileBody(file));
116
+
117
+ postRequest.setEntity(builder.build());
118
+ HttpResponse response;
119
+ try {
120
+ response = httpClient.execute(postRequest);
121
+
122
+ String jsonLic = IOUtils.toString(response.getEntity().getContent());
123
+ if (response.getStatusLine().getStatusCode() == 200) {
124
+ LOG.debug("Response content read OK: {}", jsonLic);
125
+ Map<String, Object> responseBean = JsonUtils.json2map(jsonLic);
126
+
127
+ LOG.debug("Response mail read OK: {}", responseBean);
128
+ } else {
129
+ throw new SeCurisException("Error sending email, response estatus: " + response.getStatusLine());
130
+ }
131
+ } catch (IOException e) {
132
+ LOG.error("Error sending email", e);
133
+ throw new SeCurisException("Error sending email");
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Basic method to send emails in text mode with attachment. The method is
139
+ * asynchronous, It returns immediately
140
+ *
141
+ * @param subject
142
+ * @param body
143
+ * @param to
144
+ * @param file
145
+ * @throws SeCurisException
146
+ * @throws UnsupportedEncodingException
147
+ */
148
+ public void sendEmailAsync(String subject, String body, String to, String cc, File file, EmailCallback callback) throws SeCurisException,
149
+ UnsupportedEncodingException {
150
+ Executor ex = Executors.newSingleThreadExecutor();
151
+ ex.execute(new Runnable() {
152
+
153
+ @Override
154
+ public void run() {
155
+ try {
156
+ EmailManager.this.sendEmail(subject, body, to, cc, file);
157
+ callback.success();
158
+ } catch (UnsupportedEncodingException e) {
159
+ callback.error(new SeCurisException("Error sending email", e));
160
+ } catch (SeCurisException e) {
161
+ callback.error(e);
162
+ }
163
+
164
+ }
165
+ });
166
+
167
+ }
168
+
169
+ public static interface EmailCallback {
170
+ public void success();
171
+
172
+ public void error(SeCurisException e);
173
+ }
174
+
175
+ public static void main(String[] args) throws SeCurisException, UnsupportedEncodingException {
176
+ // new EmailManager().sendEmail("España así de bien",
177
+ // "Me gusta esta prueba\nCon varias líneas\n\n\n--\nNo response",
178
+ // "info@r75.es", new File(
179
+ // "/Users/rob/Downloads/test.req"));
180
+ new EmailManager().sendEmailAsync("España así de bien", "Me gusta esta prueba\nCon varias líneas\n\n\n--\nNo response", "info@r75.es",
181
+ "dev@r75.es", new File("/Users/rob/Downloads/test.req"), new EmailCallback() {
182
+
183
+ @Override
184
+ public void success() {
185
+ LOG.info("Success!!!");
186
+ }
187
+
188
+ @Override
189
+ public void error(SeCurisException e) {
190
+ LOG.error("Error: {} !!!", e);
191
+ }
192
+ });
193
+ LOG.info("Waiting for email to be sent...");
194
+ }
195
+
196
+}
securis/src/main/resources/db/schema.sql
....@@ -14,6 +14,7 @@
1414 last_name VARCHAR(100) NULL ,
1515 last_login DATETIME NULL ,
1616 lang VARCHAR(10) NULL ,
17
+ email VARCHAR(150) NULL ,
1718 creation_timestamp DATETIME NULL ,
1819 modification_timestamp DATETIME NULL ,
1920 PRIMARY KEY (username));
....@@ -78,7 +79,7 @@
7879 num_licenses INT NOT NULL ,
7980 init_valid_date DATE NOT NULL default today(),
8081 end_valid_date DATE NOT NULL default today(),
81
- status VARCHAR(2) NOT NULL default 'PE',
82
+ status VARCHAR(2) NOT NULL default 'CR',
8283 comments VARCHAR(1024) NULL ,
8384 license_type_id INT NOT NULL,
8485 organization_id INT NOT NULL,
....@@ -113,7 +114,7 @@
113114 expiration_date DATETIME NULL ,
114115 canceled_by varchar(45) NULL ,
115116 created_by varchar(45) NULL ,
116
- status INT NOT NULL default 0,
117
+ status VARCHAR(2) NOT NULL default 'CR',
117118 PRIMARY KEY (id));
118119
119120
....@@ -127,5 +128,12 @@
127128 comments VARCHAR(512) ,
128129 PRIMARY KEY (id));
129130
130
-
131
-
131
+
132
+drop table IF EXISTS blocked_request;
133
+CREATE TABLE IF NOT EXISTS blocked_request (
134
+ hash VARCHAR(256) NOT NULL ,
135
+ request_data VARCHAR(1024) NULL ,
136
+ blocked_by varchar(45) NULL ,
137
+ creation_timestamp DATETIME NOT NULL default now(),
138
+ PRIMARY KEY (id));
139
+
securis/src/main/resources/securis-server.properties
....@@ -12,3 +12,8 @@
1212 ssl.keystore.type = PKCS12
1313 ssl.keystore.password = curist3c
1414 ssl.keystore.alias =
15
+
16
+email.from.address = SeCuris support securis@curisit.net
17
+mailgun.domain = m.curisit.net
18
+mailgun.api.key = key-7dekyqx56nc0irbnyk4z39ezhsrikqi8
19
+email.lic.default.subject = SeCuris - License file for {0} application
securis/src/main/resources/static/js/catalogs.json
....@@ -154,6 +154,12 @@
154154 "readOnly" : true,
155155 "mandatory" : true
156156 }, {
157
+ "name" : "email",
158
+ "display" : "Email",
159
+ "type" : "email",
160
+ "maxlength" : 150,
161
+ "mandatory" : true
162
+ }, {
157163 "name" : "first_name",
158164 "display" : "First name",
159165 "type" : "string",
securis/src/main/resources/static/licenses.html
....@@ -228,10 +228,21 @@
228228 </div>
229229
230230 <div class="form-group">
231
- <div class="col-md-offset-3 col-md-10" id="saveContainer">
231
+ <div class="col-md-offset-3 col-md-10 " id="saveContainer">
232232 <button id="save" type="submit" class="btn btn-primary">
233233 <span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
234234 </button>
235
+ <button id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
236
+ <span i18n class="glyphicon glyphicon-align-justify"></span> Actions
237
+ <span class="caret"></span>
238
+ </button>
239
+ <ul class="dropdown-menu" role="menu">
240
+ <li><a href="#">Activate</a></li>
241
+ <li><a href="#">On hold</a></li>
242
+ <li class="divider"></li>
243
+ <li><a href="#">Invalidate</a></li>
244
+ <li><a href="#">Delete</a></li>
245
+ </ul>
235246 </div>
236247 </div>
237248 </form>
....@@ -534,26 +545,18 @@
534545 <button id="save" type="submit" class="btn btn-primary">
535546 <span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
536547 </button>
537
- <button id="activate" class="btn btn-success"
538
- ng-click="activateLicense(license)"
539
- ng-if="isActionVisible('activate', license)">
540
- <span i18n class="glyphicon glyphicon-check"></span> Activate
548
+ <button id="acc" type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
549
+ <span i18n class="glyphicon glyphicon-align-justify"></span> Actions
550
+ <span class="caret"></span>
541551 </button>
542
- <button id="send" class="btn btn-info"
543
- ng-click="sendLicense(license)"
544
- ng-if="isActionVisible('send', license)">
545
- <span i18n class="glyphicon glyphicon-send"></span> Send
546
- </button>
547
- <button id="cancel" class="btn btn-warning"
548
- ng-click="cancelLicense(license)"
549
- ng-if="isActionVisible('cancel', license)">
550
- <span i18n class="glyphicon glyphicon-ban-circle"></span> Cancel
551
- </button>
552
- <button id="remove" class="btn btn-danger"
553
- ng-click="removeLicense(license)"
554
- ng-if="isActionVisible('delete', license)">
555
- <span i18n class="glyphicon glyphicon-trash"></span> Delete
556
- </button>
552
+ <ul class="dropdown-menu" role="menu">
553
+ <li ng-if="isActionVisible('activate', license)"><a ng-click="activateLicense(license)" href="#">Activate</a></li>
554
+ <li ng-if="isActionVisible('download', license)"><a ng-click="downloadLicense(license)" href="#">Download</a></li>
555
+ <li ng-if="isActionVisible('send', license)"><a ng-click="sendLicense(license)" href="#">Send by email</a></li>
556
+ <li ng-if="isActionVisible('cancel', license)"><a ng-click="cancelLicense(license)" href="#">Invalidate</a></li>
557
+ <li ng-if="isActionVisible('delete', license)"><a ng-click="removeLicense(license)" href="#">Delete</a></li>
558
+ </ul>
559
+
557560 </div>
558561 </div>
559562 </form>