diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java index 51737546ffad..bcca229c06bc 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -71,7 +71,6 @@ import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.config.ConfigurationGroup; -import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.alert.Alert; import com.cloud.capacity.Capacity; @@ -108,14 +107,6 @@ public interface ManagementService { static final String Name = "management-server"; - ConfigKey JsInterpretationEnabled = new ConfigKey<>("Hidden" - , Boolean.class - , "js.interpretation.enabled" - , "false" - , "Enable/Disable all JavaScript interpretation related functionalities to create or update Javascript rules." - , false - , ConfigKey.Scope.Global); - /** * returns the a map of the names/values in the configuration table * @@ -534,6 +525,4 @@ VirtualMachine upgradeSystemVM(ScaleSystemVMCmd cmd) throws ResourceUnavailableE boolean removeManagementServer(RemoveManagementServerCmd cmd); - void checkJsInterpretationAllowedIfNeededForParameterValue(String paramName, boolean paramValue); - } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.java index df4743894c9d..e51fae745af1 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42210to42300.java @@ -17,7 +17,12 @@ package com.cloud.upgrade.dao; import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.exception.CloudRuntimeException; public class Upgrade42210to42300 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { @@ -42,4 +47,46 @@ public InputStream[] getPrepareScripts() { return new InputStream[] {script}; } + + @Override + public void performDataMigration(Connection conn) { + unhideJsInterpretationEnabled(conn); + } + + protected void unhideJsInterpretationEnabled(Connection conn) { + String value = getJsInterpretationEnabled(conn); + if (value != null) { + updateJsInterpretationEnabledFields(conn, value); + } + } + + protected String getJsInterpretationEnabled(Connection conn) { + String query = "SELECT value FROM cloud.configuration WHERE name = 'js.interpretation.enabled' AND category = 'Hidden';"; + + try (PreparedStatement pstmt = conn.prepareStatement(query)) { + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return rs.getString("value"); + } + logger.debug("Unable to retrieve value of hidden configuration 'js.interpretation.enabled'. The configuration may already be unhidden."); + return null; + } catch (SQLException e) { + throw new CloudRuntimeException("Error while retrieving value of hidden configuration 'js.interpretation.enabled'.", e); + } + } + + protected void updateJsInterpretationEnabledFields(Connection conn, String encryptedValue) { + String query = "UPDATE cloud.configuration SET value = ?, category = 'System', component = 'JsInterpreter', is_dynamic = 1 WHERE name = 'js.interpretation.enabled';"; + + try (PreparedStatement pstmt = conn.prepareStatement(query)) { + String decryptedValue = DBEncryptionUtil.decrypt(encryptedValue); + logger.info("Updating setting 'js.interpretation.enabled' to decrypted value [{}], and category 'System', component 'JsInterpreter', and is_dynamic '1'.", decryptedValue); + pstmt.setString(1, decryptedValue); + pstmt.executeUpdate(); + } catch (SQLException e) { + throw new CloudRuntimeException("Error while unhiding configuration 'js.interpretation.enabled'.", e); + } catch (CloudRuntimeException e) { + logger.warn("Error while decrypting configuration 'js.interpretation.enabled'. The configuration may already be decrypted."); + } + } } diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java index d1992d4c3ad7..2d6ec3255f4a 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java @@ -49,6 +49,7 @@ import com.cloud.user.UserVO; import com.cloud.utils.DateUtil; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.QuotaBalanceCmd; @@ -163,12 +164,6 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder { private Set accountTypesThatCanListAllQuotaSummaries = Sets.newHashSet(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN); - protected void checkActivationRulesAllowed(String activationRule) { - if (!_quotaService.isJsInterpretationEnabled() && StringUtils.isNotEmpty(activationRule)) { - throw new PermissionDeniedException("Quota Tariff Activation Rule cannot be set, as Javascript interpretation is disabled in the configuration."); - } - } - @Override public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff, boolean returnActivationRule) { final QuotaTariffResponse response = new QuotaTariffResponse(); @@ -501,6 +496,7 @@ public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) { Integer position = cmd.getPosition(); warnQuotaTariffUpdateDeprecatedFields(cmd); + jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.ACTIVATION_RULE, StringUtils.isNotBlank(activationRule)); QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name); @@ -508,8 +504,6 @@ public QuotaTariffVO updateQuotaTariffPlan(QuotaTariffUpdateCmd cmd) { throw new InvalidParameterValueException(String.format("There is no quota tariffs with name [%s].", name)); } - checkActivationRulesAllowed(activationRule); - Date currentQuotaTariffStartDate = currentQuotaTariff.getEffectiveOn(); currentQuotaTariff.setRemoved(now); @@ -758,14 +752,14 @@ public QuotaTariffVO createQuotaTariff(QuotaTariffCreateCmd cmd) { String activationRule = cmd.getActivationRule(); Integer position = ObjectUtils.defaultIfNull(cmd.getPosition(), 1); + jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.ACTIVATION_RULE, StringUtils.isNotBlank(activationRule)); + QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name); if (currentQuotaTariff != null) { throw new InvalidParameterValueException(String.format("A quota tariff with name [%s] already exist.", name)); } - checkActivationRulesAllowed(activationRule); - if (startDate.compareTo(now) < 0) { throw new InvalidParameterValueException(String.format("The value passed as Quota tariff's start date is in the past: [%s]. " + "Please, inform a date in the future or do not pass the parameter to use the current date and time.", startDate)); diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java index f6a34e01be8d..a421d0f066a0 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaService.java @@ -40,6 +40,4 @@ public interface QuotaService extends PluggableService { boolean saveQuotaAccount(AccountVO account, BigDecimal aggrUsage, Date endDate); - boolean isJsInterpretationEnabled(); - } diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java index f39153ae0ac6..2d7d623d1d98 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java @@ -64,7 +64,6 @@ import com.cloud.domain.dao.DomainDao; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; -import com.cloud.server.ManagementService; import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; @@ -95,8 +94,6 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi private TimeZone _usageTimezone; - private boolean jsInterpretationEnabled = false; - public QuotaServiceImpl() { super(); } @@ -108,8 +105,6 @@ public boolean configure(String name, Map params) throws Configu String timeZoneStr = ObjectUtils.defaultIfNull(_configDao.getValue(Config.UsageAggregationTimezone.toString()), "GMT"); _usageTimezone = TimeZone.getTimeZone(timeZoneStr); - jsInterpretationEnabled = ManagementService.JsInterpretationEnabled.value(); - return true; } @@ -298,9 +293,4 @@ public void setMinBalance(Long accountId, Double balance) { _quotaAcc.updateQuotaAccount(accountId, acc); } } - - @Override - public boolean isJsInterpretationEnabled() { - return jsInterpretationEnabled; - } } diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java index 621b110486b9..c63dbe893d26 100755 --- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java @@ -78,6 +78,7 @@ import org.apache.cloudstack.framework.extensions.vo.ExtensionResourceMapVO; import org.apache.cloudstack.framework.extensions.vo.ExtensionVO; import org.apache.cloudstack.gpu.GpuService; +import org.apache.cloudstack.jsinterpreter.JsInterpreterHelper; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.identity.ManagementServerNode; @@ -310,6 +311,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, private GpuService gpuService; @Inject ManagementService managementService; + @Inject + JsInterpreterHelper jsInterpreterHelper; private List _discoverers; @@ -2818,9 +2821,6 @@ private void updateHostTags(HostVO host, Long hostId, List hostTags, Boo @Override public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException { - managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE, - Boolean.TRUE.equals(cmd.getIsTagARule())); - return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(), cmd.getAllocationState(), cmd.getUrl(), cmd.getHostTags(), cmd.getIsTagARule(), cmd.getAnnotation(), false, cmd.getExternalDetails(), cmd.isCleanupExternalDetails()); @@ -2830,6 +2830,8 @@ private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String String url, List hostTags, Boolean isTagARule, String annotation, boolean isUpdateFromHostHealthCheck, Map externalDetails, boolean cleanupExternalDetails) throws NoTransitionException { + jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(isTagARule)); + // Verify that the host exists final HostVO host = _hostDao.findById(hostId); if (host == null) { diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index dc28b5f98a46..d4266496e982 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -1082,8 +1082,6 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen protected List _planners; - private boolean jsInterpretationEnabled = false; - private final List supportedHypervisors = new ArrayList<>(); public List getPlanners() { @@ -1164,8 +1162,6 @@ public boolean configure(final String name, final Map params) th supportedHypervisors.add(HypervisorType.KVM); supportedHypervisors.add(HypervisorType.XenServer); - jsInterpretationEnabled = JsInterpretationEnabled.value(); - return true; } @@ -4374,10 +4370,8 @@ public List> getCommands() { cmdList.add(ListGuestVlansCmd.class); cmdList.add(AssignVolumeCmd.class); cmdList.add(ListSecondaryStorageSelectorsCmd.class); - if (jsInterpretationEnabled) { - cmdList.add(CreateSecondaryStorageSelectorCmd.class); - cmdList.add(UpdateSecondaryStorageSelectorCmd.class); - } + cmdList.add(CreateSecondaryStorageSelectorCmd.class); + cmdList.add(UpdateSecondaryStorageSelectorCmd.class); cmdList.add(RemoveSecondaryStorageSelectorCmd.class); cmdList.add(ListAffectedVmsForStorageScopeChangeCmd.class); cmdList.add(ListGuiThemesCmd.class); @@ -4435,8 +4429,7 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {exposeCloudStackVersionInApiXmlResponse, exposeCloudStackVersionInApiListCapabilities, vmPasswordLength, sshKeyLength, humanReadableSizes, customCsIdentifier, - JsInterpretationEnabled}; + return new ConfigKey[] {exposeCloudStackVersionInApiXmlResponse, exposeCloudStackVersionInApiListCapabilities, vmPasswordLength, sshKeyLength, humanReadableSizes, customCsIdentifier}; } protected class EventPurgeTask extends ManagedContextRunnable { @@ -6002,13 +5995,5 @@ public boolean removeManagementServer(RemoveManagementServerCmd cmd) { public Answer getExternalVmConsole(VirtualMachine vm, Host host) { return extensionsManager.getInstanceConsole(vm, host); } - @Override - public void checkJsInterpretationAllowedIfNeededForParameterValue(String paramName, boolean paramValue) { - if (!paramValue || jsInterpretationEnabled) { - return; - } - throw new InvalidParameterValueException(String.format( - "The parameter %s cannot be set to true as JS interpretation is disabled", - paramName)); - } + } diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 1f453a862947..a7c700debb74 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -111,6 +111,7 @@ import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.jsinterpreter.JsInterpreterHelper; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; @@ -417,6 +418,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C StorageManager storageManager; @Inject ManagementService managementService; + @Inject + JsInterpreterHelper jsInterpreterHelper; protected List _discoverers; @@ -962,6 +965,8 @@ protected void checkNFSMountOptionsForUpdate(Map details, Storag @Override public PrimaryDataStoreInfo createPool(CreateStoragePoolCmd cmd) throws ResourceInUseException, IllegalArgumentException, UnknownHostException, ResourceUnavailableException { + jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(cmd.isTagARule())); + String providerName = cmd.getStorageProviderName(); Map uriParams = extractUriParamsAsMap(cmd.getUrl()); boolean isFileScheme = "file".equals(uriParams.get("scheme")); @@ -1034,9 +1039,6 @@ public PrimaryDataStoreInfo createPool(CreateStoragePoolCmd cmd) throws Resource throw new PermissionDeniedException(String.format("Cannot perform this operation, Zone is currently disabled: %s", zone)); } - managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE, - Boolean.TRUE.equals(cmd.isTagARule())); - Map params = new HashMap<>(); params.put("zoneId", zone.getId()); params.put("clusterId", clusterId); @@ -1218,11 +1220,9 @@ public StoragePool enablePrimaryStoragePool(Long id) { @ActionEvent(eventType = EventTypes.EVENT_UPDATE_PRIMARY_STORAGE, eventDescription = "update storage pool") public PrimaryDataStoreInfo updateStoragePool(UpdateStoragePoolCmd cmd) throws IllegalArgumentException { // Input validation - Long id = cmd.getId(); - - managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE, - Boolean.TRUE.equals(cmd.isTagARule())); + jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(cmd.isTagARule())); + Long id = cmd.getId(); StoragePoolVO pool = _storagePoolDao.findById(id); if (pool == null) { throw new IllegalArgumentException("Unable to find storage pool with ID: " + id); @@ -2751,6 +2751,7 @@ protected void validateHeuristicRule(String heuristicRule) { if (StringUtils.isBlank(heuristicRule)) { throw new IllegalArgumentException("Unable to create a new secondary storage selector as the given heuristic rule is blank."); } + jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.HEURISTIC_RULE, true); } public void syncDatastoreClusterStoragePool(long datastoreClusterPoolId, List childDatastoreAnswerList, long hostId) { diff --git a/server/src/main/java/org/apache/cloudstack/jsinterpreter/JsInterpreterHelper.java b/server/src/main/java/org/apache/cloudstack/jsinterpreter/JsInterpreterHelper.java index b4d4b5fca452..f6b1455d097e 100644 --- a/server/src/main/java/org/apache/cloudstack/jsinterpreter/JsInterpreterHelper.java +++ b/server/src/main/java/org/apache/cloudstack/jsinterpreter/JsInterpreterHelper.java @@ -17,9 +17,13 @@ package org.apache.cloudstack.jsinterpreter; +import com.cloud.exception.InvalidParameterValueException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -36,9 +40,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class JsInterpreterHelper { +public class JsInterpreterHelper implements Configurable { private final Logger logger = LogManager.getLogger(getClass()); + public static final ConfigKey JS_INTERPRETATION_ENABLED = new ConfigKey<>(ConfigKey.CATEGORY_SYSTEM, Boolean.class, "js.interpretation.enabled", + "false", "Enable/disable all JavaScript interpretation related functionalities.", + true, ConfigKey.Scope.Global); + private static final String NAME = "name"; private static final String PROPERTY = "property"; private static final String TYPE = "type"; @@ -237,4 +245,21 @@ public Set getVariables() { public void setVariables(Set variables) { this.variables = variables; } + + public void ensureInterpreterEnabledIfParameterProvided(String paramName, boolean paramProvided) { + if (paramProvided && !JS_INTERPRETATION_ENABLED.value()) { + throw new InvalidParameterValueException(String.format( + "'%s' cannot be set because JavaScript interpretation is disabled in setting '%s'.", paramName, JS_INTERPRETATION_ENABLED.key())); + } + } + + @Override + public String getConfigComponentName() { + return JsInterpreter.class.getSimpleName(); + } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] { JS_INTERPRETATION_ENABLED }; + } }