From 9f84847681c0c860d0fadc44bf85c116725116e0 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Sat, 18 Oct 2014 14:25:11 -0400 Subject: [PATCH] First round of refactoring ESLicenceManager is no longer a static singleton Original commit: elastic/x-pack-elasticsearch@3e46f315a1233b1670d103f92cc8d589365d3d50 --- .../tools/FileBasedESLicenseProvider.java | 50 +++++ .../tools/LicenseVerificationTool.java | 4 +- .../license/manager/ESLicenseManager.java | 136 ++++++-------- .../license/manager/ESLicenseProvider.java | 176 +----------------- .../elasticsearch/license/manager/Utils.java | 67 ++----- .../license/plugin/action/Utils.java | 18 +- .../action/get/TransportGetLicenseAction.java | 12 +- .../license/plugin/core/LicensesService.java | 41 +++- .../license/AbstractLicensingTestBase.java | 25 +-- .../manager/LicenseVerificationTests.java | 17 +- .../license/plugin/LicensesServiceTests.java | 7 +- 11 files changed, 189 insertions(+), 364 deletions(-) create mode 100644 src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.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 new file mode 100644 index 00000000000..bd97847c4cf --- /dev/null +++ b/src/main/java/org/elasticsearch/license/licensor/tools/FileBasedESLicenseProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.license.licensor.tools; + +import org.elasticsearch.license.core.ESLicenses; +import org.elasticsearch.license.core.LicenseBuilders; +import org.elasticsearch.license.manager.ESLicenseProvider; + +import java.util.Set; + +/** + */ +public class FileBasedESLicenseProvider implements ESLicenseProvider { + private ESLicenses esLicenses; + + public FileBasedESLicenseProvider(ESLicenses esLicenses) { + this.esLicenses = esLicenses; + } + + public FileBasedESLicenseProvider(Set esLicensesSet) { + this(merge(esLicensesSet)); + } + + @Override + public ESLicenses.ESLicense getESLicense(ESLicenses.FeatureType featureType) { + return esLicenses.get(featureType); + } + + @Override + public ESLicenses getEffectiveLicenses() { + return esLicenses; + } + + // For testing + public void setLicenses(ESLicenses esLicenses) { + this.esLicenses = 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/LicenseVerificationTool.java b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java index 888fef84f2d..55ae4c0df8e 100644 --- a/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java +++ b/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java @@ -8,6 +8,7 @@ package org.elasticsearch.license.licensor.tools; import org.elasticsearch.license.core.ESLicenses; import org.elasticsearch.license.core.LicenseUtils; import org.elasticsearch.license.manager.ESLicenseManager; +import org.elasticsearch.license.manager.ESLicenseProvider; import java.io.File; import java.io.IOException; @@ -95,7 +96,8 @@ public class LicenseVerificationTool { Options options = parse(args); // verify licenses - ESLicenseManager licenseManager = ESLicenseManager.createLocalBasedInstance(options.licenses, options.publicKeyFilePath); + ESLicenseProvider licenseProvider = new FileBasedESLicenseProvider(options.licenses); + ESLicenseManager licenseManager = new ESLicenseManager(licenseProvider); licenseManager.verifyLicenses(); // dump effective licences diff --git a/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java b/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java index 9ad3027cedd..3d1d7e90cb3 100644 --- a/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java +++ b/src/main/java/org/elasticsearch/license/manager/ESLicenseManager.java @@ -11,21 +11,16 @@ 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.cluster.ClusterService; import org.elasticsearch.license.core.ESLicenses; import org.elasticsearch.license.core.LicenseBuilders; -import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Paths; -import java.util.Collections; import java.util.Set; import static org.elasticsearch.license.core.ESLicenses.*; -import static org.elasticsearch.license.manager.ESLicenseProvider.ClusterStateLicenseProvider; -import static org.elasticsearch.license.manager.ESLicenseProvider.FileBasedESLicenseProvider; -import static org.elasticsearch.license.manager.ESLicenseProvider.extractSignedLicence; +import static org.elasticsearch.license.manager.Utils.extractSignedLicence; /** * Class responsible for reading signed licenses, maintaining an effective esLicenses instance, verification of licenses @@ -36,65 +31,24 @@ import static org.elasticsearch.license.manager.ESLicenseProvider.extractSignedL */ public class ESLicenseManager { - private static ESLicenseManager instance = null; - private static FilePublicKeyDataProvider publicKeyDataProvider; private final ESLicenseProvider licenseProvider; private final LicenseManager licenseManager; - public static ESLicenseManager getInstance() { - if (ESLicenseManager.instance == null) { - throw new IllegalStateException("License manager has not been created!"); - } - return ESLicenseManager.instance; - } - - /** - * Creates a LicenseManager instance where the Licenses are queried from the cluster state - * - * @param clusterService used to query for appropriate license(s) for validation - * @param publicKeyPath used to decrypt the licenses - * @return {@link org.elasticsearch.license.manager.ESLicenseManager} instance backed by licenses residing - * in the cluster state - */ - public static ESLicenseManager createClusterStateBasedInstance(ClusterService clusterService, String publicKeyPath) { - if (ESLicenseManager.instance == null) { - ESLicenseManager.publicKeyDataProvider = new FilePublicKeyDataProvider(publicKeyPath); - instance = new ESLicenseManager(ESLicenseProvider.createClusterBasedLicenseProvider(clusterService, publicKeyPath)); - return instance; - } else if (ESLicenseManager.instance.licenseProvider instanceof ClusterStateLicenseProvider) { - return ESLicenseManager.instance; - } else { - throw new IllegalStateException("Manager already initiated with File based license provider"); - } - } - - public static ESLicenseManager createClusterStateBasedInstance(ClusterService clusterService) { - return createClusterStateBasedInstance(clusterService, getPublicKeyPath()); - } - - public static ESLicenseManager createLocalBasedInstance(ESLicenses esLicenses, String publicKeyPath) { - return createLocalBasedInstance(Collections.singleton(esLicenses), publicKeyPath); - } - - /** - * Creates a LicenseManager instance where the Licenses are queried from a set of pre-generated licenses - * @param esLicensesSet a set of pre-generated licenses stored in the license manager - * @param publicKeyPath used to decrypt the licenses - * @return {@link org.elasticsearch.license.manager.ESLicenseManager} instance backed by pre-generated licenses - */ - public static ESLicenseManager createLocalBasedInstance(Set esLicensesSet, String publicKeyPath) { - if (ESLicenseManager.instance == null) { - ESLicenseManager.publicKeyDataProvider = new FilePublicKeyDataProvider(publicKeyPath); - return new ESLicenseManager(ESLicenseProvider.createFileBasedLicenseProvider(merge(esLicensesSet), publicKeyPath)); - } else if (ESLicenseManager.instance.licenseProvider instanceof FileBasedESLicenseProvider) { - return ESLicenseManager.instance; - } else { - throw new IllegalStateException("Manager already initiated with Cluster state based license provider"); - } + // Initialize LicenseManager + static { + LicenseManagerProperties.setPublicKeyDataProvider(new FilePublicKeyDataProvider(getPublicKeyPath())); + LicenseManagerProperties.setPublicKeyPasswordProvider(new ESPublicKeyPasswordProvider()); + LicenseManagerProperties.setLicenseProvider(new LicenseProvider() { + @Override + public SignedLicense getLicense(Object context) { + throw new UnsupportedOperationException("This singelton license provider shouldn't be used"); + } + }); } private static String getPublicKeyPath() { + //TODO: Imporove key management URL resource = ESLicenseManager.class.getResource("public.key"); if (resource == null) { //test REMOVE NOCOMMIT!!!! @@ -107,11 +61,7 @@ public class ESLicenseManager { } } - private ESLicenseManager(ESLicenseProvider licenseProvider) { - LicenseManagerProperties.setLicenseProvider(licenseProvider); - LicenseManagerProperties.setPublicKeyDataProvider(publicKeyDataProvider); - LicenseManagerProperties.setLicenseValidator(new DefaultLicenseValidator()); - LicenseManagerProperties.setPublicKeyPasswordProvider(new ESPublicKeyPasswordProvider()); + public ESLicenseManager(ESLicenseProvider licenseProvider) { this.licenseProvider = licenseProvider; this.licenseManager = LicenseManager.getInstance(); } @@ -134,9 +84,7 @@ public class ESLicenseManager { ESLicense esLicense = esLicenses.get(featureType); // verify signature final License license = this.licenseManager.decryptAndVerifyLicense( - extractSignedLicence( - esLicense.signature(), - publicKeyDataProvider.getPublicKeyFile().getAbsolutePath())); + extractSignedLicence(esLicense.signature())); // validate license this.licenseManager.validateLicense(license); @@ -147,9 +95,6 @@ public class ESLicenseManager { throw new InvalidLicenseException("Expired License"); } catch (InvalidLicenseException e) { throw new InvalidLicenseException("Invalid License"); - } catch (IOException e) { - // bogus - throw new IllegalStateException(e); } } @@ -157,6 +102,27 @@ public class ESLicenseManager { verifyLicenses(getEffectiveLicenses()); } + public ESLicenses fromSignaturesAsIs(final Set signatures) { + final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); + for (String signature : signatures) { + licensesBuilder.licenseAsIs(getESLicenseFromSignature(signature)); + } + return licensesBuilder.build(); + } + + public ESLicense getESLicenseFromSignature(String signature) { + SignedLicense signedLicense = extractSignedLicence(signature); + return decryptAndVerifyESLicense(signedLicense, signature); + } + + public ESLicenses fromSignatures(final Set signatures) { + final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); + for (String signature : signatures) { + licensesBuilder.license(getESLicenseFromSignature(signature)); + } + return licensesBuilder.build(); + } + private static void verifyLicenseFields(License license, ESLicense eslicense) { boolean licenseValid = license.getProductKey().equals(eslicense.uid()) && license.getHolder().equals(eslicense.issuedTo()) @@ -194,13 +160,22 @@ public class ESLicenseManager { throw new InvalidLicenseException("Invalid License"); } } - + private License getLicense(FeatureType featureType) { + ESLicense esLicense = licenseProvider.getESLicense(featureType); + 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(FeatureType featureType) { try { - final License license = licenseManager.getLicense(featureType); + final License license = getLicense(featureType); if (license != null) { return license.hasLicenseForFeature(featureType.string()); } @@ -218,22 +193,22 @@ public class ESLicenseManager { } public String getIssuerForLicense(FeatureType featureType) { - final License license = licenseManager.getLicense(featureType); + final License license = getLicense(featureType); return license.getIssuer(); } public long getIssueDateForLicense(FeatureType featureType) { - final License license = licenseManager.getLicense(featureType); + final License license = getLicense(featureType); return license.getIssueDate(); } public long getExpiryDateForLicense(FeatureType featureType) { - final License license = licenseManager.getLicense(featureType); + final License license = getLicense(featureType); return license.getGoodBeforeDate(); } public String getIssuedToForLicense(FeatureType featureType) { - final License license = licenseManager.getLicense(featureType); + final License license = getLicense(featureType); return license.getHolder(); } @@ -248,7 +223,7 @@ public class ESLicenseManager { } ESLicense getESLicense(FeatureType featureType) { - final License license = licenseManager.getLicense(featureType); + final License license = getLicense(featureType); return convertToESLicense(license); } @@ -292,14 +267,7 @@ public class ESLicenseManager { return licenseBuilder.build(); } - - // only for testing - public void clearAndAddLicenses(ESLicenses licenses) { - this.licenseManager.clearLicenseCache(); - assert this.licenseProvider instanceof FileBasedESLicenseProvider; - this.licenseProvider.addLicenses(licenses); - } - + // TODO: Need a better password management private static class ESPublicKeyPasswordProvider implements PasswordProvider { private final String DEFAULT_PASS_PHRASE = "elasticsearch-license"; diff --git a/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java b/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java index e1d04468eec..ec79c3decb5 100644 --- a/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java +++ b/src/main/java/org/elasticsearch/license/manager/ESLicenseProvider.java @@ -5,184 +5,14 @@ */ package org.elasticsearch.license.manager; -import net.nicholaswilliams.java.licensing.LicenseProvider; -import net.nicholaswilliams.java.licensing.ObjectSerializer; -import net.nicholaswilliams.java.licensing.SignedLicense; -import net.nicholaswilliams.java.licensing.encryption.Hasher; -import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException; -import org.apache.commons.codec.binary.Base64; -import org.elasticsearch.cluster.ClusterService; -import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; -import org.elasticsearch.license.plugin.core.LicensesMetaData; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.Arrays; import static org.elasticsearch.license.core.ESLicenses.ESLicense; import static org.elasticsearch.license.core.ESLicenses.FeatureType; -public abstract class ESLicenseProvider implements LicenseProvider { +public interface ESLicenseProvider { - /** - * Factory for {@link org.elasticsearch.license.manager.ESLicenseProvider.ClusterStateLicenseProvider} - */ - public static ESLicenseProvider createClusterBasedLicenseProvider(ClusterService clusterService, String publicKeyPath) { - return new ClusterStateLicenseProvider(clusterService, publicKeyPath); - } + ESLicense getESLicense(FeatureType featureType); - /** - * Factory for {@link org.elasticsearch.license.manager.ESLicenseProvider.FileBasedESLicenseProvider} - */ - public static ESLicenseProvider createFileBasedLicenseProvider(ESLicenses esLicenses, String publicKeyPath) { - return new FileBasedESLicenseProvider(esLicenses, publicKeyPath); - } - - public abstract ESLicense getESLicense(FeatureType featureType); - - public abstract ESLicenses getEffectiveLicenses(); - - // only for testing - public abstract void addLicenses(ESLicenses esLicenses); - - /** - * LicenseProvider backed by the current cluster state - * Uses the clusterService to query for the latest licenses - * for {@link org.elasticsearch.license.manager.ESLicenseManager} - * consumption - */ - public static class ClusterStateLicenseProvider extends ESLicenseProvider { - private final ClusterService clusterService; - private final String publicKeyPath; - - private ClusterStateLicenseProvider(ClusterService clusterService, String publicKeyPath) { - this.clusterService = clusterService; - this.publicKeyPath = publicKeyPath; - } - - private ESLicenses getLicensesFromClusterState() { - final ClusterState state = clusterService.state(); - LicensesMetaData metaData = state.metaData().custom(LicensesMetaData.TYPE); - if (metaData != null) { - return Utils.fromSignatures(metaData.getSignatures()); - } - return LicenseBuilders.licensesBuilder().build(); - } - - - public ESLicenses getESLicenses() { - return getLicensesFromClusterState(); - } - - @Override - public SignedLicense getLicense(Object context) { - assert context instanceof FeatureType; - FeatureType featureType = (FeatureType) context; - final ESLicenses licenses = getLicensesFromClusterState(); - final ESLicense esLicense = licenses.get(featureType); - if (esLicense == null) { - throw new InvalidLicenseException("Invalid License"); - } - try { - return extractSignedLicence(esLicense.signature(), publicKeyPath); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - @Override - public ESLicense getESLicense(FeatureType featureType) { - return getLicensesFromClusterState().get(featureType); - } - - @Override - public ESLicenses getEffectiveLicenses() { - return getLicensesFromClusterState(); - } - - @Override - public void addLicenses(ESLicenses esLicenses) { - throw new UnsupportedOperationException("Licenses can only be added by updating the cluster state"); - } - } - - /** - * Extract a signedLicense (SIGNED_LICENSE_CONTENT) from the signature. - * Validates the public key used to decrypt the license by comparing their hashes - *

- * Signature structure: - * | MAGIC | HEADER_LENGTH | VERSION | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT | - * - * @param signature of a single license - * @param publicKeyPath location of the public key used to decode the signature - * @return signed license content for the license - * @throws IOException - */ - static SignedLicense extractSignedLicence(String signature, String publicKeyPath) throws IOException { - byte[] signatureBytes = Base64.decodeBase64(signature); - ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes); - byteBuffer = (ByteBuffer) byteBuffer.position(13); - int start = byteBuffer.getInt(); - int version = byteBuffer.getInt(); - byte[] hash = new byte[start - 13 - 4 - 4]; - byteBuffer.get(hash); - - final byte[] computedHash = Hasher.hash(Base64.encodeBase64String( - Files.readAllBytes(Paths.get(publicKeyPath)) - )).getBytes(Charset.forName("UTF-8")); - - if (!Arrays.equals(hash, computedHash)) { - throw new InvalidLicenseException("Invalid License"); - } - - return new ObjectSerializer().readObject(SignedLicense.class, Arrays.copyOfRange(signatureBytes, start, signatureBytes.length)); - } - - /** - * Not thread-safe (allows setting licenses); used only for testing and in command-line tools - */ - public static class FileBasedESLicenseProvider extends ESLicenseProvider { - private ESLicenses esLicenses; - private final String publicKeyPath; - - private FileBasedESLicenseProvider(ESLicenses esLicenses, String publicKeyPath) { - this.esLicenses = esLicenses; - this.publicKeyPath = publicKeyPath; - } - - @Override - public SignedLicense getLicense(Object context) { - assert context instanceof FeatureType; - FeatureType featureType = (FeatureType) context; - ESLicense esLicense = esLicenses.get(featureType); - if (esLicense == null) { - throw new InvalidLicenseException("Invalid License"); - } - try { - return extractSignedLicence(esLicense.signature(), publicKeyPath); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - @Override - public ESLicense getESLicense(FeatureType featureType) { - return esLicenses.get(featureType); - } - - @Override - public ESLicenses getEffectiveLicenses() { - return esLicenses; - } - - @Override - public void addLicenses(ESLicenses esLicenses) { - this.esLicenses = esLicenses; - } - } + ESLicenses getEffectiveLicenses(); } diff --git a/src/main/java/org/elasticsearch/license/manager/Utils.java b/src/main/java/org/elasticsearch/license/manager/Utils.java index b2cda3d3a0d..8a152a26b37 100644 --- a/src/main/java/org/elasticsearch/license/manager/Utils.java +++ b/src/main/java/org/elasticsearch/license/manager/Utils.java @@ -8,66 +8,27 @@ 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.Sets; -import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.core.LicenseBuilders; import java.nio.ByteBuffer; import java.util.Arrays; -import java.util.Set; -import static org.elasticsearch.license.core.ESLicenses.ESLicense; - -public class Utils { - - - - public static ESLicense getESLicenseFromSignature(String signature) { +public final class Utils { + /** + * Extract a signedLicense (SIGNED_LICENSE_CONTENT) from the signature. + * Validates the public key used to decrypt the license by comparing their hashes + *

+ * Signature structure: + * | MAGIC | HEADER_LENGTH | VERSION | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT | + * + * @param signature of a single license + * @return signed license content for the license + */ + public static SignedLicense extractSignedLicence(String signature) { byte[] signatureBytes = Base64.decodeBase64(signature); ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes); byteBuffer = (ByteBuffer) byteBuffer.position(13); int start = byteBuffer.getInt(); - SignedLicense signedLicense = new ObjectSerializer() - .readObject(SignedLicense.class, Arrays.copyOfRange(signatureBytes, start, signatureBytes.length)); - return ESLicenseManager.getInstance().decryptAndVerifyESLicense(signedLicense, signature); - } - - public static boolean isSame(ESLicenses firstLicenses, ESLicenses 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(); - - // check if the effective licenses have the same feature set - if (!licenses1.features().equals(licenses2.features())) { - return false; - } - - // for every feature license, check if all the attributes are the same - for (ESLicenses.FeatureType featureType : licenses1.features()) { - ESLicense license1 = licenses1.get(featureType); - ESLicense license2 = licenses2.get(featureType); - - if (!license1.uid().equals(license2.uid()) - || license1.feature() != license2.feature() - || license1.subscriptionType() != license2.subscriptionType() - || license1.type() != license2.type() - || license1.expiryDate() != license2.expiryDate() - || license1.issueDate() != license2.issueDate() - || !license1.issuedTo().equals(license2.issuedTo()) - || license1.maxNodes() != license2.maxNodes() - || license1.signature().equals(license2.signature())) { - return false; - } - } - return true; - } - - public static ESLicenses fromSignatures(final Set signatures) { - final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); - for (String signature : signatures) { - licensesBuilder.license(getESLicenseFromSignature(signature)); - } - return licensesBuilder.build(); + int version = byteBuffer.getInt(); + return new ObjectSerializer().readObject(SignedLicense.class, Arrays.copyOfRange(signatureBytes, start, signatureBytes.length)); } } diff --git a/src/main/java/org/elasticsearch/license/plugin/action/Utils.java b/src/main/java/org/elasticsearch/license/plugin/action/Utils.java index 8be1ddfc46e..4493078c8ff 100644 --- a/src/main/java/org/elasticsearch/license/plugin/action/Utils.java +++ b/src/main/java/org/elasticsearch/license/plugin/action/Utils.java @@ -6,17 +6,17 @@ package org.elasticsearch.license.plugin.action; import org.elasticsearch.common.collect.ImmutableMap; -import org.elasticsearch.common.collect.Sets; 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.*; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import static org.elasticsearch.license.core.ESLicenses.*; -import static org.elasticsearch.license.manager.Utils.getESLicenseFromSignature; public class Utils { @@ -28,18 +28,6 @@ public class Utils { return signatures.toArray(new String[signatures.size()]); } - public static ESLicenses fromSignatures(final String[] signatures) { - return org.elasticsearch.license.manager.Utils.fromSignatures(Sets.newHashSet(signatures)); - } - - public static ESLicenses fromSignaturesAsIs(final Set signatures) { - final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); - for (String signature : signatures) { - licensesBuilder.licenseAsIs(getESLicenseFromSignature(signature)); - } - return licensesBuilder.build(); - } - public static ESLicenses readGeneratedLicensesFrom(StreamInput in) throws IOException { final LicenseBuilders.LicensesBuilder licensesBuilder = LicenseBuilders.licensesBuilder(); boolean exists = in.readBoolean(); 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 ca04f5aad31..7cb0d80d97f 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 @@ -17,8 +17,9 @@ 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.plugin.action.Utils; +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.threadpool.ThreadPool; @@ -26,10 +27,13 @@ import org.elasticsearch.transport.TransportService; public class TransportGetLicenseAction extends TransportMasterNodeReadOperationAction { + private final LicensesService licensesService; + @Inject - public TransportGetLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService, + public TransportGetLicenseAction(Settings settings, TransportService transportService, ClusterService clusterService, LicensesService licensesService, ThreadPool threadPool, ActionFilters actionFilters) { super(settings, GetLicenseAction.NAME, transportService, clusterService, threadPool, actionFilters); + this.licensesService = licensesService; } @Override @@ -55,10 +59,12 @@ public class TransportGetLicenseAction extends TransportMasterNodeReadOperationA @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) { - ESLicenses esLicenses = Utils.fromSignaturesAsIs(licenses.getSignatures()); + ESLicenseManager esLicenseManager = licensesService.getEsLicenseManager(); + ESLicenses esLicenses = esLicenseManager.fromSignaturesAsIs(licenses.getSignatures()); TrialLicenses trialLicenses = TrialLicenseUtils.fromEncodedTrialLicenses(licenses.getEncodedTrialLicenses()); listener.onResponse(new GetLicenseResponse(esLicenses, trialLicenses)); } else { 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 6ea462b4918..0eaf0a963ff 100644 --- a/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java +++ b/src/main/java/org/elasticsearch/license/plugin/core/LicensesService.java @@ -26,6 +26,7 @@ import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.license.core.ESLicenses; import org.elasticsearch.license.core.LicenseBuilders; 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; @@ -54,7 +55,7 @@ import static org.elasticsearch.license.plugin.core.trial.TrialLicensesBuilder.t * */ @Singleton -public class LicensesService extends AbstractLifecycleComponent implements ClusterStateListener, LicensesManagerService, LicensesClientService { +public class LicensesService extends AbstractLifecycleComponent implements ESLicenseProvider, ClusterStateListener, LicensesManagerService, LicensesClientService { private ESLicenseManager esLicenseManager; @@ -70,7 +71,7 @@ public class LicensesService extends AbstractLifecycleComponent public LicensesService(Settings settings, ClusterService clusterService, ThreadPool threadPool) { super(settings); this.clusterService = clusterService; - this.esLicenseManager = ESLicenseManager.createClusterStateBasedInstance(clusterService); + this.esLicenseManager = new ESLicenseManager(this); this.threadPool = threadPool; } @@ -104,7 +105,7 @@ public class LicensesService extends AbstractLifecycleComponent MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses); - licensesWrapper.addSignedLicenses(newLicenses); + licensesWrapper.addSignedLicenses(esLicenseManager, newLicenses); mdBuilder.putCustom(LicensesMetaData.TYPE, licensesWrapper.createLicensesMetaData()); return ClusterState.builder(currentState).metaData(mdBuilder).build(); } @@ -143,7 +144,7 @@ public class LicensesService extends AbstractLifecycleComponent MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData()); LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE); final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses); - licensesWrapper.removeFeatures(featuresToDelete); + licensesWrapper.removeFeatures(esLicenseManager, featuresToDelete); mdBuilder.putCustom(LicensesMetaData.TYPE, licensesWrapper.createLicensesMetaData()); return ClusterState.builder(currentState).metaData(mdBuilder).build(); } @@ -296,6 +297,22 @@ public class LicensesService extends AbstractLifecycleComponent return ThreadPool.Names.GENERIC; } + @Override + public ESLicenses.ESLicense getESLicense(FeatureType featureType) { + return getEffectiveLicenses().get(featureType); + } + + @Override + public ESLicenses getEffectiveLicenses() { + final ClusterState state = clusterService.state(); + LicensesMetaData metaData = state.metaData().custom(LicensesMetaData.TYPE); + if (metaData != null) { + return esLicenseManager.fromSignatures(metaData.getSignatures()); + } + return LicenseBuilders.licensesBuilder().build(); + + } + public class SubmitReschedulingLicensingClientNotificationJob implements Runnable { @Override public void run() { @@ -312,6 +329,10 @@ public class LicensesService extends AbstractLifecycleComponent } } + //TODO: Shouldn't expose this + public ESLicenseManager getEsLicenseManager() { + return esLicenseManager; + } public class LicensingClientNotificationJob implements Runnable { @@ -483,8 +504,8 @@ public class LicensesService extends AbstractLifecycleComponent } } - public ESLicenses signedLicenses() { - return org.elasticsearch.license.manager.Utils.fromSignatures(signatures); + public ESLicenses signedLicenses(ESLicenseManager licenseManage) { + return licenseManage.fromSignatures(signatures); } public TrialLicenses trialLicenses() { @@ -496,15 +517,15 @@ public class LicensesService extends AbstractLifecycleComponent Collections.singleton(TrialLicenseUtils.toEncodedTrialLicense(trialLicense)))); } - public void addSignedLicenses(ESLicenses licenses) { - ESLicenses currentSignedLicenses = signedLicenses(); + public void addSignedLicenses(ESLicenseManager licenseManage, ESLicenses licenses) { + ESLicenses currentSignedLicenses = signedLicenses(licenseManage); final ESLicenses mergedLicenses = LicenseBuilders.merge(currentSignedLicenses, licenses); Set newSignatures = Sets.newHashSet(Utils.toSignatures(mergedLicenses)); this.signatures = ImmutableSet.copyOf(Sets.union(signatures, newSignatures)); } - public void removeFeatures(Set featuresToDelete) { - ESLicenses currentSignedLicenses = signedLicenses(); + 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)); diff --git a/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java b/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java index 45c50ba5904..2ae1001967d 100644 --- a/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java +++ b/src/test/java/org/elasticsearch/license/AbstractLicensingTestBase.java @@ -6,11 +6,12 @@ package org.elasticsearch.license; import org.elasticsearch.license.core.ESLicenses; -import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool; +import org.elasticsearch.license.manager.ESLicenseManager; import org.junit.BeforeClass; import java.io.File; import java.io.IOException; +import java.net.URL; import java.util.Map; public class AbstractLicensingTestBase { @@ -19,23 +20,15 @@ public class AbstractLicensingTestBase { protected static String priKeyPath = null; @BeforeClass - public static void setup() throws IOException { + public static void setup() throws Exception { + pubKeyPath = getResourcePath("test_pub.key"); + priKeyPath = getResourcePath("test_pri.key"); - // Generate temp KeyPair spec - File privateKeyFile = File.createTempFile("privateKey", ".key"); - File publicKeyFile = File.createTempFile("publicKey", ".key"); - AbstractLicensingTestBase.pubKeyPath = publicKeyFile.getAbsolutePath(); - AbstractLicensingTestBase.priKeyPath = privateKeyFile.getAbsolutePath(); - assert privateKeyFile.delete(); - assert publicKeyFile.delete(); + } - // Generate keyPair - String[] args = new String[4]; - args[0] = "--publicKeyPath"; - args[1] = AbstractLicensingTestBase.pubKeyPath; - args[2] = "--privateKeyPath"; - args[3] = AbstractLicensingTestBase.priKeyPath; - KeyPairGeneratorTool.main(args); + private static String getResourcePath(String resource) throws Exception { + URL url = ESLicenseManager.class.getResource("/org.elasticsearch.license.plugin/" + resource); + return url.toURI().getPath(); } public String generateSignedLicenses(Map map) throws IOException { diff --git a/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java index 35f445d62a5..d11749fc20f 100644 --- a/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java +++ b/src/test/java/org/elasticsearch/license/manager/LicenseVerificationTests.java @@ -11,6 +11,7 @@ import org.elasticsearch.license.TestUtils; import org.elasticsearch.license.core.DateUtils; import org.elasticsearch.license.core.ESLicenses; import org.elasticsearch.license.core.LicenseBuilders; +import org.elasticsearch.license.licensor.tools.FileBasedESLicenseProvider; import org.junit.After; import org.junit.BeforeClass; import org.junit.Test; @@ -26,16 +27,20 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { private static ESLicenseManager esLicenseManager; + private static FileBasedESLicenseProvider esLicenseProvider; + private final static ESLicenses EMPTY_LICENSES = LicenseBuilders.licensesBuilder().build(); @BeforeClass public static void setupManager() { - esLicenseManager = ESLicenseManager.createLocalBasedInstance(LicenseBuilders.licensesBuilder().build(), pubKeyPath); + esLicenseProvider = new FileBasedESLicenseProvider(LicenseBuilders.licensesBuilder().build()); + esLicenseManager = new ESLicenseManager(esLicenseProvider); + } @After public void clearManager() { - esLicenseManager.clearAndAddLicenses(EMPTY_LICENSES); + esLicenseProvider.setLicenses(EMPTY_LICENSES); } @@ -51,7 +56,7 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { ESLicenses esLicensesOutput = readLicensesFromString(generateSignedLicenses(map)); - esLicenseManager.clearAndAddLicenses(esLicensesOutput); + esLicenseProvider.setLicenses(esLicensesOutput); esLicenseManager.verifyLicenses(); @@ -75,7 +80,7 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { ESLicenses esLicensesOutput = readLicensesFromString(generateSignedLicenses(map)); - esLicenseManager.clearAndAddLicenses(esLicensesOutput); + esLicenseProvider.setLicenses(esLicensesOutput); //printLicense(esLicenseManager.getEffectiveLicenses()); @@ -132,7 +137,7 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { ESLicenses esLicensesOutput = readLicensesFromString(generateSignedLicenses(map)); - esLicenseManager.clearAndAddLicenses(esLicensesOutput); + esLicenseProvider.setLicenses(esLicensesOutput); // All validation for shield license should be normal as expected verifyLicenseManager(esLicenseManager, Collections.singletonMap(FeatureType.SHIELD, shildFeatureAttributes)); @@ -166,7 +171,7 @@ public class LicenseVerificationTests extends AbstractLicensingTestBase { ESLicenses tamperedLicenses = LicenseBuilders.licensesBuilder().license(tamperedLicense).build(); try { - esLicenseManager.clearAndAddLicenses(tamperedLicenses); + esLicenseProvider.setLicenses(tamperedLicenses); assertTrue("License manager should always report the original (signed) expiry date of: " + originalExpiryDate + " but got: " + esLicenseManager.getExpiryDateForLicense(FeatureType.SHIELD), esLicenseManager.getExpiryDateForLicense(FeatureType.SHIELD) == originalExpiryDate); esLicenseManager.verifyLicenses(); fail(); diff --git a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java index 597093febd0..0b8225c4957 100644 --- a/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java +++ b/src/test/java/org/elasticsearch/license/plugin/LicensesServiceTests.java @@ -18,7 +18,7 @@ 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.manager.Utils; +import org.elasticsearch.license.manager.ESLicenseManager; import org.elasticsearch.license.plugin.action.put.PutLicenseRequest; import org.elasticsearch.license.plugin.core.*; import org.elasticsearch.test.ElasticsearchIntegrationTest; @@ -134,6 +134,7 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { ESLicenses licenses = LicenseUtils.readLicensesFromString(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() { @Override @@ -151,7 +152,7 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { latch1.await(); LicensesMetaData metaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE); - ESLicenses metaDataLicense = Utils.fromSignatures(metaData.getSignatures()); + ESLicenses metaDataLicense = esLicenseManager.fromSignatures(metaData.getSignatures()); TestUtils.isSame(licenses, metaDataLicense); @@ -178,7 +179,7 @@ public class LicensesServiceTests extends ElasticsearchIntegrationTest { latch2.await(); metaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE); - metaDataLicense = Utils.fromSignatures(metaData.getSignatures()); + metaDataLicense = esLicenseManager.fromSignatures(metaData.getSignatures()); TestUtils.isSame(licenses2, metaDataLicense); }