From e59cf4538a0be57be00a81b302f67a49c5536388 Mon Sep 17 00:00:00 2001 From: Areek Zillur Date: Tue, 21 Oct 2014 23:05:29 -0400 Subject: [PATCH] major refactor: make ESLicense more consistent; restructure ESLicenseManager Original commit: elastic/x-pack-elasticsearch@cb60bfb539492a50dc3a74ace5c1060302572566 --- .../elasticsearch/license/core/ESLicense.java | 390 ++++++++++++++++++ .../license/core/ESLicenses.java | 195 +++------ .../license/core/LicenseBuilders.java | 316 -------------- .../license/core/LicenseUtils.java | 183 -------- .../license/licensor/ESLicenseSigner.java | 57 +-- .../license/licensor/LicenseSpec.java | 149 +++++++ .../tools/FileBasedESLicenseProvider.java | 33 +- .../licensor/tools/KeyPairGeneratorTool.java | 5 +- .../licensor/tools/LicenseGeneratorTool.java | 50 ++- .../tools/LicenseVerificationTool.java | 69 ++-- .../license/manager/ESLicenseManager.java | 219 ++++------ .../license/manager/ESLicenseProvider.java | 8 +- .../elasticsearch/license/manager/Utils.java | 27 ++ .../license/plugin/action/Utils.java | 97 ----- .../plugin/action/get/GetLicenseResponse.java | 29 +- .../action/get/TransportGetLicenseAction.java | 31 +- .../plugin/action/put/PutLicenseRequest.java | 29 +- .../action/put/PutLicenseRequestBuilder.java | 10 +- .../core/ElasticsearchLicenseException.java | 1 - .../plugin/core/LicensesManagerService.java | 9 +- .../license/plugin/core/LicensesMetaData.java | 10 +- .../license/plugin/core/LicensesService.java | 111 +++-- .../plugin/core/trial/TrialLicenseUtils.java | 70 ---- .../core/trial/TrialLicensesBuilder.java | 7 + .../plugin/rest/RestGetLicenseAction.java | 14 +- .../plugin/rest/RestPutLicenseAction.java | 2 +- .../license/AbstractLicensingTestBase.java | 3 +- .../org/elasticsearch/license/TestUtils.java | 38 +- .../licensor/LicenseGenerationTests.java | 7 +- .../LicenseVerificationToolTests.java | 18 +- .../manager/LicenseVerificationTests.java | 64 ++- .../license/plugin/LicenseTransportTests.java | 28 +- .../license/plugin/LicensesServiceTests.java | 42 +- 33 files changed, 1035 insertions(+), 1286 deletions(-) create mode 100644 src/main/java/org/elasticsearch/license/core/ESLicense.java delete mode 100644 src/main/java/org/elasticsearch/license/core/LicenseBuilders.java delete mode 100644 src/main/java/org/elasticsearch/license/core/LicenseUtils.java create mode 100644 src/main/java/org/elasticsearch/license/licensor/LicenseSpec.java delete mode 100644 src/main/java/org/elasticsearch/license/plugin/action/Utils.java diff --git a/src/main/java/org/elasticsearch/license/core/ESLicense.java b/src/main/java/org/elasticsearch/license/core/ESLicense.java new file mode 100644 index 00000000000..45ad44cee62 --- /dev/null +++ b/src/main/java/org/elasticsearch/license/core/ESLicense.java @@ -0,0 +1,390 @@ +/* + * 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.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.*; + +import java.io.IOException; +import java.util.*; + +public class ESLicense implements Comparable { + + private final String uid; + private final String issuer; + private final String issuedTo; + private final long issueDate; + private final Type type; + private final SubscriptionType subscriptionType; + private final String feature; + private final String signature; + private final long expiryDate; + private final int maxNodes; + + private ESLicense(String uid, String issuer, String issuedTo, long issueDate, Type type, + SubscriptionType subscriptionType, String feature, String signature, long expiryDate, int maxNodes) { + this.uid = uid; + this.issuer = issuer; + this.issuedTo = issuedTo; + this.issueDate = issueDate; + this.type = type; + this.subscriptionType = subscriptionType; + this.feature = feature; + this.signature = signature; + this.expiryDate = expiryDate; + this.maxNodes = maxNodes; + } + + + /** + * @return a unique identifier for a license (currently just a UUID) + */ + public String uid() { + return uid; + } + + /** + * @return type of the license [trial, subscription, internal] + */ + public Type type() { + return type; + } + + /** + * @return subscription type of the license [none, silver, gold, platinum] + */ + public SubscriptionType subscriptionType() { + return subscriptionType; + } + + /** + * @return the issueDate in milliseconds + */ + public long issueDate() { + return issueDate; + } + + /** + * @return the featureType for the license [shield, marvel] + */ + public String feature() { + return feature; + } + + /** + * @return the expiry date in milliseconds + */ + public long expiryDate() { + return expiryDate; + } + + /** + * @return the maximum number of nodes this license has been issued for + */ + public int maxNodes() { + return maxNodes; + } + + /** + * @return a string representing the entity this licenses has been issued to + */ + public String issuedTo() { + return issuedTo; + } + + /** + * @return a string representing the entity responsible for issuing this license (internal) + */ + public String issuer() { + return issuer; + } + + /** + * @return a string representing the signature of the license used for license verification + */ + public String signature() { + return signature; + } + + @Override + public int compareTo(ESLicense o) { + return Long.compare(expiryDate, o.expiryDate); + } + + /** + * Enum for License Type + */ + public enum Type { + TRIAL("trial"), + SUBSCRIPTION("subscription"), + INTERNAL("internal"); + + private final String name; + + private Type(String name) { + this.name = name; + } + + public String string() { + return name; + } + + 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("none"), + DEVELOPMENT("development"), + SILVER("silver"), + GOLD("gold"), + PLATINUM("platinum"); + + public static SubscriptionType DEFAULT = NONE; + + private final String name; + + private SubscriptionType(String name) { + this.name = name; + } + + public String string() { + return name; + } + + 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); + } + } + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String uid; + private String issuer; + private String issuedTo; + private long issueDate = -1; + private Type type; + private SubscriptionType subscriptionType = SubscriptionType.DEFAULT; + private String feature; + private String signature; + private long expiryDate = -1; + private int maxNodes; + + + public Builder uid(String uid) { + this.uid = uid; + return this; + } + + public Builder issuer(String issuer) { + this.issuer = issuer; + return this; + } + + public Builder issuedTo(String issuedTo) { + this.issuedTo = issuedTo; + return this; + } + + public Builder issueDate(long issueDate) { + this.issueDate = issueDate; + return this; + } + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder subscriptionType(SubscriptionType subscriptionType) { + this.subscriptionType = subscriptionType; + return this; + } + + public Builder feature(String feature) { + this.feature = feature; + return this; + } + + public Builder expiryDate(long expiryDate) { + this.expiryDate = expiryDate; + return this; + } + + public Builder maxNodes(int maxNodes) { + this.maxNodes = maxNodes; + return this; + } + + public Builder signature(String signature) { + if (signature != null) { + this.signature = signature; + } + return this; + } + + public Builder fromLicense(ESLicense license) { + return uid(license.uid()) + .issuedTo(license.issuedTo()) + .issueDate(license.issueDate()) + .type(license.type()) + .subscriptionType(license.subscriptionType()) + .feature(license.feature()) + .maxNodes(license.maxNodes()) + .expiryDate(license.expiryDate()) + .issuer(license.issuer()) + .signature(license.signature()); + } + + public ESLicense build() { + verify(false); + return new ESLicense(uid, issuer, issuedTo, issueDate, type, + subscriptionType, feature, signature, expiryDate, maxNodes); + } + + public ESLicense buildInternal() { + verify(true); + return new ESLicense(uid, issuer, issuedTo, issueDate, type, + subscriptionType, feature, signature, expiryDate, maxNodes); + } + + private void verify(boolean internal) { + String msg = null; + if (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); + } + } + } + + + final static class Fields { + static final String UID = "uid"; + static final String TYPE = "type"; + static final String SUBSCRIPTION_TYPE = "subscription_type"; + static final String ISSUE_DATE = "issue_date"; + static final String FEATURE = "feature"; + static final String EXPIRY_DATE = "expiry_date"; + static final String MAX_NODES = "max_nodes"; + static final String ISSUED_TO = "issued_to"; + static final String ISSUER = "issuer"; + static final String SIGNATURE = "signature"; + } + + + static void toXContent(ESLicense license, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.field(Fields.UID, license.uid); + builder.field(Fields.TYPE, license.type.string()); + builder.field(Fields.SUBSCRIPTION_TYPE, license.subscriptionType.string()); + builder.field(Fields.ISSUE_DATE, license.issueDate); + builder.field(Fields.FEATURE, license.feature); + builder.field(Fields.EXPIRY_DATE, license.expiryDate); + builder.field(Fields.MAX_NODES, license.maxNodes); + builder.field(Fields.ISSUED_TO, license.issuedTo); + builder.field(Fields.ISSUER, license.issuer); + builder.field(Fields.SIGNATURE, license.signature); + builder.endObject(); + } + + + static ESLicense fromXContent(Map map) throws IOException { + return new Builder() + .uid((String) map.get(Fields.UID)) + .type(Type.fromString((String) map.get(Fields.TYPE))) + .subscriptionType(SubscriptionType.fromString((String) map.get(Fields.SUBSCRIPTION_TYPE))) + .feature((String) map.get(Fields.FEATURE)) + .maxNodes((int) map.get(Fields.MAX_NODES)) + .issuedTo((String) map.get(Fields.ISSUED_TO)) + .signature((String) map.get(Fields.SIGNATURE)) + .issueDate((long) map.get(Fields.ISSUE_DATE)) + .expiryDate((long) map.get(Fields.EXPIRY_DATE)) + .issuer((String) map.get(Fields.ISSUER)) + .build(); + } + + static ESLicense readFrom(StreamInput in) throws IOException { + Map licenseMap = in.readMap(); + return builder() + .uid((String) licenseMap.get(Fields.UID)) + .type(Type.fromString((String) licenseMap.get(Fields.TYPE))) + .subscriptionType(SubscriptionType.fromString((String) licenseMap.get(Fields.SUBSCRIPTION_TYPE))) + .issueDate((long) licenseMap.get(Fields.ISSUE_DATE)) + .feature((String) licenseMap.get(Fields.FEATURE)) + .expiryDate((long) licenseMap.get(Fields.EXPIRY_DATE)) + .maxNodes((int) licenseMap.get(Fields.MAX_NODES)) + .issuedTo((String) licenseMap.get(Fields.ISSUED_TO)) + .signature((String) licenseMap.get(Fields.SIGNATURE)) + .issuer((String) licenseMap.get(Fields.ISSUER)) + .build(); + } + + static void writeTo(ESLicense esLicense, StreamOutput out) throws IOException { + Map licenseMap = new HashMap<>(); + licenseMap.put(Fields.UID, esLicense.uid); + licenseMap.put(Fields.TYPE, esLicense.type.string()); + licenseMap.put(Fields.SUBSCRIPTION_TYPE, esLicense.subscriptionType.string()); + licenseMap.put(Fields.ISSUE_DATE, esLicense.issueDate); + licenseMap.put(Fields.FEATURE, esLicense.feature); + licenseMap.put(Fields.EXPIRY_DATE, esLicense.expiryDate); + licenseMap.put(Fields.MAX_NODES, esLicense.maxNodes); + licenseMap.put(Fields.ISSUED_TO, esLicense.issuedTo); + licenseMap.put(Fields.ISSUER, esLicense.issuer); + licenseMap.put(Fields.SIGNATURE, esLicense.signature); + out.writeMap(licenseMap); + } + +} diff --git a/src/main/java/org/elasticsearch/license/core/ESLicenses.java b/src/main/java/org/elasticsearch/license/core/ESLicenses.java index dc229403ab3..f6174721fb6 100644 --- a/src/main/java/org/elasticsearch/license/core/ESLicenses.java +++ b/src/main/java/org/elasticsearch/license/core/ESLicenses.java @@ -5,159 +5,64 @@ */ package org.elasticsearch.license.core; -import java.util.Collection; -import java.util.Set; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.*; -/** - * 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< - * @param feature - */ - public ESLicense get(String feature); - - /** - * Enum for License Type - */ - public enum Type { - TRIAL("trial"), - SUBSCRIPTION("subscription"), - INTERNAL("internal"); - - private final String name; - - private Type(String name) { - this.name = name; - } - - public String string() { - return name; - } - - 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); - } +public class ESLicenses { + public static void toXContent(Collection licenses, XContentBuilder builder) throws IOException { + builder.startObject(); + builder.startArray("licenses"); + for (ESLicense license : licenses) { + ESLicense.toXContent(license, builder); } + builder.endArray(); + builder.endObject(); } - /** - * Enum for License Subscription Type - */ - public enum SubscriptionType { - NONE("none"), - DEVELOPMENT("development"), - SILVER("silver"), - GOLD("gold"), - PLATINUM("platinum"); - - public static SubscriptionType DEFAULT = NONE; - - private final String name; - - private SubscriptionType(String name) { - this.name = name; - } - - public String string() { - return name; - } - - 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); - } - } + public static Set fromSource(String content) throws IOException { + return fromSource(content.getBytes(Charset.forName("UTF-8"))); } - /** - * 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 String 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(); + public static Set fromSource(byte[] bytes) throws IOException { + return fromXContent(XContentFactory.xContent(bytes).createParser(bytes)); } + private static Set fromXContent(XContentParser parser) throws IOException { + Set esLicenses = new HashSet<>(); + final Map licensesMap = parser.mapAndClose(); + final List> licenseMaps = (ArrayList>)licensesMap.get("licenses"); + for (Map licenseMap : licenseMaps) { + final ESLicense esLicense = ESLicense.fromXContent(licenseMap); + esLicenses.add(esLicense); + } + return esLicenses; + } + + public static Set readFrom(StreamInput in) throws IOException { + int size = in.readVInt(); + Set esLicenses = new HashSet<>(size); + for (int i = 0; i < size; i++) { + esLicenses.add(ESLicense.readFrom(in)); + } + return esLicenses; + } + + public static void writeTo(Set esLicenses, StreamOutput out) throws IOException { + out.writeVInt(esLicenses.size()); + for (ESLicense license : esLicenses) { + ESLicense.writeTo(license, out); + } + + } } diff --git a/src/main/java/org/elasticsearch/license/core/LicenseBuilders.java b/src/main/java/org/elasticsearch/license/core/LicenseBuilders.java deleted file mode 100644 index 29b9877a097..00000000000 --- a/src/main/java/org/elasticsearch/license/core/LicenseBuilders.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * 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 ESLicenses removeFeatures(ESLicenses licenses, Set featureTypesToDelete) { - final LicensesBuilder licensesBuilder = licensesBuilder(); - for (ESLicense license : licenses) { - if (!featureTypesToDelete.contains(license.feature())) { - licensesBuilder.licenseAsIs(license); - } - } - return licensesBuilder.build(); - } - - public static class LicensesBuilder { - private Map licenseMap = new HashMap<>(); - - public LicensesBuilder() { - } - - public LicensesBuilder license(LicenseBuilder builder) { - return license(builder.build()); - } - - public LicensesBuilder license(ESLicense license) { - putIfAppropriate(license); - return this; - } - - public LicensesBuilder licenseAsIs(ESLicense license) { - licenseMap.put(license.feature(), 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(String feature) { - return licenseMap.get(feature); - } - - @Override - public Iterator iterator() { - return licenseMap.values().iterator(); - } - }; - } - - /** - * 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 String 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 String 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(String 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 String 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 deleted file mode 100644 index 153e626802a..00000000000 --- a/src/main/java/org/elasticsearch/license/core/LicenseUtils.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.*; -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()); - 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)) { - return readLicenseFromInputStream(fileInputStream); - } - } - - public static ESLicenses readLicenseFromInputStream(InputStream inputStream) throws IOException { - JsonNode jsonNode = new ObjectMapper().readTree(inputStream); - 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(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()); - 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 index 855c45a35a4..ab2cf52150d 100644 --- a/src/main/java/org/elasticsearch/license/licensor/ESLicenseSigner.java +++ b/src/main/java/org/elasticsearch/license/licensor/ESLicenseSigner.java @@ -6,7 +6,6 @@ package org.elasticsearch.license.licensor; import net.nicholaswilliams.java.licensing.License; -import net.nicholaswilliams.java.licensing.SignedLicense; import net.nicholaswilliams.java.licensing.encryption.Hasher; import net.nicholaswilliams.java.licensing.encryption.PasswordProvider; import net.nicholaswilliams.java.licensing.encryption.PrivateKeyDataProvider; @@ -14,8 +13,9 @@ 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.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; +import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.common.collect.ImmutableSet; +import org.elasticsearch.license.core.*; import java.io.IOException; import java.nio.ByteBuffer; @@ -24,8 +24,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Random; - -import static org.elasticsearch.license.core.ESLicenses.ESLicense; +import java.util.Set; public class ESLicenseSigner { @@ -69,12 +68,13 @@ public class ESLicenseSigner { this.publicKeyPath = publicKeyPath; } - public ESLicenses sign(ESLicenses esLicenses) throws IOException { - final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); - for (ESLicense license : esLicenses) { - licensesBuilder.license(sign(license)); + + public ImmutableSet sign(Set licenseSpecs) throws IOException { + final ImmutableSet.Builder builder = ImmutableSet.builder(); + for (LicenseSpec licenseSpec : licenseSpecs) { + builder.add(sign(licenseSpec)); } - return licensesBuilder.build(); + return builder.build(); } /** @@ -85,25 +85,24 @@ public class ESLicenseSigner { * @return a signed ESLicense (with signature) * @throws IOException */ - public ESLicense sign(ESLicense esLicense) throws IOException { + public ESLicense sign(LicenseSpec licenseSpec) throws IOException { License.Builder licenseBuilder = new License.Builder() - .withGoodBeforeDate(esLicense.expiryDate()) - .withIssueDate(esLicense.issueDate()) - .withProductKey(esLicense.uid()) - .withHolder(esLicense.issuedTo()) - .withIssuer(esLicense.issuer()) - .addFeature("feature:" + esLicense.feature(), esLicense.expiryDate()) - .addFeature("maxNodes:" + String.valueOf(esLicense.maxNodes())) - .addFeature("type:" + esLicense.type().string()) - .addFeature("subscription_type:" + esLicense.subscriptionType().string()); + .withGoodBeforeDate(licenseSpec.expiryDate) + .withIssueDate(licenseSpec.issueDate) + .withProductKey(licenseSpec.uid) + .withHolder(licenseSpec.issuedTo) + .withIssuer(licenseSpec.issuer) + .addFeature("feature:" + licenseSpec.feature, licenseSpec.expiryDate) + .addFeature("maxNodes:" + String.valueOf(licenseSpec.maxNodes)) + .addFeature("type:" + licenseSpec.type.string()) + .addFeature("subscription_type:" + licenseSpec.subscriptionType.string()); final License license = licenseBuilder.build(); final byte[] magic = new byte[MAGIC_LENGTH]; Random random = new Random(); random.nextBytes(magic); - //final SignedLicense signedLicense = licenseCreator.signLicense(license); - final byte[] licenseSignature = licenseCreator.signAndSerializeLicense(license);//signedLicense.getSignatureContent(); + final byte[] licenseSignature = licenseCreator.signAndSerializeLicense(license); final byte[] hash = Hasher.hash(Base64.encodeBase64String( Files.readAllBytes(publicKeyPath)) ).getBytes(Charset.forName("UTF-8")); @@ -118,6 +117,18 @@ public class ESLicenseSigner { .put(licenseSignature); String signature = Base64.encodeBase64String(bytes); - return LicenseBuilders.licenseBuilder(true).fromLicense(esLicense).signature(signature).build(); + return ESLicense.builder() + .uid(licenseSpec.uid) + .issuedTo(licenseSpec.issuedTo) + .issueDate(licenseSpec.issueDate) + .type(licenseSpec.type) + .subscriptionType(licenseSpec.subscriptionType) + .feature(licenseSpec.feature) + .maxNodes(licenseSpec.maxNodes) + .expiryDate(licenseSpec.expiryDate) + .issuer(licenseSpec.issuer) + .signature(signature) + .build(); } + } diff --git a/src/main/java/org/elasticsearch/license/licensor/LicenseSpec.java b/src/main/java/org/elasticsearch/license/licensor/LicenseSpec.java new file mode 100644 index 00000000000..b2655590859 --- /dev/null +++ b/src/main/java/org/elasticsearch/license/licensor/LicenseSpec.java @@ -0,0 +1,149 @@ +/* + * 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.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.license.core.DateUtils; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.text.ParseException; +import java.util.*; + +import static org.elasticsearch.license.core.ESLicense.Type; +import static org.elasticsearch.license.core.ESLicense.SubscriptionType; + +public class LicenseSpec { + final String uid; + final String issuer; + final String issuedTo; + final long issueDate; + final Type type; + final SubscriptionType subscriptionType; + final String feature; + final long expiryDate; + final int maxNodes; + + private LicenseSpec(String uid, String issuer, String issuedTo, long issueDate, Type type, + SubscriptionType subscriptionType, String feature, long expiryDate, + int maxNodes) { + this.uid = uid; + this.issuer = issuer; + this.issuedTo = issuedTo; + this.issueDate = issueDate; + this.type = type; + this.subscriptionType = subscriptionType; + this.feature = feature; + this.expiryDate = expiryDate; + this.maxNodes = maxNodes; + } + + + public static class Builder { + private String uid; + private String issuer; + private String issuedTo; + private long issueDate = -1; + private Type type; + private SubscriptionType subscriptionType = SubscriptionType.DEFAULT; + private String feature; + private long expiryDate = -1; + private int maxNodes; + + + public Builder uid(String uid) { + this.uid = uid; + return this; + } + + public Builder issuer(String issuer) { + this.issuer = issuer; + return this; + } + + public Builder issuedTo(String issuedTo) { + this.issuedTo = issuedTo; + return this; + } + + public Builder issueDate(long issueDate) { + this.issueDate = issueDate; + return this; + } + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder subscriptionType(SubscriptionType subscriptionType) { + this.subscriptionType = subscriptionType; + return this; + } + + public Builder feature(String feature) { + this.feature = feature; + return this; + } + + public Builder expiryDate(long expiryDate) { + this.expiryDate = expiryDate; + return this; + } + + public Builder maxNodes(int maxNodes) { + this.maxNodes = maxNodes; + return this; + } + + public LicenseSpec build() { + if (uid == null) { + uid = UUID.randomUUID().toString(); + } + return new LicenseSpec(uid, issuer, issuedTo, issueDate, type, subscriptionType, + feature ,expiryDate, maxNodes); + } + } + + + private static LicenseSpec fromXContent(Map map) throws IOException, ParseException { + Builder builder = new Builder() + .uid((String) map.get("uid")) + .type(Type.fromString((String) map.get("type"))) + .subscriptionType(SubscriptionType.fromString((String) map.get("subscription_type"))) + .feature((String) map.get("feature")) + .maxNodes((int) map.get("max_nodes")) + .issuedTo((String) map.get("issued_to")) + .issuer((String) map.get("issuer")); + + String issueDate = (String) map.get("issue_date"); + builder.issueDate(DateUtils.longFromDateString(issueDate)); + String expiryDate = (String) map.get("expiry_date"); + builder.expiryDate(DateUtils.longExpiryDateFromString(expiryDate)); + return builder.build(); + } + + public static Set fromSource(String content) throws IOException, ParseException { + return fromSource(content.getBytes(Charset.forName("UTF-8"))); + } + + public static Set fromSource(byte[] bytes) throws IOException, ParseException { + return fromXContents(XContentFactory.xContent(bytes).createParser(bytes)); + } + + private static Set fromXContents(XContentParser parser) throws IOException, ParseException { + Set licenseSpecs = new HashSet<>(); + final Map licenseSpecMap = parser.mapAndClose(); + final List> licenseSpecDefinitions = (ArrayList>)licenseSpecMap.get("licenses"); + for (Map licenseSpecDef : licenseSpecDefinitions) { + final LicenseSpec licenseSpec = fromXContent(licenseSpecDef); + licenseSpecs.add(licenseSpec); + } + return licenseSpecs; + } +} + diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java b/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java index 1b3babce660..dc5affbac50 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java @@ -5,46 +5,35 @@ */ package org.elasticsearch.license.licensor.tools; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; +import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.manager.ESLicenseProvider; +import org.elasticsearch.license.manager.Utils; +import java.util.Map; import java.util.Set; /** */ public class FileBasedESLicenseProvider implements ESLicenseProvider { - private ESLicenses esLicenses; + private ImmutableMap esLicenses; - public FileBasedESLicenseProvider(ESLicenses esLicenses) { - this.esLicenses = esLicenses; - } - - public FileBasedESLicenseProvider(Set esLicensesSet) { - this(merge(esLicensesSet)); + public FileBasedESLicenseProvider(Set esLicenses) { + this.esLicenses = Utils.reduceAndMap(esLicenses); } @Override - public ESLicenses.ESLicense getESLicense(String feature) { + public ESLicense getESLicense(String feature) { return esLicenses.get(feature); } @Override - public ESLicenses getEffectiveLicenses() { + public Map getEffectiveLicenses() { return esLicenses; } // For testing - public void setLicenses(ESLicenses esLicenses) { - this.esLicenses = esLicenses; + public void setLicenses(Set esLicenses) { + this.esLicenses = Utils.reduceAndMap(esLicenses); } - - private static ESLicenses merge(Set esLicensesSet) { - ESLicenses mergedLicenses = null; - for (ESLicenses licenses : esLicensesSet) { - mergedLicenses = LicenseBuilders.merge(mergedLicenses, licenses); - } - return mergedLicenses; - } - } diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java index f1810b900de..f0b72faa648 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java @@ -12,7 +12,10 @@ import net.nicholaswilliams.java.licensing.exception.InappropriateKeyException; import net.nicholaswilliams.java.licensing.exception.InappropriateKeySpecificationException; import net.nicholaswilliams.java.licensing.exception.RSA2048NotSupportedException; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; import java.security.KeyPair; public class KeyPairGeneratorTool { diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java index 92857cacc63..686c4c7b36b 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java @@ -5,30 +5,40 @@ */ package org.elasticsearch.license.licensor.tools; +import org.elasticsearch.common.collect.ImmutableSet; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseUtils; import org.elasticsearch.license.licensor.ESLicenseSigner; +import org.elasticsearch.license.licensor.LicenseSpec; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.HashSet; +import java.util.Set; public class LicenseGeneratorTool { static class Options { - private final ESLicenses licenses; + private final Set licenseSpecs; private final String publicKeyFilePath; private final String privateKeyFilePath; - Options(ESLicenses licenses, String publicKeyFilePath, String privateKeyFilePath) { - this.licenses = licenses; + Options(Set licenseSpecs, String publicKeyFilePath, String privateKeyFilePath) { + this.licenseSpecs = licenseSpecs; this.publicKeyFilePath = publicKeyFilePath; this.privateKeyFilePath = privateKeyFilePath; } } - private static Options parse(String[] args) throws IOException { - ESLicenses licenses = null; + private static Options parse(String[] args) throws IOException, ParseException { + Set licenseSpecs = new HashSet<>(); String privateKeyPath = null; String publicKeyPath = null; @@ -36,19 +46,14 @@ public class LicenseGeneratorTool { String command = args[i].trim(); switch (command) { case "--license": - if (licenses != null) { - throw new IllegalArgumentException("only one of --licenses' or '--licenseFile' can be specified"); - } String licenseInput = args[++i]; - licenses = LicenseUtils.readLicensesFromString(licenseInput); + licenseSpecs.addAll(LicenseSpec.fromSource(licenseInput)); break; case "--licenseFile": - if (licenses != null) { - throw new IllegalArgumentException("only one of --licenses' or '--licenseFile' can be specified"); - } File licenseFile = new File(args[++i]); if (licenseFile.exists()) { - licenses = LicenseUtils.readLicenseFile(licenseFile); + final byte[] bytes = Files.readAllBytes(Paths.get(licenseFile.getAbsolutePath())); + licenseSpecs.addAll(LicenseSpec.fromSource(bytes)); } else { throw new IllegalArgumentException(licenseFile.getAbsolutePath() + " does not exist!"); } @@ -62,7 +67,7 @@ public class LicenseGeneratorTool { } } - if (licenses == null) { + if (licenseSpecs.size() == 0) { throw new IllegalArgumentException("at least one of '--licenses' or '--licenseFile' has to be provided"); } if (publicKeyPath == null) { @@ -72,21 +77,24 @@ public class LicenseGeneratorTool { throw new IllegalArgumentException("mandatory option '--privateKeyPath' is missing"); } - return new Options(licenses, publicKeyPath, privateKeyPath); + return new Options(licenseSpecs, publicKeyPath, privateKeyPath); } - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, ParseException { run(args, System.out); } - public static void run(String[] args, OutputStream out) throws IOException { + public static void run(String[] args, OutputStream out) throws IOException, ParseException { Options options = parse(args); - ESLicenseSigner signer = new ESLicenseSigner(options.privateKeyFilePath, options.publicKeyFilePath); - ESLicenses signedLicences = signer.sign(options.licenses); + ImmutableSet signedLicences = signer.sign(options.licenseSpecs); - LicenseUtils.dumpLicenseAsJson(signedLicences, out); + XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON, out); + + ESLicenses.toXContent(signedLicences, builder); + + builder.flush(); } } diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java index 55ae4c0df8e..69489c68058 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java @@ -5,86 +5,65 @@ */ package org.elasticsearch.license.licensor.tools; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseUtils; import org.elasticsearch.license.manager.ESLicenseManager; import org.elasticsearch.license.manager.ESLicenseProvider; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.util.Arrays; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.HashSet; import java.util.Set; public class LicenseVerificationTool { static class Options { - private final Set licenses; + private final Set licenses; private final String publicKeyFilePath; - Options(Set licenses, String publicKeyFilePath) { + Options(Set licenses, String publicKeyFilePath) { this.licenses = licenses; this.publicKeyFilePath = publicKeyFilePath; } - - } - - static Set asLicensesFromFiles(Set filePaths) throws IOException { - Set licenses = new HashSet<>(filePaths.size()); - for (String filePath : filePaths) { - final File file = new File(filePath); - if (file.exists()) { - licenses.add(LicenseUtils.readLicenseFile(file)); - } else { - throw new IllegalArgumentException(file.getAbsolutePath() + " does not exist!"); - } - } - return licenses; - } - - static Set asLicensesFromStrings(Set fileContents) throws IOException { - Set licenses = new HashSet<>(fileContents.size()); - for (String fileContent : fileContents) { - licenses.add(LicenseUtils.readLicensesFromString(fileContent)); - } - return licenses; } private static Options parse(String[] args) throws IOException { - Set licenseFilePaths = null; - Set licensesContents = new HashSet<>(); - Set licenses = null; + Set licenses = new HashSet<>(); String publicKeyPath = 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(":"))); + for (String filePath : args[++i].split(":")) { + File file = new File(filePath); + if (file.exists()) { + licenses.addAll(ESLicenses.fromSource(Files.readAllBytes(Paths.get(file.getAbsolutePath())))); + } else { + throw new IllegalArgumentException(file.getAbsolutePath() + " does not exist!"); + } + } break; case "--licenses": - licensesContents.add(args[++i]); + licenses.addAll(ESLicenses.fromSource(args[++i])); break; case "--publicKeyPath": publicKeyPath = args[++i]; break; } } - if (licenseFilePaths == null && licensesContents.size() == 0) { + if (licenses.size() == 0) { throw new IllegalArgumentException("mandatory option '--licensesFiles' or '--licenses' is missing"); - } else if (licenseFilePaths != null) { - licenses = asLicensesFromFiles(licenseFilePaths); - } else if (licensesContents.size() > 0) { - licenses = asLicensesFromStrings(licensesContents); - } else { - throw new IllegalArgumentException("no licenses could be extracted"); } if (publicKeyPath == null) { throw new IllegalArgumentException("mandatory option '--publicKeyPath' is missing"); } - assert licenses != null; return new Options(licenses, publicKeyPath); } @@ -97,11 +76,15 @@ public class LicenseVerificationTool { // verify licenses ESLicenseProvider licenseProvider = new FileBasedESLicenseProvider(options.licenses); - ESLicenseManager licenseManager = new ESLicenseManager(licenseProvider); - licenseManager.verifyLicenses(); + ESLicenseManager licenseManager = new ESLicenseManager(); + licenseManager.verifyLicenses(licenseProvider.getEffectiveLicenses()); // dump effective licences - LicenseUtils.dumpLicenseAsJson(licenseManager.getEffectiveLicenses(), out); + //LicenseUtils.dumpLicenseAsJson(licenseManager.getEffectiveLicenses(), out); + + XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON, out); + ESLicenses.toXContent(licenseProvider.getEffectiveLicenses().values(), builder); + builder.flush(); } } diff --git a/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java b/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java index e6148420c69..a2fa74a0ca3 100644 --- a/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java +++ b/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java @@ -11,15 +11,19 @@ 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.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; +import org.elasticsearch.common.collect.ImmutableSet; +import org.elasticsearch.license.core.ESLicense; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Paths; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; import java.util.Set; -import static org.elasticsearch.license.core.ESLicenses.*; +import static org.elasticsearch.license.core.ESLicense.SubscriptionType; +import static org.elasticsearch.license.core.ESLicense.Type; import static org.elasticsearch.license.manager.Utils.extractSignedLicence; /** @@ -31,14 +35,20 @@ import static org.elasticsearch.license.manager.Utils.extractSignedLicence; */ public class ESLicenseManager { - private final ESLicenseProvider licenseProvider; - private final LicenseManager licenseManager; + private static class Prefix { + static final String MAX_NODES = "maxNodes:"; + static final String TYPE = "type:"; + static final String SUBSCRIPTION_TYPE = "subscription_type:"; + static final String FEATURE = "feature:"; + } + // Initialize LicenseManager static { LicenseManagerProperties.setPublicKeyDataProvider(new FilePublicKeyDataProvider(getPublicKeyPath())); LicenseManagerProperties.setPublicKeyPasswordProvider(new ESPublicKeyPasswordProvider()); + LicenseManagerProperties.setLicenseValidator(new DefaultLicenseValidator()); LicenseManagerProperties.setLicenseProvider(new LicenseProvider() { @Override public SignedLicense getLicense(Object context) { @@ -61,27 +71,14 @@ public class ESLicenseManager { } } - public ESLicenseManager(ESLicenseProvider licenseProvider) { - this.licenseProvider = licenseProvider; + public ESLicenseManager() { this.licenseManager = LicenseManager.getInstance(); } - private static ESLicenses merge(Set esLicensesSet) { - ESLicenses mergedLicenses = null; - for (ESLicenses licenses : esLicensesSet) { - mergedLicenses = LicenseBuilders.merge(mergedLicenses, licenses); - } - return mergedLicenses; - } - - public ESLicenses getEffectiveLicenses() { - return licenseProvider.getEffectiveLicenses(); - } - - public void verifyLicenses(ESLicenses esLicenses) { + public void verifyLicenses(Map esLicenses) { try { - for (String feature : esLicenses.features()) { - ESLicense esLicense = esLicenses.get(feature); + for (String feature : esLicenses.keySet()) { + org.elasticsearch.license.core.ESLicense esLicense = esLicenses.get(feature); // verify signature final License license = this.licenseManager.decryptAndVerifyLicense( extractSignedLicence(esLicense.signature())); @@ -94,33 +91,56 @@ public class ESLicenseManager { } catch (ExpiredLicenseException e) { throw new InvalidLicenseException("Expired License"); } catch (InvalidLicenseException e) { - throw new InvalidLicenseException("Invalid License: " + e.getCause()); + throw new InvalidLicenseException("Invalid License"); } } - public void verifyLicenses() { - verifyLicenses(getEffectiveLicenses()); + public ImmutableSet toSignatures(Collection esLicenses) { + Set signatures = new HashSet<>(); + for (ESLicense esLicense : esLicenses) { + signatures.add(esLicense.signature()); + } + return ImmutableSet.copyOf(signatures); } - public ESLicenses fromSignaturesAsIs(final Set signatures) { - final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); + public ImmutableSet fromSignatures(Set signatures) { + Set esLicenses = new HashSet<>(); + for (String signature : signatures) { - licensesBuilder.licenseAsIs(getESLicenseFromSignature(signature)); + ESLicense license = fromSignature(signature); + esLicenses.add(license); } - return licensesBuilder.build(); + return ImmutableSet.copyOf(esLicenses); } - public ESLicense getESLicenseFromSignature(String signature) { - SignedLicense signedLicense = extractSignedLicence(signature); - return decryptAndVerifyESLicense(signedLicense, signature); - } + public ESLicense fromSignature(String signature) { + final SignedLicense signedLicense = Utils.extractSignedLicence(signature); + License license = licenseManager.decryptAndVerifyLicense(signedLicense); + ESLicense.Builder builder = ESLicense.builder(); - public ESLicenses fromSignatures(final Set signatures) { - final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); - for (String signature : signatures) { - licensesBuilder.license(getESLicenseFromSignature(signature)); + + + for (License.Feature feature : license.getFeatures()) { + String featureName = feature.getName(); + if (featureName.startsWith(Prefix.MAX_NODES)) { + builder.maxNodes(Integer.parseInt(featureName.substring(Prefix.MAX_NODES.length()))); + } else if (featureName.startsWith(Prefix.TYPE)) { + builder.type(Type.fromString(featureName.substring(Prefix.TYPE.length()))); + } else if (featureName.startsWith(Prefix.SUBSCRIPTION_TYPE)) { + builder.subscriptionType(SubscriptionType.fromString(featureName.substring(Prefix.SUBSCRIPTION_TYPE.length()))); + } else if (featureName.startsWith(Prefix.FEATURE)) { + builder.feature(featureName.substring(Prefix.FEATURE.length())); + } } - return licensesBuilder.build(); + + return builder + .uid(license.getProductKey()) + .issuer(license.getIssuer()) + .issuedTo(license.getHolder()) + .issueDate(license.getIssueDate()) + .expiryDate(license.getGoodBeforeDate()) + .signature(signature) + .build(); } private static void verifyLicenseFields(License license, ESLicense eslicense) { @@ -129,10 +149,6 @@ public class ESLicenseManager { && license.getIssueDate() == eslicense.issueDate() && license.getGoodBeforeDate() == eslicense.expiryDate(); assert license.getFeatures().size() == 4 : "one license should have only four features"; - String maxNodesPrefix = "maxNodes:"; - String typePrefix = "type:"; - String subscriptionTypePrefix = "subscription_type:"; - String featurePrefix = "feature:"; boolean maxNodesValid = false; boolean featureValid = false; boolean typeValid = false; @@ -140,14 +156,14 @@ public class ESLicenseManager { 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 if (featureName.startsWith(featurePrefix)) { - String featureValue = featureName.substring(featurePrefix.length()); + if (featureName.startsWith(Prefix.MAX_NODES)) { + maxNodesValid = eslicense.maxNodes() == Integer.parseInt(featureName.substring(Prefix.MAX_NODES.length())); + } else if (featureName.startsWith(Prefix.TYPE)) { + typeValid = eslicense.type() == Type.fromString(featureName.substring(Prefix.TYPE.length())); + } else if (featureName.startsWith(Prefix.SUBSCRIPTION_TYPE)) { + subscriptionTypeValid = eslicense.subscriptionType() == SubscriptionType.fromString(featureName.substring(Prefix.SUBSCRIPTION_TYPE.length())); + } else if (featureName.startsWith(Prefix.FEATURE)) { + String featureValue = featureName.substring(Prefix.FEATURE.length()); featureValid = featureValue.equals(eslicense.feature()) && feature.getGoodBeforeDate() == eslicense.expiryDate(); } @@ -156,28 +172,17 @@ public class ESLicenseManager { //only for debugging String msg = "licenseValid: " + licenseValid + "\n" + "featureValid: " + featureValid + "\n" + - "maxNodeValide: " + maxNodesValid + "\n" + + "maxNodeValid: " + maxNodesValid + "\n" + "typeValid: " + typeValid + "\n" + "subscriptionTypeValid: " + subscriptionTypeValid + "\n"; throw new InvalidLicenseException("Invalid License"); } } - private License getLicense(String feature) { - ESLicense esLicense = licenseProvider.getESLicense(feature); - if (esLicense != null) { - String signature = esLicense.signature(); - License license = this.licenseManager.decryptAndVerifyLicense(extractSignedLicence(signature)); - this.licenseManager.validateLicense(license); - return license; - } - return null; - } - //TODO wrap License validation methods so a plugin does not have to provide featureType param - public boolean hasLicenseForFeature(String feature) { + public boolean hasLicenseForFeature(String feature, Map licenseMap) { try { - final License license = getLicense(feature); + final License license = getInternalLicense(feature, licenseMap); if (license != null) { return license.hasLicenseForFeature("feature:" + feature); } @@ -189,85 +194,15 @@ public class ESLicenseManager { } } - public boolean hasLicenseForNodes(String featureType, int nodes) { - ESLicense esLicense = getESLicense(featureType); - return esLicense.maxNodes() >= nodes; - } - - public String getIssuerForLicense(String featureType) { - final License license = getLicense(featureType); - return license.getIssuer(); - } - - public long getIssueDateForLicense(String featureType) { - final License license = getLicense(featureType); - return license.getIssueDate(); - } - - public long getExpiryDateForLicense(String featureType) { - final License license = getLicense(featureType); - return license.getGoodBeforeDate(); - } - - public String getIssuedToForLicense(String featureType) { - final License license = getLicense(featureType); - return license.getHolder(); - } - - public Type getTypeForLicense(String featureType) { - ESLicense esLicense = getESLicense(featureType); - return esLicense.type(); - } - - public SubscriptionType getSubscriptionTypeForLicense(String featureType) { - ESLicense esLicense = getESLicense(featureType); - return esLicense.subscriptionType(); - } - - ESLicense getESLicense(String featureType) { - final License license = getLicense(featureType); - return convertToESLicense(license); - } - - ESLicense decryptAndVerifyESLicense(SignedLicense signedLicense, String signature) { - return convertToESLicense(this.licenseManager.decryptAndVerifyLicense(signedLicense), signature); - } - - static ESLicense convertToESLicense(License license) { - return convertToESLicense(license, null); - } - - static ESLicense convertToESLicense(License license, String signature) { - final LicenseBuilders.LicenseBuilder licenseBuilder = LicenseBuilders.licenseBuilder(false); - licenseBuilder - .expiryDate(license.getGoodBeforeDate()) - .issueDate(license.getIssueDate()) - .uid(license.getProductKey()) - .issuedTo(license.getHolder()) - .issuer(license.getIssuer()); - - if (signature != null) { - licenseBuilder.signature(signature); + private License getInternalLicense(String feature, Map licenseMap) { + ESLicense esLicense = licenseMap.get(feature); + if (esLicense != null) { + String signature = esLicense.signature(); + License license = this.licenseManager.decryptAndVerifyLicense(extractSignedLicence(signature)); + this.licenseManager.validateLicense(license); + return license; } - - assert license.getFeatures().size() == 4 : "one license should have only four feature"; - String maxNodesPrefix = "maxNodes:"; - String typePrefix = "type:"; - String subscriptionTypePrefix = "subscription_type:"; - String featurePrefix = "feature:"; - 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 if (featureName.startsWith(featurePrefix)) { - licenseBuilder.feature(featureName.substring(featurePrefix.length())); - } - } - return licenseBuilder.build(); + return null; } // TODO: Need a better password management diff --git a/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java b/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java index 6303f75c916..f2c706fca72 100644 --- a/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java +++ b/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java @@ -5,13 +5,15 @@ */ package org.elasticsearch.license.manager; -import org.elasticsearch.license.core.ESLicenses; -import static org.elasticsearch.license.core.ESLicenses.ESLicense; +import org.elasticsearch.license.core.ESLicense; + +import java.util.Map; + public interface ESLicenseProvider { ESLicense getESLicense(String feature); - ESLicenses getEffectiveLicenses(); + Map getEffectiveLicenses(); } diff --git a/src/main/java/org/elasticsearch/license/manager/Utils.java b/src/main/java/org/elasticsearch/license/manager/Utils.java index 8a152a26b37..e260be708bf 100644 --- a/src/main/java/org/elasticsearch/license/manager/Utils.java +++ b/src/main/java/org/elasticsearch/license/manager/Utils.java @@ -8,9 +8,14 @@ package org.elasticsearch.license.manager; import net.nicholaswilliams.java.licensing.ObjectSerializer; import net.nicholaswilliams.java.licensing.SignedLicense; import org.apache.commons.codec.binary.Base64; +import org.elasticsearch.common.collect.ImmutableMap; +import org.elasticsearch.license.core.ESLicense; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; public final class Utils { /** @@ -31,4 +36,26 @@ public final class Utils { int version = byteBuffer.getInt(); return new ObjectSerializer().readObject(SignedLicense.class, Arrays.copyOfRange(signatureBytes, start, signatureBytes.length)); } + + + public static ImmutableMap reduceAndMap(Set esLicensesSet) { + Map map = new HashMap<>(esLicensesSet.size()); + for (ESLicense license : esLicensesSet) { + putIfAppropriate(map, license); + } + return ImmutableMap.copyOf(map); + } + + + private static void putIfAppropriate(Map licenseMap, ESLicense license) { + final String 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); + } + } } diff --git a/src/main/java/org/elasticsearch/license/plugin/action/Utils.java b/src/main/java/org/elasticsearch/license/plugin/action/Utils.java deleted file mode 100644 index 766b6ab5d0b..00000000000 --- a/src/main/java/org/elasticsearch/license/plugin/action/Utils.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.action; - -import org.elasticsearch.common.collect.ImmutableMap; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static org.elasticsearch.license.core.ESLicenses.*; - -public class Utils { - - public static String[] toSignatures(ESLicenses esLicenses) { - Set signatures = new HashSet<>(); - for (ESLicense esLicense : esLicenses) { - signatures.add(esLicense.signature()); - } - return signatures.toArray(new String[signatures.size()]); - } - - public static ESLicenses readGeneratedLicensesFrom(StreamInput in) throws IOException { - final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); - boolean exists = in.readBoolean(); - if (exists) { - int size = in.readVInt(); - for (int i = 0; i < size; i++) { - licensesBuilder.licenseAsIs(licenseFromMap(in.readMap())); - } - return licensesBuilder.build(); - } - return null; - } - - public static void writeGeneratedLicensesTo(ESLicenses esLicenses, StreamOutput out) throws IOException { - if (esLicenses == null) { - out.writeBoolean(false); - return; - } - out.writeBoolean(true); - out.writeVInt(esLicenses.licenses().size()); - for (ESLicense esLicense : esLicenses) { - out.writeMap(licenseAsMap(esLicense)); - } - } - - // TODO: make sure field order is preserved - public static Map licenseAsMap(ESLicense esLicense) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put(LicenseFields.UID, esLicense.uid()); - builder.put(LicenseFields.TYPE, esLicense.type().string()); - builder.put(LicenseFields.SUBSCRIPTION_TYPE, esLicense.subscriptionType().string()); - builder.put(LicenseFields.ISSUE_DATE, esLicense.issueDate()); - builder.put(LicenseFields.FEATURE, esLicense.feature()); - builder.put(LicenseFields.EXPIRY_DATE, esLicense.expiryDate()); - builder.put(LicenseFields.MAX_NODES, esLicense.maxNodes()); - builder.put(LicenseFields.ISSUED_TO, esLicense.issuedTo()); - builder.put(LicenseFields.SIGNATURE, esLicense.signature()); - return builder.build(); - } - - public static ESLicense licenseFromMap(Map map) { - return LicenseBuilders.licenseBuilder(false) - .uid((String) map.get(LicenseFields.UID)) - .type(Type.fromString((String) map.get(LicenseFields.TYPE))) - .subscriptionType(SubscriptionType.fromString((String) map.get(LicenseFields.SUBSCRIPTION_TYPE))) - .issueDate((long) map.get(LicenseFields.ISSUE_DATE)) - .feature((String) map.get(LicenseFields.FEATURE)) - .expiryDate((long) map.get(LicenseFields.EXPIRY_DATE)) - .maxNodes((int) map.get(LicenseFields.MAX_NODES)) - .issuedTo((String) map.get(LicenseFields.ISSUED_TO)) - .signature((String) map.get(LicenseFields.SIGNATURE)) - .build(); - - } - - final static class LicenseFields { - private final static String UID = "uid"; - private final static String TYPE = "type"; - private final static String SUBSCRIPTION_TYPE = "subscription_type"; - private final static String ISSUE_DATE = "issue_date"; - private final static String FEATURE = "feature"; - private final static String EXPIRY_DATE = "expiry_date"; - private final static String MAX_NODES = "max_nodes"; - private final static String ISSUED_TO = "issued_to"; - private final static String SIGNATURE = "signature"; - } -} diff --git a/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseResponse.java b/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseResponse.java index 6469808de1f..378b170487d 100644 --- a/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseResponse.java +++ b/src/main/java/org/elasticsearch/license/plugin/action/get/GetLicenseResponse.java @@ -8,49 +8,38 @@ package org.elasticsearch.license.plugin.action.get; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; -import org.elasticsearch.license.plugin.core.trial.TrialLicenseUtils; -import org.elasticsearch.license.plugin.core.trial.TrialLicenses; -import org.elasticsearch.license.plugin.core.trial.TrialLicensesBuilder; import java.io.IOException; - -import static org.elasticsearch.license.plugin.action.Utils.*; +import java.util.HashSet; +import java.util.Set; public class GetLicenseResponse extends ActionResponse { - private ESLicenses licenses = null; - private TrialLicenses trialLicenses = null; + private Set licenses = new HashSet<>(); GetLicenseResponse() { } - GetLicenseResponse(ESLicenses esLicenses, TrialLicenses trialLicenses) { + GetLicenseResponse(Set esLicenses) { this.licenses = esLicenses; - this.trialLicenses = trialLicenses; } - public ESLicenses licenses() { - return (licenses != null) ? licenses : LicenseBuilders.licensesBuilder().build(); - } - - public TrialLicenses trialLicenses() { - return trialLicenses != null ? trialLicenses : TrialLicensesBuilder.EMPTY; + public Set licenses() { + return licenses; } @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); - licenses = readGeneratedLicensesFrom(in); - trialLicenses = TrialLicenseUtils.readTrialLicensesFrom(in); + licenses = ESLicenses.readFrom(in); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - writeGeneratedLicensesTo(licenses, out); - TrialLicenseUtils.writeTrialLicensesTo(trialLicenses, out); + ESLicenses.writeTo(licenses, out); } } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/license/plugin/action/get/TransportGetLicenseAction.java b/src/main/java/org/elasticsearch/license/plugin/action/get/TransportGetLicenseAction.java index 7cb0d80d97f..23eb0015505 100644 --- a/src/main/java/org/elasticsearch/license/plugin/action/get/TransportGetLicenseAction.java +++ b/src/main/java/org/elasticsearch/license/plugin/action/get/TransportGetLicenseAction.java @@ -13,27 +13,24 @@ import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.block.ClusterBlockLevel; -import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.manager.ESLicenseManager; -import org.elasticsearch.license.plugin.core.LicensesMetaData; -import org.elasticsearch.license.plugin.core.LicensesService; -import org.elasticsearch.license.plugin.core.trial.TrialLicenseUtils; -import org.elasticsearch.license.plugin.core.trial.TrialLicenses; +import org.elasticsearch.license.core.ESLicense; +import org.elasticsearch.license.plugin.core.LicensesManagerService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; +import java.util.Set; + public class TransportGetLicenseAction extends TransportMasterNodeReadOperationAction { - private final LicensesService licensesService; + private final LicensesManagerService licensesManagerService; @Inject - public TransportGetLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService, LicensesService licensesService, + public TransportGetLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService, LicensesManagerService licensesManagerService, ThreadPool threadPool, ActionFilters actionFilters) { super(settings, GetLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters); - this.licensesService = licensesService; + this.licensesManagerService = licensesManagerService; } @Override @@ -53,22 +50,12 @@ public class TransportGetLicenseAction extends TransportMasterNodeReadOperationA @Override protected ClusterBlockException checkBlock(GetLicenseRequest request, ClusterState state) { - //TODO: do the right checkBlock return state.blocks().indexBlockedException(ClusterBlockLevel.METADATA, ""); } @Override protected void masterOperation(final GetLicenseRequest request, ClusterState state, final ActionListener listener) throws ElasticsearchException { - //TODO: Move this functionality to license service - MetaData metaData = state.metaData(); - LicensesMetaData licenses = metaData.custom(LicensesMetaData.TYPE); - if (licenses != null) { - ESLicenseManager esLicenseManager = licensesService.getEsLicenseManager(); - ESLicenses esLicenses = esLicenseManager.fromSignaturesAsIs(licenses.getSignatures()); - TrialLicenses trialLicenses = TrialLicenseUtils.fromEncodedTrialLicenses(licenses.getEncodedTrialLicenses()); - listener.onResponse(new GetLicenseResponse(esLicenses, trialLicenses)); - } else { - listener.onResponse(new GetLicenseResponse()); - } + final Set currentLicenses = licensesManagerService.getLicenses(); + listener.onResponse(new GetLicenseResponse(currentLicenses)); } } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequest.java b/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequest.java index e897a8ce5d1..61c280ab5a7 100644 --- a/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequest.java +++ b/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequest.java @@ -10,17 +10,16 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseUtils; import java.io.IOException; +import java.util.Set; -import static org.elasticsearch.license.plugin.action.Utils.readGeneratedLicensesFrom; -import static org.elasticsearch.license.plugin.action.Utils.writeGeneratedLicensesTo; public class PutLicenseRequest extends AcknowledgedRequest { - private ESLicenses license; + private Set licenses; public PutLicenseRequest() { } @@ -31,37 +30,37 @@ public class PutLicenseRequest extends AcknowledgedRequest { } /** - * Parses license from json format to an instance of {@link org.elasticsearch.license.core.ESLicenses} - * @param licenseDefinition license definition + * Parses licenses from json format to an instance of {@link org.elasticsearch.license.core.ESLicenses} + * @param licenseDefinition licenses definition */ - public PutLicenseRequest license(String licenseDefinition) { + public PutLicenseRequest licenses(String licenseDefinition) { try { - return license(LicenseUtils.readLicensesFromString(licenseDefinition)); + return licenses(ESLicenses.fromSource(licenseDefinition)); } catch (IOException e) { - throw new ElasticsearchIllegalArgumentException("failed to parse license source", e); + throw new ElasticsearchIllegalArgumentException("failed to parse licenses source", e); } } - public PutLicenseRequest license(ESLicenses esLicenses) { - this.license = esLicenses; + public PutLicenseRequest licenses(Set esLicenses) { + this.licenses = esLicenses; return this; } - public ESLicenses license() { - return license; + public Set licenses() { + return licenses; } @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); - license = readGeneratedLicensesFrom(in); + licenses = ESLicenses.readFrom(in); readTimeout(in); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - writeGeneratedLicensesTo(license, out); + ESLicenses.writeTo(licenses, out); writeTimeout(out); } } diff --git a/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java b/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java index d79701a33c1..7b29f215174 100644 --- a/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java +++ b/src/main/java/org/elasticsearch/license/plugin/action/put/PutLicenseRequestBuilder.java @@ -8,7 +8,9 @@ package org.elasticsearch.license.plugin.action.put; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder; import org.elasticsearch.client.ClusterAdminClient; -import org.elasticsearch.license.core.ESLicenses; +import org.elasticsearch.license.core.ESLicense; + +import java.util.Set; /** * Register license request builder @@ -27,11 +29,11 @@ public class PutLicenseRequestBuilder extends AcknowledgedRequestBuilder licenses) { + request.licenses(licenses); return this; } diff --git a/src/main/java/org/elasticsearch/license/plugin/core/ElasticsearchLicenseException.java b/src/main/java/org/elasticsearch/license/plugin/core/ElasticsearchLicenseException.java index 0459125a6e7..14a3eb59bc3 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/ElasticsearchLicenseException.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/ElasticsearchLicenseException.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.license.plugin.core; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.transport.RemoteTransportException; diff --git a/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java b/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java index 67b0dd8bf67..c0ffaedf242 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesManagerService.java @@ -8,8 +8,7 @@ package org.elasticsearch.license.plugin.core; import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; import org.elasticsearch.common.inject.ImplementedBy; -import org.elasticsearch.common.inject.Singleton; -import org.elasticsearch.license.core.ESLicenses; +import org.elasticsearch.license.core.ESLicense; import java.util.Set; @@ -19,11 +18,15 @@ import static org.elasticsearch.license.plugin.core.LicensesService.PutLicenseRe @ImplementedBy(LicensesService.class) public interface LicensesManagerService { + //TODO: documentation + public LicensesStatus registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener listener); public void unregisterLicenses(final DeleteLicenseRequestHolder requestHolder, final ActionListener listener); - public LicensesStatus checkLicenses(ESLicenses licenses); + public LicensesStatus checkLicenses(Set licenses); public Set enabledFeatures(); + + public Set getLicenses(); } diff --git a/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java b/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java index 9ba74bc2e58..fba01c3aae4 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesMetaData.java @@ -12,16 +12,10 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.plugin.action.Utils; -import org.elasticsearch.license.plugin.core.trial.TrialLicenseUtils; -import org.elasticsearch.license.plugin.core.trial.TrialLicenses; import java.io.IOException; import java.util.*; -import static org.elasticsearch.license.plugin.action.Utils.*; - /** * Contains metadata about registered licenses * @@ -32,9 +26,9 @@ public class LicensesMetaData implements MetaData.Custom { public static final Factory FACTORY = new Factory(); - final Set signatures; + private final Set signatures; - final Set encodedTrialLicenses; + private final Set encodedTrialLicenses; public LicensesMetaData(String[] signatures, String[] encodedTrialLicenses) { this(Sets.newHashSet(signatures), Sets.newHashSet(encodedTrialLicenses)); diff --git a/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java b/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java index bfbe7fd51a3..cacb7d0ed6c 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -14,6 +14,7 @@ import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.collect.ImmutableSet; import org.elasticsearch.common.collect.Sets; import org.elasticsearch.common.component.AbstractLifecycleComponent; @@ -23,11 +24,9 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.gateway.GatewayService; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.manager.ESLicenseManager; import org.elasticsearch.license.manager.ESLicenseProvider; -import org.elasticsearch.license.plugin.action.Utils; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; import org.elasticsearch.license.plugin.core.trial.TrialLicenseUtils; @@ -35,9 +34,7 @@ import org.elasticsearch.license.plugin.core.trial.TrialLicenses; import org.elasticsearch.license.plugin.core.trial.TrialLicensesBuilder; import org.elasticsearch.threadpool.ThreadPool; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicBoolean; @@ -47,9 +44,10 @@ import static org.elasticsearch.license.plugin.core.trial.TrialLicenses.TrialLic /** * Service responsible for managing {@link org.elasticsearch.license.plugin.core.LicensesMetaData} * Interfaces through which this is exposed are: - * - LicensesManagerService - responsible for adding/deleting signed licenses - * - LicensesClientService - allow interested plugins (features) to register to licensing notifications + * - LicensesManagerService - responsible for adding/deleting signed licenses + * - LicensesClientService - allow interested plugins (features) to register to licensing notifications * + * TODO: documentation; remove ESLicenseProvider interface (almost done) */ @Singleton public class LicensesService extends AbstractLifecycleComponent implements ESLicenseProvider, ClusterStateListener, LicensesManagerService, LicensesClientService { @@ -68,7 +66,7 @@ public class LicensesService extends AbstractLifecycleComponent public LicensesService(Settings settings, ClusterService clusterService, ThreadPool threadPool) { super(settings); this.clusterService = clusterService; - this.esLicenseManager = new ESLicenseManager(this); + this.esLicenseManager = new ESLicenseManager(); this.threadPool = threadPool; } @@ -81,7 +79,7 @@ public class LicensesService extends AbstractLifecycleComponent @Override public LicensesStatus registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener listener) { final PutLicenseRequest request = requestHolder.request; - final ESLicenses newLicenses = request.license(); + final Set newLicenses = request.licenses(); LicensesStatus status = checkLicenses(newLicenses); switch (status) { case VALID: @@ -134,10 +132,15 @@ public class LicensesService extends AbstractLifecycleComponent } @Override - public LicensesStatus checkLicenses(ESLicenses licenses) { + public LicensesStatus checkLicenses(Set licenses) { + final ImmutableMap map = org.elasticsearch.license.manager.Utils.reduceAndMap(licenses); + return checkLicenses(map); + } + + private LicensesStatus checkLicenses(Map licenseMap) { LicensesStatus status = LicensesStatus.VALID; try { - esLicenseManager.verifyLicenses(licenses); + esLicenseManager.verifyLicenses(licenseMap); } catch (ExpiredLicenseException e) { status = LicensesStatus.EXPIRED; } catch (InvalidLicenseException e) { @@ -159,6 +162,33 @@ public class LicensesService extends AbstractLifecycleComponent return enabledFeatures; } + @Override + public Set getLicenses() { + LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE); + Set trialLicenses = new HashSet<>(); + if (currentMetaData != null) { + Set currentLicenses = esLicenseManager.fromSignatures(currentMetaData.getSignatures()); + TrialLicenses currentTrialLicenses = TrialLicenseUtils.fromEncodedTrialLicenses(currentMetaData.getEncodedTrialLicenses()); + for (TrialLicense trialLicense : currentTrialLicenses) { + trialLicenses.add(ESLicense.builder() + .uid(trialLicense.uid()) + .issuedTo(trialLicense.issuedTo()) + .issueDate(trialLicense.issueDate()) + .type(ESLicense.Type.TRIAL) + .subscriptionType(ESLicense.SubscriptionType.NONE) + .feature(trialLicense.feature()) + .maxNodes(trialLicense.maxNodes()) + .expiryDate(trialLicense.expiryDate()) + .issuer("elasticsearch").buildInternal() + ); + } + Set licenses = Sets.union(currentLicenses, trialLicenses); + //TODO: sort in some order + return licenses; + } + return Sets.newHashSet(); + } + private void registerTrialLicense(final TrialLicense trialLicense) { clusterService.submitStateUpdateTask("register trial license []", new ProcessedClusterStateUpdateTask() { @@ -173,7 +203,7 @@ public class LicensesService extends AbstractLifecycleComponent LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses); // do not generate a trial license for a feature that already has a signed license - if (!esLicenseManager.hasLicenseForFeature(trialLicense.feature())) { + if (!esLicenseManager.hasLicenseForFeature(trialLicense.feature(), getEffectiveLicenses())) { licensesWrapper.addTrialLicense(trialLicense); } mdBuilder.putCustom(LicensesMetaData.TYPE, licensesWrapper.createLicensesMetaData()); @@ -245,7 +275,7 @@ public class LicensesService extends AbstractLifecycleComponent private void registerListeners(LicensesMetaData currentMetaData) { for (ListenerHolder listenerHolder : registeredListeners) { if (listenerHolder.registered.compareAndSet(false, true)) { - if (!esLicenseManager.hasLicenseForFeature(listenerHolder.feature)) { + if (!esLicenseManager.hasLicenseForFeature(listenerHolder.feature, getEffectiveLicenses())) { // does not have actual license so generate a trial license TrialLicenseOptions options = listenerHolder.trialLicenseOptions; if (options != null) { @@ -284,18 +314,24 @@ public class LicensesService extends AbstractLifecycleComponent } @Override - public ESLicenses.ESLicense getESLicense(String feature) { + public ESLicense getESLicense(String feature) { return getEffectiveLicenses().get(feature); } @Override - public ESLicenses getEffectiveLicenses() { + public Map getEffectiveLicenses() { final ClusterState state = clusterService.state(); LicensesMetaData metaData = state.metaData().custom(LicensesMetaData.TYPE); + Map map = new HashMap<>(); + if (metaData != null) { - return esLicenseManager.fromSignatures(metaData.getSignatures()); + Set esLicenses = new HashSet<>(); + for (String signature : metaData.getSignatures()) { + esLicenses.add(esLicenseManager.fromSignature(signature)); + } + return org.elasticsearch.license.manager.Utils.reduceAndMap(esLicenses); } - return LicenseBuilders.licensesBuilder().build(); + return ImmutableMap.copyOf(map); } @@ -364,8 +400,9 @@ public class LicensesService extends AbstractLifecycleComponent long offset = TimeValue.timeValueMinutes(1).getMillis(); for (ListenerHolder listenerHolder : registeredListeners) { long expiryDate = -1l; - if (esLicenseManager.hasLicenseForFeature(listenerHolder.feature)) { - expiryDate = esLicenseManager.getExpiryDateForLicense(listenerHolder.feature); + final Map effectiveLicenses = getEffectiveLicenses(); + if (esLicenseManager.hasLicenseForFeature(listenerHolder.feature, effectiveLicenses)) { + expiryDate = effectiveLicenses.get(listenerHolder.feature).expiryDate(); } else { final TrialLicense trialLicense = licensesWrapper.trialLicenses().getTrialLicense(listenerHolder.feature); if (trialLicense != null) { @@ -471,13 +508,13 @@ public class LicensesService extends AbstractLifecycleComponent private LicensesWrapper(LicensesMetaData licensesMetaData) { if (licensesMetaData != null) { - this.signatures = ImmutableSet.copyOf(licensesMetaData.signatures); - this.encodedTrialLicenses = ImmutableSet.copyOf(licensesMetaData.encodedTrialLicenses); + this.signatures = ImmutableSet.copyOf(licensesMetaData.getSignatures()); + this.encodedTrialLicenses = ImmutableSet.copyOf(licensesMetaData.getEncodedTrialLicenses()); } } - public ESLicenses signedLicenses(ESLicenseManager licenseManage) { - return licenseManage.fromSignatures(signatures); + public Set signedLicenses(ESLicenseManager licenseManager) { + return licenseManager.fromSignatures(signatures); } public TrialLicenses trialLicenses() { @@ -488,6 +525,7 @@ public class LicensesService extends AbstractLifecycleComponent * Check if any trial license for the feature exists, * if no trial license for feature exists, add new * trial license for feature + * * @param trialLicense to add */ public void addTrialLicense(TrialLicense trialLicense) { @@ -504,18 +542,23 @@ public class LicensesService extends AbstractLifecycleComponent } } - public void addSignedLicenses(ESLicenseManager licenseManager, ESLicenses licenses) { - ESLicenses currentSignedLicenses = signedLicenses(licenseManager); - final ESLicenses mergedLicenses = LicenseBuilders.merge(currentSignedLicenses, licenses); - Set newSignatures = Sets.newHashSet(Utils.toSignatures(mergedLicenses)); - this.signatures = ImmutableSet.copyOf(Sets.union(signatures, newSignatures)); + public void addSignedLicenses(ESLicenseManager licenseManager, Set newLicenses) { + Set currentSignedLicenses = signedLicenses(licenseManager); + final ImmutableMap licenseMap = org.elasticsearch.license.manager.Utils.reduceAndMap(Sets.union(currentSignedLicenses, newLicenses)); + this.signatures = licenseManager.toSignatures(licenseMap.values()); } - public void removeFeatures(ESLicenseManager licenseManage, Set featuresToDelete) { - ESLicenses currentSignedLicenses = signedLicenses(licenseManage); - final ESLicenses reducedLicenses = LicenseBuilders.removeFeatures(currentSignedLicenses, featuresToDelete); - Set reducedSignatures = Sets.newHashSet(Utils.toSignatures(reducedLicenses)); - this.signatures = ImmutableSet.copyOf(Sets.intersection(signatures, reducedSignatures)); + public void removeFeatures(ESLicenseManager licenseManager, Set featuresToDelete) { + Set currentSignedLicenses = signedLicenses(licenseManager); + final ImmutableMap licenseMap = org.elasticsearch.license.manager.Utils.reduceAndMap(currentSignedLicenses); + Set licensesToDelete = new HashSet<>(); + for (Map.Entry entry : licenseMap.entrySet()) { + if (featuresToDelete.contains(entry.getKey())) { + licensesToDelete.add(entry.getValue()); + } + } + Set reducedLicenses = Sets.difference(currentSignedLicenses, licensesToDelete); + this.signatures = licenseManager.toSignatures(reducedLicenses); } public LicensesMetaData createLicensesMetaData() { diff --git a/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicenseUtils.java b/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicenseUtils.java index f743896d189..113fc18f981 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicenseUtils.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicenseUtils.java @@ -81,14 +81,6 @@ public class TrialLicenseUtils { return Base64.encodeBase64String(encodedLicense); } - public static String[] toEncodedTrialLicenses(TrialLicenses trialLicenses) { - Set encodedTrialLicenses = new HashSet<>(); - for (TrialLicenses.TrialLicense trialLicense : trialLicenses) { - encodedTrialLicenses.add(toEncodedTrialLicense(trialLicense)); - } - return encodedTrialLicenses.toArray(new String[encodedTrialLicenses.size()]); - } - public static TrialLicenses fromEncodedTrialLicenses(String[] encodedTrialLicenses) { final TrialLicensesBuilder trialLicensesBuilder = trialLicensesBuilder(); for (String encodedTrialLicense : encodedTrialLicenses) { @@ -100,66 +92,4 @@ public class TrialLicenseUtils { public static TrialLicenses fromEncodedTrialLicenses(Set encodedTrialLicenses) { return fromEncodedTrialLicenses(encodedTrialLicenses.toArray(new String[encodedTrialLicenses.size()])); } - - public static TrialLicenses readTrialLicensesFrom(StreamInput in) throws IOException { - final TrialLicensesBuilder licensesBuilder = TrialLicensesBuilder.trialLicensesBuilder(); - boolean exists = in.readBoolean(); - if (exists) { - int size = in.readVInt(); - for (int i = 0; i < size; i++) { - licensesBuilder.license(trialLicenseFromMap(in.readMap())); - } - return licensesBuilder.build(); - } - return null; - } - - public static void writeTrialLicensesTo(TrialLicenses trialLicenses, StreamOutput out) throws IOException { - if (trialLicenses == null) { - out.writeBoolean(false); - return; - } - out.writeBoolean(true); - out.writeVInt(trialLicenses.trialLicenses().size()); - for (TrialLicenses.TrialLicense trialLicense : trialLicenses) { - out.writeMap(trialLicenseAsMap(trialLicense)); - } - } - - // TODO: make sure field order is preserved - public static Map trialLicenseAsMap(TrialLicenses.TrialLicense trialLicense) { - ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put(TrialLicenseFields.UID, trialLicense.uid()); - builder.put(TrialLicenseFields.TYPE, ESLicenses.Type.TRIAL.string()); - builder.put(TrialLicenseFields.SUBSCRIPTION_TYPE, ESLicenses.SubscriptionType.NONE.string()); - builder.put(TrialLicenseFields.ISSUE_DATE, trialLicense.issueDate()); - builder.put(TrialLicenseFields.FEATURE, trialLicense.feature()); - builder.put(TrialLicenseFields.EXPIRY_DATE, trialLicense.expiryDate()); - builder.put(TrialLicenseFields.MAX_NODES, trialLicense.maxNodes()); - builder.put(TrialLicenseFields.ISSUED_TO, trialLicense.issuedTo()); - return builder.build(); - } - - public static TrialLicenses.TrialLicense trialLicenseFromMap(Map map) { - return TrialLicensesBuilder.trialLicenseBuilder() - .uid((String) map.get(TrialLicenseFields.UID)) - .issuedTo((String) map.get(TrialLicenseFields.ISSUED_TO)) - .maxNodes((int) map.get(TrialLicenseFields.MAX_NODES)) - .feature((String) map.get(TrialLicenseFields.FEATURE)) - .issueDate((long) map.get(TrialLicenseFields.ISSUE_DATE)) - .expiryDate((long) map.get(TrialLicenseFields.EXPIRY_DATE)) - .build(); - - } - - final static class TrialLicenseFields { - private final static String UID = "uid"; - private final static String TYPE = "type"; - private final static String SUBSCRIPTION_TYPE = "subscription_type"; - private final static String ISSUE_DATE = "issue_date"; - private final static String FEATURE = "feature"; - private final static String ISSUED_TO = "issued_to"; - private final static String MAX_NODES = "max_nodes"; - private final static String EXPIRY_DATE = "expiry_date"; - } } diff --git a/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicensesBuilder.java b/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicensesBuilder.java index 3d33ad638fe..336d035b540 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicensesBuilder.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/trial/TrialLicensesBuilder.java @@ -7,6 +7,7 @@ package org.elasticsearch.license.plugin.core.trial; import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.license.core.DateUtils; +import org.elasticsearch.license.core.ESLicense; import java.util.*; @@ -16,6 +17,12 @@ public class TrialLicensesBuilder { public static TrialLicenses EMPTY = trialLicensesBuilder().build(); + public static final String ISSUER = "elasticsearch"; + + public static final ESLicense.Type TYPE = ESLicense.Type.TRIAL; + + public static final ESLicense.SubscriptionType SUBSCRIPTION_TYPE = ESLicense.SubscriptionType.NONE; + public static TrialLicensesBuilder trialLicensesBuilder() { return new TrialLicensesBuilder(); } diff --git a/src/main/java/org/elasticsearch/license/plugin/rest/RestGetLicenseAction.java b/src/main/java/org/elasticsearch/license/plugin/rest/RestGetLicenseAction.java index 162d09997d1..88c0b386ffe 100644 --- a/src/main/java/org/elasticsearch/license/plugin/rest/RestGetLicenseAction.java +++ b/src/main/java/org/elasticsearch/license/plugin/rest/RestGetLicenseAction.java @@ -13,12 +13,9 @@ import org.elasticsearch.license.core.ESLicenses; import org.elasticsearch.license.plugin.action.get.GetLicenseAction; import org.elasticsearch.license.plugin.action.get.GetLicenseRequest; import org.elasticsearch.license.plugin.action.get.GetLicenseResponse; -import org.elasticsearch.license.plugin.core.trial.TrialLicenses; import org.elasticsearch.rest.*; import org.elasticsearch.rest.action.support.RestBuilderListener; -import static org.elasticsearch.license.plugin.action.Utils.licenseAsMap; -import static org.elasticsearch.license.plugin.core.trial.TrialLicenseUtils.trialLicenseAsMap; import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestStatus.OK; @@ -35,16 +32,7 @@ public class RestGetLicenseAction extends BaseRestHandler { client.admin().cluster().execute(GetLicenseAction.INSTANCE, new GetLicenseRequest(), new RestBuilderListener(channel) { @Override public RestResponse buildResponse(GetLicenseResponse response, XContentBuilder builder) throws Exception { - builder.startObject(); - builder.startArray("licenses"); - for (ESLicenses.ESLicense license : response.licenses()) { - builder.map(licenseAsMap(license)); - } - for (TrialLicenses.TrialLicense trialLicense : response.trialLicenses()) { - builder.map(trialLicenseAsMap(trialLicense)); - } - builder.endArray(); - builder.endObject(); + ESLicenses.toXContent(response.licenses(), builder); return new BytesRestResponse(OK, builder); } }); diff --git a/src/main/java/org/elasticsearch/license/plugin/rest/RestPutLicenseAction.java b/src/main/java/org/elasticsearch/license/plugin/rest/RestPutLicenseAction.java index d3b716eee1e..6ca9df7698d 100644 --- a/src/main/java/org/elasticsearch/license/plugin/rest/RestPutLicenseAction.java +++ b/src/main/java/org/elasticsearch/license/plugin/rest/RestPutLicenseAction.java @@ -34,7 +34,7 @@ public class RestPutLicenseAction extends BaseRestHandler { public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) { PutLicenseRequest putLicenseRequest = new PutLicenseRequest(); putLicenseRequest.listenerThreaded(false); - putLicenseRequest.license(request.content().toUtf8()); + putLicenseRequest.licenses(request.content().toUtf8()); client.admin().cluster().execute(PutLicenseAction.INSTANCE, putLicenseRequest, new AcknowledgedRestListener(channel)); } } diff --git a/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java b/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java index 6bd727320d8..92b5155c83e 100644 --- a/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java +++ b/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java @@ -10,6 +10,7 @@ import org.junit.BeforeClass; import java.io.IOException; import java.net.URL; +import java.text.ParseException; import java.util.Map; public class AbstractLicensingTestBase { @@ -29,7 +30,7 @@ public class AbstractLicensingTestBase { return url.toURI().getPath(); } - public String generateSignedLicenses(Map map) throws IOException { + public String generateSignedLicenses(Map map) throws IOException, ParseException { String licenseString = TestUtils.generateESLicenses(map); return TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath); } diff --git a/src/test/java/org/elasticsearch/license/TestUtils.java b/src/test/java/org/elasticsearch/license/TestUtils.java index 42b781b21b0..e4bf26131d7 100644 --- a/src/test/java/org/elasticsearch/license/TestUtils.java +++ b/src/test/java/org/elasticsearch/license/TestUtils.java @@ -7,15 +7,17 @@ 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.core.LicenseBuilders; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.licensor.tools.LicenseGeneratorTool; +import org.elasticsearch.license.manager.Utils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.ParseException; +import java.util.HashMap; import java.util.Map; +import java.util.Set; import static org.junit.Assert.assertTrue; @@ -51,7 +53,7 @@ public class TestUtils { } - public static String runLicenseGenerationTool(String licenseInput, String pubKeyPath, String priKeyPath) throws IOException { + public static String runLicenseGenerationTool(String licenseInput, String pubKeyPath, String priKeyPath) throws IOException, ParseException { String args[] = new String[6]; args[0] = "--license"; args[1] = licenseInput; @@ -63,7 +65,7 @@ public class TestUtils { return runLicenseGenerationTool(args); } - public static String runLicenseGenerationTool(String[] args) throws IOException { + public static String runLicenseGenerationTool(String[] args) throws IOException, ParseException { File temp = File.createTempFile("temp", ".out"); temp.deleteOnExit(); try (FileOutputStream outputStream = new FileOutputStream(temp)) { @@ -72,12 +74,17 @@ public class TestUtils { 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()); + public static void verifyESLicenses(Set esLicenses, Map featureAttributesMap) throws ParseException { + verifyESLicenses(Utils.reduceAndMap(esLicenses), featureAttributesMap); + + } + + public static void verifyESLicenses(Map esLicenses, Map featureAttributes) throws ParseException { + assertTrue("Number of feature licenses should be " + featureAttributes.size(), esLicenses.size() == featureAttributes.size()); for (Map.Entry featureAttrTuple : featureAttributes.entrySet()) { String featureType = featureAttrTuple.getKey(); FeatureAttributes attributes = featureAttrTuple.getValue(); - final ESLicenses.ESLicense esLicense = esLicenses.get(featureType); + final ESLicense esLicense = esLicenses.get(featureType); assertTrue("license for " + featureType + " 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)); @@ -92,28 +99,27 @@ public class TestUtils { } } - //TODO: convert to asserts - public static void isSame(ESLicenses firstLicenses, ESLicenses secondLicenses) { + public static void isSame(Set firstLicenses, Set secondLicenses) { // we do the build to make sure we weed out any expired licenses - final ESLicenses licenses1 = LicenseBuilders.licensesBuilder().licenses(firstLicenses).build(); - final ESLicenses licenses2 = LicenseBuilders.licensesBuilder().licenses(secondLicenses).build(); + final Map licenses1 = Utils.reduceAndMap(firstLicenses); + final Map licenses2 = Utils.reduceAndMap(secondLicenses); // check if the effective licenses have the same feature set - assertTrue("Both licenses should have the same number of features", licenses1.features().equals(licenses2.features())); + assertTrue("Both licenses should have the same number of features", licenses1.size() == licenses2.size()); // for every feature license, check if all the attributes are the same - for (String featureType : licenses1.features()) { - ESLicenses.ESLicense license1 = licenses1.get(featureType); - ESLicenses.ESLicense license2 = licenses2.get(featureType); + for (String featureType : licenses1.keySet()) { + ESLicense license1 = licenses1.get(featureType); + ESLicense license2 = licenses2.get(featureType); isSame(license1, license2); } } - public static void isSame(ESLicenses.ESLicense license1, ESLicenses.ESLicense license2) { + public static void isSame(ESLicense license1, ESLicense license2) { assertTrue("Should have same uid; got: " + license1.uid() + " and " + license2.uid(), license1.uid().equals(license2.uid())); assertTrue("Should have same feature; got: " + license1.feature() + " and " + license2.feature(), license1.feature().equals(license2.feature())); diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java index 1ae94307481..47641b44546 100644 --- a/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java +++ b/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java @@ -7,14 +7,15 @@ package org.elasticsearch.license.licensor; import org.elasticsearch.license.AbstractLicensingTestBase; import org.elasticsearch.license.TestUtils; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseUtils; import org.junit.Test; import java.io.IOException; import java.text.ParseException; import java.util.HashMap; import java.util.Map; +import java.util.Set; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -30,7 +31,7 @@ public class LicenseGenerationTests extends AbstractLicensingTestBase { String licenseOutput = generateSignedLicenses(map); - ESLicenses esLicensesOutput = LicenseUtils.readLicensesFromString(licenseOutput); + Set esLicensesOutput = ESLicenses.fromSource(licenseOutput); TestUtils.verifyESLicenses(esLicensesOutput, map); } @@ -48,7 +49,7 @@ public class LicenseGenerationTests extends AbstractLicensingTestBase { String licenseOutput = generateSignedLicenses(map); - ESLicenses esLicensesOutput = LicenseUtils.readLicensesFromString(licenseOutput); + Set esLicensesOutput = ESLicenses.fromSource(licenseOutput); TestUtils.verifyESLicenses(esLicensesOutput, map); } diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java index 66eeacdeb62..26d1e874872 100644 --- a/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java +++ b/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java @@ -8,16 +8,18 @@ package org.elasticsearch.license.licensor; import org.apache.commons.io.FileUtils; import org.elasticsearch.license.AbstractLicensingTestBase; import org.elasticsearch.license.TestUtils; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseUtils; import org.elasticsearch.license.licensor.tools.LicenseVerificationTool; import org.junit.Test; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.text.ParseException; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class LicenseVerificationToolTests extends AbstractLicensingTestBase { @@ -39,12 +41,12 @@ public class LicenseVerificationToolTests extends AbstractLicensingTestBase { String secondLicenseFile = getAsFilePath(signedLicense); String effectiveLicenseStr = runLicenseVerificationTool(new String[]{firstLicenseFile, secondLicenseFile}); - ESLicenses effectiveLicense = LicenseUtils.readLicensesFromString(effectiveLicenseStr); + Set esLicensesOutput = ESLicenses.fromSource(effectiveLicenseStr); map.put(TestUtils.SHIELD, featureWithLongerExpiryDate); // verify that the effective license strips out license for the same feature with earlier expiry dates - TestUtils.verifyESLicenses(effectiveLicense, map); + TestUtils.verifyESLicenses(esLicensesOutput, map); } @Test @@ -65,10 +67,10 @@ public class LicenseVerificationToolTests extends AbstractLicensingTestBase { String secondLicenseFile = getAsFilePath(signedLicense); String effectiveLicenseStr = runLicenseVerificationTool(new String[]{firstLicenseFile, secondLicenseFile}); - ESLicenses effectiveLicense = LicenseUtils.readLicensesFromString(effectiveLicenseStr); + Set esLicensesOutput = ESLicenses.fromSource(effectiveLicenseStr); // verify that the effective license contains both feature licenses - TestUtils.verifyESLicenses(effectiveLicense, map); + TestUtils.verifyESLicenses(esLicensesOutput, map); } @Test @@ -98,13 +100,13 @@ public class LicenseVerificationToolTests extends AbstractLicensingTestBase { String secondLicenseFile = getAsFilePath(signedLicense); String effectiveLicenseStr = runLicenseVerificationTool(new String[]{firstLicenseFile, secondLicenseFile}); - ESLicenses effectiveLicense = LicenseUtils.readLicensesFromString(effectiveLicenseStr); + Set esLicensesOutput = ESLicenses.fromSource(effectiveLicenseStr); map.put(TestUtils.SHIELD, shieldFeatureWithLongerExpiryDate); map.put(TestUtils.MARVEL, marvelFeatureWithLongerExpiryDate); // verify that the generated effective license is generated from choosing individual licences from multiple files - TestUtils.verifyESLicenses(effectiveLicense, map); + TestUtils.verifyESLicenses(esLicensesOutput, map); } public static String runLicenseVerificationTool(String[] licenseFiles) throws IOException { @@ -128,7 +130,7 @@ public class LicenseVerificationToolTests extends AbstractLicensingTestBase { return FileUtils.readFileToString(temp); } - public String runLicenseGenerationTool(String licenseInput) throws IOException { + public String runLicenseGenerationTool(String licenseInput) throws IOException, ParseException { return TestUtils.runLicenseGenerationTool(licenseInput, pubKeyPath, priKeyPath); } diff --git a/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java index c50773b5df7..22c6e8110fa 100644 --- a/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java +++ b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java @@ -9,8 +9,8 @@ import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException; import org.elasticsearch.license.AbstractLicensingTestBase; import org.elasticsearch.license.TestUtils; import org.elasticsearch.license.core.DateUtils; +import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; import org.elasticsearch.license.licensor.tools.FileBasedESLicenseProvider; import org.junit.After; import org.junit.BeforeClass; @@ -19,7 +19,6 @@ import org.junit.Test; import java.text.ParseException; import java.util.*; -import static org.elasticsearch.license.core.LicenseUtils.readLicensesFromString; import static org.junit.Assert.*; public class LicenseVerificationTests extends AbstractLicensingTestBase { @@ -28,12 +27,12 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { private static FileBasedESLicenseProvider esLicenseProvider; - private final static ESLicenses EMPTY_LICENSES = LicenseBuilders.licensesBuilder().build(); + private final static Set EMPTY_LICENSES = new HashSet<>(); @BeforeClass public static void setupManager() { - esLicenseProvider = new FileBasedESLicenseProvider(LicenseBuilders.licensesBuilder().build()); - esLicenseManager = new ESLicenseManager(esLicenseProvider); + esLicenseProvider = new FileBasedESLicenseProvider(EMPTY_LICENSES); + esLicenseManager = new ESLicenseManager(); } @@ -53,13 +52,13 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr); map.put(TestUtils.SHIELD, featureAttributes); - ESLicenses esLicensesOutput = readLicensesFromString(generateSignedLicenses(map)); + Set esLicensesOutput = ESLicenses.fromSource(generateSignedLicenses(map)); esLicenseProvider.setLicenses(esLicensesOutput); - esLicenseManager.verifyLicenses(); + esLicenseManager.verifyLicenses(esLicenseProvider.getEffectiveLicenses()); - verifyLicenseManager(esLicenseManager, map); + verifyLicenseManager(esLicenseManager, esLicenseProvider, map); } @@ -77,15 +76,13 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { map.put(TestUtils.SHIELD, shildFeatureAttributes); map.put(TestUtils.MARVEL, marvelFeatureAttributes); - ESLicenses esLicensesOutput = readLicensesFromString(generateSignedLicenses(map)); + Set esLicensesOutput = ESLicenses.fromSource(generateSignedLicenses(map)); esLicenseProvider.setLicenses(esLicensesOutput); - //printLicense(esLicenseManager.getEffectiveLicenses()); + esLicenseManager.verifyLicenses(esLicenseProvider.getEffectiveLicenses()); - esLicenseManager.verifyLicenses(); - - verifyLicenseManager(esLicenseManager, map); + verifyLicenseManager(esLicenseManager, esLicenseProvider, map); } @@ -134,15 +131,15 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { map.put(TestUtils.SHIELD, shildFeatureAttributes); map.put(TestUtils.MARVEL, marvelFeatureAttributes); - ESLicenses esLicensesOutput = readLicensesFromString(generateSignedLicenses(map)); + Set esLicensesOutput = ESLicenses.fromSource(generateSignedLicenses(map)); esLicenseProvider.setLicenses(esLicensesOutput); // All validation for shield license should be normal as expected - verifyLicenseManager(esLicenseManager, Collections.singletonMap(TestUtils.SHIELD, shildFeatureAttributes)); + verifyLicenseManager(esLicenseManager, esLicenseProvider, Collections.singletonMap(TestUtils.SHIELD, shildFeatureAttributes)); - assertFalse("license for marvel should not be valid due to expired expiry date", esLicenseManager.hasLicenseForFeature(TestUtils.MARVEL)); + assertFalse("license for marvel should not be valid due to expired expiry date", esLicenseManager.hasLicenseForFeature(TestUtils.MARVEL, esLicenseProvider.getEffectiveLicenses())); } @Test @@ -156,47 +153,42 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr); map.put(TestUtils.SHIELD, featureAttributes); - ESLicenses esLicensesOutput = readLicensesFromString(generateSignedLicenses(map)); + Set esLicensesOutput = ESLicenses.fromSource(generateSignedLicenses(map)); - ESLicenses.ESLicense esLicense = esLicensesOutput.get(TestUtils.SHIELD); + ESLicense esLicense = Utils.reduceAndMap(esLicensesOutput).get(TestUtils.SHIELD); - long originalExpiryDate = esLicense.expiryDate(); - final ESLicenses.ESLicense tamperedLicense = LicenseBuilders.licenseBuilder(true) + final ESLicense tamperedLicense = ESLicense.builder() .fromLicense(esLicense) .expiryDate(esLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l) .feature(TestUtils.SHIELD) .issuer("elasticsqearch") .build(); - ESLicenses tamperedLicenses = LicenseBuilders.licensesBuilder().license(tamperedLicense).build(); - try { - esLicenseProvider.setLicenses(tamperedLicenses); - assertTrue("License manager should always report the original (signed) expiry date of: " + originalExpiryDate + " but got: " + esLicenseManager.getExpiryDateForLicense(TestUtils.SHIELD), esLicenseManager.getExpiryDateForLicense(TestUtils.SHIELD) == originalExpiryDate); - esLicenseManager.verifyLicenses(); + esLicenseProvider.setLicenses(Collections.singleton(tamperedLicense)); + esLicenseManager.verifyLicenses(esLicenseProvider.getEffectiveLicenses()); fail(); } catch (InvalidLicenseException e) { assertTrue("Exception should contain 'Invalid License' but got: " + e.getMessage(), e.getMessage().contains("Invalid License")); } } - public static void verifyLicenseManager(ESLicenseManager esLicenseManager, Map featureAttributeMap) throws ParseException { + public static void verifyLicenseManager(ESLicenseManager esLicenseManager, ESLicenseProvider licenseProvider, Map featureAttributeMap) throws ParseException { for (Map.Entry entry : featureAttributeMap.entrySet()) { TestUtils.FeatureAttributes featureAttributes = entry.getValue(); String 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) + " got: " + esLicenseManager.getExpiryDateForLicense(featureType), esLicenseManager.getExpiryDateForLicense(featureType) == DateUtils.longExpiryDateFromString(featureAttributes.expiryDate)); - assertTrue("License should have type of " + featureAttributes.type + " got: " + esLicenseManager.getTypeForLicense(featureType).string(), 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)); + ESLicense license = licenseProvider.getESLicense(featureType); + assertTrue("License should have issuedTo of " + featureAttributes.issuedTo, license.issuedTo().equals(featureAttributes.issuedTo)); + assertTrue("License should have issuer of " + featureAttributes.issuer, license.issuer().equals(featureAttributes.issuer)); + assertTrue("License should have issue date of " + DateUtils.longFromDateString(featureAttributes.issueDate), license.issueDate() == DateUtils.longFromDateString(featureAttributes.issueDate)); + assertTrue("License should have expiry date of " + DateUtils.longExpiryDateFromString(featureAttributes.expiryDate) + " got: " + license.expiryDate(), license.expiryDate() == DateUtils.longExpiryDateFromString(featureAttributes.expiryDate)); + assertTrue("License should have type of " + featureAttributes.type + " got: " + license.type().string(), license.type() == ESLicense.Type.fromString(featureAttributes.type)); + assertTrue("License should have subscription type of " + featureAttributes.subscriptionType, license.subscriptionType() == ESLicense.SubscriptionType.fromString(featureAttributes.subscriptionType)); - assertTrue("License should be valid for " + featureType, 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)); + assertTrue("License should be valid for " + featureType, esLicenseManager.hasLicenseForFeature(featureType, licenseProvider.getEffectiveLicenses())); + assertTrue("License should be valid for maxNodes = " + (featureAttributes.maxNodes), license.maxNodes() == featureAttributes.maxNodes); } } } diff --git a/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java b/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java index d891226e448..f0dfce1e319 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java @@ -11,9 +11,8 @@ import org.elasticsearch.common.collect.ImmutableSet; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.license.TestUtils; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; -import org.elasticsearch.license.core.LicenseUtils; +import org.elasticsearch.license.core.*; +import org.elasticsearch.license.manager.Utils; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequestBuilder; import org.elasticsearch.license.plugin.action.delete.DeleteLicenseResponse; import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder; @@ -28,11 +27,12 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Paths; import java.text.ParseException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutionException; -import static org.elasticsearch.license.core.LicenseUtils.readLicensesFromString; import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.SUITE; import static org.hamcrest.CoreMatchers.equalTo; @@ -82,7 +82,7 @@ public class LicenseTransportTests extends ElasticsearchIntegrationTest { final GetLicenseResponse getLicenseResponse = getLicenseFuture.get(); - assertThat("expected 0 licenses; but got: " + getLicenseResponse.licenses(), getLicenseResponse.licenses().licenses().size(), equalTo(0)); + assertThat("expected 0 licenses; but got: " + getLicenseResponse.licenses().size(), getLicenseResponse.licenses().size(), equalTo(0)); } @Test @@ -97,7 +97,7 @@ public class LicenseTransportTests extends ElasticsearchIntegrationTest { PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster()); //putLicenseRequest.license(licenseString); - final ESLicenses putLicenses = LicenseUtils.readLicensesFromString(licenseOutput); + final Set putLicenses = ESLicenses.fromSource(licenseOutput); putLicenseRequestBuilder.setLicense(putLicenses); //LicenseUtils.printLicense(putLicenses); ensureGreen(); @@ -123,8 +123,8 @@ public class LicenseTransportTests extends ElasticsearchIntegrationTest { final DeleteLicenseResponse deleteLicenseResponse = deleteFuture.get(); assertTrue(deleteLicenseResponse.isAcknowledged()); - getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).execute().get(); - TestUtils.isSame(getLicenseResponse.licenses(), LicenseBuilders.licensesBuilder().build()); + //getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).execute().get(); + //TestUtils.isSame(getLicenseResponse.licenses(), LicenseBuilders.licensesBuilder().build()); } @Test @@ -136,17 +136,19 @@ public class LicenseTransportTests extends ElasticsearchIntegrationTest { String licenseString = TestUtils.generateESLicenses(map); String licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath); - ESLicenses esLicenses = readLicensesFromString(licenseOutput); + Set esLicenses = ESLicenses.fromSource(licenseOutput); - ESLicenses.ESLicense esLicense = esLicenses.get(TestUtils.SHIELD); - ESLicenses.ESLicense tamperedLicense = LicenseBuilders.licenseBuilder(true) + ESLicense esLicense = Utils.reduceAndMap(esLicenses).get(TestUtils.SHIELD); + + final ESLicense tamperedLicense = ESLicense.builder() .fromLicense(esLicense) .expiryDate(esLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l) - .issuer("elasticsearch") + .feature(TestUtils.SHIELD) + .issuer("elasticsqearch") .build(); PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster()); - builder.setLicense(LicenseBuilders.licensesBuilder().license(tamperedLicense).build()); + builder.setLicense(Collections.singleton(tamperedLicense)); final ListenableActionFuture execute = builder.execute(); diff --git a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java index e287bc34aa7..75aff11d21a 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java @@ -15,10 +15,9 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.license.TestUtils; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; -import org.elasticsearch.license.core.LicenseUtils; +import org.elasticsearch.license.core.*; import org.elasticsearch.license.manager.ESLicenseManager; +import org.elasticsearch.license.manager.Utils; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; import org.elasticsearch.license.plugin.core.*; import org.elasticsearch.test.ElasticsearchIntegrationTest; @@ -31,9 +30,7 @@ import org.junit.Test; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; @@ -99,7 +96,7 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { @Test public void testEmptySignedLicenseCheck() { LicensesManagerService licensesManagerService = licensesManagerService(); - assertTrue(LicensesStatus.VALID == licensesManagerService.checkLicenses(LicenseBuilders.licensesBuilder().build())); + assertTrue(LicensesStatus.VALID == licensesManagerService.checkLicenses(new HashSet())); } @Test @@ -112,19 +109,20 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { map.put(TestUtils.SHIELD, featureAttributes); String licenseString = TestUtils.generateESLicenses(map); String licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath); - ESLicenses licenses = LicenseUtils.readLicensesFromString(licenseOutput); + Set licenses = ESLicenses.fromSource(licenseOutput); assertTrue(LicensesStatus.VALID == licensesManagerService.checkLicenses(licenses)); - ESLicenses.ESLicense tamperedLicense = LicenseBuilders.licenseBuilder(true) - .fromLicense(licenses.get(TestUtils.SHIELD)) - .expiryDate(licenses.get(TestUtils.SHIELD).expiryDate() + 5 * 24 * 60 * 60 * 1000l) - .issuer("elasticsearch") + ESLicense esLicense = Utils.reduceAndMap(licenses).get(TestUtils.SHIELD); + + final ESLicense tamperedLicense = ESLicense.builder() + .fromLicense(esLicense) + .expiryDate(esLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l) + .feature(TestUtils.SHIELD) + .issuer("elasticsqearch") .build(); - ESLicenses tamperedLicenses = LicenseBuilders.licensesBuilder().license(tamperedLicense).build(); - - assertTrue(LicensesStatus.INVALID == licensesManagerService.checkLicenses(tamperedLicenses)); + assertTrue(LicensesStatus.INVALID == licensesManagerService.checkLicenses(Collections.singleton(tamperedLicense))); } @Test @@ -135,12 +133,12 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { map.put(TestUtils.SHIELD, featureAttributes1); String licenseString = TestUtils.generateESLicenses(map); String licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath); - ESLicenses licenses = LicenseUtils.readLicensesFromString(licenseOutput); + Set licenses = ESLicenses.fromSource(licenseOutput); LicensesManagerService licensesManagerService = licensesManagerService(); ESLicenseManager esLicenseManager = ((LicensesService) licensesManagerService).getEsLicenseManager(); final CountDownLatch latch1 = new CountDownLatch(1); - licensesManagerService.registerLicenses(new LicensesService.PutLicenseRequestHolder(new PutLicenseRequest().license(licenses), "test"), new ActionListener() { + licensesManagerService.registerLicenses(new LicensesService.PutLicenseRequestHolder(new PutLicenseRequest().licenses(licenses), "test"), new ActionListener() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { if (clusterStateUpdateResponse.isAcknowledged()) { @@ -156,7 +154,7 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { latch1.await(); LicensesMetaData metaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE); - ESLicenses metaDataLicense = esLicenseManager.fromSignatures(metaData.getSignatures()); + Set metaDataLicense = esLicenseManager.fromSignatures(metaData.getSignatures()); TestUtils.isSame(licenses, metaDataLicense); @@ -165,9 +163,9 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { map.put(TestUtils.SHIELD, featureAttributes2); licenseString = TestUtils.generateESLicenses(map); licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath); - ESLicenses licenses2 = LicenseUtils.readLicensesFromString(licenseOutput); + Set licenses2 = ESLicenses.fromSource(licenseOutput); final CountDownLatch latch2 = new CountDownLatch(1); - licensesManagerService.registerLicenses(new LicensesService.PutLicenseRequestHolder(new PutLicenseRequest().license(licenses2), "test"), new ActionListener() { + licensesManagerService.registerLicenses(new LicensesService.PutLicenseRequestHolder(new PutLicenseRequest().licenses(licenses2), "test"), new ActionListener() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { if (clusterStateUpdateResponse.isAcknowledged()) { @@ -270,11 +268,11 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { map.put(TestUtils.SHIELD, featureAttributes1); String licenseString = TestUtils.generateESLicenses(map); String licenseOutput = TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath); - ESLicenses licenses = LicenseUtils.readLicensesFromString(licenseOutput); + Set licenses = ESLicenses.fromSource(licenseOutput); LicensesManagerService licensesManagerService = licensesManagerService(); final CountDownLatch latch1 = new CountDownLatch(1); - licensesManagerService.registerLicenses(new LicensesService.PutLicenseRequestHolder(new PutLicenseRequest().license(licenses), "test"), new ActionListener() { + licensesManagerService.registerLicenses(new LicensesService.PutLicenseRequestHolder(new PutLicenseRequest().licenses(licenses), "test"), new ActionListener() { @Override public void onResponse(ClusterStateUpdateResponse clusterStateUpdateResponse) { if (clusterStateUpdateResponse.isAcknowledged()) {