From 6c116e2fb3525da489b8f10f90f8aad9bc7fadcc Mon Sep 17 00:00:00 2001 From: Areek Zillur Date: Mon, 3 Nov 2014 20:57:25 -0500 Subject: [PATCH] - added serialization tests - added license signature tests - refactor unit tests - testing infra - cleanup license tools - nuked FileBasedLicenseProvider Original commit: elastic/x-pack-elasticsearch@2af36aecfab1876cd232175bd287fdd19387504c --- .../tools/FileBasedESLicenseProvider.java | 36 ---- .../licensor/tools/LicenseGeneratorTool.java | 6 +- .../tools/LicenseVerificationTool.java | 23 +-- .../license/plugin/core/LicensesService.java | 23 +-- .../license/AbstractLicensingTestBase.java | 139 ++++++++++++- .../org/elasticsearch/license/TestUtils.java | 138 ++----------- .../licensor/LicenseGenerationTests.java | 80 -------- .../licensor/LicenseGenerationToolTests.java | 134 +++++++++++++ .../licensor/LicenseSerializationTests.java | 94 +++++++++ .../LicenseVerificationToolTests.java | 187 ++++++++---------- .../license/manager/LicenseSignatureTest.java | 48 +++++ .../manager/LicenseVerificationTests.java | 151 ++++---------- .../license/plugin/LicenseTransportTests.java | 19 +- .../plugin/LicensesServiceClusterTest.java | 50 ++--- 14 files changed, 591 insertions(+), 537 deletions(-) delete mode 100644 src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java delete mode 100644 src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java create mode 100644 src/test/java/org/elasticsearch/license/licensor/LicenseGenerationToolTests.java create mode 100644 src/test/java/org/elasticsearch/license/licensor/LicenseSerializationTests.java create mode 100644 src/test/java/org/elasticsearch/license/manager/LicenseSignatureTest.java diff --git a/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java b/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java deleted file mode 100644 index 90ccb6419f2..00000000000 --- a/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java +++ /dev/null @@ -1,36 +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.licensor.tools; - -import org.elasticsearch.common.collect.ImmutableMap; -import org.elasticsearch.license.core.ESLicense; -import org.elasticsearch.license.core.ESLicenses; - -import java.util.Map; -import java.util.Set; - -/** - */ -public class FileBasedESLicenseProvider { - private ImmutableMap esLicenses; - - public FileBasedESLicenseProvider(Set esLicenses) { - this.esLicenses = ESLicenses.reduceAndMap(esLicenses); - } - - public ESLicense getESLicense(String feature) { - return esLicenses.get(feature); - } - - public Map getEffectiveLicenses() { - return esLicenses; - } - - // For testing - public void setLicenses(Set esLicenses) { - this.esLicenses = ESLicenses.reduceAndMap(esLicenses); - } -} 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 00bc7e9314c..751e4291b26 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java @@ -69,13 +69,17 @@ public class LicenseGeneratorTool { } if (licenseSpecs.size() == 0) { - throw new IllegalArgumentException("at least one of '--licenses' or '--licenseFile' has to be provided"); + throw new IllegalArgumentException("at least one of '--license' or '--licenseFile' has to be provided"); } if (publicKeyPath == null) { throw new IllegalArgumentException("mandatory option '--publicKeyPath' is missing"); + } else if (!Paths.get(publicKeyPath).toFile().exists()) { + throw new IllegalArgumentException("Public key file: " + publicKeyPath + " does not exist!"); } if (privateKeyPath == null) { throw new IllegalArgumentException("mandatory option '--privateKeyPath' is missing"); + } else if (!Paths.get(privateKeyPath).toFile().exists()) { + throw new IllegalArgumentException("Private key file: " + privateKeyPath + " does not exist!"); } return new Options(licenseSpecs, publicKeyPath, privateKeyPath); 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 1104ae0089b..56e98dc7caa 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.license.licensor.tools; +import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; @@ -23,15 +24,7 @@ import java.util.Set; public class LicenseVerificationTool { - static class Options { - private final Set licenses; - - Options(Set licenses) { - this.licenses = licenses; - } - } - - private static Options parse(String[] args) throws IOException { + private static Set parse(String[] args) throws IOException { Set licenses = new HashSet<>(); for (int i = 0; i < args.length; i++) { @@ -55,7 +48,7 @@ public class LicenseVerificationTool { if (licenses.size() == 0) { throw new IllegalArgumentException("mandatory option '--licensesFiles' or '--licenses' is missing"); } - return new Options(licenses); + return licenses; } public static void main(String[] args) throws IOException { @@ -63,16 +56,16 @@ public class LicenseVerificationTool { } public static void run(String[] args, OutputStream out) throws IOException { - Options options = parse(args); + Set licenses = parse(args); - // verify licenses - FileBasedESLicenseProvider licenseProvider = new FileBasedESLicenseProvider(options.licenses); + // reduce & verify licenses + ImmutableMap effectiveLicenses = ESLicenses.reduceAndMap(licenses); ESLicenseManager licenseManager = new ESLicenseManager(); - licenseManager.verifyLicenses(licenseProvider.getEffectiveLicenses()); + licenseManager.verifyLicenses(effectiveLicenses); // dump effective licences XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON, out); - ESLicenses.toXContent(licenseProvider.getEffectiveLicenses().values(), builder, ToXContent.EMPTY_PARAMS); + ESLicenses.toXContent(effectiveLicenses.values(), builder, ToXContent.EMPTY_PARAMS); builder.flush(); } 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 160502e4443..69f56e2184c 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -483,9 +483,9 @@ public class LicensesService extends AbstractLifecycleComponent if (logger.isDebugEnabled()) { String status; if (expiryDate != -1l) { - status = "license expires in : " + TimeValue.timeValueMillis(expiryDate - System.currentTimeMillis()); + status = " status: license expires in : " + TimeValue.timeValueMillis(expiryDate - System.currentTimeMillis()); } else { - status = "no trial/signed license found"; + status = " status: no trial/signed license found"; } if (expiryDuration > 0l) { status += " action: enableFeatureIfNeeded"; @@ -496,13 +496,15 @@ public class LicensesService extends AbstractLifecycleComponent } } - logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData); lastObservedLicensesState.set(currentLicensesMetaData); - if (nextScheduleFrequency == -1l) { - logger.debug("no need to schedule next notification"); - } else { - logger.debug("next notification time: " + TimeValue.timeValueMillis(nextScheduleFrequency).toString()); + if (logger.isDebugEnabled()) { + logLicenseMetaDataStats("Setting last observed metaData", currentLicensesMetaData); + if (nextScheduleFrequency == -1l) { + logger.debug("no need to schedule next notification"); + } else { + logger.debug("next notification time: " + TimeValue.timeValueMillis(nextScheduleFrequency).toString()); + } } return nextScheduleFrequency; @@ -531,19 +533,14 @@ public class LicensesService extends AbstractLifecycleComponent * if new feature has a non-null trial license option, a master node request is made to generate the trial license * if no trial license option is specified for the feature and no signed license is found, * then notifies features to be disabled + * then notifies features to be disabled * * @param listenerHolder of the feature to register * @return true if registration has been completed, false otherwise (if masterNode is not available & trail license spec is provided - * or if there is a global block on {@link org.elasticsearch.gateway.GatewayService#STATE_NOT_RECOVERED_BLOCK}) */ private boolean registerListener(final ListenerHolder listenerHolder) { logger.debug("Registering listener for " + listenerHolder.feature); ClusterState currentState = clusterService.state(); - if (currentState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { - logger.debug("Store as pendingRegistration [cluster has NOT_RECOVERED_BLOCK]"); - return false; - } - LicensesMetaData currentMetaData = currentState.metaData().custom(LicensesMetaData.TYPE); if (expiryDateForFeature(listenerHolder.feature, currentMetaData) == -1l) { // does not have any license so generate a trial license diff --git a/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java b/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java index 25adbf33c8b..98b8e6b5141 100644 --- a/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java +++ b/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java @@ -5,24 +5,51 @@ */ package org.elasticsearch.license; +import org.elasticsearch.common.joda.DateMathParser; +import org.elasticsearch.common.joda.FormatDateTimeFormatter; +import org.elasticsearch.common.joda.Joda; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.license.core.DateUtils; +import org.elasticsearch.license.core.ESLicense; +import org.elasticsearch.license.licensor.ESLicenseSigner; import org.elasticsearch.license.manager.ESLicenseManager; +import org.elasticsearch.test.ElasticsearchTestCase; import org.junit.BeforeClass; +import org.junit.runner.RunWith; import java.io.IOException; import java.net.URL; import java.text.ParseException; -import java.util.Map; +import java.util.*; +import java.util.concurrent.TimeUnit; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomRealisticUnicodeOfCodepointLengthBetween; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.test.ElasticsearchTestCase.randomFrom; +@RunWith(value = com.carrotsearch.randomizedtesting.RandomizedRunner.class) public class AbstractLicensingTestBase { protected static String pubKeyPath = null; protected static String priKeyPath = null; + private final FormatDateTimeFormatter formatDateTimeFormatter = Joda.forPattern("yyyy-MM-dd"); + private final org.elasticsearch.common.joda.time.format.DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer(); + private final DateMathParser dateMathParser = new DateMathParser(formatDateTimeFormatter, TimeUnit.MILLISECONDS); + @BeforeClass public static void setup() throws Exception { pubKeyPath = getResourcePath("/public.key"); priKeyPath = getResourcePath("/private.key"); + } + protected String dateMathString(String time, long now) { + return dateTimeFormatter.print(dateMathParser.parse(time, now)); + } + + protected long dateMath(String time, long now) { + return dateMathParser.parse(time, now); } public static String getTestPriKeyPath() throws Exception { @@ -38,8 +65,112 @@ public class AbstractLicensingTestBase { return url.toURI().getPath(); } - public String generateSignedLicenses(Map map) throws IOException, ParseException { - String licenseString = TestUtils.generateESLicenses(map); - return TestUtils.runLicenseGenerationTool(licenseString, pubKeyPath, priKeyPath); + + protected LicenseSpec generateRandomLicenseSpec() { + long now = System.currentTimeMillis(); + String issueDate = dateMathString("now", now); + String expiryDate = dateMathString("now+10d/d", now); + String uid = randomRealisticUnicodeOfCodepointLengthBetween(2, 10); + String feature = randomRealisticUnicodeOfCodepointLengthBetween(5, 15); + String issuer = randomRealisticUnicodeOfCodepointLengthBetween(5, 15); + String issuedTo = randomRealisticUnicodeOfCodepointLengthBetween(5, 15); + String type = randomFrom("subscription", "internal", "development"); + String subscriptionType = randomFrom("none", "gold", "silver", "platinum"); + int maxNodes = randomIntBetween(5, 100); + + return new LicenseSpec(uid, feature, issueDate, expiryDate, type, subscriptionType, issuedTo, issuer, maxNodes); + } + + public static String generateESLicenseSpecString(List licenseSpecs) throws IOException { + XContentBuilder licenses = jsonBuilder(); + licenses.startObject(); + licenses.startArray("licenses"); + for (LicenseSpec licenseSpec : licenseSpecs) { + licenses.startObject() + .field("uid", licenseSpec.uid) + .field("type", licenseSpec.type) + .field("subscription_type", licenseSpec.subscriptionType) + .field("issued_to", licenseSpec.issuedTo) + .field("issuer", licenseSpec.issuer) + .field("issue_date", licenseSpec.issueDate) + .field("expiry_date", licenseSpec.expiryDate) + .field("feature", licenseSpec.feature) + .field("max_nodes", licenseSpec.maxNodes) + .endObject(); + } + licenses.endArray(); + licenses.endObject(); + return licenses.string(); + } + + public static Set generateSignedLicenses(List licenseSpecs) throws Exception { + ESLicenseSigner signer = new ESLicenseSigner(getTestPriKeyPath(), getTestPubKeyPath()); + Set unSignedLicenses = new HashSet<>(); + for (LicenseSpec spec : licenseSpecs) { + unSignedLicenses.add(ESLicense.builder() + .uid(spec.uid) + .feature(spec.feature) + .expiryDate(DateUtils.endOfTheDay(spec.expiryDate)) + .issueDate(DateUtils.beginningOfTheDay(spec.issueDate)) + .type(spec.type) + .subscriptionType(spec.subscriptionType) + .issuedTo(spec.issuedTo) + .issuer(spec.issuer) + .maxNodes(spec.maxNodes) + .build() + ); + } + return signer.sign(unSignedLicenses); + } + + public static ESLicense generateSignedLicense(String feature, TimeValue expiryDuration) throws Exception { + return generateSignedLicense(feature, -1, expiryDuration); + } + + public static ESLicense generateSignedLicense(String feature, long issueDate, TimeValue expiryDuration) throws Exception { + long issue = (issueDate != -1l) ? issueDate : System.currentTimeMillis(); + final ESLicense licenseSpec = ESLicense.builder() + .uid(UUID.randomUUID().toString()) + .feature(feature) + .expiryDate(issue + expiryDuration.getMillis()) + .issueDate(issue) + .type("subscription") + .subscriptionType("gold") + .issuedTo("customer") + .issuer("elasticsearch") + .maxNodes(5) + .build(); + + ESLicenseSigner signer = new ESLicenseSigner(getTestPriKeyPath(), getTestPubKeyPath()); + return signer.sign(licenseSpec); + } + + public static class LicenseSpec { + public final String feature; + public final String issueDate; + public final String expiryDate; + public final String uid; + public final String type; + public final String subscriptionType; + public final String issuedTo; + public final String issuer; + public final int maxNodes; + + public LicenseSpec(String feature, String issueDate, String expiryDate) { + this(UUID.randomUUID().toString(), feature, issueDate, expiryDate, "trial", "none", "customer", "elasticsearch", 5); + } + + public LicenseSpec(String uid, String feature, String issueDate, String expiryDate, String type, + String subscriptionType, String issuedTo, String issuer, int maxNodes) { + this.feature = feature; + this.issueDate = issueDate; + this.expiryDate = expiryDate; + this.uid = uid; + this.type = type; + this.subscriptionType = subscriptionType; + this.issuedTo = issuedTo; + this.issuer = issuer; + this.maxNodes = maxNodes; + } } } diff --git a/src/test/java/org/elasticsearch/license/TestUtils.java b/src/test/java/org/elasticsearch/license/TestUtils.java index 313759227a9..535734cdeb3 100644 --- a/src/test/java/org/elasticsearch/license/TestUtils.java +++ b/src/test/java/org/elasticsearch/license/TestUtils.java @@ -5,96 +5,15 @@ */ package org.elasticsearch.license; -import org.apache.commons.io.FileUtils; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.license.core.DateUtils; import org.elasticsearch.license.core.ESLicense; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.licensor.tools.LicenseGeneratorTool; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.ParseException; import java.util.*; -import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; public class TestUtils { - public final static String SHIELD = "shield"; - public final static String MARVEL = "marvel"; - - public static String generateESLicenses(Map featureAttributes) throws IOException { - XContentBuilder licenses = jsonBuilder(); - licenses.startObject(); - licenses.startArray("licenses"); - for (FeatureAttributes attributes : featureAttributes.values()) { - licenses.startObject() - .field("uid", attributes.uid) - .field("type", attributes.type) - .field("subscription_type", attributes.subscriptionType) - .field("issued_to", attributes.issuedTo) - .field("issuer", attributes.issuer) - .field("issue_date", attributes.issueDate) - .field("expiry_date", attributes.expiryDate) - .field("feature", attributes.featureType) - .field("max_nodes", attributes.maxNodes) - .endObject(); - } - licenses.endArray(); - licenses.endObject(); - return licenses.string(); - - } - - public static String runLicenseGenerationTool(String licenseInput, String pubKeyPath, String priKeyPath) throws IOException, ParseException { - String args[] = new String[6]; - args[0] = "--license"; - args[1] = licenseInput; - args[2] = "--publicKeyPath"; - args[3] = pubKeyPath; - args[4] = "--privateKeyPath"; - args[5] = priKeyPath; - - return runLicenseGenerationTool(args); - } - - public static String runLicenseGenerationTool(String[] args) throws IOException, ParseException { - File temp = File.createTempFile("temp", ".out"); - temp.deleteOnExit(); - try (FileOutputStream outputStream = new FileOutputStream(temp)) { - LicenseGeneratorTool.run(args, outputStream); - } - return FileUtils.readFileToString(temp); - } - - public static void verifyESLicenses(Set esLicenses, Map featureAttributesMap) throws ParseException { - verifyESLicenses(ESLicenses.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 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(), esLicense.type().equals(attributes.type)); - assertTrue("expected value for subscriptionType was: " + attributes.subscriptionType + " but got: " + esLicense.subscriptionType(), esLicense.subscriptionType().equals(attributes.subscriptionType)); - assertTrue("expected value for feature was: " + attributes.featureType + " but got: " + esLicense.feature(), esLicense.feature().equals(attributes.featureType)); - assertTrue("expected value for issueDate was: " + DateUtils.beginningOfTheDay(attributes.issueDate) + " but got: " + esLicense.issueDate(), esLicense.issueDate() == DateUtils.beginningOfTheDay(attributes.issueDate)); - assertTrue("expected value for expiryDate: " + DateUtils.endOfTheDay(attributes.expiryDate) + " but got: " + esLicense.expiryDate(), esLicense.expiryDate() == DateUtils.endOfTheDay(attributes.expiryDate)); - assertTrue("expected value for maxNodes: " + attributes.maxNodes + " but got: " + esLicense.maxNodes(), esLicense.maxNodes() == attributes.maxNodes); - - assertTrue("generated licenses should have non-null uid field", esLicense.uid() != null); - assertTrue("generated licenses should have non-null signature field", esLicense.signature() != null); - } - } - public static void isSame(Collection firstLicenses, Collection secondLicenses) { isSame(new HashSet<>(firstLicenses), new HashSet<>(secondLicenses)); } @@ -106,58 +25,25 @@ public class TestUtils { final Map licenses2 = ESLicenses.reduceAndMap(secondLicenses); // check if the effective licenses have the same feature set - assertTrue("Both licenses should have the same number of features", licenses1.size() == licenses2.size()); - + assertThat(licenses1.size(), equalTo(licenses2.size())); // for every feature license, check if all the attributes are the same for (String featureType : licenses1.keySet()) { ESLicense license1 = licenses1.get(featureType); ESLicense license2 = licenses2.get(featureType); - isSame(license1, 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())); - assertTrue("Should have same subscriptType; got: " + license1.subscriptionType() + " and " + license2.subscriptionType(), license1.subscriptionType().equals(license2.subscriptionType())); - assertTrue("Should have same type; got: " + license1.type() + " and " + license2.type(), license1.type().equals(license2.type())); - assertTrue("Should have same issuedTo; got: " + license1.issuedTo() + " and " + license2.issuedTo(), license1.issuedTo().equals(license2.issuedTo())); - assertTrue("Should have same signature; got: " + license1.signature() + " and " + license2.signature(), license1.signature().equals(license2.signature())); - assertTrue("Should have same expiryDate; got: " + license1.expiryDate() + " and " + license2.expiryDate(), license1.expiryDate() == license2.expiryDate()); - assertTrue("Should have same issueDate; got: " + license1.issueDate() + " and " + license2.issueDate(), license1.issueDate() == license2.issueDate()); - assertTrue("Should have same maxNodes; got: " + license1.maxNodes() + " and " + license2.maxNodes(), license1.maxNodes() == license2.maxNodes()); - } - - public static class FeatureAttributes { - - public final String uid; - public final String featureType; - public final String type; - public final String subscriptionType; - public final String issuedTo; - public final int maxNodes; - public final String issueDate; - public final String expiryDate; - public final String issuer; - - public FeatureAttributes(String featureType, String type, String subscriptionType, String issuedTo, String issuer, int maxNodes, String issueDateStr, String expiryDateStr) throws ParseException { - this(UUID.randomUUID().toString(), featureType, type, subscriptionType, issuedTo, issuer, maxNodes, issueDateStr, expiryDateStr); - } - - public FeatureAttributes(String uid, String featureType, String type, String subscriptionType, String issuedTo, String issuer, int maxNodes, String issueDateStr, String expiryDateStr) throws ParseException { - this.uid = uid; - this.featureType = featureType; - this.type = type; - this.subscriptionType = subscriptionType; - this.issuedTo = issuedTo; - this.issuer = issuer; - this.maxNodes = maxNodes; - this.issueDate = issueDateStr; - this.expiryDate = expiryDateStr; - } + assertThat(license1.uid(), equalTo(license2.uid())); + assertThat(license1.feature(), equalTo(license2.feature())); + assertThat(license1.subscriptionType(), equalTo(license2.subscriptionType())); + assertThat(license1.type(), equalTo(license2.type())); + assertThat(license1.issuedTo(), equalTo(license2.issuedTo())); + assertThat(license1.signature(), equalTo(license2.signature())); + assertThat(license1.expiryDate(), equalTo(license2.expiryDate())); + assertThat(license1.issueDate(), equalTo(license2.issueDate())); + assertThat(license1.maxNodes(), equalTo(license2.maxNodes())); } } diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java deleted file mode 100644 index c1c1f1e3814..00000000000 --- a/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationTests.java +++ /dev/null @@ -1,80 +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.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.junit.Test; - -import java.io.IOException; -import java.text.ParseException; -import java.util.*; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -public class LicenseGenerationTests extends AbstractLicensingTestBase { - - @Test - public void testSimpleLicenseGeneration() throws ParseException, IOException { - Map map = new HashMap<>(); - TestUtils.FeatureAttributes featureAttributes = - new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-12-13"); - map.put(TestUtils.SHIELD, featureAttributes); - - String licenseOutput = generateSignedLicenses(map); - - Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(licenseOutput)); - - TestUtils.verifyESLicenses(esLicensesOutput, map); - } - - @Test - public void testMultipleStrings() throws ParseException, IOException { - - Map map = new HashMap<>(); - TestUtils.FeatureAttributes shildFeatureAttributes = - new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-12-13"); - TestUtils.FeatureAttributes marvelFeatureAttributes = - new TestUtils.FeatureAttributes("marvel", "subscription", "silver", "foo1 bar Inc.", "elasticsearc3h", 10, "2014-01-13", "2014-12-13"); - map.put(TestUtils.SHIELD, shildFeatureAttributes); - map.put(TestUtils.MARVEL, marvelFeatureAttributes); - - String licenseOutput = generateSignedLicenses(map); - - Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(licenseOutput)); - - TestUtils.verifyESLicenses(esLicensesOutput, map); - } - - @Test - public void testMissingCLTArgs() throws ParseException, IOException { - - Map map = new HashMap<>(); - TestUtils.FeatureAttributes featureAttributes = - new TestUtils.FeatureAttributes("shiedgdsld", "internal", "none", "foo bar Inc.", "elasticsearch", 23, "2014-12-13", "2015-12-13"); - map.put(TestUtils.SHIELD, featureAttributes); - - String licenseString = TestUtils.generateESLicenses(map); - - String[] args = new String[6]; - args[0] = "--linse"; - args[1] = licenseString; - args[2] = "--publicKeyPath"; - args[3] = pubKeyPath; - args[4] = "--privateKeyPath"; - args[5] = priKeyPath; - - try { - String licenseOutput = TestUtils.runLicenseGenerationTool(args); - fail(); - } catch (IllegalArgumentException e) { - assertTrue("Exception should indicate mandatory param --license, got: " + e.getMessage(), e.getMessage().contains("license")); - } - } -} diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationToolTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationToolTests.java new file mode 100644 index 00000000000..64e79765727 --- /dev/null +++ b/src/test/java/org/elasticsearch/license/licensor/LicenseGenerationToolTests.java @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.licensor; + +import org.apache.commons.io.FileUtils; +import org.elasticsearch.license.AbstractLicensingTestBase; +import org.elasticsearch.license.TestUtils; +import org.elasticsearch.license.core.ESLicense; +import org.elasticsearch.license.core.ESLicenses; +import org.elasticsearch.license.licensor.tools.LicenseGeneratorTool; +import org.junit.Test; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.ParseException; +import java.util.*; + +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiOfLength; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.*; + +public class LicenseGenerationToolTests extends AbstractLicensingTestBase { + + @Test + public void testSimple() throws Exception { + LicenseSpec inputLicenseSpec = generateRandomLicenseSpec(); + String[] args = new String[6]; + args[0] = "--license"; + args[1] = generateESLicenseSpecString(Arrays.asList(inputLicenseSpec)); + args[2] = "--publicKeyPath"; + args[3] = pubKeyPath; + args[4] = "--privateKeyPath"; + args[5] = priKeyPath; + + String licenseOutput = runLicenseGenerationTool(args); + List outputLicenses = ESLicenses.fromSource(licenseOutput); + assertThat(outputLicenses.size(), equalTo(1)); + assertThat(outputLicenses.get(0).signature(), notNullValue()); + + Set expectedLicenses = generateSignedLicenses(Arrays.asList(inputLicenseSpec)); + ESLicense expectedLicense = ESLicense.builder() + .fromLicenseSpec(expectedLicenses.iterator().next(), outputLicenses.get(0).signature()) + .build(); + + TestUtils.isSame(expectedLicense, outputLicenses.get(0)); + } + + @Test + public void testWithLicenseFile() throws Exception { + LicenseSpec inputLicenseSpec = generateRandomLicenseSpec(); + + Path tempFilePath = Files.createTempFile("license_spec", "json"); + File tempFile = tempFilePath.toFile(); + FileUtils.write(tempFile, generateESLicenseSpecString(Arrays.asList(inputLicenseSpec))); + tempFile.deleteOnExit(); + + String[] args = new String[6]; + args[0] = "--licenseFile"; + args[1] = tempFile.getAbsolutePath(); + args[2] = "--publicKeyPath"; + args[3] = pubKeyPath; + args[4] = "--privateKeyPath"; + args[5] = priKeyPath; + + String licenseOutput = runLicenseGenerationTool(args); + List outputLicenses = ESLicenses.fromSource(licenseOutput); + assertThat(outputLicenses.size(), equalTo(1)); + assertThat(outputLicenses.get(0).signature(), notNullValue()); + + Set expectedLicenses = generateSignedLicenses(Arrays.asList(inputLicenseSpec)); + ESLicense expectedLicense = ESLicense.builder() + .fromLicenseSpec(expectedLicenses.iterator().next(), outputLicenses.get(0).signature()) + .build(); + + TestUtils.isSame(expectedLicense, outputLicenses.get(0)); + } + + @Test + public void testBadKeyPath() throws Exception { + boolean pubKey = randomBoolean(); + + String[] args = new String[6]; + args[0] = "--license"; + args[1] = generateESLicenseSpecString(Arrays.asList(generateRandomLicenseSpec())); + args[2] = "--publicKeyPath"; + args[3] = (pubKey) ? pubKeyPath + randomAsciiOfLength(3) : pubKeyPath; + args[4] = "--privateKeyPath"; + args[5] = (!pubKey) ? priKeyPath + randomAsciiOfLength(3) : priKeyPath; + + try { + runLicenseGenerationTool(args); + fail("Should not accept non-existent key paths"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("does not exist")); + } + + } + + @Test + public void testMissingCLTArgs() throws Exception { + String[] args = new String[6]; + args[0] = "--linse"; + args[1] = generateESLicenseSpecString(Arrays.asList(generateRandomLicenseSpec())); + args[2] = "--publicKeyPath"; + args[3] = pubKeyPath; + args[4] = "--privateKeyPath"; + args[5] = priKeyPath; + + try { + runLicenseGenerationTool(args); + fail("should not accept arguments without --license"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("'--license'")); + } + } + + private static String runLicenseGenerationTool(String[] args) throws Exception { + File temp = File.createTempFile("temp", ".out"); + temp.deleteOnExit(); + try (FileOutputStream outputStream = new FileOutputStream(temp)) { + LicenseGeneratorTool.run(args, outputStream); + } + return FileUtils.readFileToString(temp); + } +} diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseSerializationTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseSerializationTests.java new file mode 100644 index 00000000000..d6c16787721 --- /dev/null +++ b/src/test/java/org/elasticsearch/license/licensor/LicenseSerializationTests.java @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.licensor; + +import org.elasticsearch.license.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.LicensesCharset; +import org.hamcrest.core.IsNull; +import org.junit.Test; + +import java.io.IOException; +import java.text.ParseException; +import java.util.*; + +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class LicenseSerializationTests extends AbstractLicensingTestBase { + + @Test + public void testSimpleIssueExpiryDate() throws Exception { + long now = System.currentTimeMillis(); + String issueDate = dateMathString("now", now); + String expiryDate = dateMathString("now+10d/d", now); + String licenseSpecs = generateESLicenseSpecString(Arrays.asList(new LicenseSpec("shield", issueDate, expiryDate))); + Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(licenseSpecs.getBytes(LicensesCharset.UTF_8), false)); + ESLicense generatedLicense = esLicensesOutput.iterator().next(); + + assertThat(esLicensesOutput.size(), equalTo(1)); + assertThat(generatedLicense.issueDate(), equalTo(DateUtils.beginningOfTheDay(issueDate))); + assertThat(generatedLicense.expiryDate(), equalTo(DateUtils.endOfTheDay(expiryDate))); + } + + @Test + public void testMultipleIssueExpiryDate() throws Exception { + long now = System.currentTimeMillis(); + String shieldIssueDate = dateMathString("now", now); + String shieldExpiryDate = dateMathString("now+30d/d", now); + String marvelIssueDate = dateMathString("now", now); + String marvelExpiryDate = dateMathString("now+60d/d", now); + String licenseSpecs = generateESLicenseSpecString(Arrays.asList(new LicenseSpec("shield", shieldIssueDate, shieldExpiryDate))); + String licenseSpecs1 = generateESLicenseSpecString(Arrays.asList(new LicenseSpec("marvel", marvelIssueDate, marvelExpiryDate))); + Set esLicensesOutput = new HashSet<>(); + esLicensesOutput.addAll(ESLicenses.fromSource(licenseSpecs.getBytes(LicensesCharset.UTF_8), false)); + esLicensesOutput.addAll(ESLicenses.fromSource(licenseSpecs1.getBytes(LicensesCharset.UTF_8), false)); + assertThat(esLicensesOutput.size(), equalTo(2)); + for (ESLicense esLicense : esLicensesOutput) { + assertThat(esLicense.issueDate(), equalTo(DateUtils.beginningOfTheDay((esLicense.feature().equals("shield")) ? shieldIssueDate : marvelIssueDate))); + assertThat(esLicense.expiryDate(), equalTo(DateUtils.endOfTheDay((esLicense.feature().equals("shield")) ? shieldExpiryDate : marvelExpiryDate))); + } + } + + @Test + public void testLicensesFields() throws Exception { + Map licenseSpecs = new HashMap<>(); + for (int i = 0; i < randomIntBetween(1, 5); i++) { + LicenseSpec randomLicenseSpec = generateRandomLicenseSpec(); + licenseSpecs.put(randomLicenseSpec.feature, randomLicenseSpec); + } + + ArrayList specs = new ArrayList<>(licenseSpecs.values()); + String licenseSpecsSource = generateESLicenseSpecString(specs); + Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(licenseSpecsSource.getBytes(LicensesCharset.UTF_8), false)); + assertThat(esLicensesOutput.size(), equalTo(licenseSpecs.size())); + + for (ESLicense license : esLicensesOutput) { + LicenseSpec spec = licenseSpecs.get(license.feature()); + assertThat(spec, notNullValue()); + + assertThat(license.uid(), equalTo(spec.uid)); + assertThat(license.feature(), equalTo(spec.feature)); + assertThat(license.issuedTo(), equalTo(spec.issuedTo)); + assertThat(license.issuer(), equalTo(spec.issuer)); + assertThat(license.type(), equalTo(spec.type)); + assertThat(license.subscriptionType(), equalTo(spec.subscriptionType)); + assertThat(license.maxNodes(), equalTo(spec.maxNodes)); + assertThat(license.issueDate(), equalTo(DateUtils.beginningOfTheDay(spec.issueDate))); + assertThat(license.expiryDate(), equalTo(DateUtils.endOfTheDay(spec.expiryDate))); + } + + } + + +} diff --git a/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java b/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java index 66b66a70df5..8151f7bd093 100644 --- a/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java +++ b/src/test/java/org/elasticsearch/license/licensor/LicenseVerificationToolTests.java @@ -6,6 +6,11 @@ package org.elasticsearch.license.licensor; import org.apache.commons.io.FileUtils; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.license.AbstractLicensingTestBase; import org.elasticsearch.license.TestUtils; import org.elasticsearch.license.core.ESLicense; @@ -16,113 +21,112 @@ 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.HashSet; -import java.util.Map; -import java.util.Set; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomRealisticUnicodeOfCodepointLengthBetween; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; public class LicenseVerificationToolTests extends AbstractLicensingTestBase { @Test - public void testEffectiveLicenseGeneration() throws Exception { - Map map = new HashMap<>(); - TestUtils.FeatureAttributes featureWithLongerExpiryDate = - new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-12-13"); - map.put(TestUtils.SHIELD, featureWithLongerExpiryDate); + public void testMissingCLTArgs() throws Exception { + ESLicense singedLicense = generateSignedLicense(randomRealisticUnicodeOfCodepointLengthBetween(5, 15), + TimeValue.timeValueHours(1)); - String signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map)); - String firstLicenseFile = getAsFilePath(signedLicense); + String[] args = new String[2]; + args[0] = "--licenssFiles"; + args[1] = dumpLicense(singedLicense); - TestUtils.FeatureAttributes featureWithShorterExpiryDate = - new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-01-13"); - map.put(TestUtils.SHIELD, featureWithShorterExpiryDate); - - signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map)); - String secondLicenseFile = getAsFilePath(signedLicense); - - String effectiveLicenseStr = runLicenseVerificationTool(new String[]{firstLicenseFile, secondLicenseFile}); - - Set esLicensesOutput = new HashSet<>(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(esLicensesOutput, map); + try { + runLicenseVerificationTool(args); + fail("mandatory param '--licensesFiles' should throw an exception"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), containsString("--licensesFiles")); + } } @Test - public void testEffectiveLicenseForMultiFeatures() throws Exception { - Map map = new HashMap<>(); - TestUtils.FeatureAttributes shieldFeatureWithLongerExpiryDate = - new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-12-13"); - map.put(TestUtils.SHIELD, shieldFeatureWithLongerExpiryDate); + public void testSimple() throws Exception { + ESLicense singedLicense = generateSignedLicense(randomRealisticUnicodeOfCodepointLengthBetween(5, 15), + TimeValue.timeValueHours(1)); - String signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map)); - String firstLicenseFile = getAsFilePath(signedLicense); + String[] args = new String[2]; + args[0] = "--licensesFiles"; + args[1] = dumpLicense(singedLicense); - TestUtils.FeatureAttributes marvelFeatureWithShorterExpiryDate = - new TestUtils.FeatureAttributes("marvel", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-01-13"); - map.put(TestUtils.MARVEL, marvelFeatureWithShorterExpiryDate); + String licenseOutput = runLicenseVerificationTool(args); + List licensesOutput = ESLicenses.fromSource(licenseOutput); - signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map)); - String secondLicenseFile = getAsFilePath(signedLicense); + assertThat(licensesOutput.size(), equalTo(1)); - String effectiveLicenseStr = runLicenseVerificationTool(new String[]{firstLicenseFile, secondLicenseFile}); - Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(effectiveLicenseStr)); + ESLicense expectedLicense = ESLicense.builder() + .fromLicenseSpec(singedLicense, licensesOutput.get(0).signature()) + .build(); - // verify that the effective license contains both feature licenses - TestUtils.verifyESLicenses(esLicensesOutput, map); + TestUtils.isSame(expectedLicense, licensesOutput.get(0)); } @Test - public void testEffectiveLicenseForMultiFeatures2() throws Exception { - Map map = new HashMap<>(); + public void testWithLicenseFiles() throws Exception { + int n = randomIntBetween(3, 10); + Set signedLicenses = new HashSet<>(); + for (int i = 0; i < n; i++) { + signedLicenses.add(generateSignedLicense(randomRealisticUnicodeOfCodepointLengthBetween(5, 15), + TimeValue.timeValueHours(1))); + } - TestUtils.FeatureAttributes shieldFeatureWithLongerExpiryDate = - new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-12-13"); - TestUtils.FeatureAttributes marvelFeatureWithShorterExpiryDate = - new TestUtils.FeatureAttributes("marvel", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-01-13"); - - map.put(TestUtils.SHIELD, shieldFeatureWithLongerExpiryDate); - map.put(TestUtils.MARVEL, marvelFeatureWithShorterExpiryDate); - - String signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map)); - String firstLicenseFile = getAsFilePath(signedLicense); - - TestUtils.FeatureAttributes shieldFeatureWithShorterExpiryDate = - new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 10, "2014-12-13", "2015-11-13"); - TestUtils.FeatureAttributes marvelFeatureWithLongerExpiryDate = - new TestUtils.FeatureAttributes("marvel", "trial", "none", "foo bar Inc.", "elasticsearch", 2, "2014-12-13", "2015-11-13"); - - map.put(TestUtils.SHIELD, shieldFeatureWithShorterExpiryDate); - map.put(TestUtils.MARVEL, marvelFeatureWithLongerExpiryDate); - - signedLicense = runLicenseGenerationTool(TestUtils.generateESLicenses(map)); - String secondLicenseFile = getAsFilePath(signedLicense); - - String effectiveLicenseStr = runLicenseVerificationTool(new String[]{firstLicenseFile, secondLicenseFile}); - Set esLicensesOutput = new HashSet<>(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(esLicensesOutput, map); - } - - public static String runLicenseVerificationTool(String[] licenseFiles) throws IOException { StringBuilder licenseFilePathString = new StringBuilder(); - for (int i = 0; i < licenseFiles.length; i++) { - licenseFilePathString.append(licenseFiles[i]); - if (i != licenseFiles.length - 1) { + ESLicense[] esLicenses = signedLicenses.toArray(new ESLicense[n]); + for (int i = 0; i < n; i++) { + licenseFilePathString.append(dumpLicense(esLicenses[i])); + if (i != esLicenses.length - 1) { licenseFilePathString.append(":"); } } - String[] args = new String[4]; + + String[] args = new String[2]; args[0] = "--licensesFiles"; args[1] = licenseFilePathString.toString(); - args[2] = "--publicKeyPath"; - args[3] = pubKeyPath; + + String licenseOutput = runLicenseVerificationTool(args); + List output = ESLicenses.fromSource(licenseOutput); + + assertThat(output.size(), equalTo(n)); + + Set licensesOutput = new HashSet<>(); + Map expectedLicenses = ESLicenses.reduceAndMap(signedLicenses); + for (ESLicense license : output) { + licensesOutput.add( + ESLicense.builder() + .fromLicenseSpec(license, expectedLicenses.get(license.feature()).signature()) + .build() + ); + } + + TestUtils.isSame(signedLicenses, licensesOutput); + + } + + private String dumpLicense(ESLicense license) throws Exception { + Path tempFilePath = Files.createTempFile("license_spec", "json"); + File tempFile = tempFilePath.toFile(); + tempFile.deleteOnExit(); + try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { + XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON, outputStream); + ESLicenses.toXContent(Collections.singletonList(license), builder, ToXContent.EMPTY_PARAMS); + builder.flush(); + } + return tempFile.getAbsolutePath(); + } + + + private static String runLicenseVerificationTool(String[] args) throws IOException { File temp = File.createTempFile("temp", ".out"); temp.deleteOnExit(); try (FileOutputStream outputStream = new FileOutputStream(temp)) { @@ -130,21 +134,4 @@ public class LicenseVerificationToolTests extends AbstractLicensingTestBase { } return FileUtils.readFileToString(temp); } - - public String runLicenseGenerationTool(String licenseInput) throws IOException, ParseException { - return TestUtils.runLicenseGenerationTool(licenseInput, pubKeyPath, priKeyPath); - } - - private static String getAsFilePath(String content) throws IOException { - File temp = File.createTempFile("license", ".out"); - temp.deleteOnExit(); - FileUtils.write(temp, content); - String tempFilePath = temp.getAbsolutePath(); - while (tempFilePath.contains(":")) { - assert temp.delete(); - tempFilePath = getAsFilePath(content); - } - return tempFilePath; - } - } diff --git a/src/test/java/org/elasticsearch/license/manager/LicenseSignatureTest.java b/src/test/java/org/elasticsearch/license/manager/LicenseSignatureTest.java new file mode 100644 index 00000000000..d212d38a599 --- /dev/null +++ b/src/test/java/org/elasticsearch/license/manager/LicenseSignatureTest.java @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.manager; + +import org.elasticsearch.license.AbstractLicensingTestBase; +import org.elasticsearch.license.TestUtils; +import org.elasticsearch.license.core.ESLicense; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.*; + +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.*; + +public class LicenseSignatureTest extends AbstractLicensingTestBase { + + private static ESLicenseManager esLicenseManager; + + @BeforeClass + public static void setupManager() { + esLicenseManager = new ESLicenseManager(); + } + + @Test + public void testLicenseGeneration() throws Exception { + int n = randomIntBetween(5, 15); + List licenseSpecs = new ArrayList<>(); + for (int i = 0; i < n; i++) { + licenseSpecs.add(generateRandomLicenseSpec()); + } + + Set generatedLicenses = generateSignedLicenses(licenseSpecs); + assertThat(generatedLicenses.size(), equalTo(n)); + + Set signatures = new HashSet<>(); + for (ESLicense license : generatedLicenses) { + signatures.add(license.signature()); + } + Set licenseFromSignatures = esLicenseManager.fromSignatures(signatures); + + TestUtils.isSame(generatedLicenses, licenseFromSignatures); + } +} diff --git a/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java index 65e0d764a01..a829bebde32 100644 --- a/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java +++ b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java @@ -6,137 +6,71 @@ package org.elasticsearch.license.manager; import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException; -import org.elasticsearch.common.joda.DateMathParser; -import org.elasticsearch.common.joda.FormatDateTimeFormatter; -import org.elasticsearch.common.joda.Joda; +import org.elasticsearch.common.unit.TimeValue; 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.licensor.tools.FileBasedESLicenseProvider; -import org.junit.After; import org.junit.BeforeClass; import org.junit.Test; -import java.text.ParseException; import java.util.*; -import java.util.concurrent.TimeUnit; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.*; public class LicenseVerificationTests extends AbstractLicensingTestBase { private static ESLicenseManager esLicenseManager; - private static FileBasedESLicenseProvider esLicenseProvider; - - private final static Set EMPTY_LICENSES = new HashSet<>(); - - private final FormatDateTimeFormatter formatDateTimeFormatter = Joda.forPattern("yyyy-MM-dd"); - - private final org.elasticsearch.common.joda.time.format.DateTimeFormatter dateTimeFormatter = formatDateTimeFormatter.printer(); - - private final DateMathParser dateMathParser = new DateMathParser(formatDateTimeFormatter, TimeUnit.MILLISECONDS); - @BeforeClass public static void setupManager() { - esLicenseProvider = new FileBasedESLicenseProvider(EMPTY_LICENSES); esLicenseManager = new ESLicenseManager(); - - } - - @After - public void clearManager() { - esLicenseProvider.setLicenses(EMPTY_LICENSES); - } - - private String dateMathString(String time, long now) { - return dateTimeFormatter.print(dateMathParser.parse(time, now)); } @Test public void testGeneratedLicenses() throws Exception { - long now = System.currentTimeMillis(); - String issueDateStr = dateMathString("now/d", now); - String expiryDateStr = dateMathString("now+2d/d", now); - Map map = new HashMap<>(); - TestUtils.FeatureAttributes featureAttributes = - new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr); - map.put(TestUtils.SHIELD, featureAttributes); - - Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(generateSignedLicenses(map))); - - esLicenseProvider.setLicenses(esLicensesOutput); - - esLicenseManager.verifyLicenses(esLicenseProvider.getEffectiveLicenses()); - - verifyLicenses(esLicenseProvider, map); - + ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24)); + Map shieldLicenseMap = new HashMap<>(); + shieldLicenseMap.put("shield", shieldLicense); + esLicenseManager.verifyLicenses(shieldLicenseMap); } @Test public void testMultipleFeatureLicenses() throws Exception { - long now = System.currentTimeMillis(); - String issueDateStr = dateMathString("now/d", now); - String expiryDateStr = dateMathString("now+2d/d", now); - - Map map = new HashMap<>(); - TestUtils.FeatureAttributes shildFeatureAttributes = - new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr); - TestUtils.FeatureAttributes marvelFeatureAttributes = - new TestUtils.FeatureAttributes("marvel", "subscription", "silver", "foo1 bar Inc.", "elasticsearc3h", 10, issueDateStr, expiryDateStr); - map.put(TestUtils.SHIELD, shildFeatureAttributes); - map.put(TestUtils.MARVEL, marvelFeatureAttributes); - - Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(generateSignedLicenses(map))); - - esLicenseProvider.setLicenses(esLicensesOutput); - - esLicenseManager.verifyLicenses(esLicenseProvider.getEffectiveLicenses()); - - verifyLicenses(esLicenseProvider, map); + ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24)); + ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2 * 24)); + Map licenseMap = new HashMap<>(); + licenseMap.put("shield", shieldLicense); + licenseMap.put("marvel", marvelLicense); + esLicenseManager.verifyLicenses(licenseMap); } @Test public void testLicenseExpiry() throws Exception { long now = System.currentTimeMillis(); - String issueDateStr = dateMathString("now-60d/d", now); - String expiryDateStr = dateMathString("now+30d/d", now); - String expiredExpiryDateStr = dateMathString("now-10d/d", now); + long marvelIssueDate = dateMath("now-10d/d", now); - Map map = new HashMap<>(); - TestUtils.FeatureAttributes shildFeatureAttributes = - new TestUtils.FeatureAttributes("shield", "trial", "none", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr); - TestUtils.FeatureAttributes marvelFeatureAttributes = - new TestUtils.FeatureAttributes("marvel", "internal", "silver", "foo1 bar Inc.", "elasticsearc3h", 10, issueDateStr, expiredExpiryDateStr); - map.put(TestUtils.SHIELD, shildFeatureAttributes); - map.put(TestUtils.MARVEL, marvelFeatureAttributes); + ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24)); + ESLicense marvelLicense = generateSignedLicense("marvel", marvelIssueDate, TimeValue.timeValueHours(2 * 24)); + Map licenseMap = new HashMap<>(); + licenseMap.put("shield", shieldLicense); + licenseMap.put("marvel", marvelLicense); - Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(generateSignedLicenses(map))); - - esLicenseProvider.setLicenses(esLicensesOutput); - - // All validation for shield license should be normal as expected - - verifyLicenses(esLicenseProvider, Collections.singletonMap(TestUtils.SHIELD, shildFeatureAttributes)); + try { + esLicenseManager.verifyLicenses(licenseMap); + fail("verifyLicenses should throw InvalidLicenseException [expired license]"); + } catch (InvalidLicenseException e) { + assertThat(e.getMessage(), containsString("Expired License")); + } + licenseMap.clear(); + licenseMap.put("shield", shieldLicense); + esLicenseManager.verifyLicenses(licenseMap); } @Test public void testLicenseTampering() throws Exception { - long now = System.currentTimeMillis(); - String issueDateStr = dateMathString("now/d", now); - String expiryDateStr = dateMathString("now+2d/d", now); - Map map = new HashMap<>(); - TestUtils.FeatureAttributes featureAttributes = - new TestUtils.FeatureAttributes("shield", "subscription", "platinum", "foo bar Inc.", "elasticsearch", 2, issueDateStr, expiryDateStr); - map.put(TestUtils.SHIELD, featureAttributes); - - Set esLicensesOutput = new HashSet<>(ESLicenses.fromSource(generateSignedLicenses(map))); - - ESLicense esLicense = ESLicenses.reduceAndMap(esLicensesOutput).get(TestUtils.SHIELD); + ESLicense esLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2)); final ESLicense tamperedLicense = ESLicense.builder() .fromLicenseSpec(esLicense, esLicense.signature()) @@ -144,31 +78,14 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { .verify() .build(); + Map licenseMap = new HashMap<>(); + licenseMap.put("shield", tamperedLicense); + try { - esLicenseProvider.setLicenses(Collections.singleton(tamperedLicense)); - esLicenseManager.verifyLicenses(esLicenseProvider.getEffectiveLicenses()); - fail(); + esLicenseManager.verifyLicenses(licenseMap); + fail("Tampered license should throw exception"); } catch (InvalidLicenseException e) { - assertTrue("Exception should contain 'Invalid License' but got: " + e.getMessage(), e.getMessage().contains("Invalid License")); - } - } - - // TODO: move to TestUtils - public static void verifyLicenses(FileBasedESLicenseProvider licenseProvider, Map featureAttributeMap) throws ParseException { - - for (Map.Entry entry : featureAttributeMap.entrySet()) { - TestUtils.FeatureAttributes featureAttributes = entry.getValue(); - String featureType = entry.getKey(); - 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.beginningOfTheDay(featureAttributes.issueDate), license.issueDate() == DateUtils.beginningOfTheDay(featureAttributes.issueDate)); - assertTrue("License should have expiry date of " + DateUtils.endOfTheDay(featureAttributes.expiryDate) + " got: " + license.expiryDate(), license.expiryDate() == DateUtils.endOfTheDay(featureAttributes.expiryDate)); - assertTrue("License should have type of " + featureAttributes.type + " got: " + license.type(), license.type().equals(featureAttributes.type)); - assertTrue("License should have subscription type of " + featureAttributes.subscriptionType, license.subscriptionType().equals(featureAttributes.subscriptionType)); - - - assertTrue("License should be valid for maxNodes = " + (featureAttributes.maxNodes), license.maxNodes() == featureAttributes.maxNodes); + assertThat(e.getMessage(), containsString("Invalid License")); } } } diff --git a/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java b/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java index 454a8e92bd1..b08b622d882 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicenseTransportTests.java @@ -6,7 +6,6 @@ package org.elasticsearch.license.plugin; import org.elasticsearch.action.ActionFuture; -import org.elasticsearch.common.collect.Lists; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.license.TestUtils; import org.elasticsearch.license.core.ESLicense; @@ -44,7 +43,7 @@ public class LicenseTransportTests extends AbstractLicensesIntegrationTests { @Test public void testPutLicense() throws Exception { - ESLicense signedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2)); + ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2)); List actualLicenses = Collections.singletonList(signedLicense); // put license @@ -64,7 +63,7 @@ public class LicenseTransportTests extends AbstractLicensesIntegrationTests { @Test public void testPutInvalidLicense() throws Exception { - ESLicense signedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2)); + ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2)); // modify content of signed license ESLicense tamperedLicense = ESLicense.builder() @@ -88,8 +87,8 @@ public class LicenseTransportTests extends AbstractLicensesIntegrationTests { @Test public void testPutLicensesForSameFeature() throws Exception { - ESLicense shortedSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2)); - ESLicense longerSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(5)); + ESLicense shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2)); + ESLicense longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5)); List actualLicenses = Arrays.asList(longerSignedLicense, shortedSignedLicense); // put license @@ -109,8 +108,8 @@ public class LicenseTransportTests extends AbstractLicensesIntegrationTests { @Test public void testPutLicensesForMultipleFeatures() throws Exception { - ESLicense shieldLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2)); - ESLicense marvelLicense = generateSignedLicense(TestUtils.MARVEL, TimeValue.timeValueMinutes(5)); + ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2)); + ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5)); List actualLicenses = Arrays.asList(marvelLicense, shieldLicense); // put license @@ -130,9 +129,9 @@ public class LicenseTransportTests extends AbstractLicensesIntegrationTests { @Test public void testPutMultipleLicensesForMultipleFeatures() throws Exception { - ESLicense shortedSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(2)); - ESLicense longerSignedLicense = generateSignedLicense(TestUtils.SHIELD, TimeValue.timeValueMinutes(5)); - ESLicense marvelLicense = generateSignedLicense(TestUtils.MARVEL, TimeValue.timeValueMinutes(5)); + ESLicense shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2)); + ESLicense longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5)); + ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5)); List actualLicenses = Arrays.asList(marvelLicense, shortedSignedLicense, longerSignedLicense); // put license diff --git a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTest.java b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTest.java index 72e59d51f55..5539c361333 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTest.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceClusterTest.java @@ -6,10 +6,13 @@ package org.elasticsearch.license.plugin; import org.elasticsearch.client.ClusterAdminClient; +import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.block.ClusterBlockLevel; +import org.elasticsearch.common.base.Predicate; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.license.TestUtils; import org.elasticsearch.license.core.ESLicense; @@ -21,7 +24,6 @@ import org.elasticsearch.license.plugin.consumer.TestConsumerPlugin1; import org.elasticsearch.license.plugin.consumer.TestPluginService1; import org.elasticsearch.license.plugin.core.LicensesStatus; import org.elasticsearch.node.internal.InternalNode; -import org.junit.Before; import org.junit.Test; import java.util.Arrays; @@ -31,12 +33,13 @@ import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilde import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.Matchers.hasItem; @ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0) public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests { private final String FEATURE_NAME = TestPluginService1.FEATURE_NAME; + + private final int trialLicenseDurationInSeconds = 2; protected Settings transportClientSettings() { return super.transportClientSettings(); @@ -54,18 +57,14 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests .put("plugins.load_classpath_plugins", false) .put("node.data", true) .put("format", "json") - .put(TestConsumerPlugin1.NAME + ".trial_license_duration_in_seconds", 5) + .put(TestConsumerPlugin1.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds) .putArray("plugin.types", LicensePlugin.class.getName(), TestConsumerPlugin1.class.getName()) .put(InternalNode.HTTP_ENABLED, true); } - @Before - public void beforeTest() throws Exception { - wipeAllLicenses(); - } - @Test public void testClusterRestart() throws Exception { + wipeAllLicenses(); int numNodes = randomIntBetween(1, 5); logger.info("--> starting " + numNodes + " node(s)"); @@ -83,40 +82,21 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests logger.info("--> get and check signed license"); getAndCheckLicense(esLicenses); + + wipeAllLicenses(); } @Test public void testClusterNotRecovered() throws Exception { - - logger.info("--> start first node (should not recover)"); - String name = internalCluster().startNode(nodeSettingsBuilder(0).put("gateway.recover_after_master_nodes", 2).put("node.master", true)); - /* TODO: figure out why STATE_NOT_RECOVERED_BLOCK is not on - assertThat(internalCluster().client(name).admin().cluster().prepareState().setLocal(true).execute().actionGet() - .getState().blocks().global(ClusterBlockLevel.METADATA), - hasItem(GatewayService.STATE_NOT_RECOVERED_BLOCK)); - */ - assertLicenseManagerEnabledFeatureFor(FEATURE_NAME); - assertConsumerPlugin1EnableNotification(1); - - logger.info("--> start second node (should recover)"); - internalCluster().startNode(nodeSettingsBuilder(1).put("gateway.recover_after_master_nodes", 2).put("node.master", true)); - assertLicenseManagerEnabledFeatureFor(FEATURE_NAME); - assertConsumerPlugin1EnableNotification(1); - - //internalCluster().startNode(nodeSettingsBuilder(2).put("gateway.expected_master_nodes", 3)); - //assertLicenseManagerEnabledFeatureFor(TestPluginService.FEATURE_NAME); - //assertConsumerPlugin1EnableNotification(1); - - logger.info("--> kill master node"); - internalCluster().stopCurrentMasterNode(); - assertLicenseManagerEnabledFeatureFor(FEATURE_NAME); - assertConsumerPlugin1EnableNotification(1); - - Thread.sleep(5 * 1050l); - internalCluster().startNode(nodeSettingsBuilder(3).put("gateway.recover_after_master_nodes", 2).put("node.master", true)); + logger.info("--> start one master out of two [recovery state]"); + internalCluster().startNode(nodeSettingsBuilder(0).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true)); assertLicenseManagerDisabledFeatureFor(FEATURE_NAME); assertConsumerPlugin1DisableNotification(1); + logger.info("--> start second master out of two [recovered state]"); + internalCluster().startNode(nodeSettingsBuilder(1).put("discovery.zen.minimum_master_nodes", 2).put("node.master", true)); + assertLicenseManagerEnabledFeatureFor(FEATURE_NAME); + assertConsumerPlugin1EnableNotification(1); } private List generateAndPutLicense() throws Exception {