mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-09 14:34:43 +00:00
Remove third-party licensing library
Incorporate Feedback: - verify signature for signed licenses whenever it is read from cluster state - encrypt trial licenses with default pass phrase when storing it - moved toSignature & fromSignature to License Make LicenseManager a Utility class Refactor: - renamed LicenseManager to LicenseVerifier - LicensesMetaData now holds a list of license objects (for signed licenses) and a set of encoded strings (trial licenses) - minor test cleanup incorporate feedback incorporated feedback switch to a stronger secret key gen algo; clean up build files & LicensesMetaData cosmetic changes to LicenseSigner incorporate LicnesesMetaData feedback Original commit: elastic/x-pack-elasticsearch@0510091d2d
This commit is contained in:
parent
eb5a06a1f9
commit
32af5a9d9c
pom.xml
src
main
assemblies
java/org/elasticsearch/license
test
java/org/elasticsearch/license
AbstractLicensingTestBase.javaLicenseSerializationTests.javaLicenseVerificationTests.javaTestUtils.java
manager
plugin
AbstractLicensesIntegrationTests.javaAbstractLicensesServiceTests.javaLicensesClientServiceTests.javaLicensesManagerServiceTests.javaLicensesMetaDataSerializationTests.javaLicensesServiceClusterTest.javaLicensesTransportTests.javaTrailLicenseSerializationTests.java
tools
resources
12
pom.xml
12
pom.xml
@ -119,18 +119,6 @@
|
||||
</dependency>
|
||||
|
||||
<!-- actual deps -->
|
||||
<dependency>
|
||||
<groupId>net.nicholaswilliams.java.licensing</groupId>
|
||||
<artifactId>licensing-core</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.nicholaswilliams.java.licensing</groupId>
|
||||
<artifactId>licensing-licensor-base</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch</artifactId>
|
||||
|
@ -28,8 +28,6 @@
|
||||
<useTransitiveFiltering>true</useTransitiveFiltering>
|
||||
<includes>
|
||||
<include>org.elasticsearch:elasticsearch-license:*:exec</include>
|
||||
<include>net.nicholaswilliams.java.licensing:licensing-core</include>
|
||||
<include>net.nicholaswilliams.java.licensing:licensing-licensor-base</include>
|
||||
<include>org.elasticsearch:elasticsearch</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
|
@ -14,13 +14,5 @@
|
||||
<exclude>org.elasticsearch:elasticsearch</exclude>
|
||||
</excludes>
|
||||
</dependencySet>
|
||||
<dependencySet>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useProjectArtifact>true</useProjectArtifact>
|
||||
<useTransitiveFiltering>true</useTransitiveFiltering>
|
||||
<includes>
|
||||
<include>net.nicholaswilliams.java.licensing:licensing-core</include>
|
||||
</includes>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
245
src/main/java/org/elasticsearch/license/core/CryptUtils.java
Normal file
245
src/main/java/org/elasticsearch/license/core/CryptUtils.java
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license.core;
|
||||
|
||||
|
||||
import org.elasticsearch.common.Base64;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
public class CryptUtils {
|
||||
private static final int minimumPadding = 20;
|
||||
private static final byte[] salt = {
|
||||
(byte) 0xA9, (byte) 0xA2, (byte) 0xB5, (byte) 0xDE,
|
||||
(byte) 0x2A, (byte) 0x8A, (byte) 0x9A, (byte) 0xE6
|
||||
};
|
||||
private static final int iterationCount = 1024;
|
||||
private static final int aesKeyLength = 128;
|
||||
private static final String keyAlgorithm = "RSA";
|
||||
private static final String passHashAlgorithm = "SHA-512";
|
||||
private static final String DEFAULT_PASS_PHRASE = "elasticsearch-license";
|
||||
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
|
||||
/**
|
||||
* Read encrypted private key file content with default pass phrase
|
||||
*/
|
||||
public static PrivateKey readEncryptedPrivateKey(byte[] fileContents) {
|
||||
try {
|
||||
return readEncryptedPrivateKey(fileContents, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read encrypted public key file content with default pass phrase
|
||||
*/
|
||||
public static PublicKey readEncryptedPublicKey(byte[] fileContents) {
|
||||
try {
|
||||
return readEncryptedPublicKey(fileContents, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted public key file content with default pass phrase
|
||||
*/
|
||||
public static byte[] writeEncryptedPublicKey(PublicKey publicKey) {
|
||||
try {
|
||||
return writeEncryptedPublicKey(publicKey, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted private key file content with default pass phrase
|
||||
*/
|
||||
public static byte[] writeEncryptedPrivateKey(PrivateKey privateKey) {
|
||||
try {
|
||||
return writeEncryptedPrivateKey(privateKey, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read encrypted private key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static PrivateKey readEncryptedPrivateKey(byte[] fileContents, char[] passPhrase) {
|
||||
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(decrypt(fileContents, passPhrase));
|
||||
try {
|
||||
return KeyFactory.getInstance(keyAlgorithm).generatePrivate(privateKeySpec);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read encrypted public key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static PublicKey readEncryptedPublicKey(byte[] fileContents, char[] passPhrase) {
|
||||
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(decrypt(fileContents, passPhrase));
|
||||
try {
|
||||
return KeyFactory.getInstance(CryptUtils.keyAlgorithm).generatePublic(publicKeySpec);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted public key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static byte[] writeEncryptedPublicKey(PublicKey publicKey, char[] passPhrase) {
|
||||
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
return encrypt(encodedKeySpec.getEncoded(), passPhrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns encrypted private key file content with provided <code>passPhrase</code>
|
||||
*/
|
||||
public static byte[] writeEncryptedPrivateKey(PrivateKey privateKey, char[] passPhrase) {
|
||||
PKCS8EncodedKeySpec encodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
|
||||
return encrypt(encodedKeySpec.getEncoded(), passPhrase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts provided <code>data</code> with <code>DEFAULT_PASS_PHRASE</code>
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data) {
|
||||
try {
|
||||
return encrypt(data, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts provided <code>encryptedData</code> with <code>DEFAULT_PASS_PHRASE</code>
|
||||
*/
|
||||
public static byte[] decrypt(byte[] encryptedData) {
|
||||
try {
|
||||
return decrypt(encryptedData, hashPassPhrase(DEFAULT_PASS_PHRASE));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts provided <code>data</code> with <code>passPhrase</code>
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, char[] passPhrase) {
|
||||
try {
|
||||
final Cipher encryptionCipher = getEncryptionCipher(getSecretKey(passPhrase));
|
||||
return encryptionCipher.doFinal(pad(data, minimumPadding));
|
||||
} catch (InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts provided <code>encryptedData</code> with <code>passPhrase</code>
|
||||
*/
|
||||
private static byte[] decrypt(byte[] encryptedData, char[] passPhrase) {
|
||||
try {
|
||||
final Cipher cipher = getDecryptionCipher(getSecretKey(passPhrase));
|
||||
return unPad(cipher.doFinal(encryptedData));
|
||||
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static SecretKey getSecretKey(char[] passPhrase) throws InvalidKeySpecException {
|
||||
try {
|
||||
PBEKeySpec keySpec = new PBEKeySpec(passPhrase, salt, iterationCount, aesKeyLength);
|
||||
|
||||
byte[] shortKey = SecretKeyFactory.getInstance("PBEWithSHA1AndDESede").
|
||||
generateSecret(keySpec).getEncoded();
|
||||
|
||||
byte[] intermediaryKey = new byte[aesKeyLength / 8];
|
||||
for (int i = 0, j = 0; i < aesKeyLength / 8; i++) {
|
||||
intermediaryKey[i] = shortKey[j];
|
||||
if (++j == shortKey.length)
|
||||
j = 0;
|
||||
}
|
||||
|
||||
return new SecretKeySpec(intermediaryKey, "AES");
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Cipher getEncryptionCipher(SecretKey secretKey) {
|
||||
return getCipher(Cipher.ENCRYPT_MODE, secretKey);
|
||||
}
|
||||
|
||||
private static Cipher getDecryptionCipher(SecretKey secretKey) {
|
||||
return getCipher(Cipher.DECRYPT_MODE, secretKey);
|
||||
}
|
||||
|
||||
private static Cipher getCipher(int mode, SecretKey secretKey) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
|
||||
cipher.init(mode, secretKey, random);
|
||||
return cipher;
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] pad(byte[] bytes, int length) {
|
||||
if (bytes.length >= length) {
|
||||
byte[] out = new byte[bytes.length + 1];
|
||||
System.arraycopy(bytes, 0, out, 0, bytes.length);
|
||||
out[bytes.length] = (byte) 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
byte[] out = new byte[length + 1];
|
||||
|
||||
int i = 0;
|
||||
for (; i < bytes.length; i++)
|
||||
out[i] = bytes[i];
|
||||
|
||||
int padded = length - i;
|
||||
|
||||
// fill the rest with random bytes
|
||||
byte[] fill = new byte[padded - 1];
|
||||
random.nextBytes(fill);
|
||||
System.arraycopy(fill, 0, out, i, padded - 1);
|
||||
|
||||
out[length] = (byte) (padded + 1);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static byte[] unPad(byte[] bytes) {
|
||||
int padded = (int) bytes[bytes.length - 1];
|
||||
int targetLength = bytes.length - padded;
|
||||
|
||||
byte[] out = new byte[targetLength];
|
||||
|
||||
System.arraycopy(bytes, 0, out, 0, targetLength);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
private static char[] hashPassPhrase(String passPhrase) throws NoSuchAlgorithmException {
|
||||
final byte[] passBytes = passPhrase.getBytes(StandardCharsets.UTF_8);
|
||||
final byte[] digest = MessageDigest.getInstance(passHashAlgorithm).digest(passBytes);
|
||||
return new String(Base64.encodeBytesToBytes(digest), StandardCharsets.UTF_8).toCharArray();
|
||||
}
|
||||
}
|
@ -8,14 +8,11 @@ package org.elasticsearch.license.core;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilderString;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ESLicense implements ToXContent {
|
||||
public class License implements ToXContent {
|
||||
|
||||
private final String uid;
|
||||
private final String issuer;
|
||||
@ -28,8 +25,8 @@ public class ESLicense implements ToXContent {
|
||||
private final long expiryDate;
|
||||
private final int maxNodes;
|
||||
|
||||
private ESLicense(String uid, String issuer, String issuedTo, long issueDate, String type,
|
||||
String subscriptionType, String feature, String signature, long expiryDate, int maxNodes) {
|
||||
private License(String uid, String issuer, String issuedTo, long issueDate, String type,
|
||||
String subscriptionType, String feature, String signature, long expiryDate, int maxNodes) {
|
||||
this.uid = uid;
|
||||
this.issuer = issuer;
|
||||
this.issuedTo = issuedTo;
|
||||
@ -113,7 +110,7 @@ public class ESLicense implements ToXContent {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
public void validate() {
|
||||
if (issuer == null) {
|
||||
throw new IllegalStateException("issuer can not be null");
|
||||
} else if (issuedTo == null) {
|
||||
@ -137,8 +134,7 @@ public class ESLicense implements ToXContent {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ESLicense readESLicense(StreamInput in) throws IOException {
|
||||
static License readLicense(StreamInput in) throws IOException {
|
||||
in.readVInt(); // Version for future extensibility
|
||||
Builder builder = builder();
|
||||
builder.uid(in.readString());
|
||||
@ -170,7 +166,11 @@ public class ESLicense implements ToXContent {
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
boolean restViewMode = params.paramAsBoolean(ESLicenses.REST_VIEW_MODE, false);
|
||||
boolean licenseSpecMode = params.paramAsBoolean(Licenses.LICENSE_SPEC_VIEW_MODE, false);
|
||||
boolean restViewMode = params.paramAsBoolean(Licenses.REST_VIEW_MODE, false);
|
||||
if (licenseSpecMode && restViewMode) {
|
||||
throw new IllegalArgumentException("can have either " + Licenses.REST_VIEW_MODE + " or " + Licenses.LICENSE_SPEC_VIEW_MODE);
|
||||
}
|
||||
builder.startObject();
|
||||
if (restViewMode) {
|
||||
builder.field(XFields.STATUS, ((expiryDate - System.currentTimeMillis()) > 0l) ? "active" : "expired");
|
||||
@ -184,7 +184,7 @@ public class ESLicense implements ToXContent {
|
||||
builder.field(XFields.MAX_NODES, maxNodes);
|
||||
builder.field(XFields.ISSUED_TO, issuedTo);
|
||||
builder.field(XFields.ISSUER, issuer);
|
||||
if (signature != null && !restViewMode) {
|
||||
if (!licenseSpecMode && !restViewMode && signature != null) {
|
||||
builder.field(XFields.SIGNATURE, signature);
|
||||
}
|
||||
builder.endObject();
|
||||
@ -310,7 +310,7 @@ public class ESLicense implements ToXContent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder fromLicenseSpec(ESLicense license, String signature) {
|
||||
public Builder fromLicenseSpec(License license, String signature) {
|
||||
return uid(license.uid())
|
||||
.issuedTo(license.issuedTo())
|
||||
.issueDate(license.issueDate())
|
||||
@ -372,12 +372,12 @@ public class ESLicense implements ToXContent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ESLicense build() {
|
||||
return new ESLicense(uid, issuer, issuedTo, issueDate, type,
|
||||
public License build() {
|
||||
return new License(uid, issuer, issuedTo, issueDate, type,
|
||||
subscriptionType, feature, signature, expiryDate, maxNodes);
|
||||
}
|
||||
|
||||
public Builder verify() {
|
||||
public Builder validate() {
|
||||
if (issuer == null) {
|
||||
throw new IllegalStateException("issuer can not be null");
|
||||
} else if (issuedTo == null) {
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license.core;
|
||||
|
||||
import org.elasticsearch.common.Base64;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Responsible for verifying signed licenses
|
||||
*/
|
||||
public class LicenseVerifier {
|
||||
|
||||
/**
|
||||
* Verifies Licenses using {@link #verifyLicense(License)}
|
||||
*/
|
||||
public static boolean verifyLicenses(final Collection<License> licenses) {
|
||||
for (License license : licenses) {
|
||||
if (!verifyLicense(license)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* verifies the license content with the signature and ensures that an expected public key is used
|
||||
* @param license to verify
|
||||
* @return true if valid, false otherwise
|
||||
*/
|
||||
public static boolean verifyLicense(final License license) {
|
||||
LicenseSignature licenseSignature = null;
|
||||
try {
|
||||
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
license.toXContent(contentBuilder, new ToXContent.MapParams(Collections.singletonMap(Licenses.LICENSE_SPEC_VIEW_MODE, "true")));
|
||||
licenseSignature = parseSignature(license.signature());
|
||||
if(!verifyContent(contentBuilder.bytes().toBytes(), licenseSignature.contentSignature)) {
|
||||
return false;
|
||||
}
|
||||
final byte[] hash = Base64.encodeBytesToBytes(getPublicKeyContent("/public.key"));
|
||||
return Arrays.equals(hash, licenseSignature.hash);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
} finally {
|
||||
if (licenseSignature != null) {
|
||||
licenseSignature.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean verifyContent(byte[] data, byte[] contentSignature) {
|
||||
try {
|
||||
Signature rsa = Signature.getInstance("SHA512withRSA");
|
||||
rsa.initVerify(CryptUtils.readEncryptedPublicKey(getPublicKeyContent("/public.key")));
|
||||
rsa.update(data);
|
||||
return rsa.verify(contentSignature);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getPublicKeyContent(String resource) {
|
||||
try (InputStream inputStream = LicenseVerifier.class.getResourceAsStream(resource)) {
|
||||
return Streams.copyToByteArray(inputStream);
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signature structure:
|
||||
* | VERSION | MAGIC | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT |
|
||||
*/
|
||||
private static LicenseSignature parseSignature(String signature) throws IOException {
|
||||
byte[] signatureBytes = Base64.decode(signature);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes);
|
||||
int version = byteBuffer.getInt();
|
||||
int magicLen = byteBuffer.getInt();
|
||||
byte[] magic = new byte[magicLen];
|
||||
byteBuffer.get(magic);
|
||||
int hashLen = byteBuffer.getInt();
|
||||
byte[] hash = new byte[hashLen];
|
||||
byteBuffer.get(hash);
|
||||
int signedContentLen = byteBuffer.getInt();
|
||||
byte[] signedContent = new byte[signedContentLen];
|
||||
byteBuffer.get(signedContent);
|
||||
return new LicenseSignature(version, hash, signedContent);
|
||||
}
|
||||
|
||||
private static class LicenseSignature {
|
||||
private final int version;
|
||||
private final byte[] hash;
|
||||
private final byte[] contentSignature;
|
||||
|
||||
private LicenseSignature(int version, byte[] hash, byte[] contentSignature) {
|
||||
this.version = version;
|
||||
this.hash = hash;
|
||||
this.contentSignature = contentSignature;
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
Arrays.fill(hash, (byte)0);
|
||||
Arrays.fill(contentSignature, (byte)0);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,9 +15,10 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class ESLicenses {
|
||||
public final class Licenses {
|
||||
|
||||
public static final String REST_VIEW_MODE = "rest_api";
|
||||
public static final String REST_VIEW_MODE = "rest_view";
|
||||
public static final String LICENSE_SPEC_VIEW_MODE = "license_spec_view";
|
||||
|
||||
private final static class Fields {
|
||||
static final String LICENSES = "licenses";
|
||||
@ -27,41 +28,43 @@ public class ESLicenses {
|
||||
static final XContentBuilderString LICENSES = new XContentBuilderString(Fields.LICENSES);
|
||||
}
|
||||
|
||||
public static void toXContent(Collection<ESLicense> licenses, XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
private Licenses() {}
|
||||
|
||||
public static void toXContent(Collection<License> licenses, XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.startArray(XFields.LICENSES);
|
||||
for (ESLicense license : licenses) {
|
||||
for (License license : licenses) {
|
||||
license.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
public static List<ESLicense> fromSource(String content) throws IOException {
|
||||
public static List<License> fromSource(String content) throws IOException {
|
||||
return fromSource(content.getBytes(StandardCharsets.UTF_8), true);
|
||||
}
|
||||
|
||||
public static List<ESLicense> fromSource(byte[] bytes) throws IOException {
|
||||
public static List<License> fromSource(byte[] bytes) throws IOException {
|
||||
return fromXContent(XContentFactory.xContent(bytes).createParser(bytes), true);
|
||||
}
|
||||
|
||||
public static List<ESLicense> fromSource(byte[] bytes, boolean verify) throws IOException {
|
||||
public static List<License> fromSource(byte[] bytes, boolean verify) throws IOException {
|
||||
return fromXContent(XContentFactory.xContent(bytes).createParser(bytes), verify);
|
||||
}
|
||||
|
||||
public static List<ESLicense> fromXContent(XContentParser parser, boolean verify) throws IOException {
|
||||
List<ESLicense> esLicenses = new ArrayList<>();
|
||||
public static List<License> fromXContent(XContentParser parser, boolean verify) throws IOException {
|
||||
List<License> licenses = new ArrayList<>();
|
||||
if (parser.nextToken() == XContentParser.Token.START_OBJECT) {
|
||||
if (parser.nextToken() == XContentParser.Token.FIELD_NAME) {
|
||||
String currentFieldName = parser.currentName();
|
||||
if (Fields.LICENSES.equals(currentFieldName)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
ESLicense.Builder builder = ESLicense.builder().fromXContent(parser);
|
||||
License.Builder builder = License.builder().fromXContent(parser);
|
||||
if (verify) {
|
||||
builder.verify();
|
||||
builder.validate();
|
||||
}
|
||||
esLicenses.add(builder.build());
|
||||
licenses.add(builder.build());
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("failed to parse licenses expected an array of licenses");
|
||||
@ -74,37 +77,37 @@ public class ESLicenses {
|
||||
} else {
|
||||
throw new ElasticsearchParseException("failed to parse licenses expected start object");
|
||||
}
|
||||
return esLicenses;
|
||||
return licenses;
|
||||
}
|
||||
|
||||
public static List<ESLicense> readFrom(StreamInput in) throws IOException {
|
||||
public static List<License> readFrom(StreamInput in) throws IOException {
|
||||
int size = in.readVInt();
|
||||
List<ESLicense> esLicenses = new ArrayList<>(size);
|
||||
List<License> licenses = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
esLicenses.add(ESLicense.readESLicense(in));
|
||||
licenses.add(License.readLicense(in));
|
||||
}
|
||||
return esLicenses;
|
||||
return licenses;
|
||||
}
|
||||
|
||||
public static void writeTo(List<ESLicense> esLicenses, StreamOutput out) throws IOException {
|
||||
out.writeVInt(esLicenses.size());
|
||||
for (ESLicense license : esLicenses) {
|
||||
public static void writeTo(List<License> licenses, StreamOutput out) throws IOException {
|
||||
out.writeVInt(licenses.size());
|
||||
for (License license : licenses) {
|
||||
license.writeTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
public static ImmutableMap<String, ESLicense> reduceAndMap(Set<ESLicense> esLicensesSet) {
|
||||
Map<String, ESLicense> map = new HashMap<>(esLicensesSet.size());
|
||||
for (ESLicense license : esLicensesSet) {
|
||||
public static ImmutableMap<String, License> reduceAndMap(Set<License> licensesSet) {
|
||||
Map<String, License> map = new HashMap<>(licensesSet.size());
|
||||
for (License license : licensesSet) {
|
||||
putIfAppropriate(map, license);
|
||||
}
|
||||
return ImmutableMap.copyOf(map);
|
||||
}
|
||||
|
||||
private static void putIfAppropriate(Map<String, ESLicense> licenseMap, ESLicense license) {
|
||||
private static void putIfAppropriate(Map<String, License> licenseMap, License license) {
|
||||
final String featureType = license.feature();
|
||||
if (licenseMap.containsKey(featureType)) {
|
||||
final ESLicense previousLicense = licenseMap.get(featureType);
|
||||
final License previousLicense = licenseMap.get(featureType);
|
||||
if (license.expiryDate() > previousLicense.expiryDate()) {
|
||||
licenseMap.put(featureType, license);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license.core;
|
||||
|
||||
import net.nicholaswilliams.java.licensing.encryption.PublicKeyDataProvider;
|
||||
import net.nicholaswilliams.java.licensing.exception.KeyNotFoundException;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class ResourcePublicKeyDataProvider implements PublicKeyDataProvider {
|
||||
|
||||
private final String resource;
|
||||
|
||||
public ResourcePublicKeyDataProvider(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncryptedPublicKeyData() throws KeyNotFoundException {
|
||||
try (InputStream inputStream = this.getClass().getResourceAsStream(resource)) {
|
||||
return Streams.copyToByteArray(inputStream);
|
||||
} catch (IOException ex) {
|
||||
throw new KeyNotFoundException(ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,139 +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 net.nicholaswilliams.java.licensing.License;
|
||||
import net.nicholaswilliams.java.licensing.encryption.Hasher;
|
||||
import net.nicholaswilliams.java.licensing.encryption.PasswordProvider;
|
||||
import net.nicholaswilliams.java.licensing.encryption.PrivateKeyDataProvider;
|
||||
import net.nicholaswilliams.java.licensing.exception.KeyNotFoundException;
|
||||
import net.nicholaswilliams.java.licensing.licensor.LicenseCreator;
|
||||
import net.nicholaswilliams.java.licensing.licensor.LicenseCreatorProperties;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class ESLicenseSigner {
|
||||
|
||||
public final static String DEFAULT_PASS_PHRASE = "elasticsearch-license";
|
||||
|
||||
private final static int VERSION_START = 0;
|
||||
private final static int VERSION = VERSION_START;
|
||||
|
||||
private final static int MAGIC_LENGTH = 13;
|
||||
|
||||
private final LicenseCreator licenseCreator;
|
||||
|
||||
private final Path publicKeyPath;
|
||||
|
||||
public ESLicenseSigner(final String privateKeyPath, final String publicKeyPath) {
|
||||
this(Paths.get(privateKeyPath), Paths.get(publicKeyPath));
|
||||
}
|
||||
|
||||
public ESLicenseSigner(final Path privateKeyPath, final Path publicKeyPath) {
|
||||
LicenseCreatorProperties.setPrivateKeyDataProvider(new PrivateKeyDataProvider() {
|
||||
@Override
|
||||
public byte[] getEncryptedPrivateKeyData() throws KeyNotFoundException {
|
||||
assert privateKeyPath.toFile().exists();
|
||||
try {
|
||||
return Files.readAllBytes(privateKeyPath);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
LicenseCreatorProperties.setPrivateKeyPasswordProvider(new PasswordProvider() {
|
||||
|
||||
@Override
|
||||
public char[] getPassword() {
|
||||
return Hasher.hash(DEFAULT_PASS_PHRASE).toCharArray();
|
||||
}
|
||||
});
|
||||
this.licenseCreator = LicenseCreator.getInstance();
|
||||
this.publicKeyPath = publicKeyPath;
|
||||
}
|
||||
|
||||
|
||||
public ImmutableSet<ESLicense> sign(Set<ESLicense> licenseSpecs) throws IOException {
|
||||
final ImmutableSet.Builder<ESLicense> builder = ImmutableSet.builder();
|
||||
for (ESLicense licenseSpec : licenseSpecs) {
|
||||
builder.add(sign(licenseSpec));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signature for the <code>esLicense</code>.
|
||||
* Signature structure:
|
||||
* | MAGIC | HEADER_LENGTH | VERSION | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT |
|
||||
*
|
||||
* @return a signed ESLicense (with signature)
|
||||
* @throws IOException
|
||||
*/
|
||||
public ESLicense sign(ESLicense licenseSpec) throws IOException {
|
||||
License.Builder licenseBuilder = new License.Builder()
|
||||
.withGoodBeforeDate(licenseSpec.expiryDate())
|
||||
.withIssueDate(licenseSpec.issueDate())
|
||||
.withProductKey(licenseSpec.uid())
|
||||
.withHolder(licenseSpec.issuedTo())
|
||||
.withIssuer(licenseSpec.issuer());
|
||||
|
||||
// NOTE: to add additional feature(s) to the internal license
|
||||
// encode the new feature(s) in featureToXContent rather
|
||||
// than doing licenseBuilder.addFeature(..)
|
||||
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
featureToXContent(licenseSpec, contentBuilder);
|
||||
licenseBuilder.addFeature(contentBuilder.string());
|
||||
|
||||
final License license = licenseBuilder.build();
|
||||
|
||||
final byte[] magic = new byte[MAGIC_LENGTH];
|
||||
Random random = new Random();
|
||||
random.nextBytes(magic);
|
||||
final byte[] licenseSignature = licenseCreator.signAndSerializeLicense(license);
|
||||
final byte[] hash = Hasher.hash(Base64.encodeBase64String(
|
||||
Files.readAllBytes(publicKeyPath))
|
||||
).getBytes(StandardCharsets.UTF_8);
|
||||
int headerLength = MAGIC_LENGTH + hash.length + 4 + 4;
|
||||
byte[] bytes = new byte[headerLength + licenseSignature.length];
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.put(magic)
|
||||
.putInt(headerLength)
|
||||
.putInt(VERSION)
|
||||
.put(hash)
|
||||
.put(licenseSignature);
|
||||
String signature = Base64.encodeBase64String(bytes);
|
||||
|
||||
return ESLicense.builder()
|
||||
.fromLicenseSpec(licenseSpec, signature)
|
||||
.verify()
|
||||
.build();
|
||||
}
|
||||
|
||||
private void featureToXContent(ESLicense license, XContentBuilder builder) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field("feature", license.feature());
|
||||
builder.field("type", license.type());
|
||||
builder.field("subscription_type", license.subscriptionType());
|
||||
builder.field("max_nodes", license.maxNodes());
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license.licensor;
|
||||
|
||||
import org.elasticsearch.common.Base64;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
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.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.core.CryptUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class LicenseSigner {
|
||||
|
||||
private final static int VERSION_START = 0;
|
||||
private final static int VERSION = VERSION_START;
|
||||
|
||||
private final static int MAGIC_LENGTH = 13;
|
||||
|
||||
private final Path publicKeyPath;
|
||||
|
||||
private final Path privateKeyPath;
|
||||
|
||||
public LicenseSigner(final String privateKeyPath, final String publicKeyPath) {
|
||||
this(Paths.get(privateKeyPath), Paths.get(publicKeyPath));
|
||||
}
|
||||
|
||||
public LicenseSigner(final Path privateKeyPath, final Path publicKeyPath) {
|
||||
this.publicKeyPath = publicKeyPath;
|
||||
this.privateKeyPath = privateKeyPath;
|
||||
}
|
||||
|
||||
|
||||
public ImmutableSet<License> sign(Set<License> licenseSpecs) throws IOException {
|
||||
final ImmutableSet.Builder<License> builder = ImmutableSet.builder();
|
||||
for (License licenseSpec : licenseSpecs) {
|
||||
builder.add(sign(licenseSpec));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a signature for the <code>licenseSpec</code>.
|
||||
* Signature structure:
|
||||
* | VERSION | MAGIC | PUB_KEY_DIGEST | SIGNED_LICENSE_CONTENT |
|
||||
*
|
||||
* @return a signed License
|
||||
* @throws IOException
|
||||
*/
|
||||
public License sign(License licenseSpec) throws IOException {
|
||||
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
licenseSpec.toXContent(contentBuilder, new ToXContent.MapParams(Collections.singletonMap(Licenses.LICENSE_SPEC_VIEW_MODE, "true")));
|
||||
|
||||
final byte[] signedContent = sign(contentBuilder.bytes().toBytes(), privateKeyPath);
|
||||
final byte[] magic = new byte[MAGIC_LENGTH];
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.nextBytes(magic);
|
||||
final byte[] hash = Base64.encodeBytesToBytes(Files.readAllBytes(publicKeyPath));
|
||||
assert hash != null;
|
||||
byte[] bytes = new byte[4 + 4 + MAGIC_LENGTH + 4 + hash.length + 4 + signedContent.length];
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.putInt(VERSION)
|
||||
.putInt(magic.length)
|
||||
.put(magic)
|
||||
.putInt(hash.length)
|
||||
.put(hash)
|
||||
.putInt(signedContent.length)
|
||||
.put(signedContent);
|
||||
|
||||
return License.builder()
|
||||
.fromLicenseSpec(licenseSpec, Base64.encodeBytes(bytes))
|
||||
.validate()
|
||||
.build();
|
||||
}
|
||||
|
||||
private static byte[] sign(byte[] data, Path privateKeyPath) {
|
||||
try {
|
||||
final Signature rsa = Signature.getInstance("SHA512withRSA");
|
||||
rsa.initSign(CryptUtils.readEncryptedPrivateKey(Files.readAllBytes(privateKeyPath)));
|
||||
rsa.update(data);
|
||||
return rsa.sign();
|
||||
} catch (InvalidKeyException | IOException | NoSuchAlgorithmException | SignatureException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,12 +5,6 @@
|
||||
*/
|
||||
package org.elasticsearch.license.licensor.tools;
|
||||
|
||||
import net.nicholaswilliams.java.licensing.encryption.Hasher;
|
||||
import net.nicholaswilliams.java.licensing.encryption.RSAKeyPairGenerator;
|
||||
import net.nicholaswilliams.java.licensing.exception.AlgorithmNotSupportedException;
|
||||
import net.nicholaswilliams.java.licensing.exception.InappropriateKeyException;
|
||||
import net.nicholaswilliams.java.licensing.exception.InappropriateKeySpecificationException;
|
||||
import net.nicholaswilliams.java.licensing.exception.RSA2048NotSupportedException;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolConfig;
|
||||
import org.elasticsearch.common.cli.Terminal;
|
||||
@ -20,17 +14,21 @@ import org.elasticsearch.env.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.*;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.cmd;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.Builder.option;
|
||||
import static org.elasticsearch.common.cli.CliToolConfig.config;
|
||||
import static org.elasticsearch.license.core.CryptUtils.writeEncryptedPrivateKey;
|
||||
import static org.elasticsearch.license.core.CryptUtils.writeEncryptedPublicKey;
|
||||
|
||||
public class KeyPairGeneratorTool extends CliTool {
|
||||
|
||||
public static final String NAME = "key-pair-generator";
|
||||
private static final CliToolConfig CONFIG = config(NAME, KeyPairGeneratorTool.class)
|
||||
.cmds(KeyPairGenerator.CMD)
|
||||
.cmds(KeyGenerator.CMD)
|
||||
.build();
|
||||
|
||||
public KeyPairGeneratorTool() {
|
||||
@ -39,14 +37,12 @@ public class KeyPairGeneratorTool extends CliTool {
|
||||
|
||||
@Override
|
||||
protected Command parse(String s, CommandLine commandLine) throws Exception {
|
||||
return KeyPairGenerator.parse(terminal, commandLine);
|
||||
return KeyGenerator.parse(terminal, commandLine);
|
||||
}
|
||||
|
||||
public static class KeyPairGenerator extends Command {
|
||||
public static class KeyGenerator extends Command {
|
||||
|
||||
public static final String DEFAULT_PASS_PHRASE = "elasticsearch-license";
|
||||
|
||||
private static final CliToolConfig.Cmd CMD = cmd(NAME, KeyPairGenerator.class)
|
||||
private static final CliToolConfig.Cmd CMD = cmd(NAME, KeyGenerator.class)
|
||||
.options(
|
||||
option("pub", "publicKeyPath").required(true).hasArg(true),
|
||||
option("pri", "privateKeyPath").required(true).hasArg(true)
|
||||
@ -55,7 +51,7 @@ public class KeyPairGeneratorTool extends CliTool {
|
||||
public final String publicKeyPath;
|
||||
public final String privateKeyPath;
|
||||
|
||||
protected KeyPairGenerator(Terminal terminal, String publicKeyPath, String privateKeyPath) {
|
||||
protected KeyGenerator(Terminal terminal, String publicKeyPath, String privateKeyPath) {
|
||||
super(terminal);
|
||||
this.privateKeyPath = privateKeyPath;
|
||||
this.publicKeyPath = publicKeyPath;
|
||||
@ -70,7 +66,7 @@ public class KeyPairGeneratorTool extends CliTool {
|
||||
} else if (exists(publicKeyPath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, publicKeyPath + " already exists");
|
||||
}
|
||||
return new KeyPairGenerator(terminal, publicKeyPath, privateKeyPath);
|
||||
return new KeyGenerator(terminal, publicKeyPath, privateKeyPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,25 +80,23 @@ public class KeyPairGeneratorTool extends CliTool {
|
||||
return new File(filePath).exists();
|
||||
}
|
||||
|
||||
private static KeyPair generateKeyPair(String privateKeyFileName, String publicKeyFileName) {
|
||||
RSAKeyPairGenerator generator = new RSAKeyPairGenerator();
|
||||
private static KeyPair generateKeyPair(String privateKeyFileName, String publicKeyFileName) throws IOException, NoSuchAlgorithmException {
|
||||
SecureRandom random = new SecureRandom();
|
||||
|
||||
KeyPair keyPair;
|
||||
try {
|
||||
keyPair = generator.generateKeyPair();
|
||||
} catch (RSA2048NotSupportedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
|
||||
keyGen.initialize(2048, random);
|
||||
KeyPair keyPair = keyGen.generateKeyPair();
|
||||
|
||||
try {
|
||||
generator.saveKeyPairToFiles(keyPair, privateKeyFileName, publicKeyFileName, Hasher.hash(DEFAULT_PASS_PHRASE).toCharArray());
|
||||
} catch (IOException | AlgorithmNotSupportedException | InappropriateKeyException | InappropriateKeySpecificationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
saveKeyPairToFiles(keyPair, privateKeyFileName, publicKeyFileName);
|
||||
return keyPair;
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveKeyPairToFiles(KeyPair keyPair, String privateKeyFileName, String publicKeyFileName) throws IOException {
|
||||
Files.write(Paths.get(privateKeyFileName), writeEncryptedPrivateKey(keyPair.getPrivate()));
|
||||
Files.write(Paths.get(publicKeyFileName), writeEncryptedPublicKey(keyPair.getPublic()));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int status = new KeyPairGeneratorTool().execute(args);
|
||||
System.exit(status);
|
||||
|
@ -16,9 +16,9 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.licensor.ESLicenseSigner;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.LicenseSigner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -59,11 +59,11 @@ public class LicenseGeneratorTool extends CliTool {
|
||||
option("lf", "licenseFile").required(false).hasArg(true)
|
||||
).build();
|
||||
|
||||
public final Set<ESLicense> licenseSpecs;
|
||||
public final Set<License> licenseSpecs;
|
||||
public final String publicKeyFilePath;
|
||||
public final String privateKeyFilePath;
|
||||
|
||||
public LicenseGenerator(Terminal terminal, String publicKeyFilePath, String privateKeyFilePath, Set<ESLicense> licenseSpecs) {
|
||||
public LicenseGenerator(Terminal terminal, String publicKeyFilePath, String privateKeyFilePath, Set<License> licenseSpecs) {
|
||||
super(terminal);
|
||||
this.licenseSpecs = licenseSpecs;
|
||||
this.privateKeyFilePath = privateKeyFilePath;
|
||||
@ -82,10 +82,10 @@ public class LicenseGeneratorTool extends CliTool {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, publicKeyPath + " does not exist");
|
||||
}
|
||||
|
||||
Set<ESLicense> licenseSpecs = new HashSet<>();
|
||||
Set<License> licenseSpecs = new HashSet<>();
|
||||
if (licenseSpecSources != null) {
|
||||
for (String licenseSpec : licenseSpecSources) {
|
||||
licenseSpecs.addAll(ESLicenses.fromSource(licenseSpec.getBytes(StandardCharsets.UTF_8), false));
|
||||
licenseSpecs.addAll(Licenses.fromSource(licenseSpec.getBytes(StandardCharsets.UTF_8), false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ public class LicenseGeneratorTool extends CliTool {
|
||||
if (doesNotExist(licenseSpecFilePath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, licenseSpecFilePath + " does not exist");
|
||||
}
|
||||
licenseSpecs.addAll(ESLicenses.fromSource(Files.readAllBytes(licenseSpecPath), false));
|
||||
licenseSpecs.addAll(Licenses.fromSource(Files.readAllBytes(licenseSpecPath), false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,12 +109,12 @@ public class LicenseGeneratorTool extends CliTool {
|
||||
public ExitStatus execute(Settings settings, Environment env) throws Exception {
|
||||
|
||||
// sign
|
||||
ESLicenseSigner signer = new ESLicenseSigner(privateKeyFilePath, publicKeyFilePath);
|
||||
ImmutableSet<ESLicense> signedLicences = signer.sign(licenseSpecs);
|
||||
LicenseSigner signer = new LicenseSigner(privateKeyFilePath, publicKeyFilePath);
|
||||
ImmutableSet<License> signedLicences = signer.sign(licenseSpecs);
|
||||
|
||||
// dump
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
ESLicenses.toXContent(signedLicences, builder, ToXContent.EMPTY_PARAMS);
|
||||
Licenses.toXContent(signedLicences, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
terminal.print(builder.string());
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
*/
|
||||
package org.elasticsearch.license.licensor.tools;
|
||||
|
||||
import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolConfig;
|
||||
import org.elasticsearch.common.cli.Terminal;
|
||||
@ -16,9 +15,9 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.manager.ESLicenseManager;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -38,7 +37,7 @@ public class LicenseVerificationTool extends CliTool {
|
||||
public static final String NAME = "verify-license";
|
||||
|
||||
private static final CliToolConfig CONFIG = config(NAME, LicenseVerificationTool.class)
|
||||
.cmds(LicenseVerifier.CMD)
|
||||
.cmds(LicenseVerificationTool.LicenseVerifier.CMD)
|
||||
.build();
|
||||
|
||||
public LicenseVerificationTool() {
|
||||
@ -47,7 +46,7 @@ public class LicenseVerificationTool extends CliTool {
|
||||
|
||||
@Override
|
||||
protected Command parse(String s, CommandLine commandLine) throws Exception {
|
||||
return LicenseVerifier.parse(terminal, commandLine);
|
||||
return LicenseVerificationTool.LicenseVerifier.parse(terminal, commandLine);
|
||||
}
|
||||
|
||||
public static class LicenseVerifier extends Command {
|
||||
@ -58,9 +57,9 @@ public class LicenseVerificationTool extends CliTool {
|
||||
option("lf", "licenseFile").required(false).hasArg(true)
|
||||
).build();
|
||||
|
||||
public final Set<ESLicense> licenses;
|
||||
public final Set<License> licenses;
|
||||
|
||||
public LicenseVerifier(Terminal terminal, Set<ESLicense> licenses) {
|
||||
public LicenseVerifier(Terminal terminal, Set<License> licenses) {
|
||||
super(terminal);
|
||||
this.licenses = licenses;
|
||||
}
|
||||
@ -69,10 +68,10 @@ public class LicenseVerificationTool extends CliTool {
|
||||
String[] licenseSources = commandLine.getOptionValues("license");
|
||||
String[] licenseSourceFiles = commandLine.getOptionValues("licenseFile");
|
||||
|
||||
Set<ESLicense> esLicenses = new HashSet<>();
|
||||
Set<License> licenses = new HashSet<>();
|
||||
if (licenseSources != null) {
|
||||
for (String licenseSpec : licenseSources) {
|
||||
esLicenses.addAll(ESLicenses.fromSource(licenseSpec.getBytes(StandardCharsets.UTF_8)));
|
||||
licenses.addAll(Licenses.fromSource(licenseSpec.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,31 +81,30 @@ public class LicenseVerificationTool extends CliTool {
|
||||
if (!exists(licenseFilePath)) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, licenseFilePath + " does not exist");
|
||||
}
|
||||
esLicenses.addAll(ESLicenses.fromSource(Files.readAllBytes(licensePath)));
|
||||
licenses.addAll(Licenses.fromSource(Files.readAllBytes(licensePath)));
|
||||
}
|
||||
}
|
||||
|
||||
if (esLicenses.size() == 0) {
|
||||
if (licenses.size() == 0) {
|
||||
return exitCmd(ExitStatus.USAGE, terminal, "no license provided");
|
||||
}
|
||||
return new LicenseVerifier(terminal, esLicenses);
|
||||
return new LicenseVerifier(terminal, licenses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExitStatus execute(Settings settings, Environment env) throws Exception {
|
||||
|
||||
// verify
|
||||
Map<String, ESLicense> effectiveLicenses = ESLicenses.reduceAndMap(licenses);
|
||||
ESLicenseManager licenseManager = new ESLicenseManager();
|
||||
try {
|
||||
licenseManager.verifyLicenses(effectiveLicenses);
|
||||
} catch (InvalidLicenseException e) {
|
||||
Map<String, License> effectiveLicenses = Licenses.reduceAndMap(licenses);
|
||||
org.elasticsearch.license.core.LicenseVerifier licenseVerifier = new org.elasticsearch.license.core.LicenseVerifier();
|
||||
|
||||
if (!org.elasticsearch.license.core.LicenseVerifier.verifyLicenses(effectiveLicenses.values())) {
|
||||
return ExitStatus.DATA_ERROR;
|
||||
}
|
||||
|
||||
// dump effective licences
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
ESLicenses.toXContent(effectiveLicenses.values(), builder, ToXContent.EMPTY_PARAMS);
|
||||
Licenses.toXContent(effectiveLicenses.values(), builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
terminal.print(builder.string());
|
||||
|
||||
|
@ -1,234 +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.manager;
|
||||
|
||||
import net.nicholaswilliams.java.licensing.*;
|
||||
import net.nicholaswilliams.java.licensing.encryption.Hasher;
|
||||
import net.nicholaswilliams.java.licensing.encryption.PasswordProvider;
|
||||
import net.nicholaswilliams.java.licensing.exception.ExpiredLicenseException;
|
||||
import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ResourcePublicKeyDataProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Class responsible for reading signed licenses, maintaining an effective esLicenses instance, verification of licenses
|
||||
* and querying against licenses on a feature basis
|
||||
* <p/>
|
||||
*/
|
||||
public class ESLicenseManager {
|
||||
|
||||
private final LicenseManager licenseManager;
|
||||
|
||||
private static class FeatureFields {
|
||||
static final String MAX_NODES = "max_nodes";
|
||||
static final String TYPE = "type";
|
||||
static final String SUBSCRIPTION_TYPE = "subscription_type";
|
||||
static final String FEATURE = "feature";
|
||||
}
|
||||
|
||||
// Initialize LicenseManager
|
||||
static {
|
||||
LicenseManagerProperties.setPublicKeyDataProvider(new ResourcePublicKeyDataProvider("/public.key"));
|
||||
LicenseManagerProperties.setPublicKeyPasswordProvider(new ESPublicKeyPasswordProvider());
|
||||
LicenseManagerProperties.setLicenseValidator(new DefaultLicenseValidator());
|
||||
LicenseManagerProperties.setLicenseProvider(new LicenseProvider() {
|
||||
@Override
|
||||
public SignedLicense getLicense(Object context) {
|
||||
throw new UnsupportedOperationException("This singleton license provider shouldn't be used");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Inject
|
||||
public ESLicenseManager() {
|
||||
this.licenseManager = LicenseManager.getInstance();
|
||||
}
|
||||
|
||||
public ImmutableSet<String> toSignatures(Collection<ESLicense> esLicenses) {
|
||||
Set<String> signatures = new HashSet<>();
|
||||
for (ESLicense esLicense : esLicenses) {
|
||||
signatures.add(esLicense.signature());
|
||||
}
|
||||
return ImmutableSet.copyOf(signatures);
|
||||
}
|
||||
|
||||
public ImmutableSet<ESLicense> fromSignatures(Set<String> signatures) {
|
||||
Set<ESLicense> esLicenses = new HashSet<>();
|
||||
for (String signature : signatures) {
|
||||
esLicenses.add(fromSignature(signature));
|
||||
}
|
||||
return ImmutableSet.copyOf(esLicenses);
|
||||
}
|
||||
|
||||
public void verifyLicenses(Map<String, ESLicense> esLicenses) {
|
||||
try {
|
||||
for (String feature : esLicenses.keySet()) {
|
||||
ESLicense esLicense = esLicenses.get(feature);
|
||||
// verify signature
|
||||
final License license = this.licenseManager.decryptAndVerifyLicense(
|
||||
extractSignedLicence(esLicense.signature()));
|
||||
// validate license
|
||||
this.licenseManager.validateLicense(license);
|
||||
|
||||
// verify all readable license fields
|
||||
verifyLicenseFields(license, esLicense);
|
||||
}
|
||||
} catch (InvalidLicenseException e) {
|
||||
throw new InvalidLicenseException("Invalid License");
|
||||
}
|
||||
}
|
||||
|
||||
private ESLicense fromSignature(String signature) {
|
||||
final SignedLicense signedLicense = extractSignedLicence(signature);
|
||||
License license = licenseManager.decryptAndVerifyLicense(signedLicense);
|
||||
ESLicense.Builder builder = ESLicense.builder();
|
||||
|
||||
if (license.getFeatures().size() == 1) {
|
||||
try {
|
||||
String featureName = license.getFeatures().get(0).getName();
|
||||
LicenseFeatures licenseFeatures = licenseFeaturesFromSource(featureName);
|
||||
builder.maxNodes(licenseFeatures.maxNodes)
|
||||
.feature(licenseFeatures.feature)
|
||||
.type(licenseFeatures.type)
|
||||
.subscriptionType(licenseFeatures.subscriptionType);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return builder
|
||||
.uid(license.getProductKey())
|
||||
.issuer(license.getIssuer())
|
||||
.issuedTo(license.getHolder())
|
||||
.issueDate(license.getIssueDate())
|
||||
.expiryDate(license.getGoodBeforeDate())
|
||||
.signature(signature)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static void verifyLicenseFields(License license, ESLicense eslicense) {
|
||||
boolean licenseValid = license.getProductKey().equals(eslicense.uid())
|
||||
&& license.getHolder().equals(eslicense.issuedTo())
|
||||
&& license.getIssueDate() == eslicense.issueDate()
|
||||
&& license.getGoodBeforeDate() == eslicense.expiryDate();
|
||||
boolean maxNodesValid = false;
|
||||
boolean featureValid = false;
|
||||
boolean typeValid = false;
|
||||
boolean subscriptionTypeValid = false;
|
||||
|
||||
if (license.getFeatures().size() == 1) {
|
||||
try {
|
||||
String featureName = license.getFeatures().get(0).getName();
|
||||
LicenseFeatures licenseFeatures = licenseFeaturesFromSource(featureName);
|
||||
maxNodesValid = eslicense.maxNodes() == licenseFeatures.maxNodes;
|
||||
typeValid = eslicense.type().equals(licenseFeatures.type);
|
||||
subscriptionTypeValid = eslicense.subscriptionType().equals(licenseFeatures.subscriptionType);
|
||||
featureValid = eslicense.feature().equals(licenseFeatures.feature);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
if (!licenseValid || !featureValid || !maxNodesValid || !typeValid || !subscriptionTypeValid) {
|
||||
throw new InvalidLicenseException("Invalid License");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a signedLicense (SIGNED_LICENSE_CONTENT) from the signature.
|
||||
* Validates the public key used to decrypt the license by comparing their hashes
|
||||
* <p/>
|
||||
* 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
|
||||
*/
|
||||
private static SignedLicense extractSignedLicence(String signature) {
|
||||
byte[] signatureBytes = Base64.decodeBase64(signature);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(signatureBytes);
|
||||
byteBuffer = (ByteBuffer) byteBuffer.position(13);
|
||||
int start = byteBuffer.getInt();
|
||||
int version = byteBuffer.getInt();
|
||||
return new ObjectSerializer().readObject(SignedLicense.class, Arrays.copyOfRange(signatureBytes, start, signatureBytes.length));
|
||||
}
|
||||
|
||||
private static LicenseFeatures licenseFeaturesFromSource(String source) throws IOException {
|
||||
XContentParser parser = XContentFactory.xContent(source).createParser(source);
|
||||
|
||||
String feature = null;
|
||||
String type = null;
|
||||
String subscriptionType = null;
|
||||
int maxNodes = -1;
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
String currentFieldName = parser.currentName();
|
||||
token = parser.nextToken();
|
||||
if (token.isValue()) {
|
||||
if (token == XContentParser.Token.VALUE_STRING) {
|
||||
switch (currentFieldName) {
|
||||
case FeatureFields.FEATURE:
|
||||
feature = parser.text();
|
||||
break;
|
||||
case FeatureFields.TYPE:
|
||||
type = parser.text();
|
||||
break;
|
||||
case FeatureFields.SUBSCRIPTION_TYPE:
|
||||
subscriptionType = parser.text();
|
||||
break;
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||
if (FeatureFields.MAX_NODES.equals(currentFieldName)) {
|
||||
maxNodes = parser.intValue();
|
||||
}
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
// It was probably created by newer version - ignoring
|
||||
parser.skipChildren();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
// It was probably created by newer version - ignoring
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Should we throw a ElasticsearchParseException here?
|
||||
return new LicenseFeatures(feature, type, subscriptionType, maxNodes);
|
||||
}
|
||||
|
||||
private static class LicenseFeatures {
|
||||
private final String feature;
|
||||
private final String type;
|
||||
private final String subscriptionType;
|
||||
private final int maxNodes;
|
||||
|
||||
private LicenseFeatures(String feature, String type, String subscriptionType, int maxNodes) {
|
||||
this.feature = feature;
|
||||
this.type = type;
|
||||
this.subscriptionType = subscriptionType;
|
||||
this.maxNodes = maxNodes;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Need a better password management
|
||||
private static class ESPublicKeyPasswordProvider implements PasswordProvider {
|
||||
private final String DEFAULT_PASS_PHRASE = "elasticsearch-license";
|
||||
|
||||
@Override
|
||||
public char[] getPassword() {
|
||||
return Hasher.hash(DEFAULT_PASS_PHRASE).toCharArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,13 +7,13 @@ package org.elasticsearch.license.plugin;
|
||||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.Scopes;
|
||||
import org.elasticsearch.license.manager.ESLicenseManager;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
|
||||
public class LicenseModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ESLicenseManager.class).in(Scopes.SINGLETON);
|
||||
bind(LicenseVerifier.class).in(Scopes.SINGLETON);
|
||||
bind(LicensesService.class).in(Scopes.SINGLETON);
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ package org.elasticsearch.license.plugin.action.get;
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -17,29 +17,29 @@ import java.util.List;
|
||||
|
||||
public class GetLicenseResponse extends ActionResponse {
|
||||
|
||||
private List<ESLicense> licenses = new ArrayList<>();
|
||||
private List<License> licenses = new ArrayList<>();
|
||||
|
||||
GetLicenseResponse() {
|
||||
}
|
||||
|
||||
GetLicenseResponse(List<ESLicense> esLicenses) {
|
||||
this.licenses = esLicenses;
|
||||
GetLicenseResponse(List<License> licenses) {
|
||||
this.licenses = licenses;
|
||||
}
|
||||
|
||||
public List<ESLicense> licenses() {
|
||||
public List<License> licenses() {
|
||||
return licenses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
licenses = ESLicenses.readFrom(in);
|
||||
licenses = Licenses.readFrom(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
ESLicenses.writeTo(licenses, out);
|
||||
Licenses.writeTo(licenses, out);
|
||||
}
|
||||
|
||||
}
|
@ -10,8 +10,8 @@ import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@ -19,7 +19,7 @@ import java.util.List;
|
||||
|
||||
public class PutLicenseRequest extends AcknowledgedRequest<PutLicenseRequest> {
|
||||
|
||||
private List<ESLicense> licenses;
|
||||
private List<License> licenses;
|
||||
|
||||
public PutLicenseRequest() {
|
||||
}
|
||||
@ -30,38 +30,38 @@ public class PutLicenseRequest extends AcknowledgedRequest<PutLicenseRequest> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses licenses from json format to an instance of {@link org.elasticsearch.license.core.ESLicenses}
|
||||
* Parses licenses from json format to an instance of {@link org.elasticsearch.license.core.Licenses}
|
||||
*
|
||||
* @param licenseDefinition licenses definition
|
||||
*/
|
||||
public PutLicenseRequest licenses(String licenseDefinition) {
|
||||
try {
|
||||
return licenses(ESLicenses.fromSource(licenseDefinition));
|
||||
return licenses(Licenses.fromSource(licenseDefinition));
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchIllegalArgumentException("failed to parse licenses source", e);
|
||||
}
|
||||
}
|
||||
|
||||
public PutLicenseRequest licenses(List<ESLicense> esLicenses) {
|
||||
this.licenses = esLicenses;
|
||||
public PutLicenseRequest licenses(List<License> licenses) {
|
||||
this.licenses = licenses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<ESLicense> licenses() {
|
||||
public List<License> licenses() {
|
||||
return licenses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
licenses = ESLicenses.readFrom(in);
|
||||
licenses = Licenses.readFrom(in);
|
||||
readTimeout(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
ESLicenses.writeTo(licenses, out);
|
||||
Licenses.writeTo(licenses, out);
|
||||
writeTimeout(out);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ package org.elasticsearch.license.plugin.action.put;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedRequestBuilder;
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.License;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -32,7 +32,7 @@ public class PutLicenseRequestBuilder extends AcknowledgedRequestBuilder<PutLice
|
||||
* @param licenses license
|
||||
* @return this builder
|
||||
*/
|
||||
public PutLicenseRequestBuilder setLicense(List<ESLicense> licenses) {
|
||||
public PutLicenseRequestBuilder setLicense(List<License> licenses) {
|
||||
request.licenses(licenses);
|
||||
return this;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ package org.elasticsearch.license.plugin.core;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.common.inject.ImplementedBy;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.License;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -39,5 +39,5 @@ public interface LicensesManagerService {
|
||||
/**
|
||||
* @return a list of licenses, contains one license (with the latest expiryDate) per registered features sorted by latest issueDate
|
||||
*/
|
||||
public List<ESLicense> getLicenses();
|
||||
public List<License> getLicenses();
|
||||
}
|
||||
|
@ -6,17 +6,18 @@
|
||||
package org.elasticsearch.license.plugin.core;
|
||||
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.collect.Sets;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Contains metadata about registered licenses
|
||||
@ -27,69 +28,43 @@ public class LicensesMetaData implements MetaData.Custom {
|
||||
|
||||
public static final Factory FACTORY = new Factory();
|
||||
|
||||
private final Set<String> signatures;
|
||||
private final ImmutableList<License> signedLicenses;
|
||||
|
||||
private final Set<String> encodedTrialLicenses;
|
||||
|
||||
public LicensesMetaData(String[] signatures, String[] encodedTrialLicenses) {
|
||||
this(Sets.newHashSet(signatures), Sets.newHashSet(encodedTrialLicenses));
|
||||
}
|
||||
private final ImmutableList<License> trialLicenses;
|
||||
|
||||
/**
|
||||
* Constructs new licenses metadata
|
||||
*
|
||||
* @param signatures set of esLicense signatures
|
||||
* @param encodedTrialLicenses set of encoded trial licenses
|
||||
* @param signedLicenses list of signed Licenses
|
||||
* @param trialLicenses set of encoded trial licenses
|
||||
*/
|
||||
public LicensesMetaData(Set<String> signatures, Set<String> encodedTrialLicenses) {
|
||||
this.signatures = signatures;
|
||||
this.encodedTrialLicenses = encodedTrialLicenses;
|
||||
public LicensesMetaData(List<License> signedLicenses, List<License> trialLicenses) {
|
||||
this.signedLicenses = ImmutableList.copyOf(signedLicenses);
|
||||
this.trialLicenses = ImmutableList.copyOf(trialLicenses);
|
||||
}
|
||||
|
||||
public Set<String> getSignatures() {
|
||||
return signatures;
|
||||
public List<License> getSignedLicenses() {
|
||||
return signedLicenses;
|
||||
}
|
||||
|
||||
public Set<String> getEncodedTrialLicenses() {
|
||||
return encodedTrialLicenses;
|
||||
public List<License> getTrialLicenses() {
|
||||
return trialLicenses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof LicensesMetaData) {
|
||||
LicensesMetaData other = (LicensesMetaData) obj;
|
||||
boolean signaturesEqual;
|
||||
boolean trialLicensesEqual;
|
||||
LicensesMetaData that = (LicensesMetaData) obj;
|
||||
return signedLicenses.equals(that.signedLicenses)
|
||||
&& trialLicenses.equals(that.trialLicenses);
|
||||
}
|
||||
|
||||
if (other.getSignatures() != null) {
|
||||
if (this.getSignatures() != null) {
|
||||
signaturesEqual = other.getSignatures().equals(this.getSignatures());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
signaturesEqual = this.getSignatures() == null;
|
||||
}
|
||||
|
||||
if (other.getEncodedTrialLicenses() != null) {
|
||||
if (this.getEncodedTrialLicenses() != null) {
|
||||
trialLicensesEqual = other.getEncodedTrialLicenses().equals(this.getEncodedTrialLicenses());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
trialLicensesEqual = this.getEncodedTrialLicenses() == null;
|
||||
}
|
||||
|
||||
return signaturesEqual && trialLicensesEqual;
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return signedLicenses.hashCode() + 31 * trialLicenses.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,13 +85,13 @@ public class LicensesMetaData implements MetaData.Custom {
|
||||
*/
|
||||
@Override
|
||||
public LicensesMetaData readFrom(StreamInput in) throws IOException {
|
||||
String[] signatures = new String[0];
|
||||
String[] encodedTrialLicenses = new String[0];
|
||||
if (in.readBoolean()) {
|
||||
signatures = in.readStringArray();
|
||||
encodedTrialLicenses = in.readStringArray();
|
||||
List<License> signedLicenses = Licenses.readFrom(in);
|
||||
int numTrialLicenses = in.readVInt();
|
||||
List<License> trialLicenses = new ArrayList<>(numTrialLicenses);
|
||||
for (int i = 0; i < numTrialLicenses; i++) {
|
||||
trialLicenses.add(TrialLicenseUtils.fromEncodedTrialLicense(in.readString()));
|
||||
}
|
||||
return new LicensesMetaData(signatures, encodedTrialLicenses);
|
||||
return new LicensesMetaData(signedLicenses, trialLicenses);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -124,12 +99,10 @@ public class LicensesMetaData implements MetaData.Custom {
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(LicensesMetaData licensesMetaData, StreamOutput out) throws IOException {
|
||||
if (licensesMetaData == null) {
|
||||
out.writeBoolean(false);
|
||||
} else {
|
||||
out.writeBoolean(true);
|
||||
out.writeStringArray(licensesMetaData.signatures.toArray(new String[licensesMetaData.signatures.size()]));
|
||||
out.writeStringArray(licensesMetaData.encodedTrialLicenses.toArray(new String[licensesMetaData.encodedTrialLicenses.size()]));
|
||||
Licenses.writeTo(licensesMetaData.signedLicenses, out);
|
||||
out.writeVInt(licensesMetaData.trialLicenses.size());
|
||||
for (License trialLicense : licensesMetaData.trialLicenses) {
|
||||
out.writeString(TrialLicenseUtils.toEncodedTrialLicense(trialLicense));
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,36 +111,35 @@ public class LicensesMetaData implements MetaData.Custom {
|
||||
*/
|
||||
@Override
|
||||
public LicensesMetaData fromXContent(XContentParser parser) throws IOException {
|
||||
List<License> trialLicenses = new ArrayList<>();
|
||||
List<License> signedLicenses = new ArrayList<>();
|
||||
XContentParser.Token token;
|
||||
String fieldName = null;
|
||||
Set<String> encodedTrialLicenses = new HashSet<>();
|
||||
Set<String> signatures = new HashSet<>();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
while (parser.currentToken() != XContentParser.Token.END_OBJECT) {
|
||||
token = parser.nextToken();
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
fieldName = parser.currentName();
|
||||
}
|
||||
if (fieldName != null) {
|
||||
if (fieldName.equals(Fields.LICENSES)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
if (parser.currentToken().isValue()) {
|
||||
signatures.add(parser.text());
|
||||
String fieldName = parser.text();
|
||||
if (fieldName != null) {
|
||||
if (fieldName.equals(Fields.TRIAL_LICENSES)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
if (parser.currentToken().isValue()) {
|
||||
trialLicenses.add(TrialLicenseUtils.fromEncodedTrialLicense(parser.text()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fieldName.equals(Fields.TRIAL_LICENSES)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
if (parser.currentToken().isValue()) {
|
||||
encodedTrialLicenses.add(parser.text());
|
||||
if (fieldName.equals(Fields.SIGNED_LICENCES)) {
|
||||
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
License.Builder builder = License.builder().fromXContent(parser);
|
||||
signedLicenses.add(builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new LicensesMetaData(signatures, encodedTrialLicenses);
|
||||
return new LicensesMetaData(signedLicenses, trialLicenses);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,8 +147,17 @@ public class LicensesMetaData implements MetaData.Custom {
|
||||
*/
|
||||
@Override
|
||||
public void toXContent(LicensesMetaData licensesMetaData, XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.array(Fields.LICENSES, licensesMetaData.signatures.toArray(new String[licensesMetaData.signatures.size()]));
|
||||
builder.array(Fields.TRIAL_LICENSES, licensesMetaData.encodedTrialLicenses.toArray(new String[licensesMetaData.encodedTrialLicenses.size()]));
|
||||
builder.startArray(Fields.TRIAL_LICENSES);
|
||||
for (License trailLicense : licensesMetaData.trialLicenses) {
|
||||
builder.value(TrialLicenseUtils.toEncodedTrialLicense(trailLicense));
|
||||
}
|
||||
builder.endArray();
|
||||
|
||||
builder.startArray(Fields.SIGNED_LICENCES);
|
||||
for (License license : licensesMetaData.signedLicenses) {
|
||||
license.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -184,12 +165,9 @@ public class LicensesMetaData implements MetaData.Custom {
|
||||
return EnumSet.of(MetaData.XContentContext.GATEWAY);
|
||||
}
|
||||
|
||||
|
||||
private final static class Fields {
|
||||
private static final String LICENSES = "licenses";
|
||||
private static final String SIGNED_LICENCES = "signed_licenses";
|
||||
private static final String TRIAL_LICENSES = "trial_licenses";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
*/
|
||||
package org.elasticsearch.license.plugin.core;
|
||||
|
||||
import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.*;
|
||||
@ -13,8 +12,8 @@ import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.cluster.metadata.MetaData;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.collect.Sets;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.component.Lifecycle;
|
||||
@ -26,8 +25,8 @@ import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
|
||||
import org.elasticsearch.gateway.GatewayService;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.manager.ESLicenseManager;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
@ -42,7 +41,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.elasticsearch.license.core.ESLicenses.reduceAndMap;
|
||||
import static org.elasticsearch.license.core.Licenses.reduceAndMap;
|
||||
|
||||
/**
|
||||
* Service responsible for managing {@link LicensesMetaData}
|
||||
@ -89,8 +88,6 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
|
||||
public static final String REGISTER_TRIAL_LICENSE_ACTION_NAME = "internal:plugin/licenses/cluster/register_trial_license";
|
||||
|
||||
private final ESLicenseManager licenseManager;
|
||||
|
||||
private final ClusterService clusterService;
|
||||
|
||||
private final ThreadPool threadPool;
|
||||
@ -119,10 +116,9 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
private final AtomicReference<LicensesMetaData> lastObservedLicensesState;
|
||||
|
||||
@Inject
|
||||
public LicensesService(Settings settings, ClusterService clusterService, ThreadPool threadPool, TransportService transportService, ESLicenseManager licenseManager) {
|
||||
public LicensesService(Settings settings, ClusterService clusterService, ThreadPool threadPool, TransportService transportService) {
|
||||
super(settings);
|
||||
this.clusterService = clusterService;
|
||||
this.licenseManager = licenseManager;
|
||||
this.threadPool = threadPool;
|
||||
this.transportService = transportService;
|
||||
this.lastObservedLicensesState = new AtomicReference<>(null);
|
||||
@ -137,7 +133,7 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
@Override
|
||||
public void registerLicenses(final PutLicenseRequestHolder requestHolder, final ActionListener<LicensesUpdateResponse> listener) {
|
||||
final PutLicenseRequest request = requestHolder.request;
|
||||
final Set<ESLicense> newLicenses = Sets.newHashSet(request.licenses());
|
||||
final Set<License> newLicenses = Sets.newHashSet(request.licenses());
|
||||
LicensesStatus status = checkLicenses(newLicenses);
|
||||
if (status == LicensesStatus.VALID) {
|
||||
clusterService.submitStateUpdateTask(requestHolder.source, new AckedClusterStateUpdateTask<LicensesUpdateResponse>(request, listener) {
|
||||
@ -152,10 +148,9 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
|
||||
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses);
|
||||
Set<String> newSignatures = licenseManager.toSignatures(newLicenses);
|
||||
Set<String> newLicenseSignatures = Sets.union(licensesWrapper.signatures, newSignatures);
|
||||
if (newLicenseSignatures.size() != licensesWrapper.signatures.size()) {
|
||||
LicensesMetaData newLicensesMetaData = new LicensesMetaData(newLicenseSignatures, licensesWrapper.encodedTrialLicenses);
|
||||
List<License> updatedSignedLicenses = licensesWrapper.addAndGetSignedLicenses(newLicenses);
|
||||
if (updatedSignedLicenses.size() != licensesWrapper.signedLicenses.size()) {
|
||||
LicensesMetaData newLicensesMetaData = new LicensesMetaData(updatedSignedLicenses, licensesWrapper.trialLicenses);
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
@ -197,18 +192,9 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
MetaData metaData = currentState.metaData();
|
||||
LicensesMetaData currentLicenses = metaData.custom(LicensesMetaData.TYPE);
|
||||
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicenses);
|
||||
|
||||
Set<ESLicense> currentSignedLicenses = licensesWrapper.signedLicenses(licenseManager);
|
||||
Set<ESLicense> licensesToDelete = new HashSet<>();
|
||||
for (ESLicense license : currentSignedLicenses) {
|
||||
if (request.features().contains(license.feature())) {
|
||||
licensesToDelete.add(license);
|
||||
}
|
||||
}
|
||||
if (!licensesToDelete.isEmpty()) {
|
||||
Set<ESLicense> reducedLicenses = Sets.difference(currentSignedLicenses, licensesToDelete);
|
||||
Set<String> newSignatures = licenseManager.toSignatures(reducedLicenses);
|
||||
LicensesMetaData newLicensesMetaData = new LicensesMetaData(newSignatures, licensesWrapper.encodedTrialLicenses);
|
||||
List<License> updatedSignedLicenses = licensesWrapper.removeAndGetSignedLicenses(request.features());
|
||||
if (updatedSignedLicenses.size() != licensesWrapper.signedLicenses.size()) {
|
||||
LicensesMetaData newLicensesMetaData = new LicensesMetaData(updatedSignedLicenses, licensesWrapper.trialLicenses);
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
@ -237,20 +223,21 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<ESLicense> getLicenses() {
|
||||
LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
|
||||
public List<License> getLicenses() {
|
||||
final LicensesMetaData currentMetaData = clusterService.state().metaData().custom(LicensesMetaData.TYPE);
|
||||
if (currentMetaData != null) {
|
||||
// don't use ESLicenses.reduceAndMap, as it will merge out expired licenses
|
||||
Set<ESLicense> licenses = Sets.union(licenseManager.fromSignatures(currentMetaData.getSignatures()),
|
||||
TrialLicenseUtils.fromEncodedTrialLicenses(currentMetaData.getEncodedTrialLicenses()));
|
||||
List<License> currentLicenses = new ArrayList<>();
|
||||
currentLicenses.addAll(currentMetaData.getSignedLicenses());
|
||||
currentLicenses.addAll(currentMetaData.getTrialLicenses());
|
||||
|
||||
// bucket license for feature with the latest expiry date
|
||||
Map<String, ESLicense> licenseMap = new HashMap<>();
|
||||
for (ESLicense license : licenses) {
|
||||
Map<String, License> licenseMap = new HashMap<>();
|
||||
for (License license : currentLicenses) {
|
||||
if (!licenseMap.containsKey(license.feature())) {
|
||||
licenseMap.put(license.feature(), license);
|
||||
} else {
|
||||
ESLicense prevLicense = licenseMap.get(license.feature());
|
||||
License prevLicense = licenseMap.get(license.feature());
|
||||
if (license.expiryDate() > prevLicense.expiryDate()) {
|
||||
licenseMap.put(license.feature(), license);
|
||||
}
|
||||
@ -258,10 +245,10 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
}
|
||||
|
||||
// sort the licenses by issue date
|
||||
List<ESLicense> reducedLicenses = new ArrayList<>(licenseMap.values());
|
||||
Collections.sort(reducedLicenses, new Comparator<ESLicense>() {
|
||||
List<License> reducedLicenses = new ArrayList<>(licenseMap.values());
|
||||
Collections.sort(reducedLicenses, new Comparator<License>() {
|
||||
@Override
|
||||
public int compare(ESLicense license1, ESLicense license2) {
|
||||
public int compare(License license1, License license2) {
|
||||
return (int) (license2.issueDate() - license1.issueDate());
|
||||
}
|
||||
});
|
||||
@ -270,19 +257,13 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private LicensesStatus checkLicenses(Set<ESLicense> licenses) {
|
||||
final ImmutableMap<String, ESLicense> map = reduceAndMap(licenses);
|
||||
return checkLicenses(map);
|
||||
}
|
||||
|
||||
private LicensesStatus checkLicenses(Map<String, ESLicense> licenseMap) {
|
||||
LicensesStatus status = LicensesStatus.VALID;
|
||||
try {
|
||||
licenseManager.verifyLicenses(licenseMap);
|
||||
} catch (InvalidLicenseException e) {
|
||||
status = LicensesStatus.INVALID;
|
||||
private LicensesStatus checkLicenses(Set<License> licenses) {
|
||||
final ImmutableMap<String, License> map = reduceAndMap(licenses);
|
||||
if (LicenseVerifier.verifyLicenses(map.values())) {
|
||||
return LicensesStatus.VALID;
|
||||
} else {
|
||||
return LicensesStatus.INVALID;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,10 +287,10 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
final LicensesWrapper licensesWrapper = LicensesWrapper.wrap(currentLicensesMetaData);
|
||||
// do not generate a trial license for a feature that already has a signed/trial license
|
||||
if (checkTrialLicenseGenerationCondition(request.feature, licensesWrapper)) {
|
||||
Set<String> newTrialLicenses = Sets.union(licensesWrapper.encodedTrialLicenses,
|
||||
Sets.newHashSet(generateEncodedTrialLicense(request.feature, request.duration, request.maxNodes)));
|
||||
List<License> currentTrailLicenses = new ArrayList<>(licensesWrapper.trialLicenses);
|
||||
currentTrailLicenses.add(generateEncodedTrialLicense(request.feature, request.duration, request.maxNodes));
|
||||
final LicensesMetaData newLicensesMetaData = new LicensesMetaData(
|
||||
licensesWrapper.signatures, newTrialLicenses);
|
||||
licensesWrapper.signedLicenses, ImmutableList.copyOf(currentTrailLicenses));
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, newLicensesMetaData);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
@ -322,8 +303,10 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
}
|
||||
|
||||
private boolean checkTrialLicenseGenerationCondition(String feature, LicensesWrapper licensesWrapper) {
|
||||
for (ESLicense license : Sets.union(licensesWrapper.signedLicenses(licenseManager),
|
||||
licensesWrapper.trialLicenses())) {
|
||||
final List<License> currentLicenses = new ArrayList<>();
|
||||
currentLicenses.addAll(licensesWrapper.signedLicenses);
|
||||
currentLicenses.addAll(licensesWrapper.trialLicenses);
|
||||
for (License license : currentLicenses) {
|
||||
if (license.feature().equals(feature)) {
|
||||
return false;
|
||||
}
|
||||
@ -331,16 +314,14 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
return true;
|
||||
}
|
||||
|
||||
private String generateEncodedTrialLicense(String feature, TimeValue duration, int maxNodes) {
|
||||
return TrialLicenseUtils.toEncodedTrialLicense(
|
||||
TrialLicenseUtils.builder()
|
||||
.issuedTo(clusterService.state().getClusterName().value())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.duration(duration)
|
||||
.feature(feature)
|
||||
.maxNodes(maxNodes)
|
||||
.build()
|
||||
);
|
||||
private License generateEncodedTrialLicense(String feature, TimeValue duration, int maxNodes) {
|
||||
return TrialLicenseUtils.builder()
|
||||
.issuedTo(clusterService.state().getClusterName().value())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.duration(duration)
|
||||
.feature(feature)
|
||||
.maxNodes(maxNodes)
|
||||
.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -510,12 +491,12 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (licensesMetaData != null) {
|
||||
StringBuilder signedFeatures = new StringBuilder();
|
||||
for (ESLicense license : licenseManager.fromSignatures(licensesMetaData.getSignatures())) {
|
||||
for (License license : licensesMetaData.getSignedLicenses()) {
|
||||
signedFeatures.append(license.feature());
|
||||
signedFeatures.append(", ");
|
||||
}
|
||||
StringBuilder trialFeatures = new StringBuilder();
|
||||
for (ESLicense license : TrialLicenseUtils.fromEncodedTrialLicenses(licensesMetaData.getEncodedTrialLicenses())) {
|
||||
for (License license : licensesMetaData.getTrialLicenses()) {
|
||||
trialFeatures.append(license.feature());
|
||||
trialFeatures.append(", ");
|
||||
}
|
||||
@ -596,21 +577,25 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
}
|
||||
|
||||
private long expiryDateForFeature(String feature, final LicensesMetaData currentLicensesMetaData) {
|
||||
final Map<String, ESLicense> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
ESLicense featureLicense;
|
||||
final Map<String, License> effectiveLicenses = getEffectiveLicenses(currentLicensesMetaData);
|
||||
License featureLicense;
|
||||
if ((featureLicense = effectiveLicenses.get(feature)) != null) {
|
||||
return featureLicense.expiryDate();
|
||||
}
|
||||
return -1l;
|
||||
}
|
||||
|
||||
private Map<String, ESLicense> getEffectiveLicenses(final LicensesMetaData metaData) {
|
||||
Map<String, ESLicense> map = new HashMap<>();
|
||||
private Map<String, License> getEffectiveLicenses(final LicensesMetaData metaData) {
|
||||
Map<String, License> map = new HashMap<>();
|
||||
if (metaData != null) {
|
||||
Set<ESLicense> esLicenses = new HashSet<>();
|
||||
esLicenses.addAll(licenseManager.fromSignatures(metaData.getSignatures()));
|
||||
esLicenses.addAll(TrialLicenseUtils.fromEncodedTrialLicenses(metaData.getEncodedTrialLicenses()));
|
||||
return reduceAndMap(esLicenses);
|
||||
Set<License> licenses = new HashSet<>();
|
||||
for (License license : metaData.getSignedLicenses()) {
|
||||
if (LicenseVerifier.verifyLicense(license)) {
|
||||
licenses.add(license);
|
||||
}
|
||||
}
|
||||
licenses.addAll(metaData.getTrialLicenses());
|
||||
return reduceAndMap(licenses);
|
||||
}
|
||||
return ImmutableMap.copyOf(map);
|
||||
|
||||
@ -756,22 +741,48 @@ public class LicensesService extends AbstractLifecycleComponent<LicensesService>
|
||||
return new LicensesWrapper(licensesMetaData);
|
||||
}
|
||||
|
||||
private ImmutableSet<String> signatures = ImmutableSet.of();
|
||||
private ImmutableSet<String> encodedTrialLicenses = ImmutableSet.of();
|
||||
private ImmutableList<License> signedLicenses = ImmutableList.of();
|
||||
private ImmutableList<License> trialLicenses = ImmutableList.of();
|
||||
|
||||
private LicensesWrapper(LicensesMetaData licensesMetaData) {
|
||||
if (licensesMetaData != null) {
|
||||
this.signatures = ImmutableSet.copyOf(licensesMetaData.getSignatures());
|
||||
this.encodedTrialLicenses = ImmutableSet.copyOf(licensesMetaData.getEncodedTrialLicenses());
|
||||
this.signedLicenses = ImmutableList.copyOf(licensesMetaData.getSignedLicenses());
|
||||
this.trialLicenses = ImmutableList.copyOf(licensesMetaData.getTrialLicenses());
|
||||
}
|
||||
}
|
||||
|
||||
public Set<ESLicense> signedLicenses(ESLicenseManager licenseManager) {
|
||||
return licenseManager.fromSignatures(signatures);
|
||||
/**
|
||||
* Returns existingLicenses + newLicenses.
|
||||
* A new license is added if:
|
||||
* - there is no current license for the feature
|
||||
* - current license for feature has a earlier expiry date
|
||||
*/
|
||||
private List<License> addAndGetSignedLicenses(Set<License> newLicenses) {
|
||||
final ImmutableMap<String, License> newLicensesMap = reduceAndMap(newLicenses);
|
||||
List<License> newSignedLicenses = new ArrayList<>(signedLicenses);
|
||||
final ImmutableMap<String, License> oldLicenseMap = reduceAndMap(Sets.newHashSet(signedLicenses));
|
||||
for (String newFeature : newLicensesMap.keySet()) {
|
||||
final License newFeatureLicense = newLicensesMap.get(newFeature);
|
||||
if (oldLicenseMap.containsKey(newFeature)) {
|
||||
final License oldFeatureLicense = oldLicenseMap.get(newFeature);
|
||||
if (oldFeatureLicense.expiryDate() < newFeatureLicense.expiryDate()) {
|
||||
newSignedLicenses.add(newFeatureLicense);
|
||||
}
|
||||
} else {
|
||||
newSignedLicenses.add(newFeatureLicense);
|
||||
}
|
||||
}
|
||||
return ImmutableList.copyOf(newSignedLicenses);
|
||||
}
|
||||
|
||||
public Set<ESLicense> trialLicenses() {
|
||||
return TrialLicenseUtils.fromEncodedTrialLicenses(encodedTrialLicenses);
|
||||
private List<License> removeAndGetSignedLicenses(Set<String> features) {
|
||||
List<License> updatedSignedLicenses = new ArrayList<>();
|
||||
for (License license : signedLicenses) {
|
||||
if (!features.contains(license.feature())) {
|
||||
updatedSignedLicenses.add(license);
|
||||
}
|
||||
}
|
||||
return ImmutableList.copyOf(updatedSignedLicenses);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,16 +5,17 @@
|
||||
*/
|
||||
package org.elasticsearch.license.plugin.core;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.Base64;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import static org.elasticsearch.license.core.CryptUtils.decrypt;
|
||||
import static org.elasticsearch.license.core.CryptUtils.encrypt;
|
||||
|
||||
public class TrialLicenseUtils {
|
||||
|
||||
@ -73,14 +74,14 @@ public class TrialLicenseUtils {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ESLicense build() {
|
||||
public License build() {
|
||||
if (expiryDate == -1) {
|
||||
expiryDate = issueDate + duration.millis();
|
||||
}
|
||||
if (uid == null) {
|
||||
uid = UUID.randomUUID().toString();
|
||||
}
|
||||
return ESLicense.builder()
|
||||
return License.builder()
|
||||
.type(DEFAULT_TYPE)
|
||||
.subscriptionType(DEFAULT_SUBSCRIPTION_TYPE)
|
||||
.issuer(DEFAULT_ISSUER)
|
||||
@ -95,70 +96,17 @@ public class TrialLicenseUtils {
|
||||
|
||||
}
|
||||
|
||||
public static Set<ESLicense> fromEncodedTrialLicenses(Set<String> encodedTrialLicenses) {
|
||||
Set<ESLicense> licenses = new HashSet<>(encodedTrialLicenses.size());
|
||||
for (String encodedTrialLicense : encodedTrialLicenses) {
|
||||
licenses.add(fromEncodedTrialLicense(encodedTrialLicense));
|
||||
}
|
||||
return ImmutableSet.copyOf(licenses);
|
||||
public static License fromEncodedTrialLicense(String encodedTrialLicense) throws IOException {
|
||||
byte[] data = decrypt(Base64.decode(encodedTrialLicense));
|
||||
XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(data);
|
||||
parser.nextToken();
|
||||
return License.builder().fromXContent(parser).build();
|
||||
}
|
||||
|
||||
public static ESLicense fromEncodedTrialLicense(String encodedTrialLicense) {
|
||||
byte[] encodedBytes = Base64.decodeBase64(encodedTrialLicense);
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(encodedBytes);
|
||||
|
||||
int uidLen = byteBuffer.getInt();
|
||||
byte[] uidBytes = new byte[uidLen];
|
||||
byteBuffer.get(uidBytes);
|
||||
String uid = new String(uidBytes, StandardCharsets.UTF_8);
|
||||
|
||||
int issuedToLen = byteBuffer.getInt();
|
||||
byte[] issuedToBytes = new byte[issuedToLen];
|
||||
byteBuffer.get(issuedToBytes);
|
||||
String issuedTo = new String(issuedToBytes, StandardCharsets.UTF_8);
|
||||
|
||||
int featureLen = byteBuffer.getInt();
|
||||
byte[] featureBytes = new byte[featureLen];
|
||||
byteBuffer.get(featureBytes);
|
||||
String feature = new String(featureBytes, StandardCharsets.UTF_8);
|
||||
|
||||
int maxNodes = byteBuffer.getInt();
|
||||
long issueDate = byteBuffer.getLong();
|
||||
long expiryDate = byteBuffer.getLong();
|
||||
|
||||
return builder()
|
||||
.uid(uid)
|
||||
.issuedTo(issuedTo)
|
||||
.feature(feature)
|
||||
.maxNodes(maxNodes)
|
||||
.issueDate(issueDate)
|
||||
.expiryDate(expiryDate)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static String toEncodedTrialLicense(ESLicense trialLicense) {
|
||||
byte[] uidBytes = trialLicense.uid().getBytes(StandardCharsets.UTF_8);
|
||||
byte[] featureBytes = trialLicense.feature().getBytes(StandardCharsets.UTF_8);
|
||||
byte[] issuedToBytes = trialLicense.issuedTo().getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
// uid len + uid bytes + issuedTo len + issuedTo bytes + feature bytes length + feature bytes + maxNodes + issueDate + expiryDate
|
||||
int len = 4 + uidBytes.length + 4 + issuedToBytes.length + 4 + featureBytes.length + 4 + 8 + 8;
|
||||
final byte[] encodedLicense = new byte[len];
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(encodedLicense);
|
||||
|
||||
byteBuffer.putInt(uidBytes.length);
|
||||
byteBuffer.put(uidBytes);
|
||||
|
||||
byteBuffer.putInt(issuedToBytes.length);
|
||||
byteBuffer.put(issuedToBytes);
|
||||
|
||||
byteBuffer.putInt(featureBytes.length);
|
||||
byteBuffer.put(featureBytes);
|
||||
|
||||
byteBuffer.putInt(trialLicense.maxNodes());
|
||||
byteBuffer.putLong(trialLicense.issueDate());
|
||||
byteBuffer.putLong(trialLicense.expiryDate());
|
||||
|
||||
return Base64.encodeBase64String(encodedLicense);
|
||||
public static String toEncodedTrialLicense(License trialLicense) throws IOException {
|
||||
XContentBuilder contentBuilder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
// trial license is equivalent to a license spec (no signature)
|
||||
trialLicense.toXContent(contentBuilder, new ToXContent.MapParams(Collections.singletonMap(Licenses.LICENSE_SPEC_VIEW_MODE, "true")));
|
||||
return Base64.encodeBytes(encrypt(contentBuilder.bytes().toBytes()));
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseAction;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseResponse;
|
||||
@ -39,14 +39,14 @@ public class RestGetLicenseAction extends BaseRestHandler {
|
||||
*/
|
||||
@Override
|
||||
public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {
|
||||
final Map<String, String> overrideParams = ImmutableMap.of(ESLicenses.REST_VIEW_MODE, "true");
|
||||
final Map<String, String> overrideParams = ImmutableMap.of(Licenses.REST_VIEW_MODE, "true");
|
||||
final ToXContent.Params params = new ToXContent.DelegatingMapParams(overrideParams, request);
|
||||
GetLicenseRequest getLicenseRequest = new GetLicenseRequest();
|
||||
getLicenseRequest.local(request.paramAsBoolean("local", getLicenseRequest.local()));
|
||||
client.admin().cluster().execute(GetLicenseAction.INSTANCE, getLicenseRequest, new RestBuilderListener<GetLicenseResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(GetLicenseResponse response, XContentBuilder builder) throws Exception {
|
||||
ESLicenses.toXContent(response.licenses(), builder, params);
|
||||
Licenses.toXContent(response.licenses(), builder, params);
|
||||
return new BytesRestResponse(OK, builder);
|
||||
}
|
||||
});
|
||||
|
@ -11,11 +11,10 @@ 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.license.core.License;
|
||||
import org.elasticsearch.license.licensor.LicenseSigner;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -50,28 +49,19 @@ public abstract class AbstractLicensingTestBase {
|
||||
priKeyPath = getResourcePath("/private.key");
|
||||
}
|
||||
|
||||
protected static String dateMathString(String time, long now) {
|
||||
public static String dateMathString(String time, long now) {
|
||||
return dateTimeFormatter.print(dateMathParser.parse(time, now));
|
||||
}
|
||||
|
||||
protected static long dateMath(String time, long now) {
|
||||
public static long dateMath(String time, long now) {
|
||||
return dateMathParser.parse(time, now);
|
||||
}
|
||||
|
||||
public static String getTestPriKeyPath() throws Exception {
|
||||
return getResourcePath("/private.key");
|
||||
}
|
||||
|
||||
public static String getTestPubKeyPath() throws Exception {
|
||||
return getResourcePath("/public.key");
|
||||
}
|
||||
|
||||
public static String getResourcePath(String resource) throws Exception {
|
||||
URL url = ESLicenseManager.class.getResource(resource);
|
||||
URL url = LicenseVerifier.class.getResource(resource);
|
||||
return url.toURI().getPath();
|
||||
}
|
||||
|
||||
|
||||
public static LicenseSpec generateRandomLicenseSpec() {
|
||||
boolean datesInMillis = randomBoolean();
|
||||
long now = System.currentTimeMillis();
|
||||
@ -93,7 +83,7 @@ public abstract class AbstractLicensingTestBase {
|
||||
}
|
||||
}
|
||||
|
||||
public static String generateESLicenseSpecString(List<LicenseSpec> licenseSpecs) throws IOException {
|
||||
public static String generateLicenseSpecString(List<LicenseSpec> licenseSpecs) throws IOException {
|
||||
XContentBuilder licenses = jsonBuilder();
|
||||
licenses.startObject();
|
||||
licenses.startArray("licenses");
|
||||
@ -124,11 +114,11 @@ public abstract class AbstractLicensingTestBase {
|
||||
return licenses.string();
|
||||
}
|
||||
|
||||
public static Set<ESLicense> generateSignedLicenses(List<LicenseSpec> licenseSpecs) throws Exception {
|
||||
ESLicenseSigner signer = new ESLicenseSigner(getTestPriKeyPath(), getTestPubKeyPath());
|
||||
Set<ESLicense> unSignedLicenses = new HashSet<>();
|
||||
public static Set<License> generateSignedLicenses(List<LicenseSpec> licenseSpecs) throws Exception {
|
||||
LicenseSigner signer = new LicenseSigner(priKeyPath, pubKeyPath);
|
||||
Set<License> unSignedLicenses = new HashSet<>();
|
||||
for (LicenseSpec spec : licenseSpecs) {
|
||||
ESLicense.Builder builder = ESLicense.builder()
|
||||
License.Builder builder = License.builder()
|
||||
.uid(spec.uid)
|
||||
.feature(spec.feature)
|
||||
.type(spec.type)
|
||||
@ -152,28 +142,6 @@ public abstract class AbstractLicensingTestBase {
|
||||
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;
|
||||
@ -222,7 +190,7 @@ public abstract class AbstractLicensingTestBase {
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertLicenseSpec(LicenseSpec spec, ESLicense license) {
|
||||
public static void assertLicenseSpec(LicenseSpec spec, License license) {
|
||||
assertThat(license.uid(), equalTo(spec.uid));
|
||||
assertThat(license.feature(), equalTo(spec.feature));
|
||||
assertThat(license.issuedTo(), equalTo(spec.issuedTo));
|
||||
|
@ -3,14 +3,14 @@
|
||||
* 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;
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.xcontent.*;
|
||||
import org.elasticsearch.license.AbstractLicensingTestBase;
|
||||
import org.elasticsearch.license.core.DateUtils;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -29,11 +29,11 @@ public class LicenseSerializationTests extends AbstractLicensingTestBase {
|
||||
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<ESLicense> esLicensesOutput = new HashSet<>(ESLicenses.fromSource(licenseSpecs.getBytes(StandardCharsets.UTF_8), false));
|
||||
ESLicense generatedLicense = esLicensesOutput.iterator().next();
|
||||
String licenseSpecs = generateLicenseSpecString(Arrays.asList(new LicenseSpec("shield", issueDate, expiryDate)));
|
||||
Set<License> licensesOutput = new HashSet<>(Licenses.fromSource(licenseSpecs.getBytes(StandardCharsets.UTF_8), false));
|
||||
License generatedLicense = licensesOutput.iterator().next();
|
||||
|
||||
assertThat(esLicensesOutput.size(), equalTo(1));
|
||||
assertThat(licensesOutput.size(), equalTo(1));
|
||||
assertThat(generatedLicense.issueDate(), equalTo(DateUtils.beginningOfTheDay(issueDate)));
|
||||
assertThat(generatedLicense.expiryDate(), equalTo(DateUtils.endOfTheDay(expiryDate)));
|
||||
}
|
||||
@ -45,15 +45,15 @@ public class LicenseSerializationTests extends AbstractLicensingTestBase {
|
||||
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<ESLicense> esLicensesOutput = new HashSet<>();
|
||||
esLicensesOutput.addAll(ESLicenses.fromSource(licenseSpecs.getBytes(StandardCharsets.UTF_8), false));
|
||||
esLicensesOutput.addAll(ESLicenses.fromSource(licenseSpecs1.getBytes(StandardCharsets.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)));
|
||||
String licenseSpecs = generateLicenseSpecString(Arrays.asList(new LicenseSpec("shield", shieldIssueDate, shieldExpiryDate)));
|
||||
String licenseSpecs1 = generateLicenseSpecString(Arrays.asList(new LicenseSpec("marvel", marvelIssueDate, marvelExpiryDate)));
|
||||
Set<License> licensesOutput = new HashSet<>();
|
||||
licensesOutput.addAll(Licenses.fromSource(licenseSpecs.getBytes(StandardCharsets.UTF_8), false));
|
||||
licensesOutput.addAll(Licenses.fromSource(licenseSpecs1.getBytes(StandardCharsets.UTF_8), false));
|
||||
assertThat(licensesOutput.size(), equalTo(2));
|
||||
for (License license : licensesOutput) {
|
||||
assertThat(license.issueDate(), equalTo(DateUtils.beginningOfTheDay((license.feature().equals("shield")) ? shieldIssueDate : marvelIssueDate)));
|
||||
assertThat(license.expiryDate(), equalTo(DateUtils.endOfTheDay((license.feature().equals("shield")) ? shieldExpiryDate : marvelExpiryDate)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,11 +66,11 @@ public class LicenseSerializationTests extends AbstractLicensingTestBase {
|
||||
}
|
||||
|
||||
ArrayList<LicenseSpec> specs = new ArrayList<>(licenseSpecs.values());
|
||||
String licenseSpecsSource = generateESLicenseSpecString(specs);
|
||||
Set<ESLicense> esLicensesOutput = new HashSet<>(ESLicenses.fromSource(licenseSpecsSource.getBytes(StandardCharsets.UTF_8), false));
|
||||
assertThat(esLicensesOutput.size(), equalTo(licenseSpecs.size()));
|
||||
String licenseSpecsSource = generateLicenseSpecString(specs);
|
||||
Set<License> licensesOutput = new HashSet<>(Licenses.fromSource(licenseSpecsSource.getBytes(StandardCharsets.UTF_8), false));
|
||||
assertThat(licensesOutput.size(), equalTo(licenseSpecs.size()));
|
||||
|
||||
for (ESLicense license : esLicensesOutput) {
|
||||
for (License license : licensesOutput) {
|
||||
LicenseSpec spec = licenseSpecs.get(license.feature());
|
||||
assertThat(spec, notNullValue());
|
||||
assertLicenseSpec(spec, license);
|
||||
@ -83,13 +83,13 @@ public class LicenseSerializationTests extends AbstractLicensingTestBase {
|
||||
String expiredLicenseExpiryDate = dateMathString("now-1d/d", now);
|
||||
String validLicenseIssueDate = dateMathString("now-10d/d", now);
|
||||
String validLicenseExpiryDate = dateMathString("now+1d/d", now);
|
||||
Set<ESLicense> licenses = generateSignedLicenses(Arrays.asList(new LicenseSpec("expired_feature", validLicenseIssueDate, expiredLicenseExpiryDate)
|
||||
Set<License> licenses = generateSignedLicenses(Arrays.asList(new LicenseSpec("expired_feature", validLicenseIssueDate, expiredLicenseExpiryDate)
|
||||
, new LicenseSpec("valid_feature", validLicenseIssueDate, validLicenseExpiryDate)));
|
||||
|
||||
assertThat(licenses.size(), equalTo(2));
|
||||
for (ESLicense license : licenses) {
|
||||
for (License license : licenses) {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
license.toXContent(builder, new ToXContent.MapParams(ImmutableMap.of(ESLicenses.REST_VIEW_MODE, "true")));
|
||||
license.toXContent(builder, new ToXContent.MapParams(ImmutableMap.of(Licenses.REST_VIEW_MODE, "true")));
|
||||
builder.flush();
|
||||
Map<String, Object> map = XContentHelper.convertToMap(builder.bytesStream().bytes(), false).v2();
|
||||
assertThat(map.get("status"), notNullValue());
|
||||
@ -100,7 +100,7 @@ public class LicenseSerializationTests extends AbstractLicensingTestBase {
|
||||
}
|
||||
}
|
||||
|
||||
for (ESLicense license : licenses) {
|
||||
for (License license : licenses) {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
license.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
import org.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.XContentParser;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.LicenseVerifier;
|
||||
import org.elasticsearch.license.plugin.core.LicensesMetaData;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class LicenseVerificationTests extends AbstractLicensingTestBase {
|
||||
|
||||
@Test
|
||||
public void testGeneratedLicenses() throws Exception {
|
||||
License shieldLicense = TestUtils.generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24));
|
||||
assertThat(LicenseVerifier.verifyLicense(shieldLicense), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleFeatureLicenses() throws Exception {
|
||||
License shieldLicense = TestUtils.generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24));
|
||||
License marvelLicense = TestUtils.generateSignedLicense("marvel", TimeValue.timeValueHours(2 * 24));
|
||||
|
||||
assertThat(LicenseVerifier.verifyLicenses(Arrays.asList(shieldLicense, marvelLicense)), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseTampering() throws Exception {
|
||||
License license = TestUtils.generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
|
||||
final License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(license, license.signature())
|
||||
.expiryDate(license.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
|
||||
.validate()
|
||||
.build();
|
||||
|
||||
assertThat(LicenseVerifier.verifyLicense(tamperedLicense), equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandomLicenseVerification() throws Exception {
|
||||
int n = randomIntBetween(5, 15);
|
||||
List<LicenseSpec> licenseSpecs = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
licenseSpecs.add(generateRandomLicenseSpec());
|
||||
}
|
||||
|
||||
Set<License> generatedLicenses = generateSignedLicenses(licenseSpecs);
|
||||
assertThat(generatedLicenses.size(), equalTo(n));
|
||||
|
||||
for (License generatedLicense: generatedLicenses) {
|
||||
assertThat(LicenseVerifier.verifyLicense(generatedLicense), equalTo(true));
|
||||
}
|
||||
}
|
||||
}
|
@ -5,13 +5,16 @@
|
||||
*/
|
||||
package org.elasticsearch.license;
|
||||
|
||||
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.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.LicenseSigner;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
@ -19,28 +22,36 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
public static void isSame(Collection<ESLicense> firstLicenses, Collection<ESLicense> secondLicenses) {
|
||||
public static String getTestPriKeyPath() throws Exception {
|
||||
return getResourcePath("/private.key");
|
||||
}
|
||||
|
||||
public static String getTestPubKeyPath() throws Exception {
|
||||
return getResourcePath("/public.key");
|
||||
}
|
||||
|
||||
public static void isSame(Collection<License> firstLicenses, Collection<License> secondLicenses) {
|
||||
isSame(new HashSet<>(firstLicenses), new HashSet<>(secondLicenses));
|
||||
}
|
||||
|
||||
public static void isSame(Set<ESLicense> firstLicenses, Set<ESLicense> secondLicenses) {
|
||||
public static void isSame(Set<License> firstLicenses, Set<License> secondLicenses) {
|
||||
|
||||
// we do the verifyAndBuild to make sure we weed out any expired licenses
|
||||
final Map<String, ESLicense> licenses1 = ESLicenses.reduceAndMap(firstLicenses);
|
||||
final Map<String, ESLicense> licenses2 = ESLicenses.reduceAndMap(secondLicenses);
|
||||
final Map<String, License> licenses1 = Licenses.reduceAndMap(firstLicenses);
|
||||
final Map<String, License> licenses2 = Licenses.reduceAndMap(secondLicenses);
|
||||
|
||||
// check if the effective licenses have the same feature set
|
||||
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);
|
||||
License license1 = licenses1.get(featureType);
|
||||
License license2 = licenses2.get(featureType);
|
||||
isSame(license1, license2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void isSame(ESLicense license1, ESLicense license2) {
|
||||
public static void isSame(License license1, License license2) {
|
||||
assertThat(license1.uid(), equalTo(license2.uid()));
|
||||
assertThat(license1.feature(), equalTo(license2.feature()));
|
||||
assertThat(license1.subscriptionType(), equalTo(license2.subscriptionType()));
|
||||
@ -52,10 +63,37 @@ public class TestUtils {
|
||||
assertThat(license1.maxNodes(), equalTo(license2.maxNodes()));
|
||||
}
|
||||
|
||||
public static String dumpLicense(ESLicense license) throws Exception {
|
||||
public static String dumpLicense(License license) throws Exception {
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||
ESLicenses.toXContent(Collections.singletonList(license), builder, ToXContent.EMPTY_PARAMS);
|
||||
Licenses.toXContent(Collections.singletonList(license), builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.flush();
|
||||
return builder.string();
|
||||
}
|
||||
|
||||
public static License generateSignedLicense(String feature, TimeValue expiryDuration) throws Exception {
|
||||
return generateSignedLicense(feature, -1, expiryDuration);
|
||||
}
|
||||
|
||||
public static License generateSignedLicense(String feature, long issueDate, TimeValue expiryDuration) throws Exception {
|
||||
long issue = (issueDate != -1l) ? issueDate : System.currentTimeMillis();
|
||||
final License licenseSpec = License.builder()
|
||||
.uid(UUID.randomUUID().toString())
|
||||
.feature(feature)
|
||||
.expiryDate(issue + expiryDuration.getMillis())
|
||||
.issueDate(issue)
|
||||
.type("subscription")
|
||||
.subscriptionType("gold")
|
||||
.issuedTo("customer")
|
||||
.issuer("elasticsearch")
|
||||
.maxNodes(5)
|
||||
.build();
|
||||
|
||||
LicenseSigner signer = new LicenseSigner(getTestPriKeyPath(), getTestPubKeyPath());
|
||||
return signer.sign(licenseSpec);
|
||||
}
|
||||
|
||||
private static String getResourcePath(String resource) throws Exception {
|
||||
URL url = TestUtils.class.getResource(resource);
|
||||
return url.toURI().getPath();
|
||||
}
|
||||
}
|
||||
|
@ -1,48 +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.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.assertThat;
|
||||
|
||||
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<LicenseSpec> licenseSpecs = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
licenseSpecs.add(generateRandomLicenseSpec());
|
||||
}
|
||||
|
||||
Set<ESLicense> generatedLicenses = generateSignedLicenses(licenseSpecs);
|
||||
assertThat(generatedLicenses.size(), equalTo(n));
|
||||
|
||||
Set<String> signatures = new HashSet<>();
|
||||
for (ESLicense license : generatedLicenses) {
|
||||
signatures.add(license.signature());
|
||||
}
|
||||
Set<ESLicense> licenseFromSignatures = esLicenseManager.fromSignatures(signatures);
|
||||
|
||||
TestUtils.isSame(generatedLicenses, licenseFromSignatures);
|
||||
}
|
||||
}
|
@ -1,93 +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.manager;
|
||||
|
||||
import net.nicholaswilliams.java.licensing.exception.InvalidLicenseException;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.AbstractLicensingTestBase;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class LicenseVerificationTests extends AbstractLicensingTestBase {
|
||||
|
||||
private static ESLicenseManager esLicenseManager;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupManager() {
|
||||
esLicenseManager = new ESLicenseManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGeneratedLicenses() throws Exception {
|
||||
ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24));
|
||||
Map<String, ESLicense> shieldLicenseMap = new HashMap<>();
|
||||
shieldLicenseMap.put("shield", shieldLicense);
|
||||
esLicenseManager.verifyLicenses(shieldLicenseMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleFeatureLicenses() throws Exception {
|
||||
ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24));
|
||||
ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2 * 24));
|
||||
Map<String, ESLicense> licenseMap = new HashMap<>();
|
||||
licenseMap.put("shield", shieldLicense);
|
||||
licenseMap.put("marvel", marvelLicense);
|
||||
|
||||
esLicenseManager.verifyLicenses(licenseMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseExpiry() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
long marvelIssueDate = dateMath("now-10d/d", now);
|
||||
|
||||
ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2 * 24));
|
||||
ESLicense marvelLicense = generateSignedLicense("marvel", marvelIssueDate, TimeValue.timeValueHours(2 * 24));
|
||||
Map<String, ESLicense> licenseMap = new HashMap<>();
|
||||
licenseMap.put("shield", shieldLicense);
|
||||
licenseMap.put("marvel", marvelLicense);
|
||||
|
||||
try {
|
||||
esLicenseManager.verifyLicenses(licenseMap);
|
||||
fail("verifyLicenses should throw InvalidLicenseException [expired license]");
|
||||
} catch (InvalidLicenseException e) {
|
||||
assertThat(e.getMessage(), containsString("Invalid License"));
|
||||
}
|
||||
|
||||
licenseMap.clear();
|
||||
licenseMap.put("shield", shieldLicense);
|
||||
esLicenseManager.verifyLicenses(licenseMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLicenseTampering() throws Exception {
|
||||
ESLicense esLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
|
||||
final ESLicense tamperedLicense = ESLicense.builder()
|
||||
.fromLicenseSpec(esLicense, esLicense.signature())
|
||||
.expiryDate(esLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
|
||||
.verify()
|
||||
.build();
|
||||
|
||||
Map<String, ESLicense> licenseMap = new HashMap<>();
|
||||
licenseMap.put("shield", tamperedLicense);
|
||||
|
||||
try {
|
||||
esLicenseManager.verifyLicenses(licenseMap);
|
||||
fail("Tampered license should throw exception");
|
||||
} catch (InvalidLicenseException e) {
|
||||
assertThat(e.getMessage(), containsString("Invalid License"));
|
||||
}
|
||||
}
|
||||
}
|
@ -15,8 +15,7 @@ import org.elasticsearch.common.collect.Lists;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.licensor.ESLicenseSigner;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.consumer.EagerLicenseRegistrationPluginService;
|
||||
@ -27,16 +26,13 @@ import org.elasticsearch.license.plugin.core.LicensesMetaData;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.elasticsearch.license.AbstractLicensingTestBase.getTestPriKeyPath;
|
||||
import static org.elasticsearch.license.AbstractLicensingTestBase.getTestPubKeyPath;
|
||||
import static org.elasticsearch.license.TestUtils.generateSignedLicense;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
|
||||
@ -68,7 +64,7 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte
|
||||
@Override
|
||||
public ClusterState execute(ClusterState currentState) throws Exception {
|
||||
MetaData.Builder mdBuilder = MetaData.builder(currentState.metaData());
|
||||
mdBuilder.putCustom(LicensesMetaData.TYPE, null);
|
||||
mdBuilder.removeCustom(LicensesMetaData.TYPE);
|
||||
return ClusterState.builder(currentState).metaData(mdBuilder).build();
|
||||
}
|
||||
|
||||
@ -80,25 +76,8 @@ public abstract class AbstractLicensesIntegrationTests extends ElasticsearchInte
|
||||
latch.await();
|
||||
}
|
||||
|
||||
public static ESLicense generateSignedLicense(String feature, TimeValue expiryDate) throws Exception {
|
||||
final ESLicense licenseSpec = ESLicense.builder()
|
||||
.uid(UUID.randomUUID().toString())
|
||||
.feature(feature)
|
||||
.expiryDate(System.currentTimeMillis() + expiryDate.getMillis())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.type("subscription")
|
||||
.subscriptionType("gold")
|
||||
.issuedTo("customer")
|
||||
.issuer("elasticsearch")
|
||||
.maxNodes(randomIntBetween(5, 100))
|
||||
.build();
|
||||
|
||||
ESLicenseSigner signer = new ESLicenseSigner(getTestPriKeyPath(), getTestPubKeyPath());
|
||||
return signer.sign(licenseSpec);
|
||||
}
|
||||
|
||||
protected void putLicense(String feature, TimeValue expiryDuration) throws Exception {
|
||||
ESLicense license1 = generateSignedLicense(feature, expiryDuration);
|
||||
License license1 = generateSignedLicense(feature, expiryDuration);
|
||||
final PutLicenseResponse putLicenseResponse = new PutLicenseRequestBuilder(client().admin().cluster()).setLicense(Lists.newArrayList(license1)).get();
|
||||
assertThat(putLicenseResponse.isAcknowledged(), equalTo(true));
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
@ -11,7 +11,7 @@ import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNodes;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
@ -19,7 +19,6 @@ import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesStatus;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -50,7 +49,7 @@ public abstract class AbstractLicensesServiceTests extends AbstractLicensesInteg
|
||||
node = nodes[randomIntBetween(0, nodes.length - 1)];
|
||||
}
|
||||
|
||||
protected void registerAndAckSignedLicenses(final LicensesManagerService masterLicensesManagerService, final List<ESLicense> license, final LicensesStatus expectedStatus) {
|
||||
protected void registerAndAckSignedLicenses(final LicensesManagerService masterLicensesManagerService, final List<License> license, final LicensesStatus expectedStatus) {
|
||||
PutLicenseRequest putLicenseRequest = new PutLicenseRequest().licenses(license);
|
||||
LicensesService.PutLicenseRequestHolder requestHolder = new LicensesService.PutLicenseRequestHolder(putLicenseRequest, "test");
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
@ -6,7 +6,7 @@
|
||||
package org.elasticsearch.license.plugin;
|
||||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesManagerService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.elasticsearch.license.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
@ -192,7 +193,7 @@ public class LicensesClientServiceTests extends AbstractLicensesServiceTests {
|
||||
return new Action(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ESLicense license;
|
||||
License license;
|
||||
try {
|
||||
license = generateSignedLicense(feature, expiryDuration);
|
||||
} catch (Exception e) {
|
||||
|
@ -7,15 +7,12 @@ package org.elasticsearch.license.plugin;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.collect.Sets;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.manager.ESLicenseManager;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
|
||||
import org.elasticsearch.license.plugin.core.*;
|
||||
import org.elasticsearch.test.InternalTestCluster;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -25,6 +22,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.elasticsearch.license.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
@ -35,36 +33,35 @@ public class LicensesManagerServiceTests extends AbstractLicensesServiceTests {
|
||||
@Test
|
||||
public void testStoreAndGetLicenses() throws Exception {
|
||||
LicensesManagerService licensesManagerService = masterLicensesManagerService();
|
||||
ESLicense shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
|
||||
ESLicense shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
ESLicense marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
|
||||
ESLicense marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
|
||||
License shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
|
||||
License shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
License marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
|
||||
License marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
|
||||
|
||||
List<ESLicense> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
|
||||
List<License> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
|
||||
Collections.shuffle(licenses);
|
||||
registerAndAckSignedLicenses(licensesManagerService, licenses, LicensesStatus.VALID);
|
||||
|
||||
final ImmutableSet<String> licenseSignatures = masterLicenseManager().toSignatures(licenses);
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
|
||||
// all licenses should be stored in the metaData
|
||||
assertThat(licenseSignatures, equalTo(licensesMetaData.getSignatures()));
|
||||
TestUtils.isSame(licenses, licensesMetaData.getSignedLicenses());
|
||||
|
||||
// only the latest expiry date license for each feature should be returned by getLicenses()
|
||||
final List<ESLicense> getLicenses = licensesManagerService.getLicenses();
|
||||
final List<License> getLicenses = licensesManagerService.getLicenses();
|
||||
TestUtils.isSame(getLicenses, Arrays.asList(shieldLongLicense, marvelLongLicense));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidLicenseStorage() throws Exception {
|
||||
LicensesManagerService licensesManagerService = masterLicensesManagerService();
|
||||
ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
|
||||
// modify content of signed license
|
||||
ESLicense tamperedLicense = ESLicense.builder()
|
||||
License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(signedLicense, signedLicense.signature())
|
||||
.expiryDate(signedLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
|
||||
.verify()
|
||||
.validate()
|
||||
.build();
|
||||
|
||||
registerAndAckSignedLicenses(licensesManagerService, Arrays.asList(tamperedLicense), LicensesStatus.INVALID);
|
||||
@ -72,7 +69,7 @@ public class LicensesManagerServiceTests extends AbstractLicensesServiceTests {
|
||||
// ensure that the invalid license never made it to cluster state
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
if (licensesMetaData != null) {
|
||||
assertThat(licensesMetaData.getSignatures().size(), equalTo(0));
|
||||
assertThat(licensesMetaData.getSignedLicenses().size(), equalTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,29 +83,28 @@ public class LicensesManagerServiceTests extends AbstractLicensesServiceTests {
|
||||
registerWithTrialLicense(clientService, clientListener, "shield", TimeValue.timeValueHours(1)).run();
|
||||
|
||||
// generate signed licenses for multiple features
|
||||
ESLicense shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
|
||||
ESLicense shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
ESLicense marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
|
||||
ESLicense marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
|
||||
License shieldShortLicense = generateSignedLicense("shield", TimeValue.timeValueHours(1));
|
||||
License shieldLongLicense = generateSignedLicense("shield", TimeValue.timeValueHours(2));
|
||||
License marvelShortLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(1));
|
||||
License marvelLongLicense = generateSignedLicense("marvel", TimeValue.timeValueHours(2));
|
||||
|
||||
List<ESLicense> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
|
||||
List<License> licenses = Arrays.asList(shieldLongLicense, shieldShortLicense, marvelLongLicense, marvelShortLicense);
|
||||
Collections.shuffle(licenses);
|
||||
registerAndAckSignedLicenses(licensesManagerService, licenses, LicensesStatus.VALID);
|
||||
|
||||
// remove license(s) for one feature out of two
|
||||
removeAndAckSignedLicenses(licensesManagerService, Sets.newHashSet("shield"));
|
||||
final ImmutableSet<String> licenseSignatures = masterLicenseManager().toSignatures(Arrays.asList(marvelLongLicense, marvelShortLicense));
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
assertThat(licenseSignatures, equalTo(licensesMetaData.getSignatures()));
|
||||
TestUtils.isSame(Arrays.asList(marvelLongLicense, marvelShortLicense), licensesMetaData.getSignedLicenses());
|
||||
// check that trial license is not removed
|
||||
assertThat(licensesMetaData.getEncodedTrialLicenses().size(), equalTo(1));
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(1));
|
||||
|
||||
// remove license(s) for all features
|
||||
removeAndAckSignedLicenses(licensesManagerService, Sets.newHashSet("shield", "marvel"));
|
||||
licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
assertThat(licensesMetaData.getSignatures().size(), equalTo(0));
|
||||
assertThat(licensesMetaData.getSignedLicenses().size(), equalTo(0));
|
||||
// check that trial license is not removed
|
||||
assertThat(licensesMetaData.getEncodedTrialLicenses().size(), equalTo(1));
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(1));
|
||||
}
|
||||
|
||||
private void removeAndAckSignedLicenses(final LicensesManagerService masterLicensesManagerService, final Set<String> featuresToDelete) {
|
||||
@ -137,10 +133,4 @@ public class LicensesManagerServiceTests extends AbstractLicensesServiceTests {
|
||||
}
|
||||
assertThat("remove license(s) failed", success.get(), equalTo(true));
|
||||
}
|
||||
|
||||
private ESLicenseManager masterLicenseManager() {
|
||||
InternalTestCluster clients = internalCluster();
|
||||
return clients.getInstance(ESLicenseManager.class, clients.getMasterName());
|
||||
}
|
||||
|
||||
}
|
||||
|
156
src/test/java/org/elasticsearch/license/plugin/LicensesMetaDataSerializationTests.java
Normal file
156
src/test/java/org/elasticsearch/license/plugin/LicensesMetaDataSerializationTests.java
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license.plugin;
|
||||
|
||||
import org.elasticsearch.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.XContentParser;
|
||||
import org.elasticsearch.license.AbstractLicensingTestBase;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesMetaData;
|
||||
import org.elasticsearch.license.plugin.core.TrialLicenseUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class LicensesMetaDataSerializationTests extends AbstractLicensingTestBase {
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationOneSignedLicense() throws Exception {
|
||||
License license = TestUtils.generateSignedLicense("feature", TimeValue.timeValueHours(2));
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(Arrays.asList(license), new ArrayList<License>());
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
LicensesMetaData.FACTORY.toXContent(licensesMetaData, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(1));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getSignedLicenses().get(0), license);
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationManySignedLicense() throws Exception {
|
||||
List<License> licenses = new ArrayList<>();
|
||||
int n = randomIntBetween(2, 5);
|
||||
for (int i = 0; i < n; i++) {
|
||||
licenses.add(TestUtils.generateSignedLicense("feature__" + String.valueOf(i), TimeValue.timeValueHours(2)));
|
||||
}
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(licenses, new ArrayList<License>());
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
LicensesMetaData.FACTORY.toXContent(licensesMetaData, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(n));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getSignedLicenses(), licenses);
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationOneTrial() throws Exception {
|
||||
final License trialLicense = TrialLicenseUtils.builder()
|
||||
.feature("feature")
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.build();
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(new ArrayList<License>(), Arrays.asList(trialLicense));
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
LicensesMetaData.FACTORY.toXContent(licensesMetaData, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(1));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getTrialLicenses().iterator().next(), trialLicense);
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationManyTrial() throws Exception {
|
||||
final TrialLicenseUtils.TrialLicenseBuilder trialLicenseBuilder = TrialLicenseUtils.builder()
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis());
|
||||
int n = randomIntBetween(2, 5);
|
||||
List<License> trialLicenses = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
trialLicenses.add(trialLicenseBuilder.feature("feature__" + String.valueOf(i)).build());
|
||||
}
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(new ArrayList<License>(), trialLicenses);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
LicensesMetaData.FACTORY.toXContent(licensesMetaData, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(n));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getTrialLicenses(), trialLicenses);
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXContentSerializationManyTrialAndSignedLicenses() throws Exception {
|
||||
final TrialLicenseUtils.TrialLicenseBuilder trialLicenseBuilder = TrialLicenseUtils.builder()
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis());
|
||||
int n = randomIntBetween(2, 5);
|
||||
List<License> trialLicenses = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
trialLicenses.add(trialLicenseBuilder.feature("feature__" + String.valueOf(i)).build());
|
||||
}
|
||||
List<License> licenses = new ArrayList<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
licenses.add(TestUtils.generateSignedLicense("feature__" + String.valueOf(i), TimeValue.timeValueHours(2)));
|
||||
}
|
||||
LicensesMetaData licensesMetaData = new LicensesMetaData(licenses, trialLicenses);
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
builder.startObject("licensesMetaData");
|
||||
LicensesMetaData.FACTORY.toXContent(licensesMetaData, builder, ToXContent.EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
byte[] serializedBytes = builder.bytes().toBytes();
|
||||
|
||||
LicensesMetaData licensesMetaDataFromXContent = getLicensesMetaDataFromXContent(serializedBytes);
|
||||
|
||||
assertThat(licensesMetaDataFromXContent.getTrialLicenses().size(), equalTo(n));
|
||||
assertThat(licensesMetaDataFromXContent.getSignedLicenses().size(), equalTo(n));
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getTrialLicenses(), trialLicenses);
|
||||
TestUtils.isSame(licensesMetaDataFromXContent.getSignedLicenses(), licenses);
|
||||
}
|
||||
|
||||
private static LicensesMetaData getLicensesMetaDataFromXContent(byte[] bytes) throws Exception {
|
||||
final XContentParser parser = XContentFactory.xContent(bytes).createParser(bytes);
|
||||
parser.nextToken(); // consume null
|
||||
parser.nextToken(); // consume "licensesMetaData"
|
||||
LicensesMetaData licensesMetaDataFromXContent = LicensesMetaData.FACTORY.fromXContent(parser);
|
||||
parser.nextToken(); // consume endObject
|
||||
assertThat(parser.nextToken(), nullValue());
|
||||
return licensesMetaDataFromXContent;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.action.put.PutLicenseRequestBuilder;
|
||||
@ -28,17 +28,17 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.elasticsearch.license.TestUtils.generateSignedLicense;
|
||||
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.CoreMatchers.notNullValue;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 0, numClientNodes = 0, maxNumDataNodes = 0, transportClientRatio = 0)
|
||||
public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests {
|
||||
|
||||
private final String[] FEATURES = {EagerLicenseRegistrationPluginService.FEATURE_NAME, LazyLicenseRegistrationPluginService.FEATURE_NAME};
|
||||
|
||||
private final int trialLicenseDurationInSeconds = 2;
|
||||
|
||||
protected Settings transportClientSettings() {
|
||||
return super.transportClientSettings();
|
||||
}
|
||||
@ -55,8 +55,8 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests
|
||||
.put("plugins.load_classpath_plugins", false)
|
||||
.put("node.data", true)
|
||||
.put("format", "json")
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", trialLicenseDurationInSeconds)
|
||||
.put(EagerLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 2)
|
||||
.put(LazyLicenseRegistrationConsumerPlugin.NAME + ".trial_license_duration_in_seconds", 2)
|
||||
.putArray("plugin.types", LicensePlugin.class.getName(), EagerLicenseRegistrationConsumerPlugin.class.getName(), LazyLicenseRegistrationConsumerPlugin.class.getName())
|
||||
.put(InternalNode.HTTP_ENABLED, true);
|
||||
}
|
||||
@ -73,7 +73,7 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests
|
||||
ensureGreen();
|
||||
|
||||
logger.info("--> put signed license");
|
||||
final List<ESLicense> licenses = generateAndPutLicenses();
|
||||
final List<License> licenses = generateAndPutLicenses();
|
||||
getAndCheckLicense(licenses);
|
||||
logger.info("--> restart all nodes");
|
||||
internalCluster().fullRestart();
|
||||
@ -114,14 +114,14 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests
|
||||
|
||||
logger.info("--> check if multiple trial licenses are found for a feature");
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
assertThat(licensesMetaData.getEncodedTrialLicenses().size(), equalTo(FEATURES.length));
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(FEATURES.length));
|
||||
|
||||
wipeAllLicenses();
|
||||
}
|
||||
|
||||
private List<ESLicense> generateAndPutLicenses() throws Exception {
|
||||
private List<License> generateAndPutLicenses() throws Exception {
|
||||
ClusterAdminClient cluster = internalCluster().client().admin().cluster();
|
||||
List<ESLicense> putLicenses = new ArrayList<>(FEATURES.length);
|
||||
List<License> putLicenses = new ArrayList<>(FEATURES.length);
|
||||
for (String feature : FEATURES) {
|
||||
putLicenses.add(generateSignedLicense(feature, TimeValue.timeValueMinutes(1)));
|
||||
}
|
||||
@ -137,11 +137,15 @@ public class LicensesServiceClusterTest extends AbstractLicensesIntegrationTests
|
||||
return putLicenses;
|
||||
}
|
||||
|
||||
private void getAndCheckLicense(List<ESLicense> licenses) {
|
||||
private void getAndCheckLicense(List<License> licenses) {
|
||||
ClusterAdminClient cluster = internalCluster().client().admin().cluster();
|
||||
final GetLicenseResponse response = new GetLicenseRequestBuilder(cluster).get();
|
||||
assertThat(response.licenses().size(), equalTo(licenses.size()));
|
||||
TestUtils.isSame(licenses, response.licenses());
|
||||
|
||||
LicensesMetaData licensesMetaData = clusterService().state().metaData().custom(LicensesMetaData.TYPE);
|
||||
assertThat(licensesMetaData, notNullValue());
|
||||
assertThat(licensesMetaData.getTrialLicenses().size(), equalTo(2));
|
||||
}
|
||||
|
||||
private void assertLicenseManagerFeatureEnabled() throws Exception {
|
||||
|
@ -9,9 +9,7 @@ import org.elasticsearch.action.ActionFuture;
|
||||
import org.elasticsearch.common.collect.Sets;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequest;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseRequestBuilder;
|
||||
import org.elasticsearch.license.plugin.action.delete.DeleteLicenseResponse;
|
||||
import org.elasticsearch.license.plugin.action.get.GetLicenseRequestBuilder;
|
||||
@ -26,6 +24,8 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.license.AbstractLicensingTestBase.dateMath;
|
||||
import static org.elasticsearch.license.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
@ -48,8 +48,8 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testPutLicense() throws Exception {
|
||||
ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
List<ESLicense> actualLicenses = Collections.singletonList(signedLicense);
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
List<License> actualLicenses = Collections.singletonList(signedLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
|
||||
@ -69,7 +69,7 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testPutLicenseFromString() throws Exception {
|
||||
ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
String licenseString = TestUtils.dumpLicense(signedLicense);
|
||||
|
||||
// put license source
|
||||
@ -90,13 +90,13 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testPutInvalidLicense() throws Exception {
|
||||
ESLicense signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License signedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
|
||||
// modify content of signed license
|
||||
ESLicense tamperedLicense = ESLicense.builder()
|
||||
License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(signedLicense, signedLicense.signature())
|
||||
.expiryDate(signedLicense.expiryDate() + 10 * 24 * 60 * 60 * 1000l)
|
||||
.verify()
|
||||
.validate()
|
||||
.build();
|
||||
|
||||
PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster());
|
||||
@ -111,11 +111,30 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutExpiredLicense() throws Exception {
|
||||
License expiredLicense = generateSignedLicense("expiredFeature", dateMath("now-10d/d", System.currentTimeMillis()), TimeValue.timeValueMinutes(2));
|
||||
License signedLicense = generateSignedLicense("feature", TimeValue.timeValueMinutes(2));
|
||||
|
||||
PutLicenseRequestBuilder builder = new PutLicenseRequestBuilder(client().admin().cluster());
|
||||
builder.setLicense(Arrays.asList(signedLicense, expiredLicense));
|
||||
|
||||
// put license should return valid (as there is one valid license)
|
||||
final PutLicenseResponse putLicenseResponse = builder.get();
|
||||
assertThat(putLicenseResponse.status(), equalTo(LicensesStatus.VALID));
|
||||
|
||||
// get license should not return the expired license
|
||||
GetLicenseResponse getLicenseResponse = new GetLicenseRequestBuilder(client().admin().cluster()).get();
|
||||
assertThat(getLicenseResponse.licenses().size(), equalTo(1));
|
||||
|
||||
TestUtils.isSame(getLicenseResponse.licenses().get(0), signedLicense);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutLicensesForSameFeature() throws Exception {
|
||||
ESLicense shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
ESLicense longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5));
|
||||
List<ESLicense> actualLicenses = Arrays.asList(longerSignedLicense, shortedSignedLicense);
|
||||
License shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(longerSignedLicense, shortedSignedLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
|
||||
@ -135,9 +154,9 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testPutLicensesForMultipleFeatures() throws Exception {
|
||||
ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<ESLicense> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
|
||||
@ -156,10 +175,10 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testPutMultipleLicensesForMultipleFeatures() throws Exception {
|
||||
ESLicense shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
ESLicense longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5));
|
||||
ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<ESLicense> actualLicenses = Arrays.asList(marvelLicense, shortedSignedLicense, longerSignedLicense);
|
||||
License shortedSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License longerSignedLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(5));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shortedSignedLicense, longerSignedLicense);
|
||||
|
||||
// put license
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
|
||||
@ -179,9 +198,9 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testRemoveLicenseSimple() throws Exception {
|
||||
ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<ESLicense> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
|
||||
// put two licenses
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
|
||||
@ -209,9 +228,9 @@ public class LicensesTransportTests extends AbstractLicensesIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testRemoveLicenses() throws Exception {
|
||||
ESLicense shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
ESLicense marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<ESLicense> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
License shieldLicense = generateSignedLicense("shield", TimeValue.timeValueMinutes(2));
|
||||
License marvelLicense = generateSignedLicense("marvel", TimeValue.timeValueMinutes(5));
|
||||
List<License> actualLicenses = Arrays.asList(marvelLicense, shieldLicense);
|
||||
|
||||
// put two licenses
|
||||
PutLicenseRequestBuilder putLicenseRequestBuilder = new PutLicenseRequestBuilder(client().admin().cluster())
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.license.plugin;
|
||||
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.AbstractLicensingTestBase;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.TrialLicenseUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween;
|
||||
|
||||
public class TrailLicenseSerializationTests extends AbstractLicensingTestBase {
|
||||
|
||||
@Test
|
||||
public void testSerialization() throws Exception {
|
||||
final TrialLicenseUtils.TrialLicenseBuilder trialLicenseBuilder = TrialLicenseUtils.builder()
|
||||
.duration(TimeValue.timeValueHours(2))
|
||||
.maxNodes(5)
|
||||
.issuedTo("customer")
|
||||
.issueDate(System.currentTimeMillis());
|
||||
int n = randomIntBetween(2, 5);
|
||||
List<License> trialLicenses = new ArrayList<>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
trialLicenses.add(trialLicenseBuilder.feature("feature__" + String.valueOf(i)).build());
|
||||
}
|
||||
for (License trialLicense : trialLicenses) {
|
||||
String encodedTrialLicense = TrialLicenseUtils.toEncodedTrialLicense(trialLicense);
|
||||
final License fromEncodedTrialLicense = TrialLicenseUtils.fromEncodedTrialLicense(encodedTrialLicense);
|
||||
TestUtils.isSame(fromEncodedTrialLicense, trialLicense);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,13 +3,14 @@
|
||||
* 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;
|
||||
package org.elasticsearch.license.tools;
|
||||
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.elasticsearch.common.cli.commons.MissingOptionException;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool.KeyPairGenerator;
|
||||
import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool;
|
||||
import org.elasticsearch.license.licensor.tools.KeyPairGeneratorTool.KeyGenerator;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
@ -85,15 +86,15 @@ public class KeyPairGenerationToolTests extends CliToolTestCase {
|
||||
Command command = keyPairGeneratorTool.parse(KeyPairGeneratorTool.NAME, args("--privateKeyPath " + privateKeyPath
|
||||
+ " --publicKeyPath " + publicKeyPath));
|
||||
|
||||
assertThat(command, instanceOf(KeyPairGenerator.class));
|
||||
KeyPairGenerator keyPairGenerator = (KeyPairGenerator) command;
|
||||
assertThat(keyPairGenerator.privateKeyPath, equalTo(privateKeyPath));
|
||||
assertThat(keyPairGenerator.publicKeyPath, equalTo(publicKeyPath));
|
||||
assertThat(command, instanceOf(KeyGenerator.class));
|
||||
KeyGenerator keyGenerator = (KeyGenerator) command;
|
||||
assertThat(keyGenerator.privateKeyPath, equalTo(privateKeyPath));
|
||||
assertThat(keyGenerator.publicKeyPath, equalTo(publicKeyPath));
|
||||
|
||||
assertThat(Paths.get(publicKeyPath).toFile().exists(), equalTo(false));
|
||||
assertThat(Paths.get(privateKeyPath).toFile().exists(), equalTo(false));
|
||||
|
||||
assertThat(keyPairGenerator.execute(ImmutableSettings.EMPTY, new Environment(ImmutableSettings.EMPTY)), equalTo(ExitStatus.OK));
|
||||
assertThat(keyGenerator.execute(ImmutableSettings.EMPTY, new Environment(ImmutableSettings.EMPTY)), equalTo(ExitStatus.OK));
|
||||
assertThat(Paths.get(publicKeyPath).toFile().exists(), equalTo(true));
|
||||
assertThat(Paths.get(privateKeyPath).toFile().exists(), equalTo(true));
|
||||
|
@ -3,17 +3,17 @@
|
||||
* 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;
|
||||
package org.elasticsearch.license.tools;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.elasticsearch.common.cli.commons.MissingOptionException;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.tools.LicenseGeneratorTool;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@ -21,6 +21,8 @@ import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliTool.ExitStatus;
|
||||
@ -52,7 +54,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
LicenseSpec inputLicenseSpec = generateRandomLicenseSpec();
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
args("--license " + generateESLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
args("--license " + generateLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
+ " --publicKeyPath " + pubKeyPath.concat("invalid")
|
||||
+ " --privateKeyPath " + priKeyPath));
|
||||
|
||||
@ -61,7 +63,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
assertThat(exitCommand.status(), equalTo(ExitStatus.USAGE));
|
||||
|
||||
command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
args("--license " + generateESLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
args("--license " + generateLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
+ " --privateKeyPath " + priKeyPath.concat("invalid")
|
||||
+ " --publicKeyPath " + pubKeyPath));
|
||||
|
||||
@ -89,7 +91,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
boolean pubKeyMissing = randomBoolean();
|
||||
try {
|
||||
licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
args("--license " + generateESLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
args("--license " + generateLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
+ ((!pubKeyMissing) ? " --publicKeyPath " + pubKeyPath : "")
|
||||
+ ((pubKeyMissing) ? " --privateKeyPath " + priKeyPath : "")));
|
||||
fail("missing argument: " + ((pubKeyMissing) ? "publicKeyPath" : "privateKeyPath") + " should throw an exception");
|
||||
@ -103,7 +105,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
LicenseSpec inputLicenseSpec = generateRandomLicenseSpec();
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
args("--license " + generateESLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
args("--license " + generateLicenseSpecString(Arrays.asList(inputLicenseSpec))
|
||||
+ " --publicKeyPath " + pubKeyPath
|
||||
+ " --privateKeyPath " + priKeyPath));
|
||||
|
||||
@ -112,7 +114,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
assertThat(licenseGenerator.publicKeyFilePath, equalTo(pubKeyPath));
|
||||
assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath));
|
||||
assertThat(licenseGenerator.licenseSpecs.size(), equalTo(1));
|
||||
ESLicense outputLicenseSpec = licenseGenerator.licenseSpecs.iterator().next();
|
||||
License outputLicenseSpec = licenseGenerator.licenseSpecs.iterator().next();
|
||||
|
||||
assertLicenseSpec(inputLicenseSpec, outputLicenseSpec);
|
||||
}
|
||||
@ -121,7 +123,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
public void testParsingLicenseFile() throws Exception {
|
||||
LicenseSpec inputLicenseSpec = generateRandomLicenseSpec();
|
||||
File tempFile = temporaryFolder.newFile("license_spec.json");
|
||||
FileUtils.write(tempFile, generateESLicenseSpecString(Arrays.asList(inputLicenseSpec)));
|
||||
Files.write(Paths.get(tempFile.getAbsolutePath()), generateLicenseSpecString(Arrays.asList(inputLicenseSpec)).getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
@ -134,7 +136,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
assertThat(licenseGenerator.publicKeyFilePath, equalTo(pubKeyPath));
|
||||
assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath));
|
||||
assertThat(licenseGenerator.licenseSpecs.size(), equalTo(1));
|
||||
ESLicense outputLicenseSpec = licenseGenerator.licenseSpecs.iterator().next();
|
||||
License outputLicenseSpec = licenseGenerator.licenseSpecs.iterator().next();
|
||||
|
||||
assertLicenseSpec(inputLicenseSpec, outputLicenseSpec);
|
||||
}
|
||||
@ -149,7 +151,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
}
|
||||
LicenseGeneratorTool licenseGeneratorTool = new LicenseGeneratorTool();
|
||||
Command command = licenseGeneratorTool.parse(LicenseGeneratorTool.NAME,
|
||||
args("--license " + generateESLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values()))
|
||||
args("--license " + generateLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values()))
|
||||
+ " --publicKeyPath " + pubKeyPath
|
||||
+ " --privateKeyPath " + priKeyPath));
|
||||
|
||||
@ -159,7 +161,7 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
assertThat(licenseGenerator.privateKeyFilePath, equalTo(priKeyPath));
|
||||
assertThat(licenseGenerator.licenseSpecs.size(), equalTo(inputLicenseSpecs.size()));
|
||||
|
||||
for (ESLicense outputLicenseSpec : licenseGenerator.licenseSpecs) {
|
||||
for (License outputLicenseSpec : licenseGenerator.licenseSpecs) {
|
||||
LicenseSpec inputLicenseSpec = inputLicenseSpecs.get(outputLicenseSpec.feature());
|
||||
assertThat(inputLicenseSpec, notNullValue());
|
||||
assertLicenseSpec(inputLicenseSpec, outputLicenseSpec);
|
||||
@ -174,20 +176,20 @@ public class LicenseGenerationToolTests extends CliToolTestCase {
|
||||
LicenseSpec licenseSpec = generateRandomLicenseSpec();
|
||||
inputLicenseSpecs.put(licenseSpec.feature, licenseSpec);
|
||||
}
|
||||
List<ESLicense> licenseSpecs = ESLicenses.fromSource(generateESLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values())).getBytes(StandardCharsets.UTF_8), false);
|
||||
List<License> licenseSpecs = Licenses.fromSource(generateLicenseSpecString(new ArrayList<>(inputLicenseSpecs.values())).getBytes(StandardCharsets.UTF_8), false);
|
||||
|
||||
String output = runLicenseGenerationTool(pubKeyPath, priKeyPath, new HashSet<>(licenseSpecs), ExitStatus.OK);
|
||||
List<ESLicense> outputLicenses = ESLicenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
|
||||
List<License> outputLicenses = Licenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
|
||||
assertThat(outputLicenses.size(), equalTo(inputLicenseSpecs.size()));
|
||||
|
||||
for (ESLicense outputLicense : outputLicenses) {
|
||||
for (License outputLicense : outputLicenses) {
|
||||
LicenseSpec inputLicenseSpec = inputLicenseSpecs.get(outputLicense.feature());
|
||||
assertThat(inputLicenseSpec, notNullValue());
|
||||
assertLicenseSpec(inputLicenseSpec, outputLicense);
|
||||
}
|
||||
}
|
||||
|
||||
private String runLicenseGenerationTool(String pubKeyPath, String priKeyPath, Set<ESLicense> licenseSpecs, ExitStatus expectedExitStatus) throws Exception {
|
||||
private String runLicenseGenerationTool(String pubKeyPath, String priKeyPath, Set<License> licenseSpecs, ExitStatus expectedExitStatus) throws Exception {
|
||||
CaptureOutputTerminal outputTerminal = new CaptureOutputTerminal();
|
||||
LicenseGenerator licenseGenerator = new LicenseGenerator(outputTerminal, pubKeyPath, priKeyPath, licenseSpecs);
|
||||
assertThat(execute(licenseGenerator, ImmutableSettings.EMPTY), equalTo(expectedExitStatus));
|
@ -3,28 +3,30 @@
|
||||
* 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;
|
||||
package org.elasticsearch.license.tools;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.license.TestUtils;
|
||||
import org.elasticsearch.license.core.ESLicense;
|
||||
import org.elasticsearch.license.core.ESLicenses;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.Licenses;
|
||||
import org.elasticsearch.license.licensor.tools.LicenseVerificationTool;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
import static org.elasticsearch.common.cli.CliTool.Command;
|
||||
import static org.elasticsearch.common.cli.CliTool.ExitStatus;
|
||||
import static org.elasticsearch.license.AbstractLicensingTestBase.generateSignedLicense;
|
||||
import static org.elasticsearch.license.TestUtils.generateSignedLicense;
|
||||
import static org.elasticsearch.license.licensor.tools.LicenseVerificationTool.LicenseVerifier;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
@ -47,7 +49,7 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
|
||||
@Test
|
||||
public void testParsingSimple() throws Exception {
|
||||
ESLicense inputLicense = generateSignedLicense("feature__1",
|
||||
License inputLicense = TestUtils.generateSignedLicense("feature__1",
|
||||
TimeValue.timeValueHours(1));
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
Command command = licenseVerificationTool.parse(LicenseVerificationTool.NAME,
|
||||
@ -55,13 +57,13 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
assertThat(command, instanceOf(LicenseVerifier.class));
|
||||
LicenseVerifier licenseVerifier = (LicenseVerifier) command;
|
||||
assertThat(licenseVerifier.licenses.size(), equalTo(1));
|
||||
ESLicense outputLicense = licenseVerifier.licenses.iterator().next();
|
||||
License outputLicense = licenseVerifier.licenses.iterator().next();
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsingLicenseFile() throws Exception {
|
||||
ESLicense inputLicense = generateSignedLicense("feature__1",
|
||||
License inputLicense = TestUtils.generateSignedLicense("feature__1",
|
||||
TimeValue.timeValueHours(1));
|
||||
|
||||
LicenseVerificationTool licenseVerificationTool = new LicenseVerificationTool();
|
||||
@ -70,7 +72,7 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
assertThat(command, instanceOf(LicenseVerifier.class));
|
||||
LicenseVerifier licenseVerifier = (LicenseVerifier) command;
|
||||
assertThat(licenseVerifier.licenses.size(), equalTo(1));
|
||||
ESLicense outputLicense = licenseVerifier.licenses.iterator().next();
|
||||
License outputLicense = licenseVerifier.licenses.iterator().next();
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
|
||||
}
|
||||
@ -78,15 +80,15 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
@Test
|
||||
public void testParsingMultipleLicense() throws Exception {
|
||||
int n = randomIntBetween(2, 5);
|
||||
Map<String, ESLicense> inputLicenses = new HashMap<>();
|
||||
Map<String, License> inputLicenses = new HashMap<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
ESLicense esLicense = generateSignedLicense("feature__" + i,
|
||||
License license = TestUtils.generateSignedLicense("feature__" + i,
|
||||
TimeValue.timeValueHours(1));
|
||||
inputLicenses.put(esLicense.feature(), esLicense);
|
||||
inputLicenses.put(license.feature(), license);
|
||||
}
|
||||
|
||||
StringBuilder argsBuilder = new StringBuilder();
|
||||
for (ESLicense inputLicense : inputLicenses.values()) {
|
||||
for (License inputLicense : inputLicenses.values()) {
|
||||
argsBuilder.append(" --license ")
|
||||
.append(TestUtils.dumpLicense(inputLicense));
|
||||
}
|
||||
@ -97,8 +99,8 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
LicenseVerifier licenseVerifier = (LicenseVerifier) command;
|
||||
assertThat(licenseVerifier.licenses.size(), equalTo(inputLicenses.size()));
|
||||
|
||||
for (ESLicense outputLicense : licenseVerifier.licenses) {
|
||||
ESLicense inputLicense = inputLicenses.get(outputLicense.feature());
|
||||
for (License outputLicense : licenseVerifier.licenses) {
|
||||
License inputLicense = inputLicenses.get(outputLicense.feature());
|
||||
assertThat(inputLicense, notNullValue());
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
}
|
||||
@ -107,19 +109,19 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
@Test
|
||||
public void testToolSimple() throws Exception {
|
||||
int n = randomIntBetween(2, 5);
|
||||
Map<String, ESLicense> inputLicenses = new HashMap<>();
|
||||
Map<String, License> inputLicenses = new HashMap<>();
|
||||
for (int i = 0; i < n; i++) {
|
||||
ESLicense esLicense = generateSignedLicense("feature__" + i,
|
||||
License license = TestUtils.generateSignedLicense("feature__" + i,
|
||||
TimeValue.timeValueHours(1));
|
||||
inputLicenses.put(esLicense.feature(), esLicense);
|
||||
inputLicenses.put(license.feature(), license);
|
||||
}
|
||||
|
||||
String output = runLicenseVerificationTool(new HashSet<>(inputLicenses.values()), ExitStatus.OK);
|
||||
List<ESLicense> outputLicenses = ESLicenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
|
||||
List<License> outputLicenses = Licenses.fromSource(output.getBytes(StandardCharsets.UTF_8), true);
|
||||
assertThat(outputLicenses.size(), equalTo(inputLicenses.size()));
|
||||
|
||||
for (ESLicense outputLicense : outputLicenses) {
|
||||
ESLicense inputLicense = inputLicenses.get(outputLicense.feature());
|
||||
for (License outputLicense : outputLicenses) {
|
||||
License inputLicense = inputLicenses.get(outputLicense.feature());
|
||||
assertThat(inputLicense, notNullValue());
|
||||
TestUtils.isSame(inputLicense, outputLicense);
|
||||
}
|
||||
@ -127,23 +129,23 @@ public class LicenseVerificationToolTests extends CliToolTestCase {
|
||||
|
||||
@Test
|
||||
public void testToolInvalidLicense() throws Exception {
|
||||
ESLicense signedLicense = generateSignedLicense("feature__1"
|
||||
License signedLicense = TestUtils.generateSignedLicense("feature__1"
|
||||
, TimeValue.timeValueHours(1));
|
||||
|
||||
ESLicense tamperedLicense = ESLicense.builder()
|
||||
License tamperedLicense = License.builder()
|
||||
.fromLicenseSpec(signedLicense, signedLicense.signature())
|
||||
.expiryDate(signedLicense.expiryDate() + randomIntBetween(1, 1000)).build();
|
||||
|
||||
runLicenseVerificationTool(Collections.singleton(tamperedLicense), ExitStatus.DATA_ERROR);
|
||||
}
|
||||
|
||||
private String dumpLicenseAsFile(ESLicense license) throws Exception {
|
||||
private String dumpLicenseAsFile(License license) throws Exception {
|
||||
File tempFile = temporaryFolder.newFile();
|
||||
FileUtils.write(tempFile, TestUtils.dumpLicense(license));
|
||||
Files.write(Paths.get(tempFile.getAbsolutePath()), TestUtils.dumpLicense(license).getBytes(StandardCharsets.UTF_8));
|
||||
return tempFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
private String runLicenseVerificationTool(Set<ESLicense> licenses, ExitStatus expectedExitStatus) throws Exception {
|
||||
private String runLicenseVerificationTool(Set<License> licenses, ExitStatus expectedExitStatus) throws Exception {
|
||||
CaptureOutputTerminal outputTerminal = new CaptureOutputTerminal();
|
||||
LicenseVerifier licenseVerifier = new LicenseVerifier(outputTerminal, licenses);
|
||||
assertThat(execute(licenseVerifier, ImmutableSettings.EMPTY), equalTo(expectedExitStatus));
|
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user