From 72cf242dc443f0704517f6d59d37d32e1c8a63c9 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 21 Feb 2020 14:22:24 +1100 Subject: [PATCH] Refactor license checking (#52118) Improve code resuse and readility. Add convenience checking method which covers most use cases without having to pass many boolean arguments. --- .../elasticsearch/license/LicenseService.java | 4 +- .../license/XPackLicenseState.java | 284 ++++++------------ .../core/ml/inference/TrainedModelConfig.java | 7 +- .../ml/inference/TrainedModelConfigTests.java | 72 +++-- 4 files changed, 132 insertions(+), 235 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java index 484f97f4a26ae..cecdecb64a851 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicenseService.java @@ -255,9 +255,7 @@ && isProductionMode(settings, clusterService.localNode())) { throw new IllegalStateException("Cannot install a [" + newLicense.operationMode() + "] license unless TLS is configured or security is disabled"); } else if (XPackSettings.FIPS_MODE_ENABLED.get(settings) - && newLicense.operationMode() != License.OperationMode.PLATINUM - && newLicense.operationMode() != License.OperationMode.ENTERPRISE - && newLicense.operationMode() != License.OperationMode.TRIAL) { + && false == XPackLicenseState.isFipsAllowedForOperationMode(newLicense.operationMode())) { throw new IllegalStateException("Cannot install a [" + newLicense.operationMode() + "] license unless FIPS mode is disabled"); } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java index 798f310f20f27..5def09fc46614 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/XPackLicenseState.java @@ -358,6 +358,15 @@ public OperationMode getOperationMode() { return executeAgainstStatus(status -> status.mode); } + /** + * Checks that the cluster has a valid licence of any level. + * @see #isActive() + */ + public boolean allowForAllLicenses() { + return checkAgainstStatus(status -> status.active); + } + + // Package private for tests /** Return true if the license is currently within its time boundaries, false otherwise. */ public boolean isActive() { return checkAgainstStatus(status -> status.active); @@ -368,31 +377,19 @@ public boolean isActive() { * @see #allowedRealmType() for the enabled realms */ public boolean isAuthAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.BASIC, true, false, true); + return isAllowedBySecurityAndLicense(OperationMode.BASIC, false, true); } - /** - * @return true if IP filtering should be enabled - */ public boolean isIpFilteringAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.GOLD, true, false, true); + return isAllowedBySecurityAndLicense(OperationMode.GOLD, false, true); } - /** - * @return true if auditing should be enabled - */ public boolean isAuditingAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.GOLD, true, false, true); + return isAllowedBySecurityAndLicense(OperationMode.GOLD, false, true); } - /** - * Indicates whether the stats and health API calls should be allowed. If a license is expired and past the grace - * period then we deny these calls. - * - * @return true if the license allows for the stats and health APIs to be used. - */ public boolean isStatsAndHealthAllowed() { - return isActive(); + return allowForAllLicenses(); } /** @@ -400,15 +397,16 @@ public boolean isStatsAndHealthAllowed() { *

* DLS and FLS are only disabled when the mode is not: *

* Note: This does not consider the state of the license so that Security does not suddenly leak information! + * i.e. the same DLS guarantee keeps working for existing configuration even after license expires. * * @return {@code true} to enable DLS and FLS. Otherwise {@code false}. */ public boolean isDocumentAndFieldLevelSecurityAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, true, false, true); + return isAllowedBySecurityAndLicense(OperationMode.PLATINUM, false, true); } /** Classes of realms that may be available based on the license type. */ @@ -445,67 +443,46 @@ public AllowedRealmType allowedRealmType() { }); } - /** - * @return whether custom role providers are allowed based on the license {@link OperationMode} - */ public boolean isCustomRoleProvidersAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, true, true, true); + return isAllowedBySecurityAndLicense(OperationMode.PLATINUM, true, true); } /** - * @return whether the Elasticsearch {@code TokenService} is allowed based on the license {@link OperationMode} + * Whether the Elasticsearch {@code TokenService} is allowed */ public boolean isTokenServiceAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.GOLD, true, false, true); + return isAllowedBySecurityAndLicense(OperationMode.GOLD, false, true); } /** - * @return whether the Elasticsearch {@code ApiKeyService} is allowed based on the current node/cluster state + * Whether the Elasticsearch {@code ApiKeyService} is allowed */ public boolean isApiKeyServiceAllowed() { - return isAllowedBySecurity(); + return isAllowedBySecurityAndLicense(OperationMode.MISSING, false, true); } /** - * @return whether "authorization_realms" are allowed based on the license {@link OperationMode} + * Whether "authorization_realms" is allowed * @see org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings */ public boolean isAuthorizationRealmAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, true, true, true); + return isAllowedBySecurityAndLicense(OperationMode.PLATINUM, true, true); } /** - * @return whether a custom authorization engine is allowed based on the license {@link OperationMode} + * Whether a custom authorization engine is allowed * @see org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings */ public boolean isAuthorizationEngineAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, true, true, true); + return isAllowedBySecurityAndLicense(OperationMode.PLATINUM, true, true); } - /** - * Determine if Watcher is available based on the current license. - *

- * Watcher is available if the license is active (hasn't expired) and of one of the following types: - *

- * - * @return {@code true} as long as the license is valid. Otherwise {@code false}. - */ public boolean isWatcherAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.STANDARD, false, true, true); + return isAllowedByLicense(OperationMode.STANDARD); } - /** - * Monitoring is always available as long as there is a valid license - * - * @return true if the license is active - */ public boolean isMonitoringAllowed() { - return isActive(); + return allowForAllLicenses(); } /** @@ -528,52 +505,23 @@ public boolean isMonitoringClusterAlertsAllowed() { * @return {@code true} if the user is allowed to modify the retention. Otherwise {@code false}. */ public boolean isUpdateRetentionAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.STANDARD, false, false, true); + return isAllowedByLicense(OperationMode.STANDARD, false, true); } - /** - * Determine if Graph Exploration should be enabled. - *

- * Exploration is only disabled when the license has expired or if the mode is not: - *

- * - * @return {@code true} as long as the license is valid. Otherwise {@code false}. - */ public boolean isGraphAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, false, true, true); + return isAllowedByLicense(OperationMode.PLATINUM); } - /** - * Determine if Machine Learning should be enabled. - *

- * Machine Learning is only disabled when the license has expired or if the - * mode is not: - *

- * - * @return {@code true} as long as the license is valid. Otherwise - * {@code false}. - */ public boolean isMachineLearningAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, false, true, true); + return isAllowedByLicense(OperationMode.PLATINUM); } public static boolean isMachineLearningAllowedForOperationMode(final OperationMode operationMode) { return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM, true); } - /** - * Transform is always available as long as there is a valid license - * - * @return true if the license is active - */ public boolean isTransformAllowed() { - return isActive(); + return allowForAllLicenses(); } public static boolean isTransformAllowedForOperationMode(final OperationMode operationMode) { @@ -585,154 +533,75 @@ public static boolean isFipsAllowedForOperationMode(final OperationMode operatio return isAllowedByOperationMode(operationMode, OperationMode.PLATINUM, true); } - /** - * Rollup is always available as long as there is a valid license - * - * @return true if the license is active - */ public boolean isRollupAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Voting only node functionality is always available as long as there is a valid license - * - * @return true if the license is active - */ public boolean isVotingOnlyAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Logstash is allowed as long as there is an active license of type TRIAL, STANDARD, GOLD or PLATINUM - * @return {@code true} as long as there is a valid license - */ public boolean isLogstashAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.STANDARD, false, true, true); + return isAllowedByLicense(OperationMode.STANDARD); } - /** - * Beats is allowed as long as there is an active license of type TRIAL, STANDARD, GOLD or PLATINUM - * @return {@code true} as long as there is a valid license - */ public boolean isBeatsAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.STANDARD, false, true, true); + return isAllowedByLicense(OperationMode.STANDARD); } - /** - * Deprecation APIs are always allowed as long as there is an active license - * @return {@code true} as long as there is a valid license - */ public boolean isDeprecationAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Determine if Upgrade API should be enabled. - * - * @return {@code true} as long as the license is valid. Otherwise - * {@code false}. - */ public boolean isUpgradeAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Determine if Index Lifecycle API should be enabled. - * - * @return {@code true} as long as the license is valid. Otherwise - * {@code false}. - */ public boolean isIndexLifecycleAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Determine if the enrich processor and related APIs are allowed to be used. - * - * @return {@code true} as long as the license is valid. Otherwise - * {@code false}. - */ public boolean isEnrichAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Determine if EQL support should be enabled. - *

- * EQL is available for all license types except {@link OperationMode#MISSING} - */ public boolean isEqlAllowed() { - return checkAgainstStatus(status -> status.active); + return allowForAllLicenses(); } - /** - * Determine if SQL support should be enabled. - */ public boolean isSqlAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Determine if JDBC support should be enabled. - *

- * JDBC is available only in for {@link OperationMode#PLATINUM} and {@link OperationMode#TRIAL} licences - */ public boolean isJdbcAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, false, true, true); + return isAllowedByLicense(OperationMode.PLATINUM); } - /** - * Determine if support for flattened object fields should be enabled. - */ public boolean isFlattenedAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Determine if Vectors support should be enabled. - */ public boolean isVectorsAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Determine if ODBC support should be enabled. - *

- * ODBC is available only in for {@link OperationMode#PLATINUM} and {@link OperationMode#TRIAL} licences - */ public boolean isOdbcAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, false, true, true); + return isAllowedByLicense(OperationMode.PLATINUM); } - /** - * Determine if Spatial features should be enabled. - * - * @return {@code true} as long as the license is valid. Otherwise - * {@code false}. - */ public boolean isSpatialAllowed() { - return isActive(); + return allowForAllLicenses(); } - /** - * Datascience is always available as long as there is a valid license - * - * @return true if the license is active - */ public boolean isDataScienceAllowed() { - return isActive(); + return allowForAllLicenses(); } /** * @return true if security is available to be used with the current license type */ public boolean isSecurityAvailable() { - return checkAgainstStatus(status -> { - OperationMode mode = status.mode; - return mode == OperationMode.GOLD || mode == OperationMode.PLATINUM || mode == OperationMode.STANDARD || - mode == OperationMode.TRIAL || mode == OperationMode.BASIC || mode == OperationMode.ENTERPRISE; - }); + return checkAgainstStatus(status -> status.mode != OperationMode.MISSING); } /** @@ -786,18 +655,10 @@ private static boolean isSecurityEnabled(final OperationMode mode, final boolean } /** - * Determine if cross-cluster replication should be enabled. - *

- * Cross-cluster replication is only disabled when the license has expired or if the mode is not: - *

- * - * @return true is the license is compatible, otherwise false + * Determine if cross-cluster replication is allowed */ public boolean isCcrAllowed() { - return isAllowedByLicenseAndSecurity(OperationMode.PLATINUM, false, true, true); + return isAllowedByLicense(OperationMode.PLATINUM); } public static boolean isCcrAllowedForOperationMode(final OperationMode operationMode) { @@ -823,26 +684,43 @@ public XPackLicenseState copyCurrentLicenseState() { return executeAgainstStatus(status -> new XPackLicenseState(listeners, isSecurityEnabled, isSecurityExplicitlyEnabled, status)); } - private boolean isAllowedBySecurity() { - return checkAgainstStatus(status -> isSecurityEnabled(status.mode, isSecurityExplicitlyEnabled, isSecurityEnabled)); + /** + * Test whether a feature is allowed by the status of license and security configuration. + * Note the difference to {@link #isAllowedByLicense(OperationMode, boolean, boolean)} + * is this method requires security to be enabled. + * + * @param minimumMode The minimum license to meet or exceed + * @param needActive Whether current license needs to be active. + * @param allowTrial Whether the feature is allowed for trial license + * + * @return true if feature is allowed, otherwise false + */ + private boolean isAllowedBySecurityAndLicense(OperationMode minimumMode, boolean needActive, boolean allowTrial) { + return checkAgainstStatus(status -> { + if (false == isSecurityEnabled(status.mode, isSecurityExplicitlyEnabled, isSecurityEnabled)) { + return false; + } + // Do not delegate to isAllowedByLicense as it also captures "status" which may be different from here + if (needActive && false == status.active) { + return false; + } + return isAllowedByOperationMode(status.mode, minimumMode, allowTrial); + }); } /** - * Test whether a feature is allowed by the status of current license and security configuration. + * Test whether a feature is allowed by the status of license. Note difference to + * {@link #isAllowedBySecurityAndLicense} is this method does Not require security + * to be enabled. * * @param minimumMode The minimum license to meet or exceed - * @param needSecurity Whether security is required for feature to be allowed * @param needActive Whether current license needs to be active * @param allowTrial Whether the feature is allowed for trial license * * @return true if feature is allowed, otherwise false */ - private boolean isAllowedByLicenseAndSecurity( - OperationMode minimumMode, boolean needSecurity, boolean needActive, boolean allowTrial) { + public boolean isAllowedByLicense(OperationMode minimumMode, boolean needActive, boolean allowTrial) { return checkAgainstStatus(status -> { - if (needSecurity && false == isSecurityEnabled(status.mode, isSecurityExplicitlyEnabled, isSecurityEnabled)) { - return false; - } if (needActive && false == status.active) { return false; } @@ -850,4 +728,14 @@ private boolean isAllowedByLicenseAndSecurity( }); } + /** + * A convenient method to test whether a feature is by license status. + * @see #isAllowedByLicense(OperationMode, boolean, boolean) + * + * @param minimumMode The minimum license to meet or exceed + */ + public boolean isAllowedByLicense(OperationMode minimumMode) { + return isAllowedByLicense(minimumMode, true, true); + } + } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java index dcc2d513a4dc3..e2511c39db119 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfig.java @@ -242,14 +242,13 @@ public boolean isAvailableWithLicense(XPackLicenseState licenseState) { return true; } - // The model license does not matter, this is the highest licensed level - if (licenseState.isActive() && XPackLicenseState.isAllowedByOperationMode( - licenseState.getOperationMode(), License.OperationMode.PLATINUM, true)) { + // The model license does not matter, Platinum license gets the same functions as the highest license + if (licenseState.isAllowedByLicense(License.OperationMode.PLATINUM)) { return true; } // catch the rest, if the license is active and is at least the required model license - return licenseState.isActive() && License.OperationMode.compare(licenseState.getOperationMode(), licenseLevel) >= 0; + return licenseState.isAllowedByLicense(licenseLevel, true, false); } @Override diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java index 03e155cf9d5ec..77b9ab2691f0e 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/inference/TrainedModelConfigTests.java @@ -44,6 +44,8 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -307,53 +309,63 @@ public void testSerializationWithCompressedLazyDefinition() throws IOException { public void testIsAvailableWithLicense() { TrainedModelConfig.Builder builder = createTestInstance(randomAlphaOfLength(10)); - XPackLicenseState licenseState = mock(XPackLicenseState.class); - when(licenseState.isActive()).thenReturn(false); - when(licenseState.getOperationMode()).thenReturn(License.OperationMode.BASIC); + // Reject everything + when(licenseState.isAllowedByLicense(any(License.OperationMode.class), anyBoolean(), anyBoolean())).thenAnswer( + invocationOnMock -> { + final Object[] arguments = invocationOnMock.getArguments(); + assertTrue((boolean) arguments[1]); // ensure the call is made to require active license + return false; + } + ); assertFalse(builder.setLicenseLevel(License.OperationMode.ENTERPRISE.description()).build().isAvailableWithLicense(licenseState)); assertFalse(builder.setLicenseLevel(License.OperationMode.PLATINUM.description()).build().isAvailableWithLicense(licenseState)); + assertFalse(builder.setLicenseLevel(License.OperationMode.GOLD.description()).build().isAvailableWithLicense(licenseState)); + // Basic license always works not matter what assertTrue(builder.setLicenseLevel(License.OperationMode.BASIC.description()).build().isAvailableWithLicense(licenseState)); + } - when(licenseState.isActive()).thenReturn(true); - when(licenseState.getOperationMode()).thenReturn(License.OperationMode.ENTERPRISE); - assertTrue(builder.setLicenseLevel(License.OperationMode.ENTERPRISE.description()).build().isAvailableWithLicense(licenseState)); - assertTrue(builder.setLicenseLevel(License.OperationMode.PLATINUM.description()).build().isAvailableWithLicense(licenseState)); - assertTrue(builder.setLicenseLevel(License.OperationMode.BASIC.description()).build().isAvailableWithLicense(licenseState)); - assertTrue(builder.setLicenseLevel(License.OperationMode.GOLD.description()).build().isAvailableWithLicense(licenseState)); + public void testActivePlatinumLicenseAlwaysWorks() { + TrainedModelConfig.Builder builder = createTestInstance(randomAlphaOfLength(10)); + XPackLicenseState licenseState = mock(XPackLicenseState.class); - when(licenseState.isActive()).thenReturn(false); - assertFalse(builder.setLicenseLevel(License.OperationMode.ENTERPRISE.description()).build().isAvailableWithLicense(licenseState)); - assertFalse(builder.setLicenseLevel(License.OperationMode.PLATINUM.description()).build().isAvailableWithLicense(licenseState)); - assertTrue(builder.setLicenseLevel(License.OperationMode.BASIC.description()).build().isAvailableWithLicense(licenseState)); - assertFalse(builder.setLicenseLevel(License.OperationMode.GOLD.description()).build().isAvailableWithLicense(licenseState)); + when(licenseState.isAllowedByLicense(License.OperationMode.PLATINUM)).thenReturn(true); - when(licenseState.isActive()).thenReturn(true); - when(licenseState.getOperationMode()).thenReturn(License.OperationMode.PLATINUM); + // Active Platinum license functions the same as Enterprise license (highest) and should always work + when(licenseState.isAllowedByLicense(any(License.OperationMode.class), anyBoolean(), anyBoolean())).thenAnswer( + invocationOnMock -> { + final Object[] arguments = invocationOnMock.getArguments(); + assertEquals(License.OperationMode.PLATINUM, arguments[0]); + assertTrue((boolean) arguments[1]); // ensure the call is made to require active license + assertTrue((boolean) arguments[2]); + return true; + } + ); assertTrue(builder.setLicenseLevel(License.OperationMode.ENTERPRISE.description()).build().isAvailableWithLicense(licenseState)); assertTrue(builder.setLicenseLevel(License.OperationMode.PLATINUM.description()).build().isAvailableWithLicense(licenseState)); assertTrue(builder.setLicenseLevel(License.OperationMode.BASIC.description()).build().isAvailableWithLicense(licenseState)); assertTrue(builder.setLicenseLevel(License.OperationMode.GOLD.description()).build().isAvailableWithLicense(licenseState)); + } - when(licenseState.isActive()).thenReturn(false); - assertFalse(builder.setLicenseLevel(License.OperationMode.ENTERPRISE.description()).build().isAvailableWithLicense(licenseState)); - assertFalse(builder.setLicenseLevel(License.OperationMode.PLATINUM.description()).build().isAvailableWithLicense(licenseState)); - assertTrue(builder.setLicenseLevel(License.OperationMode.BASIC.description()).build().isAvailableWithLicense(licenseState)); - assertFalse(builder.setLicenseLevel(License.OperationMode.GOLD.description()).build().isAvailableWithLicense(licenseState)); + public void testActiveGoldLicenseWillWorkWhenRequiredLevelIsGold() { + TrainedModelConfig.Builder builder = createTestInstance(randomAlphaOfLength(10)); + XPackLicenseState licenseState = mock(XPackLicenseState.class); - when(licenseState.isActive()).thenReturn(true); - when(licenseState.getOperationMode()).thenReturn(License.OperationMode.GOLD); + // Active Gold license should work when required level is gold + when(licenseState.isAllowedByLicense(any(License.OperationMode.class), anyBoolean(), anyBoolean())).thenAnswer( + invocationOnMock -> { + final Object[] arguments = invocationOnMock.getArguments(); + assertTrue((boolean) arguments[1]); // ensure the call is made to require active license + if (License.OperationMode.PLATINUM == arguments[0] && Boolean.TRUE.equals(arguments[2])) { + return false; + } else + return License.OperationMode.GOLD == arguments[0] && Boolean.FALSE.equals(arguments[2]); + } + ); assertFalse(builder.setLicenseLevel(License.OperationMode.ENTERPRISE.description()).build().isAvailableWithLicense(licenseState)); assertFalse(builder.setLicenseLevel(License.OperationMode.PLATINUM.description()).build().isAvailableWithLicense(licenseState)); assertTrue(builder.setLicenseLevel(License.OperationMode.BASIC.description()).build().isAvailableWithLicense(licenseState)); assertTrue(builder.setLicenseLevel(License.OperationMode.GOLD.description()).build().isAvailableWithLicense(licenseState)); - - when(licenseState.isActive()).thenReturn(false); - assertFalse(builder.setLicenseLevel(License.OperationMode.ENTERPRISE.description()).build().isAvailableWithLicense(licenseState)); - assertFalse(builder.setLicenseLevel(License.OperationMode.PLATINUM.description()).build().isAvailableWithLicense(licenseState)); - assertTrue(builder.setLicenseLevel(License.OperationMode.BASIC.description()).build().isAvailableWithLicense(licenseState)); - assertFalse(builder.setLicenseLevel(License.OperationMode.GOLD.description()).build().isAvailableWithLicense(licenseState)); } - }