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