diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000000..715dedf3c2a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,165 @@
+
+
+ 4.0.0
+
+ org.elasticsearch
+ elasticsearch-license
+ 1.0-SNAPSHOT
+
+
+ 1.4.0-SNAPSHOT
+
+
+
+
+
+
+ org.elasticsearch
+ elasticsearch
+ ${elasticsearch.version}
+ test
+ test-jar
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+ net.nicholaswilliams.java.licensing
+ licensing-core
+ 1.1.0
+
+
+
+ net.nicholaswilliams.java.licensing
+ licensing-licensor-base
+ 1.1.0
+
+
+
+ org.codehaus.jackson
+ jackson-mapper-asl
+ 1.8.5
+
+
+
+ org.elasticsearch
+ elasticsearch
+ ${elasticsearch.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 1.3.1
+
+
+ enforce-versions
+
+ enforce
+
+
+
+
+ [1.7,)
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+
+ 1.7
+ true
+ 512m
+
+ false
+
+ -XDignore.symbol.file
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.16
+
+
+ **/*Tests.java
+
+
+
+
+
+
+
+ maven-assembly-plugin
+ 2.3
+
+ false
+ ${project.build.directory}/releases/
+
+ ${basedir}/src/main/assemblies/plugin.xml
+
+
+ jar-with-dependencies
+
+
+
+
+ package
+
+ attached
+
+
+
+ assemble-all
+ package
+
+ single
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/gen_license.json b/sample/gen_license.json
new file mode 100644
index 00000000000..0e3058826bc
--- /dev/null
+++ b/sample/gen_license.json
@@ -0,0 +1,13 @@
+{
+ "licenses" : [ {
+ "uid" : "d8bcf9e8-bcb0-4f72-81ca-8a7537a436c5",
+ "type" : "internal",
+ "subscription_type" : "none",
+ "issued_to" : "issuedTo",
+ "issue_date" : "2014-09-29",
+ "expiry_date" : "2015-08-29",
+ "feature" : "shield",
+ "max_nodes" : 1,
+ "signature" : "naPgicfKM2+IJ0AoYgAAAG0AAAAAVGdIQ01qZUtCeEZNbS8wcTF4RU5mYUpiY01hdFlQNEVkdFJhYitoZndrSTI5eVZrY3ZRZ3lYU0s1QWdYb0Y5d1dBQmRUK01leE1aR0RUOHhoRVVhVUE9PaztAAVzcgAxbmV0Lm5pY2hvbGFzd2lsbGlhbXMuamF2YS5saWNlbnNpbmcuU2lnbmVkTGljZW5zZYqE/59+smqEAgACWwAObGljZW5zZUNvbnRlbnR0AAJbQlsAEHNpZ25hdHVyZUNvbnRlbnRxAH4AAXhwdXIAAltCrPMX+AYIVOACAAB4cAAAAMCsH5r77/8FtWY+JxKd9MiBTYQLcXgmXMm+Y83VaNwmlr1lASJ2yf7rWojiuHTWemtUNtOZcXeSrLfs/oKwBzXIfvEZV8X/vPCWnpi7VtU4Hp+OZUFO4c0NQ1PnVdDk1uns16Dqe99/ota3FSvdFrmlzkz2E+2bbx0fwWbKnGDXFXy6eE7OISRJdCqa8gljMo9PA1+RI7MFQ8bSzs9up0cEkSuPzgtafFW5zfyn2vpoPZTxDpJslTBk7S3mdchE0eJ1cQB+AAMAAAEAdikZHpJVMxWMxNsksYnNOD7F+15SK3MCtUWJnQdhYCuVHdKQUE3YxWv59QQuDmKuLbnvi0DsuPGlq3hEx0AXmbpaBOhkwTv3DKZH7V6C0YmXj7RLZobaDTtGY2pwV6Qf5+teq5dV493a1k6YGFiwUoERuWQxqmA36naLdVo2diCSh8QmZ4ihKnhqxwswh2TlnCVuaNN3E7HuGeE0wYgFEfgISJOFlEOnLOItRlrQOTzCq+mhASKbANxx/Z42eMGrgs+GJsxYQZfnBh8K3NQFQk2SjWR1sEgqUPXC+0Z7ungzkkwoSBbrdJfRPKbqXFDthWI1DY9SSZnTbwpUC2XA6Q=="
+ } ]
+}
\ No newline at end of file
diff --git a/sample/license_spec.json b/sample/license_spec.json
new file mode 100644
index 00000000000..92997a9f242
--- /dev/null
+++ b/sample/license_spec.json
@@ -0,0 +1,14 @@
+{
+ "licenses": [
+ {
+ "type": "internal",
+ "subscription_type": "none",
+ "issued_to": "issuedTo",
+ "issuer": "issuer",
+ "issue_date": "2014-09-29",
+ "expiry_date": "2015-08-29",
+ "feature": "shield",
+ "max_nodes": 1
+ }
+ ]
+}
diff --git a/src/main/assemblies/plugin.xml b/src/main/assemblies/plugin.xml
new file mode 100644
index 00000000000..8e8e42e7c05
--- /dev/null
+++ b/src/main/assemblies/plugin.xml
@@ -0,0 +1,18 @@
+
+
+ plugin
+
+ zip
+
+ false
+
+
+ /
+ true
+ true
+
+ org.elasticsearch:elasticsearch
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/org/elasticsearch/license/core/DateUtils.java b/src/main/java/org/elasticsearch/license/core/DateUtils.java
new file mode 100644
index 00000000000..5ad19febae9
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/core/DateUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.core;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class DateUtils {
+ public static final DateFormat DATE_FORMAT;
+ public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("UTC");
+
+ static {
+ DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+ DATE_FORMAT.setTimeZone(DateUtils.TIME_ZONE);
+ DATE_FORMAT.setLenient(false);
+ }
+
+ public static long longExpiryDateFromDate(long date) {
+ Date dateObj = new Date(date);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.setTimeZone(TIME_ZONE);
+ calendar.setTimeInMillis(dateObj.getTime());
+
+ calendar.set(Calendar.HOUR, 23);
+ calendar.set(Calendar.MINUTE, 59);
+ calendar.set(Calendar.SECOND, 59);
+
+ return calendar.getTimeInMillis();
+ }
+
+ public static long longFromDateString(String dateStr) throws ParseException {
+ Date dateObj = DATE_FORMAT.parse(dateStr);
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.setTimeZone(TIME_ZONE);
+ calendar.setTimeInMillis(dateObj.getTime());
+ return calendar.getTimeInMillis();
+ }
+
+ public static long longExpiryDateFromString(String dateStr) throws ParseException {
+ return longExpiryDateFromDate(longFromDateString(dateStr));
+ }
+
+ public static String dateStringFromLongDate(long date) {
+ Date dateObj = new Date(date);
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.setTimeZone(TIME_ZONE);
+ calendar.setTimeInMillis(dateObj.getTime());
+ return DATE_FORMAT.format(calendar.getTime());
+ }
+}
diff --git a/src/main/java/org/elasticsearch/license/core/ESLicenses.java b/src/main/java/org/elasticsearch/license/core/ESLicenses.java
new file mode 100644
index 00000000000..015a5bc28f1
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/core/ESLicenses.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.core;
+
+import java.util.Collection;
+import java.util.Set;
+
+
+/**
+ * Interface for ESLicenses, ESLicense
+ * and enums for Type, SubscriptionType and FeatureType.
+ *
+ * This is the main contract between the licensor and the license manager
+ */
+public interface ESLicenses extends Iterable {
+
+ /**
+ * @return list of licenses contained under this instance
+ */
+ public Collection licenses();
+
+ /**
+ * @return Set of features for which there exists an underlying license
+ */
+ public Set features();
+
+ /**
+ * @return a license for a code>featureType<
+ */
+ public ESLicense get(FeatureType featureType);
+
+ /**
+ * Enum for License Type
+ */
+ public enum Type {
+ TRIAL((byte) 0, "trial"),
+ SUBSCRIPTION((byte) 1, "subscription"),
+ INTERNAL((byte) 2, "internal");
+
+ private final byte id;
+ private final String name;
+
+ private Type(byte id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String string() {
+ return name;
+ }
+
+ public byte id() {
+ return id;
+ }
+
+ public static Type fromId(byte id) {
+ switch (id) {
+ case 0:
+ return TRIAL;
+ case 1:
+ return SUBSCRIPTION;
+ case 2:
+ return INTERNAL;
+ default:
+ throw new IllegalArgumentException("Invalid Type id=" + id);
+ }
+ }
+
+ public static Type fromString(String type) {
+ if (type.equalsIgnoreCase(TRIAL.string())) {
+ return TRIAL;
+ } else if (type.equalsIgnoreCase(SUBSCRIPTION.string())) {
+ return SUBSCRIPTION;
+ } else if (type.equalsIgnoreCase(INTERNAL.string())) {
+ return INTERNAL;
+ } else {
+ throw new IllegalArgumentException("Invalid Type=" + type);
+ }
+
+ }
+ }
+
+ /**
+ * Enum for License Subscription Type
+ */
+ public enum SubscriptionType {
+ NONE((byte) 0, "none"),
+ DEVELOPMENT((byte) 1, "development"),
+ SILVER((byte) 2, "silver"),
+ GOLD((byte) 3, "gold"),
+ PLATINUM((byte) 4, "platinum");
+
+ public static SubscriptionType DEFAULT = NONE;
+
+ private final byte id;
+ private final String name;
+
+ private SubscriptionType(byte id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String string() {
+ return name;
+ }
+
+ public byte id() {
+ return id;
+ }
+
+ public static SubscriptionType fromId(byte id) {
+ switch (id) {
+ case 0:
+ return NONE;
+ case 1:
+ return DEVELOPMENT;
+ case 2:
+ return SILVER;
+ case 3:
+ return GOLD;
+ case 4:
+ return PLATINUM;
+ default:
+ throw new IllegalArgumentException("Invalid SubscriptionType id=" + id);
+ }
+ }
+
+
+ public static SubscriptionType fromString(String subscriptionType) {
+ if (subscriptionType.equalsIgnoreCase(NONE.string())) {
+ return NONE;
+ } else if (subscriptionType.equalsIgnoreCase(DEVELOPMENT.string())) {
+ return DEVELOPMENT;
+ } else if (subscriptionType.equalsIgnoreCase(SILVER.string())) {
+ return SILVER;
+ } else if (subscriptionType.equalsIgnoreCase(GOLD.string())) {
+ return GOLD;
+ } else if (subscriptionType.equalsIgnoreCase(PLATINUM.string())) {
+ return PLATINUM;
+ } else {
+ throw new IllegalArgumentException("Invalid SubscriptionType=" + subscriptionType);
+ }
+ }
+ }
+
+ /**
+ * Enum for License FeatureType
+ */
+ public enum FeatureType {
+ SHIELD((byte) 0, "shield"),
+ MARVEL((byte) 1, "marvel");
+
+ private final byte id;
+
+ private final String name;
+
+ private FeatureType(byte id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String string() {
+ return name;
+ }
+
+ public byte id() {
+ return id;
+ }
+
+ public static FeatureType fromId(byte id) {
+ switch (id) {
+ case 0:
+ return SHIELD;
+ case 1:
+ return MARVEL;
+ default:
+ throw new IllegalArgumentException("Invalid FeatureType id=" + id);
+ }
+ }
+
+ public static FeatureType fromString(String featureType) {
+ if (featureType.equalsIgnoreCase(SHIELD.string())) {
+ return SHIELD;
+ } else if (featureType.equalsIgnoreCase(MARVEL.string())) {
+ return MARVEL;
+ } else {
+ throw new IllegalArgumentException("Invalid FeatureType=" + featureType);
+ }
+ }
+ }
+
+ /**
+ * Interface representing all the license fields
+ */
+ public interface ESLicense {
+
+ /**
+ * @return a unique identifier for a license (currently just a UUID)
+ */
+ public String uid();
+
+ /**
+ * @return type of the license [trial, subscription, internal]
+ */
+ public Type type();
+
+ /**
+ * @return subscription type of the license [none, silver, gold, platinum]
+ */
+ public SubscriptionType subscriptionType();
+
+ /**
+ * @return the issueDate in milliseconds
+ */
+ public long issueDate();
+
+ /**
+ * @return the featureType for the license [shield, marvel]
+ */
+ public FeatureType feature();
+
+ /**
+ * @return the expiry date in milliseconds
+ */
+ public long expiryDate();
+
+ /**
+ * @return the maximum number of nodes this license has been issued for
+ */
+ public int maxNodes();
+
+ /**
+ * @return a string representing the entity this licenses has been issued to
+ */
+ public String issuedTo();
+
+ /**
+ * @return a string representing the entity responsible for issuing this license (internal)
+ */
+ public String issuer();
+
+ /**
+ * @return a string representing the signature of the license used for license verification
+ */
+ public String signature();
+ }
+
+}
diff --git a/src/main/java/org/elasticsearch/license/core/LicenseBuilders.java b/src/main/java/org/elasticsearch/license/core/LicenseBuilders.java
new file mode 100644
index 00000000000..1799e8e883e
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/core/LicenseBuilders.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.core;
+
+import java.util.*;
+
+import static org.elasticsearch.license.core.ESLicenses.*;
+
+public class LicenseBuilders {
+
+ /**
+ * @return a licenses builder instance to build a {@link org.elasticsearch.license.core.ESLicenses}
+ */
+ public static LicensesBuilder licensesBuilder() {
+ return new LicensesBuilder();
+ }
+
+ /**
+ * @return a license builder instance to build a {@link org.elasticsearch.license.core.ESLicenses.ESLicense}
+ * if internal is set to true, then license fields (which are internal) are required to be set
+ */
+ public static LicenseBuilder licenseBuilder(boolean internal) {
+ return new LicenseBuilder(internal);
+ }
+
+ /**
+ * Merges all the sub-licenses of the provided licenses parameters by
+ * longest expiry date for each license feature and merges out any
+ * sub-licenses that have already expired
+ *
+ * @return a merged ESLicenses
instance from licenses
+ * and mergedLicenses
+ */
+ public static ESLicenses merge(ESLicenses licenses, ESLicenses mergeLicenses) {
+ if (licenses == null && mergeLicenses == null) {
+ throw new IllegalArgumentException("both licenses can not be null");
+ } else if (licenses == null) {
+ return mergeLicenses;
+ } else if (mergeLicenses == null) {
+ return licenses;
+ } else {
+ return licensesBuilder()
+ .licenses(licenses)
+ .licenses(mergeLicenses)
+ .build();
+ }
+ }
+
+ public static class LicensesBuilder {
+ private Map licenseMap;
+
+ public LicensesBuilder() {
+ }
+
+ public LicensesBuilder license(LicenseBuilder builder) {
+ return license(builder.build());
+ }
+
+ public LicensesBuilder license(ESLicense license) {
+ initLicenses();
+ putIfAppropriate(license);
+ return this;
+ }
+
+ public LicensesBuilder licenses(Collection licenses) {
+ for (ESLicense esLicense : licenses) {
+ license(esLicense);
+ }
+ return this;
+ }
+
+ public LicensesBuilder licenses(ESLicenses licenses) {
+ return licenses(licenses.licenses());
+ }
+
+ public ESLicenses build() {
+ return new ESLicenses() {
+ @Override
+ public Collection licenses() {
+ return licenseMap.values();
+ }
+
+ @Override
+ public Set features() {
+ return licenseMap.keySet();
+ }
+
+ @Override
+ public ESLicense get(FeatureType featureType) {
+ return licenseMap.get(featureType);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return licenseMap.values().iterator();
+ }
+ };
+ }
+
+ private void initLicenses() {
+ if (licenseMap == null) {
+ licenseMap = new HashMap<>();
+ }
+ }
+
+ /**
+ * Add a {@link org.elasticsearch.license.core.ESLicenses.ESLicense} to
+ * {@link org.elasticsearch.license.core.ESLicenses} only if
+ * there exists no License for the feature that has a longer expiry date
+ * and if the license in question has an expiryDate
that has
+ * not expired yet
+ *
+ * @param license license in question
+ */
+ private void putIfAppropriate(ESLicense license) {
+ final FeatureType featureType = license.feature();
+ if (licenseMap.containsKey(featureType)) {
+ final ESLicense previousLicense = licenseMap.get(featureType);
+ if (license.expiryDate() > previousLicense.expiryDate()) {
+ licenseMap.put(featureType, license);
+ }
+ } else if (license.expiryDate() > System.currentTimeMillis()) {
+ licenseMap.put(featureType, license);
+ }
+ }
+ }
+
+ public static class LicenseBuilder {
+ private String uid;
+ private String issuer;
+ private String issuedTo;
+ private long issueDate = -1;
+ private Type type;
+ private SubscriptionType subscriptionType = SubscriptionType.DEFAULT;
+ private FeatureType feature;
+ private String signature;
+ private long expiryDate = -1;
+ private int maxNodes;
+
+
+ private final boolean internal;
+
+ public LicenseBuilder(boolean internal) {
+ this.internal = internal;
+ }
+
+ public LicenseBuilder uid(String uid) {
+ this.uid = uid;
+ return this;
+ }
+
+ public LicenseBuilder issuer(String issuer) {
+ this.issuer = issuer;
+ return this;
+ }
+
+ public LicenseBuilder issuedTo(String issuedTo) {
+ this.issuedTo = issuedTo;
+ return this;
+ }
+
+ public LicenseBuilder issueDate(long issueDate) {
+ this.issueDate = issueDate;
+ return this;
+ }
+
+ public LicenseBuilder type(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public LicenseBuilder subscriptionType(SubscriptionType subscriptionType) {
+ this.subscriptionType = subscriptionType;
+ return this;
+ }
+
+ public LicenseBuilder feature(FeatureType feature) {
+ this.feature = feature;
+ return this;
+ }
+
+ public LicenseBuilder expiryDate(long expiryDate) {
+ this.expiryDate = expiryDate;
+ return this;
+ }
+
+ public LicenseBuilder maxNodes(int maxNodes) {
+ this.maxNodes = maxNodes;
+ return this;
+ }
+
+ public LicenseBuilder signature(String signature) {
+ if (signature != null) {
+ this.signature = signature;
+ }
+ return this;
+ }
+
+ public LicenseBuilder fromLicense(ESLicense license) {
+ LicenseBuilder builder = this.uid(license.uid())
+ .issuedTo(license.issuedTo())
+ .issueDate(license.issueDate())
+ .type(license.type())
+ .subscriptionType(license.subscriptionType())
+ .feature(license.feature())
+ .maxNodes(license.maxNodes())
+ .expiryDate(license.expiryDate());
+
+ return (internal)
+ ? builder.issuer(license.issuer()).signature(license.signature())
+ : builder;
+
+
+ }
+
+ public ESLicense build() {
+ if (uid == null) {
+ uid = UUID.randomUUID().toString();
+ }
+ verify();
+ return new ESLicense() {
+ @Override
+ public String uid() {
+ return uid;
+ }
+
+ @Override
+ public Type type() {
+ return type;
+ }
+
+ @Override
+ public SubscriptionType subscriptionType() {
+ return subscriptionType;
+ }
+
+ @Override
+ public long issueDate() {
+ return issueDate;
+ }
+
+ @Override
+ public FeatureType feature() {
+ return feature;
+ }
+
+ @Override
+ public long expiryDate() {
+ return expiryDate;
+ }
+
+ @Override
+ public int maxNodes() {
+ return maxNodes;
+ }
+
+ @Override
+ public String issuer() {
+ return issuer;
+ }
+
+ @Override
+ public String issuedTo() {
+ return issuedTo;
+ }
+
+ @Override
+ public String signature() {
+ return signature;
+ }
+ };
+ }
+
+ private void verify() {
+ String msg = null;
+ if (internal && issuer == null) {
+ msg = "issuer can not be null";
+ } else if (issuedTo == null) {
+ msg = "issuedTo can not be null";
+ } else if (issueDate == -1) {
+ msg = "issueDate has to be set";
+ } else if (type == null) {
+ msg = "type can not be null";
+ } else if (subscriptionType == null) {
+ msg = "subscriptionType can not be null";
+ } else if (uid == null) {
+ msg = "uid can not be null";
+ } else if (feature == null) {
+ msg = "at least one feature has to be enabled";
+ } else if (internal && signature == null) {
+ msg = "signature can not be null";
+ } else if (maxNodes == -1) {
+ msg = "maxNodes has to be set";
+ } else if (expiryDate == -1) {
+ msg = "expiryDate has to be set";
+ }
+
+ if (msg != null) {
+ throw new IllegalStateException(msg);
+ }
+ }
+ }
+
+
+}
diff --git a/src/main/java/org/elasticsearch/license/core/LicenseUtils.java b/src/main/java/org/elasticsearch/license/core/LicenseUtils.java
new file mode 100644
index 00000000000..c535a8943c7
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/core/LicenseUtils.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.core;
+import org.apache.commons.io.FileUtils;
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonGenerator;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.ParseException;
+import java.util.HashSet;
+import java.util.Set;
+
+public class LicenseUtils {
+
+ public static void dumpLicenseAsJson(ESLicenses esLicenses, OutputStream out) throws IOException {
+ JsonGenerator generator = new JsonFactory().createJsonGenerator(out);
+ generator.useDefaultPrettyPrinter();
+
+ generator.writeStartObject();
+ {
+ generator.writeArrayFieldStart("licenses");
+ {
+ for (ESLicenses.ESLicense esLicense : esLicenses) {
+ generator.writeStartObject();
+ {
+ generator.writeStringField("uid", esLicense.uid());
+ generator.writeStringField("type", esLicense.type().string());
+ generator.writeStringField("subscription_type", esLicense.subscriptionType().string());
+ generator.writeStringField("issued_to", esLicense.issuedTo());
+ generator.writeStringField("issue_date", DateUtils.dateStringFromLongDate(esLicense.issueDate()));
+ generator.writeStringField("expiry_date", DateUtils.dateStringFromLongDate(esLicense.expiryDate()));
+ generator.writeStringField("feature", esLicense.feature().string());
+ generator.writeNumberField("max_nodes", esLicense.maxNodes());
+ generator.writeStringField("signature", esLicense.signature());
+ }
+ generator.writeEndObject();
+ }
+ }
+ generator.writeEndArray();
+ }
+ generator.writeEndObject();
+ generator.flush();
+ }
+
+ public static Set readLicensesFromFiles(Set licenseFiles) throws IOException {
+ Set esLicensesSet = new HashSet<>();
+ for (File licenseFile : licenseFiles) {
+ esLicensesSet.add(LicenseUtils.readLicenseFile(licenseFile));
+ }
+ return esLicensesSet;
+ }
+
+
+ public static Set readLicensesFromDirectory(File licenseDirectory) throws IOException {
+ Set esLicensesSet = new HashSet<>();
+ if (!licenseDirectory.exists()) {
+ throw new IllegalArgumentException(licenseDirectory.getAbsolutePath() + " does not exist!");
+ }
+ if (licenseDirectory.isDirectory()) {
+ for (File licenseFile : FileUtils.listFiles(licenseDirectory, new String[]{"json"}, false)) {
+ esLicensesSet.add(readLicenseFile(licenseFile));
+ }
+ } else if (licenseDirectory.isFile()) {
+ esLicensesSet.add(readLicenseFile(licenseDirectory));
+ } else {
+ throw new IllegalArgumentException(licenseDirectory.getAbsolutePath() + "is not a file or a directory");
+ }
+ return esLicensesSet;
+ }
+
+ public static ESLicenses readLicenseFile(File licenseFile) throws IOException {
+ try (FileInputStream fileInputStream = new FileInputStream(licenseFile)) {
+ JsonNode jsonNode = new ObjectMapper().readTree(fileInputStream);
+ return extractLicenseFromJson(jsonNode);
+ }
+ }
+
+ public static ESLicenses readLicensesFromString(String licensesString) throws IOException {
+ JsonNode jsonNode = new ObjectMapper().readTree(licensesString);
+ return extractLicenseFromJson(jsonNode);
+ }
+
+ private static ESLicenses extractLicenseFromJson(final JsonNode jsonNode) {
+ final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder();
+ JsonNode licensesNode = jsonNode.get("licenses");
+ if (licensesNode.isArray()) {
+ for (JsonNode licenseNode : licensesNode) {
+ licensesBuilder.license(LicenseBuilders.licenseBuilder(false)
+ .uid(getValueAsString(licenseNode, "uid", true))
+ .issuedTo(getValueAsString(licenseNode, "issued_to"))
+ .issuer(getValueAsString(licenseNode, "issuer", true))
+ .issueDate(getValueAsDate(licenseNode, "issue_date"))
+ .type(ESLicenses.Type.fromString(getValueAsString(licenseNode, "type")))
+ .subscriptionType(ESLicenses.SubscriptionType.fromString(getValueAsString(licenseNode, "subscription_type")))
+ .feature(ESLicenses.FeatureType.fromString(getValueAsString(licenseNode, "feature")))
+ .expiryDate(getValueAsExpiryDate(licenseNode, "expiry_date"))
+ .maxNodes(getValueAsInt(licenseNode, "max_nodes"))
+ .signature(getValueAsString(licenseNode, "signature", true))
+ .build());
+ }
+ } else {
+ throw new IllegalStateException("'licenses' field is not an array");
+ }
+ return licensesBuilder.build();
+
+ }
+
+ private static int getValueAsInt(final JsonNode jsonNode, String field) {
+ JsonNode node = getFieldNode(jsonNode, field, false);
+ assert node.isNumber();
+ return node.getValueAsInt();
+ }
+
+ private static String getValueAsString(final JsonNode jsonNode, String field) {
+ return getValueAsString(jsonNode, field, false);
+ }
+
+ private static String getValueAsString(final JsonNode jsonNode, String field, boolean optional) {
+ JsonNode node = getFieldNode(jsonNode, field, optional);
+ assert node != null || optional;
+ if (node == null) {
+ return null;
+ }
+ assert !node.isObject();
+ return node.getTextValue();
+ }
+
+ private static long getValueAsDate(final JsonNode jsonNode, String field) {
+ JsonNode node = getFieldNode(jsonNode, field, false);
+ assert !node.isObject();
+ final String value = node.getTextValue();
+ try {
+ return DateUtils.longFromDateString(value);
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private static long getValueAsExpiryDate(final JsonNode jsonNode, String field) {
+ long actualDate = getValueAsDate(jsonNode, field);
+ return DateUtils.longExpiryDateFromDate(actualDate);
+ }
+
+
+ private static JsonNode getFieldNode(final JsonNode jsonNode, String field, boolean optional) {
+ JsonNode node = jsonNode.get(field);
+ if (node == null && !optional) {
+ throw new IllegalArgumentException("field ['" + field + "'] is missing");
+ }
+ return node;
+ }
+
+
+ public static void printLicense(ESLicenses licenses) {
+ for (ESLicenses.ESLicense license : licenses) {
+ System.out.println("===");
+ printValue(" uid", license.uid());
+ printValue(" type", license.type().string());
+ printValue(" subscription_type", license.subscriptionType().string());
+ printValue(" issueDate", DateUtils.dateStringFromLongDate(license.issueDate()));
+ printValue(" issuedTo", license.issuedTo());
+ printValue(" feature", license.feature().string());
+ printValue(" maxNodes", license.maxNodes());
+ printValue(" expiryDate", DateUtils.dateStringFromLongDate(license.expiryDate()));
+ printValue(" signature", license.signature());
+ System.out.println("===");
+ }
+
+ }
+
+ private static void printValue(String name, Object value) {
+ System.out.println(name + " : " + value);
+ }
+}
diff --git a/src/main/java/org/elasticsearch/license/licensor/ESLicenseSigner.java b/src/main/java/org/elasticsearch/license/licensor/ESLicenseSigner.java
new file mode 100644
index 00000000000..fa7681723b7
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/licensor/ESLicenseSigner.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.licensor;
+
+import net.nicholaswilliams.java.licensing.License;
+import net.nicholaswilliams.java.licensing.encryption.Hasher;
+import net.nicholaswilliams.java.licensing.encryption.PasswordProvider;
+import net.nicholaswilliams.java.licensing.encryption.PrivateKeyDataProvider;
+import net.nicholaswilliams.java.licensing.exception.KeyNotFoundException;
+import net.nicholaswilliams.java.licensing.licensor.LicenseCreator;
+import net.nicholaswilliams.java.licensing.licensor.LicenseCreatorProperties;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseBuilders;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Random;
+
+import static org.elasticsearch.license.core.ESLicenses.ESLicense;
+
+public class ESLicenseSigner {
+
+ private final static int VERSION_START = 0;
+ private final static int VERSION = VERSION_START;
+
+ private final static int MAGIC_LENGTH = 13;
+
+ private final LicenseCreator licenseCreator;
+ private final SignerOptions options;
+
+ public static class SignerOptions {
+ final String privateKeyPath;
+ final String publicKeyPath;
+ final String password;
+
+ public SignerOptions(String privateKeyPath, String publicKeyPath, String password) {
+ this.privateKeyPath = privateKeyPath;
+ this.publicKeyPath = publicKeyPath;
+ this.password = password;
+ }
+ }
+
+ public ESLicenseSigner(final SignerOptions options) {
+ LicenseCreatorProperties.setPrivateKeyDataProvider(new PrivateKeyDataProvider() {
+ @Override
+ public byte[] getEncryptedPrivateKeyData() throws KeyNotFoundException {
+ File privateKeyFile = new File(options.privateKeyPath);
+ assert privateKeyFile.exists();
+ try {
+ return FileUtils.readFileToByteArray(privateKeyFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new IllegalStateException(e);
+ }
+
+ }
+ });
+ LicenseCreatorProperties.setPrivateKeyPasswordProvider(new PasswordProvider() {
+ @Override
+ public char[] getPassword() {
+ return options.password.toCharArray();
+ }
+ });
+ this.licenseCreator = LicenseCreator.getInstance();
+ this.options = options;
+ }
+
+ public ESLicenses sign(ESLicenses esLicenses) throws IOException {
+ final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder();
+ for (ESLicense license : esLicenses) {
+ licensesBuilder.license(sign(license));
+ }
+ return licensesBuilder.build();
+ }
+
+ /**
+ * Generates a signature for the esLicense
.
+ * Signature structure:
+ * | MAGIC | HEADER_LENGTH | VERSION | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT |
+ *
+ * @return a signed ESLicense (with signature)
+ * @throws IOException
+ */
+ public ESLicense sign(ESLicense esLicense) throws IOException {
+ License.Builder licenseBuilder = new License.Builder()
+ .withGoodBeforeDate(esLicense.expiryDate())
+ .withIssueDate(esLicense.issueDate())
+ .withProductKey(esLicense.uid())
+ .withHolder(esLicense.issuedTo())
+ .withIssuer(esLicense.issuer())
+ .addFeature(esLicense.feature().string(), esLicense.expiryDate())
+ .addFeature("maxNodes:" + String.valueOf(esLicense.maxNodes()))
+ .addFeature("type:" + esLicense.type().string())
+ .addFeature("subscription_type:" + esLicense.subscriptionType().string());
+
+ final License license = licenseBuilder.build();
+
+ final byte[] magic = new byte[MAGIC_LENGTH];
+ Random random = new Random();
+ random.nextBytes(magic);
+ final byte[] licenseSignature = licenseCreator.signAndSerializeLicense(license);
+ final byte[] hash = Hasher.hash(Base64.encodeBase64String(
+ FileUtils.readFileToByteArray(new File(options.publicKeyPath)))
+ ).getBytes(Charset.forName("UTF-8"));
+ int headerLength = MAGIC_LENGTH + hash.length + 4 + 4;
+ byte[] bytes = new byte[headerLength + licenseSignature.length];
+
+ ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.put(magic)
+ .putInt(headerLength)
+ .putInt(VERSION)
+ .put(hash)
+ .put(licenseSignature);
+ String signature = Base64.encodeBase64String(bytes);
+
+ return LicenseBuilders.licenseBuilder(true).fromLicense(esLicense).signature(signature).build();
+ }
+}
diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java
new file mode 100644
index 00000000000..a5568a75d1f
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.licensor.tools;
+
+import net.nicholaswilliams.java.licensing.encryption.RSAKeyPairGenerator;
+import net.nicholaswilliams.java.licensing.exception.AlgorithmNotSupportedException;
+import net.nicholaswilliams.java.licensing.exception.InappropriateKeyException;
+import net.nicholaswilliams.java.licensing.exception.InappropriateKeySpecificationException;
+import net.nicholaswilliams.java.licensing.exception.RSA2048NotSupportedException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.security.KeyPair;
+
+public class KeyPairGeneratorTool {
+
+ static class Options {
+ private final String publicKeyFilePath;
+ private final String privateKeyFilePath;
+ private final String keyPass;
+
+ Options(String publicKeyFilePath, String privateKeyFilePath, String keyPass) {
+ this.publicKeyFilePath = publicKeyFilePath;
+ this.privateKeyFilePath = privateKeyFilePath;
+ this.keyPass = keyPass;
+ }
+ }
+
+ private static Options parse(String[] args) {
+ String privateKeyPath = null;
+ String publicKeyPath = null;
+ String keyPass = null;
+
+ for (int i = 0; i < args.length; i++) {
+ String command = args[i];
+ switch (command) {
+ case "--publicKeyPath":
+ publicKeyPath = args[++i];
+ break;
+ case "--privateKeyPath":
+ privateKeyPath = args[++i];
+ break;
+ case "--keyPass":
+ keyPass = args[++i];
+ break;
+ }
+ }
+
+ if (publicKeyPath == null) {
+ throw new IllegalArgumentException("mandatory option '--publicKeyPath' is missing");
+ }
+ if (privateKeyPath == null) {
+ throw new IllegalArgumentException("mandatory option '--privateKeyPath' is missing");
+ }
+ if (keyPass == null) {
+ throw new IllegalArgumentException("mandatory option '--keyPass' is missing");
+ }
+
+ return new Options(publicKeyPath, privateKeyPath, keyPass);
+ }
+
+ public static void main(String[] args) throws IOException {
+ run(args, System.out);
+ }
+
+ public static void run(String[] args, OutputStream out) throws IOException {
+ PrintWriter printWriter = new PrintWriter(out);
+
+ Options options = parse(args);
+
+ if (exists(options.privateKeyFilePath)) {
+ throw new IllegalArgumentException("private key already exists in " + options.privateKeyFilePath);
+ } else if (exists(options.publicKeyFilePath)) {
+ throw new IllegalArgumentException("public key already exists in " + options.publicKeyFilePath);
+ }
+
+ KeyPair keyPair = generateKeyPair(options.privateKeyFilePath, options.publicKeyFilePath, options.keyPass);
+ if (keyPair != null) {
+ printWriter.println("Successfully generated new keyPair [publicKey: " + options.publicKeyFilePath + ", privateKey: " + options.privateKeyFilePath + "]");
+ }
+ }
+
+ private static boolean exists(String filePath) {
+ return new File(filePath).exists();
+ }
+
+
+ private static KeyPair generateKeyPair(String privateKeyFileName, String publicKeyFileName, String password) {
+ RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
+
+ KeyPair keyPair;
+ try {
+ keyPair = generator.generateKeyPair();
+ } catch (RSA2048NotSupportedException e) {
+ return null;
+ }
+
+ try {
+ generator.saveKeyPairToFiles(keyPair, privateKeyFileName, publicKeyFileName, password.toCharArray());
+ } catch (IOException | AlgorithmNotSupportedException | InappropriateKeyException | InappropriateKeySpecificationException e) {
+ throw new IllegalStateException(e);
+ }
+ return keyPair;
+ }
+}
diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java
new file mode 100644
index 00000000000..57d72587e6a
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.licensor.tools;
+
+import org.apache.commons.io.FileUtils;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseUtils;
+import org.elasticsearch.license.licensor.ESLicenseSigner;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+public class LicenseGeneratorTool {
+
+ static class Options {
+ private final String licensesInput;
+ private final String publicKeyFilePath;
+ private final String privateKeyFilePath;
+ private final String keyPass;
+
+ Options(String licensesInput, String publicKeyFilePath, String privateKeyFilePath, String keyPass) {
+ this.licensesInput = licensesInput;
+ this.publicKeyFilePath = publicKeyFilePath;
+ this.privateKeyFilePath = privateKeyFilePath;
+ this.keyPass = keyPass;
+ }
+ }
+
+ private static Options parse(String[] args) throws IOException {
+ String licenseInput = null;
+ String licenseFilePath = null;
+ String privateKeyPath = null;
+ String publicKeyPath = null;
+ String keyPass = null;
+
+ for (int i = 0; i < args.length; i++) {
+ String command = args[i].trim();
+ switch (command) {
+ case "--license":
+ licenseInput = args[++i];
+ break;
+ case "--licenseFile":
+ licenseFilePath = args[++i];
+ break;
+ case "--publicKeyPath":
+ publicKeyPath = args[++i];
+ break;
+ case "--privateKeyPath":
+ privateKeyPath = args[++i];
+ break;
+ case "--keyPass":
+ keyPass = args[++i];
+ break;
+ }
+ }
+
+ if ((licenseInput == null && licenseFilePath == null) || (licenseInput != null && licenseFilePath != null)) {
+ throw new IllegalArgumentException("only one of '--license' or '--licenseFile' option should be set");
+ } else if (licenseFilePath != null) {
+ File licenseFile = new File(licenseFilePath);
+ if (licenseFile.exists()) {
+ licenseInput = FileUtils.readFileToString(licenseFile, Charset.forName("UTF-8"));
+ } else {
+ throw new IllegalArgumentException("provided --licenseFile " + licenseFile.getAbsolutePath() + " does not exist!");
+ }
+ }
+ if (publicKeyPath == null) {
+ throw new IllegalArgumentException("mandatory option '--publicKeyPath' is missing");
+ }
+ if (privateKeyPath == null) {
+ throw new IllegalArgumentException("mandatory option '--privateKeyPath' is missing");
+ }
+ if (keyPass == null) {
+ throw new IllegalArgumentException("mandatory option '--keyPass' is missing");
+ }
+
+ return new Options(licenseInput, publicKeyPath, privateKeyPath, keyPass);
+ }
+
+ public static void main(String[] args) throws IOException {
+ run(args, System.out);
+ }
+
+ public static void run(String[] args, OutputStream out) throws IOException {
+ Options options = parse(args);
+
+ ESLicenses esLicenses = LicenseUtils.readLicensesFromString(options.licensesInput);
+
+ ESLicenseSigner signer = new ESLicenseSigner(new ESLicenseSigner.SignerOptions(options.privateKeyFilePath, options.publicKeyFilePath, options.keyPass));
+ ESLicenses signedLicences = signer.sign(esLicenses);
+
+ LicenseUtils.dumpLicenseAsJson(signedLicences, out);
+ }
+
+}
diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java
new file mode 100644
index 00000000000..0e982ed5d93
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.licensor.tools;
+
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseUtils;
+import org.elasticsearch.license.manager.ESLicenseManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class LicenseVerificationTool {
+
+ static class Options {
+ private final Set licensesFiles;
+ private final String publicKeyFilePath;
+ private final String keyPass;
+
+ Options(Set licensesFiles, String publicKeyFilePath, String keyPass) {
+ this.licensesFiles = licensesFiles;
+ this.publicKeyFilePath = publicKeyFilePath;
+ this.keyPass = keyPass;
+ }
+
+ static Set asFiles(Set filePaths) {
+ Set files = new HashSet<>(filePaths.size());
+ for (String filePath : filePaths) {
+ final File file = new File(filePath);
+ if (file.exists()) {
+ files.add(file);
+ } else {
+ throw new IllegalArgumentException(file.getAbsolutePath() + " does not exist!");
+ }
+ }
+ return files;
+ }
+ }
+
+ private static Options parse(String[] args) {
+ Set licenseFilePaths = null;
+ Set licenseFiles = null;
+ String publicKeyPath = null;
+ String keyPass = null;
+
+ for (int i = 0; i < args.length; i++) {
+ String command = args[i];
+ switch (command) {
+ case "--licensesFiles":
+ licenseFilePaths = new HashSet<>();
+ licenseFilePaths.addAll(Arrays.asList(args[++i].split(":")));
+ break;
+ case "--publicKeyPath":
+ publicKeyPath = args[++i];
+ break;
+ case "--keyPass":
+ keyPass = args[++i];
+ break;
+ }
+ }
+ if (licenseFilePaths == null) {
+ throw new IllegalArgumentException("mandatory option '--licensesFiles' is missing");
+ } else {
+ licenseFiles = Options.asFiles(licenseFilePaths);
+ if (licenseFiles.size() == 0) {
+ throw new IllegalArgumentException("no license file found for provided license files");
+ }
+ }
+ if (publicKeyPath == null) {
+ throw new IllegalArgumentException("mandatory option '--publicKeyPath' is missing");
+ }
+ if (keyPass == null) {
+ throw new IllegalArgumentException("mandatory option '--keyPass' is missing");
+ }
+ return new Options(licenseFiles, publicKeyPath, keyPass);
+ }
+
+ public static void main(String[] args) throws IOException {
+ run(args, System.out);
+ }
+
+ public static void run(String[] args, OutputStream out) throws IOException {
+ Options options = parse(args);
+
+ // read licenses
+ Set esLicensesSet = LicenseUtils.readLicensesFromFiles(options.licensesFiles);
+
+ // verify licenses
+ ESLicenseManager licenseManager = new ESLicenseManager(esLicensesSet, options.publicKeyFilePath, options.keyPass);
+ licenseManager.verifyLicenses();
+
+ // dump effective licences
+ LicenseUtils.dumpLicenseAsJson(licenseManager.getEffectiveLicenses(), out);
+ }
+
+}
diff --git a/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java b/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java
new file mode 100644
index 00000000000..0e654610662
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.manager;
+
+import net.nicholaswilliams.java.licensing.*;
+import net.nicholaswilliams.java.licensing.encryption.FilePublicKeyDataProvider;
+import net.nicholaswilliams.java.licensing.encryption.Hasher;
+import net.nicholaswilliams.java.licensing.encryption.PasswordProvider;
+import net.nicholaswilliams.java.licensing.exception.ExpiredLicenseException;
+import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseBuilders;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.elasticsearch.license.core.ESLicenses.*;
+
+/**
+ * Class responsible for reading signed licenses, maintaining an effective esLicenses instance, verification of licenses
+ * and querying against licenses on a feature basis
+ *
+ * TODO:
+ * - integration with cluster state
+ * - use ESLicenseProvider to query license from cluster state
+ */
+public class ESLicenseManager {
+
+ private final LicenseManager licenseManager;
+ private final ESLicenses esLicenses;
+ private final FilePublicKeyDataProvider publicKeyDataProvider;
+
+ public ESLicenseManager(Set esLicensesSet, String publicKeyFile, String password) throws IOException {
+ this.publicKeyDataProvider = new FilePublicKeyDataProvider(publicKeyFile);
+ this.esLicenses = merge(esLicensesSet);
+ LicenseManagerProperties.setLicenseProvider(new ESLicenseProvider());
+ LicenseManagerProperties.setPublicKeyDataProvider(publicKeyDataProvider);
+ LicenseManagerProperties.setLicenseValidator(new DefaultLicenseValidator());
+ LicenseManagerProperties.setPublicKeyPasswordProvider(new ESPublicKeyPasswordProvider(password));
+ this.licenseManager = LicenseManager.getInstance();
+ }
+
+
+ public ESLicenseManager(ESLicenses esLicenses, String publicKeyFile, String password) throws IOException {
+ this(Collections.singleton(esLicenses), publicKeyFile, password);
+ }
+
+ private static ESLicenses merge(Set esLicensesSet) {
+ ESLicenses mergedLicenses = null;
+ for (ESLicenses licenses : esLicensesSet) {
+ mergedLicenses = LicenseBuilders.merge(mergedLicenses, licenses);
+ }
+ return mergedLicenses;
+ }
+
+ public ESLicenses getEffectiveLicenses() {
+ return esLicenses;
+ }
+
+ private License getLicense(FeatureType featureType) {
+ ESLicense esLicense = esLicenses.get(featureType);
+ if (esLicense != null) {
+ String signature = esLicense.signature();
+ try {
+ License license = this.licenseManager.decryptAndVerifyLicense(extractSignedLicence(signature));
+ this.licenseManager.validateLicense(license);
+ return license;
+ } catch (IOException e) {
+ throw new IllegalStateException("bogus");
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Extract a signedLicense (SIGNED_LICENSE_CONTENT) from the signature.
+ * Validates the public key used to decrypt the license by comparing their hashes
+ *
+ * Signature structure:
+ * | MAGIC | HEADER_LENGTH | VERSION | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT |
+ *
+ * @param signature of a single license
+ * @return signed license content for the license
+ * @throws IOException
+ */
+ private SignedLicense extractSignedLicence(String signature) throws IOException {
+ byte[] signatureBytes = Base64.decodeBase64(signature);
+ ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes);
+ byteBuffer = (ByteBuffer) byteBuffer.position(13);
+ int start = byteBuffer.getInt();
+ int version = byteBuffer.getInt();
+ byte[] hash = new byte[start - 13 - 4 - 4];
+ byteBuffer.get(hash);
+
+ final byte[] computedHash = Hasher.hash(Base64.encodeBase64String(
+ FileUtils.readFileToByteArray(publicKeyDataProvider.getPublicKeyFile()))
+ ).getBytes(Charset.forName("UTF-8"));
+
+ if (!Arrays.equals(hash, computedHash)) {
+ throw new InvalidLicenseException("Invalid License");
+ }
+
+ return new ObjectSerializer().readObject(SignedLicense.class, Arrays.copyOfRange(signatureBytes, start, signatureBytes.length));
+ }
+
+
+ public void verifyLicenses() {
+ for (FeatureType featureType : esLicenses.features()) {
+ final License license = getLicense(featureType);
+ assert license != null : "license should not be null for feature: " + featureType.string();
+ verifyLicenseFields(license, esLicenses.get(featureType));
+ }
+ }
+
+
+ private static void verifyLicenseFields(License license, ESLicense eslicense) {
+ boolean licenseValid = license.getProductKey().equals(eslicense.uid())
+ && license.getHolder().equals(eslicense.issuedTo())
+ && license.getIssueDate() == eslicense.issueDate()
+ && license.getGoodBeforeDate() == eslicense.expiryDate();
+ assert license.getFeatures().size() == 4 : "one license should have only four feature";
+ String maxNodesPrefix = "maxNodes:";
+ String typePrefix = "type:";
+ String subscriptionTypePrefix = "subscription_type:";
+ boolean maxNodesValid = false;
+ boolean featureValid = false;
+ boolean typeValid = false;
+ boolean subscriptionTypeValid = false;
+ for (License.Feature feature : license.getFeatures()) {
+ String featureName = feature.getName();
+ if (featureName.startsWith(maxNodesPrefix)) {
+ maxNodesValid = eslicense.maxNodes() == Integer.parseInt(featureName.substring(maxNodesPrefix.length()));
+ } else if (featureName.startsWith(typePrefix)) {
+ typeValid = eslicense.type() == Type.fromString(featureName.substring(typePrefix.length()));
+ } else if (featureName.startsWith(subscriptionTypePrefix)) {
+ subscriptionTypeValid = eslicense.subscriptionType() == SubscriptionType.fromString(featureName.substring(subscriptionTypePrefix.length()));
+ } else {
+ featureValid = feature.getName().equals(eslicense.feature().string())
+ && feature.getGoodBeforeDate() == eslicense.expiryDate();
+ }
+ }
+ if (!licenseValid || !featureValid || !maxNodesValid || !typeValid || !subscriptionTypeValid) {
+ String msg = "licenseValid: " + licenseValid + "\n" +
+ "featureValid: " + featureValid + "\n" +
+ "maxNodeValide: " + maxNodesValid + "\n" +
+ "typeValid: " + typeValid + "\n" +
+ "subscriptionTypeValid: " + subscriptionTypeValid + "\n";
+ throw new InvalidLicenseException("Invalid License");
+ }
+ }
+
+
+ public boolean hasLicenseForFeature(FeatureType featureType) {
+ try {
+ final License license = getLicense(featureType);
+ if (license == null) {
+ return false;
+ }
+ return license.hasLicenseForFeature(featureType.string());
+ } catch (ExpiredLicenseException e) {
+ return false;
+ } catch (InvalidLicenseException e) {
+ return false;
+ }
+ }
+
+ public boolean hasLicenseForNodes(FeatureType featureType, int nodes) {
+ ESLicense esLicense = generateESLicense(featureType);
+ return esLicense.maxNodes() >= nodes;
+ }
+
+ public String getIssuerForLicense(FeatureType featureType) {
+ final License license = getLicense(featureType);
+ return license.getIssuer();
+ }
+
+ public long getIssueDateForLicense(FeatureType featureType) {
+ final License license = getLicense(featureType);
+ return license.getIssueDate();
+ }
+
+ public long getExpiryDateForLicense(FeatureType featureType) {
+ final License license = getLicense(featureType);
+ return license.getGoodBeforeDate();
+ }
+
+ public String getIssuedToForLicense(FeatureType featureType) {
+ final License license = getLicense(featureType);
+ return license.getHolder();
+ }
+
+ public Type getTypeForLicense(FeatureType featureType) {
+ ESLicense esLicense = generateESLicense(featureType);
+ return esLicense.type();
+ }
+
+ public SubscriptionType getSubscriptionTypeForLicense(FeatureType featureType) {
+ ESLicense esLicense = generateESLicense(featureType);
+ return esLicense.subscriptionType();
+ }
+
+ private ESLicense generateESLicense(FeatureType featureType) {
+ final License license = getLicense(featureType);
+ return convertToESLicense(license);
+ }
+
+ static ESLicense convertToESLicense(License license) {
+ final LicenseBuilders.LicenseBuilder licenseBuilder = LicenseBuilders.licenseBuilder(false);
+ licenseBuilder
+ .expiryDate(license.getGoodBeforeDate())
+ .issueDate(license.getIssueDate())
+ .uid(license.getProductKey())
+ .issuedTo(license.getHolder())
+ .issuer(license.getIssuer());
+
+ assert license.getFeatures().size() == 4 : "one license should have only four feature";
+ String maxNodesPrefix = "maxNodes:";
+ String typePrefix = "type:";
+ String subscriptionTypePrefix = "subscription_type:";
+ for (License.Feature feature : license.getFeatures()) {
+ String featureName = feature.getName();
+ if (featureName.startsWith(maxNodesPrefix)) {
+ licenseBuilder.maxNodes(Integer.parseInt(featureName.substring(maxNodesPrefix.length())));
+ } else if (featureName.startsWith(typePrefix)) {
+ licenseBuilder.type(Type.fromString(featureName.substring(typePrefix.length())));
+ } else if (featureName.startsWith(subscriptionTypePrefix)) {
+ licenseBuilder.subscriptionType(SubscriptionType.fromString(featureName.substring(subscriptionTypePrefix.length())));
+ } else {
+ licenseBuilder.feature(FeatureType.fromString(featureName));
+ }
+ }
+ return licenseBuilder.build();
+ }
+
+ /**
+ * Used by the underlying license manager (make sure it is never called for now)
+ * This should be retrieving licenses from the custom metadata in the cluster state
+ */
+ public class ESLicenseProvider implements LicenseProvider {
+ @Override
+ public SignedLicense getLicense(Object context) {
+ throw new NotImplementedException();
+ }
+ }
+
+ private class ESPublicKeyPasswordProvider implements PasswordProvider {
+
+ private final String pass;
+
+ private ESPublicKeyPasswordProvider(String pass) {
+ this.pass = pass;
+ }
+
+ @Override
+ public char[] getPassword() {
+ return pass.toCharArray();
+ }
+ }
+}
diff --git a/src/main/java/org/elasticsearch/license/manager/Utils.java b/src/main/java/org/elasticsearch/license/manager/Utils.java
new file mode 100644
index 00000000000..d62f734e29e
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/manager/Utils.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.manager;
+
+import net.nicholaswilliams.java.licensing.LicenseManager;
+import net.nicholaswilliams.java.licensing.ObjectSerializer;
+import net.nicholaswilliams.java.licensing.SignedLicense;
+import org.apache.commons.codec.binary.Base64;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseBuilders;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Set;
+
+import static org.elasticsearch.license.core.ESLicenses.ESLicense;
+
+public class Utils {
+
+ private Utils() {
+ }
+
+ static ESLicenses getESLicensesFromSignatures(final LicenseManager licenseManager, Set signatures) {
+ final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder();
+ for (String signature : signatures) {
+ licensesBuilder.license(getESLicenseFromSignature(licenseManager, signature));
+ }
+ return licensesBuilder.build();
+ }
+
+ private static ESLicense getESLicenseFromSignature(LicenseManager licenseManager, String signature) {
+ byte[] signatureBytes = Base64.decodeBase64(signature);
+ ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes);
+ byteBuffer = (ByteBuffer) byteBuffer.position(13);
+ int start = byteBuffer.getInt();
+ SignedLicense signedLicense = new ObjectSerializer()
+ .readObject(SignedLicense.class, Arrays.copyOfRange(signatureBytes, start, signatureBytes.length));
+ return ESLicenseManager.convertToESLicense(licenseManager.decryptAndVerifyLicense(signedLicense));
+ }
+
+
+}
diff --git a/src/main/java/org/elasticsearch/license/plugin/LicensePlugin.java b/src/main/java/org/elasticsearch/license/plugin/LicensePlugin.java
new file mode 100644
index 00000000000..fc5834648d6
--- /dev/null
+++ b/src/main/java/org/elasticsearch/license/plugin/LicensePlugin.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.plugin;
+
+import org.elasticsearch.plugins.AbstractPlugin;
+
+//TODO: plugin hooks
+public class LicensePlugin extends AbstractPlugin {
+
+ @Override
+ public String name() {
+ return "license";
+ }
+
+ @Override
+ public String description() {
+ return "Internal Elasticsearch Licensing Plugin";
+ }
+}
diff --git a/src/main/resources/es-plugin.properties b/src/main/resources/es-plugin.properties
new file mode 100644
index 00000000000..4c64a9302c6
--- /dev/null
+++ b/src/main/resources/es-plugin.properties
@@ -0,0 +1 @@
+plugin=org.elasticsearch.license.plugin.LicensePlugin
\ No newline at end of file
diff --git a/src/test/java/org/elasticsearch/license/TestUtils.java b/src/test/java/org/elasticsearch/license/TestUtils.java
new file mode 100644
index 00000000000..5fdbfe3dc67
--- /dev/null
+++ b/src/test/java/org/elasticsearch/license/TestUtils.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license;
+
+import org.apache.commons.io.FileUtils;
+import org.elasticsearch.license.core.DateUtils;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.licensor.tools.LicenseGeneratorTool;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Map;
+
+import static org.junit.Assert.assertTrue;
+
+public class TestUtils {
+
+
+ public static String generateESLicenses(Map featureAttributes) {
+ StringBuilder licenseBuilder = new StringBuilder();
+ int size = featureAttributes.values().size();
+ int i = 0;
+ for (FeatureAttributes attributes : featureAttributes.values()) {
+ licenseBuilder.append("{\n" +
+ " \"type\" : \"" + attributes.type + "\",\n" +
+ " \"subscription_type\" : \"" + attributes.subscriptionType + "\",\n" +
+ " \"issued_to\" : \"" + attributes.issuedTo + "\",\n" +
+ " \"issuer\" : \"" + attributes.issuer + "\",\n" +
+ " \"issue_date\" : \"" + attributes.issueDate + "\",\n" +
+ " \"expiry_date\" : \"" + attributes.expiryDate + "\",\n" +
+ " \"feature\" : \"" + attributes.featureType + "\",\n" +
+ " \"max_nodes\" : " + attributes.maxNodes +
+ "}");
+ if (++i < size) {
+ licenseBuilder.append(",\n");
+ }
+ }
+ return "{\n" +
+ " \"licenses\" : [" +
+ licenseBuilder.toString() +
+ "]\n" +
+ "}";
+
+ }
+
+ public static String runLicenseGenerationTool(String[] args) throws IOException {
+ File temp = File.createTempFile("temp", ".out");
+ temp.deleteOnExit();
+ try (FileOutputStream outputStream = new FileOutputStream(temp)) {
+ LicenseGeneratorTool.run(args, outputStream);
+ }
+ return FileUtils.readFileToString(temp);
+ }
+
+ public static void verifyESLicenses(ESLicenses esLicenses, Map featureAttributes) throws ParseException {
+ assertTrue("Number of feature licenses should be " + featureAttributes.size(), esLicenses.features().size() == featureAttributes.size());
+ for (Map.Entry featureAttrTuple : featureAttributes.entrySet()) {
+ ESLicenses.FeatureType featureType = featureAttrTuple.getKey();
+ FeatureAttributes attributes = featureAttrTuple.getValue();
+ final ESLicenses.ESLicense esLicense = esLicenses.get(featureType);
+ assertTrue("license for " + featureType.string() + " should be present", esLicense != null);
+ assertTrue("expected value for issuedTo was: " + attributes.issuedTo + " but got: " + esLicense.issuedTo(), esLicense.issuedTo().equals(attributes.issuedTo));
+ assertTrue("expected value for type was: " + attributes.type + " but got: " + esLicense.type().string(), esLicense.type().string().equals(attributes.type));
+ assertTrue("expected value for subscriptionType was: " + attributes.subscriptionType + " but got: " + esLicense.subscriptionType().string(), esLicense.subscriptionType().string().equals(attributes.subscriptionType));
+ assertTrue("expected value for feature was: " + attributes.featureType + " but got: " + esLicense.feature().string(), esLicense.feature().string().equals(attributes.featureType));
+ assertTrue("expected value for issueDate was: " + DateUtils.longFromDateString(attributes.issueDate) + " but got: " + esLicense.issueDate(), esLicense.issueDate() == DateUtils.longFromDateString(attributes.issueDate));
+ assertTrue("expected value for expiryDate: " + DateUtils.longExpiryDateFromString(attributes.expiryDate) + " but got: " + esLicense.expiryDate(), esLicense.expiryDate() == DateUtils.longExpiryDateFromString(attributes.expiryDate));
+ assertTrue("expected value for maxNodes: " + attributes.maxNodes + " but got: " + esLicense.maxNodes(), esLicense.maxNodes() == attributes.maxNodes);
+
+ assertTrue("generated licenses should have non-null uid field", esLicense.uid() != null);
+ assertTrue("generated licenses should have non-null signature field", esLicense.signature() != null);
+ }
+ }
+
+ public static class FeatureAttributes {
+
+ public final String featureType;
+ public final String type;
+ public final String subscriptionType;
+ public final String issuedTo;
+ public final int maxNodes;
+ public final String issueDate;
+ public final String expiryDate;
+ public final String issuer;
+
+ public FeatureAttributes(String featureType, String type, String subscriptionType, String issuedTo, String issuer, int maxNodes, String issueDateStr, String expiryDateStr) throws ParseException {
+ this.featureType = featureType;
+ this.type = type;
+ this.subscriptionType = subscriptionType;
+ this.issuedTo = issuedTo;
+ this.issuer = issuer;
+ this.maxNodes = maxNodes;
+ this.issueDate = issueDateStr;
+ this.expiryDate = expiryDateStr;
+ }
+ }
+}
diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java
new file mode 100644
index 00000000000..cf02a5f5644
--- /dev/null
+++ b/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.licensor;
+
+import org.elasticsearch.license.TestUtils;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseUtils;
+import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.elasticsearch.license.core.ESLicenses.FeatureType;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class LicenseGenerationTests {
+
+ private static String pubKeyPath = null;
+ private static String priKeyPath = null;
+ private static String keyPass = null;
+
+ @BeforeClass
+ public static void setup() throws IOException {
+
+ // Generate temp KeyPair spec
+ File privateKeyFile = File.createTempFile("privateKey", ".key");
+ File publicKeyFile = File.createTempFile("publicKey", ".key");
+ LicenseGenerationTests.pubKeyPath = publicKeyFile.getAbsolutePath();
+ LicenseGenerationTests.priKeyPath = privateKeyFile.getAbsolutePath();
+ assert privateKeyFile.delete();
+ assert publicKeyFile.delete();
+ String keyPass = "password";
+ LicenseGenerationTests.keyPass = keyPass;
+
+ // Generate keyPair
+ String[] args = new String[6];
+ args[0] = "--publicKeyPath";
+ args[1] = LicenseGenerationTests.pubKeyPath;
+ args[2] = "--privateKeyPath";
+ args[3] = LicenseGenerationTests.priKeyPath;
+ args[4] = "--keyPass";
+ args[5] = LicenseGenerationTests.keyPass;
+ KeyPairGeneratorTool.main(args);
+ }
+
+ @Test
+ public void testSimpleLicenseGeneration() throws ParseException, IOException {
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureAttributes =
+ new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-12-13");
+ map.put(FeatureType.SHIELD, featureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+
+ ESLicenses esLicensesOutput = LicenseUtils.readLicensesFromString(licenseOutput);
+
+ TestUtils.verifyESLicenses(esLicensesOutput, map);
+ }
+
+ @Test
+ public void testMultipleFeatureTypes() throws ParseException, IOException {
+
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes shildFeatureAttributes =
+ new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-12-13");
+ TestUtils.FeatureAttributes marvelFeatureAttributes =
+ new TestUtils.FeatureAttributes("marvel", "subscription", "silver", "foo1 bar Inc.", "elasticsearc3h", 10, "2014-01-13", "2014-12-13");
+ map.put(FeatureType.SHIELD, shildFeatureAttributes);
+ map.put(FeatureType.MARVEL, marvelFeatureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+
+ ESLicenses esLicensesOutput = LicenseUtils.readLicensesFromString(licenseOutput);
+
+ TestUtils.verifyESLicenses(esLicensesOutput, map);
+ }
+
+ @Test
+ public void testMissingCLTArgs() throws ParseException, IOException {
+
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureAttributes =
+ new TestUtils.FeatureAttributes("shiedgdsld", "internal", "none", "foo bar Inc.", "elasticsearch", 23, "2014-12-13", "2015-12-13");
+ map.put(FeatureType.SHIELD, featureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--linse";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ try {
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue("Exception should indicate mandatory param --license, got: " + e.getMessage(), e.getMessage().contains("license"));
+ }
+ }
+
+ @Test
+ public void testInvalidFeatureType() throws ParseException, IOException {
+
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureAttributes =
+ new TestUtils.FeatureAttributes("shiedgdsld", "internal", "none", "foo bar Inc.", "elasticsearch", 23, "2014-12-13", "2015-12-13");
+ map.put(FeatureType.SHIELD, featureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ try {
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue("Exception should indicate invalid FeatureType, got: " + e.getMessage(), e.getMessage().contains("Invalid FeatureType"));
+ }
+ }
+
+ @Test
+ public void testInvalidSubscriptionType() throws ParseException, IOException {
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureAttributes =
+ new TestUtils.FeatureAttributes("shield", "trial", "nodavne", "foo bar Inc.", "elasticsearch", 25, "2014-12-13", "2015-12-13");
+ map.put(FeatureType.SHIELD, featureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ try {
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue("Exception should indicate invalid SubscriptionType, got: " + e.getMessage(), e.getMessage().contains("Invalid SubscriptionType"));
+ }
+ }
+
+ @Test
+ public void testInvalidType() throws ParseException, IOException {
+
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureAttributes =
+ new TestUtils.FeatureAttributes("shield", "inininternal", "gold", "foo bar Inc.", "elasticsearch", 12, "2014-12-13", "2015-12-13");
+ map.put(FeatureType.SHIELD, featureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ try {
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue("Exception should indicate invalid Type, got: " + e.getMessage(), e.getMessage().contains("Invalid Type"));
+ }
+ }
+
+}
diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java
new file mode 100644
index 00000000000..4ae012be99f
--- /dev/null
+++ b/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.licensor;
+
+import org.apache.commons.io.FileUtils;
+import org.elasticsearch.license.TestUtils;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseUtils;
+import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool;
+import org.elasticsearch.license.licensor.tools.LicenseVerificationTool;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Ignore("Enable once maven is setup properly; now it throws invalid signature error for all the tests when the tests always pass in intellij")
+public class LicenseVerificationToolTests {
+
+ private static String pubKeyPath = null;
+ private static String priKeyPath = null;
+ private static String keyPass = null;
+
+ @BeforeClass
+ public static void setup() throws IOException {
+
+ // Generate temp KeyPair spec
+ File privateKeyFile = File.createTempFile("privateKey", ".key");
+ File publicKeyFile = File.createTempFile("publicKey", ".key");
+ LicenseVerificationToolTests.pubKeyPath = publicKeyFile.getAbsolutePath();
+ LicenseVerificationToolTests.priKeyPath = privateKeyFile.getAbsolutePath();
+ assert privateKeyFile.delete();
+ assert publicKeyFile.delete();
+ LicenseVerificationToolTests.keyPass = "password";
+
+ // Generate keyPair
+ String[] args = new String[6];
+ args[0] = "--publicKeyPath";
+ args[1] = LicenseVerificationToolTests.pubKeyPath;
+ args[2] = "--privateKeyPath";
+ args[3] = LicenseVerificationToolTests.priKeyPath;
+ args[4] = "--keyPass";
+ args[5] = LicenseVerificationToolTests.keyPass;
+ KeyPairGeneratorTool.main(args);
+ }
+
+ @Test
+ public void testEffectiveLicenseGeneration() throws Exception {
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureWithLongerExpiryDate =
+ new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-12-13");
+ map.put(ESLicenses.FeatureType.SHIELD, featureWithLongerExpiryDate);
+
+ String signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map));
+ String firstLicenseFile = getAsFilePath(signedLicense);
+
+ TestUtils.FeatureAttributes featureWithShorterExpiryDate =
+ new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-01-13");
+ map.put(ESLicenses.FeatureType.SHIELD, featureWithShorterExpiryDate);
+
+ signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map));
+ String secondLicenseFile = getAsFilePath(signedLicense);
+
+ String[] args = new String[6];
+ args[0] = "--licensesFiles";
+ args[1] = firstLicenseFile + ":" + secondLicenseFile;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--keyPass";
+ args[5] = keyPass;
+
+ String effectiveLicenseStr = runLicenseVerificationTool(args);
+ ESLicenses effectiveLicense = LicenseUtils.readLicensesFromString(effectiveLicenseStr);
+
+ map.put(ESLicenses.FeatureType.SHIELD, featureWithLongerExpiryDate);
+
+ // verify that the effective license strips out license for the same feature with earlier expiry dates
+ TestUtils.verifyESLicenses(effectiveLicense, map);
+ }
+
+ @Test
+ public void testEffectiveLicenseForMultiFeatures() throws Exception {
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes shieldFeatureWithLongerExpiryDate =
+ new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-12-13");
+ map.put(ESLicenses.FeatureType.SHIELD, shieldFeatureWithLongerExpiryDate);
+
+ String signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map));
+ String firstLicenseFile = getAsFilePath(signedLicense);
+
+ TestUtils.FeatureAttributes marvelFeatureWithShorterExpiryDate =
+ new TestUtils.FeatureAttributes("marvel", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-01-13");
+ map.put(ESLicenses.FeatureType.MARVEL, marvelFeatureWithShorterExpiryDate);
+
+ signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map));
+ String secondLicenseFile = getAsFilePath(signedLicense);
+
+ String[] args = new String[6];
+ args[0] = "--licensesFiles";
+ args[1] = firstLicenseFile + ":" + secondLicenseFile;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--keyPass";
+ args[5] = keyPass;
+
+ String effectiveLicenseStr = runLicenseVerificationTool(args);
+ ESLicenses effectiveLicense = LicenseUtils.readLicensesFromString(effectiveLicenseStr);
+
+ // verify that the effective license contains both feature licenses
+ TestUtils.verifyESLicenses(effectiveLicense, map);
+ }
+
+ @Test
+ public void testEffectiveLicenseForMultiFeatures2() throws Exception {
+ Map map = new HashMap<>();
+
+ TestUtils.FeatureAttributes shieldFeatureWithLongerExpiryDate =
+ new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-12-13");
+ TestUtils.FeatureAttributes marvelFeatureWithShorterExpiryDate =
+ new TestUtils.FeatureAttributes("marvel", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-01-13");
+
+ map.put(ESLicenses.FeatureType.SHIELD, shieldFeatureWithLongerExpiryDate);
+ map.put(ESLicenses.FeatureType.MARVEL, marvelFeatureWithShorterExpiryDate);
+
+ String signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map));
+ String firstLicenseFile = getAsFilePath(signedLicense);
+
+ TestUtils.FeatureAttributes shieldFeatureWithShorterExpiryDate =
+ new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-11-13");
+ TestUtils.FeatureAttributes marvelFeatureWithLongerExpiryDate =
+ new TestUtils.FeatureAttributes("marvel", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-11-13");
+
+ map.put(ESLicenses.FeatureType.SHIELD, shieldFeatureWithShorterExpiryDate);
+ map.put(ESLicenses.FeatureType.MARVEL, marvelFeatureWithLongerExpiryDate);
+
+ signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map));
+ String secondLicenseFile = getAsFilePath(signedLicense);
+
+ String[] args = new String[6];
+ args[0] = "--licensesFiles";
+ args[1] = firstLicenseFile + ":" + secondLicenseFile;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--keyPass";
+ args[5] = keyPass;
+
+ String effectiveLicenseStr = runLicenseVerificationTool(args);
+ ESLicenses effectiveLicense = LicenseUtils.readLicensesFromString(effectiveLicenseStr);
+
+ map.put(ESLicenses.FeatureType.SHIELD, shieldFeatureWithLongerExpiryDate);
+ map.put(ESLicenses.FeatureType.MARVEL, marvelFeatureWithLongerExpiryDate);
+
+ // verify that the generated effective license is generated from choosing individual licences from multiple files
+ TestUtils.verifyESLicenses(effectiveLicense, map);
+ }
+
+ public static String runLicenseVerificationTool(String[] args) throws IOException {
+ File temp = File.createTempFile("temp", ".out");
+ temp.deleteOnExit();
+ try (FileOutputStream outputStream = new FileOutputStream(temp)) {
+ LicenseVerificationTool.run(args, outputStream);
+ }
+ return FileUtils.readFileToString(temp);
+ }
+
+ public static String runLicenseGenerationTool(String licenseInput) throws IOException {
+ String args[] = new String[8];
+
+ args[0] = "--license";
+ args[1] = licenseInput;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ return TestUtils.runLicenseGenerationTool(args);
+ }
+
+ private static String getAsFilePath(String content) throws IOException {
+ File temp = File.createTempFile("license", ".out");
+ temp.deleteOnExit();
+ FileUtils.write(temp, content);
+ String tempFilePath = temp.getAbsolutePath();
+ while (tempFilePath.contains(":")) {
+ assert temp.delete();
+ tempFilePath = getAsFilePath(content);
+ }
+ return tempFilePath;
+ }
+
+}
diff --git a/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java
new file mode 100644
index 00000000000..0807076d663
--- /dev/null
+++ b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.license.manager;
+
+import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException;
+import org.elasticsearch.license.TestUtils;
+import org.elasticsearch.license.core.DateUtils;
+import org.elasticsearch.license.core.ESLicenses;
+import org.elasticsearch.license.core.LicenseBuilders;
+import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.elasticsearch.license.core.ESLicenses.FeatureType;
+import static org.elasticsearch.license.core.LicenseUtils.readLicensesFromString;
+import static org.junit.Assert.*;
+
+@Ignore("Enable once maven is setup properly; now it throws invalid signature error for all the tests when the tests always pass in intellij")
+public class LicenseVerificationTests {
+
+ private static String pubKeyPath = null;
+ private static String priKeyPath = null;
+ private static String keyPass = null;
+
+ @BeforeClass
+ public static void setup() throws IOException {
+
+ // Generate temp KeyPair spec
+ File privateKeyFile = File.createTempFile("privateKey", ".key");
+ File publicKeyFile = File.createTempFile("publicKey", ".key");
+ LicenseVerificationTests.pubKeyPath = publicKeyFile.getAbsolutePath();
+ LicenseVerificationTests.priKeyPath = privateKeyFile.getAbsolutePath();
+ assert privateKeyFile.delete();
+ assert publicKeyFile.delete();
+ LicenseVerificationTests.keyPass = "password";
+
+ // Generate keyPair
+ String[] args = new String[6];
+ args[0] = "--publicKeyPath";
+ args[1] = LicenseVerificationTests.pubKeyPath;
+ args[2] = "--privateKeyPath";
+ args[3] = LicenseVerificationTests.priKeyPath;
+ args[4] = "--keyPass";
+ args[5] = LicenseVerificationTests.keyPass;
+ KeyPairGeneratorTool.main(args);
+ }
+
+ @Test
+ public void testGeneratedLicenses() throws Exception {
+ Date issueDate = new Date();
+ String issueDateStr = DateUtils.dateStringFromLongDate(issueDate.getTime());
+ String expiryDateStr = DateUtils.dateStringFromLongDate(DateUtils.longExpiryDateFromDate(issueDate.getTime() + 24 * 60 * 60l));
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureAttributes =
+ new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr);
+ map.put(FeatureType.SHIELD, featureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+
+ ESLicenses esLicensesOutput = readLicensesFromString(licenseOutput);
+
+ ESLicenseManager esLicenseManager = new ESLicenseManager(esLicensesOutput, pubKeyPath, keyPass);
+
+ esLicenseManager.verifyLicenses();
+
+ verifyLicenseManager(esLicenseManager, map);
+ }
+
+ @Test
+ public void testMultipleFeatureLicenses() throws Exception {
+ Date issueDate = new Date();
+ String issueDateStr = DateUtils.dateStringFromLongDate(issueDate.getTime());
+ String expiryDateStr = DateUtils.dateStringFromLongDate(DateUtils.longExpiryDateFromDate(issueDate.getTime() + 24 * 60 * 60 * 1000l));
+
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes shildFeatureAttributes =
+ new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr);
+ TestUtils.FeatureAttributes marvelFeatureAttributes =
+ new TestUtils.FeatureAttributes("marvel", "subscription", "silver", "foo1 bar Inc.", "elasticsearc3h", 10, issueDateStr, expiryDateStr);
+ map.put(FeatureType.SHIELD, shildFeatureAttributes);
+ map.put(FeatureType.MARVEL, marvelFeatureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+
+ ESLicenses esLicensesOutput = readLicensesFromString(licenseOutput);
+
+ ESLicenseManager esLicenseManager = new ESLicenseManager(esLicensesOutput, pubKeyPath, keyPass);
+
+ esLicenseManager.verifyLicenses();
+
+ verifyLicenseManager(esLicenseManager, map);
+ }
+
+ @Test
+ public void testLicenseExpiry() throws Exception {
+
+ Date issueDate = new Date();
+ String issueDateStr = DateUtils.dateStringFromLongDate(issueDate.getTime());
+ String expiryDateStr = DateUtils.dateStringFromLongDate(DateUtils.longExpiryDateFromDate(issueDate.getTime() + 24 * 60 * 60l));
+
+ String expiredExpiryDateStr = DateUtils.dateStringFromLongDate(DateUtils.longExpiryDateFromDate(issueDate.getTime() - 5 * 24 * 60 * 60 * 1000l));
+
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes shildFeatureAttributes =
+ new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr);
+ TestUtils.FeatureAttributes marvelFeatureAttributes =
+ new TestUtils.FeatureAttributes("marvel", "subscription", "silver", "foo1 bar Inc.", "elasticsearc3h", 10, issueDateStr, expiredExpiryDateStr);
+ map.put(FeatureType.SHIELD, shildFeatureAttributes);
+ map.put(FeatureType.MARVEL, marvelFeatureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+
+ ESLicenses esLicensesOutput = readLicensesFromString(licenseOutput);
+
+ ESLicenseManager esLicenseManager = new ESLicenseManager(esLicensesOutput, pubKeyPath, keyPass);
+
+ // All validation for shield license should be normal as expected
+ verifyLicenseManager(esLicenseManager, Collections.singletonMap(FeatureType.SHIELD, shildFeatureAttributes));
+
+ assertFalse("license for marvel should not be valid due to expired expiry date", esLicenseManager.hasLicenseForFeature(FeatureType.MARVEL));
+ }
+
+ @Test
+ public void testLicenseTampering() throws Exception {
+
+ Date issueDate = new Date();
+ String issueDateStr = DateUtils.dateStringFromLongDate(issueDate.getTime());
+ String expiryDateStr = DateUtils.dateStringFromLongDate(DateUtils.longExpiryDateFromDate(issueDate.getTime() + 24 * 60 * 60l));
+ Map map = new HashMap<>();
+ TestUtils.FeatureAttributes featureAttributes =
+ new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr);
+ map.put(FeatureType.SHIELD, featureAttributes);
+
+ String licenseString = TestUtils.generateESLicenses(map);
+
+ String[] args = new String[8];
+ args[0] = "--license";
+ args[1] = licenseString;
+ args[2] = "--publicKeyPath";
+ args[3] = pubKeyPath;
+ args[4] = "--privateKeyPath";
+ args[5] = priKeyPath;
+ args[6] = "--keyPass";
+ args[7] = keyPass;
+
+ String licenseOutput = TestUtils.runLicenseGenerationTool(args);
+
+ ESLicenses esLicensesOutput = readLicensesFromString(licenseOutput);
+
+ ESLicenses.ESLicense esLicense = esLicensesOutput.get(FeatureType.SHIELD);
+
+ long originalExpiryDate = esLicense.expiryDate();
+ final ESLicenses.ESLicense tamperedLicense = LicenseBuilders.licenseBuilder(true)
+ .fromLicense(esLicense)
+ .expiryDate(esLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
+ .feature(FeatureType.SHIELD)
+ .issuer("elasticsqearch")
+ .build();
+
+ ESLicenses tamperedLicenses = LicenseBuilders.licensesBuilder().license(tamperedLicense).build();
+
+ ESLicenseManager esLicenseManager = null;
+ try {
+ esLicenseManager = new ESLicenseManager(tamperedLicenses, pubKeyPath, keyPass);
+ assertTrue("License manager should always report the original (signed) expiry date", esLicenseManager.getExpiryDateForLicense(FeatureType.SHIELD) == originalExpiryDate);
+ esLicenseManager.verifyLicenses();
+ fail();
+ } catch (InvalidLicenseException e) {
+ assertTrue("Exception should contain 'Invalid License' ", e.getMessage().contains("Invalid License"));
+ }
+ }
+
+ public static void verifyLicenseManager(ESLicenseManager esLicenseManager, Map featureAttributeMap) throws ParseException {
+
+ for (Map.Entry entry : featureAttributeMap.entrySet()) {
+ TestUtils.FeatureAttributes featureAttributes = entry.getValue();
+ FeatureType featureType = entry.getKey();
+ assertTrue("License should have issuedTo of " + featureAttributes.issuedTo, esLicenseManager.getIssuedToForLicense(featureType).equals(featureAttributes.issuedTo));
+ assertTrue("License should have issuer of " + featureAttributes.issuer, esLicenseManager.getIssuerForLicense(featureType).equals(featureAttributes.issuer));
+ assertTrue("License should have issue date of " + DateUtils.longFromDateString(featureAttributes.issueDate), esLicenseManager.getIssueDateForLicense(featureType) == DateUtils.longFromDateString(featureAttributes.issueDate));
+ assertTrue("License should have expiry date of " + DateUtils.longExpiryDateFromString(featureAttributes.expiryDate), esLicenseManager.getExpiryDateForLicense(featureType) == DateUtils.longExpiryDateFromString(featureAttributes.expiryDate));
+ assertTrue("License should have type of " + featureAttributes.featureType, esLicenseManager.getTypeForLicense(featureType) == ESLicenses.Type.fromString(featureAttributes.type));
+ assertTrue("License should have subscription type of " + featureAttributes.subscriptionType, esLicenseManager.getSubscriptionTypeForLicense(featureType) == ESLicenses.SubscriptionType.fromString(featureAttributes.subscriptionType));
+
+
+ assertTrue("License should be valid for shield", esLicenseManager.hasLicenseForFeature(featureType));
+ assertTrue("License should be valid for maxNodes = " + (featureAttributes.maxNodes - 1), esLicenseManager.hasLicenseForNodes(featureType, featureAttributes.maxNodes - 1));
+ assertTrue("License should be valid for maxNodes = " + (featureAttributes.maxNodes), esLicenseManager.hasLicenseForNodes(featureType, featureAttributes.maxNodes));
+ assertFalse("License should not be valid for maxNodes = " + (featureAttributes.maxNodes + 1), esLicenseManager.hasLicenseForNodes(featureType, featureAttributes.maxNodes + 1));
+ }
+ }
+}