Skip to content

Commit

Permalink
Support "enterprise" license types (#49474)
Browse files Browse the repository at this point in the history
This adds "enterprise" as an acceptable type for a license loaded
through the PUT _license API.

Internally an enterprise license is treated as having a "platinum"
operating mode.

The handling of License types was refactored to have a new explicit
"LicenseType" enum in addition to the existing "OperatingMode" enum.

By default (in 7.x) the GET license API will return "platinum" when an
enterprise license is active in order to be compatible with existing
consumers of that API.
A new "accept_enterprise" flag has been introduced to allow clients to
opt-in to receive the correct "enterprise" type.

Backport of: #49223
  • Loading branch information
tvernum authored Dec 12, 2019
1 parent 54467b5 commit 47e5e34
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 140 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ protected void execute(Terminal terminal, OptionSet options) throws Exception {
ExitCodes.USAGE,
"Must specify either --license or --licenseFile");
}
if (licenseSpec == null) {
throw new UserException(ExitCodes.DATA_ERROR, "Could not parse license spec");
}

// sign
License license = new LicenseSigner(privateKeyPath, publicKeyPath).sign(licenseSpec);
Expand Down
131 changes: 103 additions & 28 deletions x-pack/plugin/core/src/main/java/org/elasticsearch/license/License.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,6 @@
*/
package org.elasticsearch.license;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

import org.apache.lucene.util.CollectionUtil;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
Expand All @@ -31,11 +22,83 @@
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.protocol.xpack.license.LicenseStatus;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Data structure for license. Use {@link Builder} to build a license.
* Provides serialization/deserialization & validation methods for license object
*/
public class License implements ToXContentObject {

public enum LicenseType {
BASIC,
STANDARD,
GOLD,
PLATINUM,
ENTERPRISE,
TRIAL;

public String getTypeName() {
return name().toLowerCase(Locale.ROOT);
}

public static LicenseType parse(String type) throws IllegalArgumentException {
try {
return LicenseType.valueOf(type.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("unrecognised license type [ " + type + "], supported license types are ["
+ Stream.of(values()).map(LicenseType::getTypeName).collect(Collectors.joining(",")) + "]");
}
}

/**
* Backward compatible license type parsing for older license models
*/
public static LicenseType resolve(String name) {
switch (name.toLowerCase(Locale.ROOT)) {
case "missing":
return null;
case "trial":
case "none": // bwc for 1.x subscription_type field
case "dev": // bwc for 1.x subscription_type field
case "development": // bwc for 1.x subscription_type field
return TRIAL;
case "basic":
return BASIC;
case "standard":
return STANDARD;
case "silver":
case "gold":
return GOLD;
case "platinum":
case "cloud_internal":
case "internal": // bwc for 1.x subscription_type field
return PLATINUM;
case "enterprise":
return ENTERPRISE;
default:
throw new IllegalArgumentException("unknown license type [" + name + "]");
}
}

static boolean isBasic(String typeName) {
return BASIC.getTypeName().equals(typeName);
}

static boolean isTrial(String typeName) {
return TRIAL.getTypeName().equals(typeName);
}
}

public static final int VERSION_START = 1;
public static final int VERSION_NO_FEATURE_TYPE = 2;
public static final int VERSION_START_DATE = 3;
Expand All @@ -49,6 +112,12 @@ public class License implements ToXContentObject {
* and in a human readable format
*/
public static final String REST_VIEW_MODE = "rest_view";
/**
* XContent param name to map the "enterprise" license type to "platinum"
* for backwards compatibility with older clients
*/
public static final String XCONTENT_HIDE_ENTERPRISE = "hide_enterprise";

/**
* XContent param name to deserialize license(s) with
* no signature
Expand Down Expand Up @@ -102,28 +171,25 @@ public static int compare(OperationMode opMode1, OperationMode opMode2) {
return Integer.compare(opMode1.id, opMode2.id);
}

public static OperationMode resolve(String type) {
switch (type.toLowerCase(Locale.ROOT)) {
case "missing":
return MISSING;
case "trial":
case "none": // bwc for 1.x subscription_type field
case "dev": // bwc for 1.x subscription_type field
case "development": // bwc for 1.x subscription_type field
return TRIAL;
case "basic":
public static OperationMode resolve(String typeName) {
LicenseType type = LicenseType.resolve(typeName);
if (type == null) {
return MISSING;
}
switch (type) {
case BASIC:
return BASIC;
case "standard":
case STANDARD:
return STANDARD;
case "silver":
case "gold":
case GOLD:
return GOLD;
case "platinum":
case "cloud_internal":
case "internal": // bwc for 1.x subscription_type field
case PLATINUM:
case ENTERPRISE: // TODO Add an explicit enterprise operating mode
return PLATINUM;
case TRIAL:
return TRIAL;
default:
throw new IllegalArgumentException("unknown type [" + type + "]");
throw new IllegalArgumentException("unsupported license type [" + type.getTypeName() + "]");
}
}

Expand Down Expand Up @@ -301,7 +367,7 @@ private void validate() {
throw new IllegalStateException("maxNodes has to be set");
} else if (expiryDate == -1) {
throw new IllegalStateException("expiryDate has to be set");
} else if (expiryDate == LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS && "basic".equals(type) == false) {
} else if (expiryDate == LicenseService.BASIC_SELF_GENERATED_LICENSE_EXPIRATION_MILLIS && LicenseType.isBasic(type) == false) {
throw new IllegalStateException("only basic licenses are allowed to have no expiration");
}
}
Expand Down Expand Up @@ -377,6 +443,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) throws IOException {
boolean licenseSpecMode = params.paramAsBoolean(LICENSE_SPEC_VIEW_MODE, false);
boolean restViewMode = params.paramAsBoolean(REST_VIEW_MODE, false);
boolean hideEnterprise = params.paramAsBoolean(XCONTENT_HIDE_ENTERPRISE, false);
boolean previouslyHumanReadable = builder.humanReadable();
if (licenseSpecMode && restViewMode) {
throw new IllegalArgumentException("can have either " + REST_VIEW_MODE + " or " + LICENSE_SPEC_VIEW_MODE);
Expand All @@ -395,7 +462,10 @@ public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) t
builder.field(Fields.STATUS, status().label());
}
builder.field(Fields.UID, uid);
builder.field(Fields.TYPE, type);

final String bwcType = hideEnterprise && "enterprise".equals(type) ? "platinum" : type;
builder.field(Fields.TYPE, bwcType);

if (version == VERSION_START) {
builder.field(Fields.SUBSCRIPTION_TYPE, subscriptionType);
}
Expand Down Expand Up @@ -689,6 +759,10 @@ public Builder issueDate(long issueDate) {
return this;
}

public Builder type(LicenseType type) {
return type(type.getTypeName());
}

public Builder type(String type) {
this.type = type;
return this;
Expand Down Expand Up @@ -778,6 +852,7 @@ public Builder validate() {
}
return this;
}

}

}
Loading

0 comments on commit 47e5e34

Please sign in to comment.