Unhide setting `js.interpretation.enabled` (#12605)

* Unhide setting 'js.interpretation.enabled'

* Fix grammar mistake
This commit is contained in:
Fabricio Duarte 2026-04-10 23:45:07 -03:00 committed by GitHub
parent 11538df710
commit 9f57a4dd19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 94 additions and 63 deletions

View File

@ -71,7 +71,6 @@ import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd;
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 @@ import com.cloud.vm.VirtualMachineProfile;
public interface ManagementService {
static final String Name = "management-server";
ConfigKey<Boolean> 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 @@ public interface ManagementService {
boolean removeManagementServer(RemoveManagementServerCmd cmd);
void checkJsInterpretationAllowedIfNeededForParameterValue(String paramName, boolean paramValue);
}

View File

@ -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 class Upgrade42210to42300 extends DbUpgradeAbstractImpl implements DbUpgr
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 [{}], 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.");
}
}
}

View File

@ -49,6 +49,7 @@ import com.cloud.user.User;
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<Account.Type> 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 class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
Integer position = cmd.getPosition();
warnQuotaTariffUpdateDeprecatedFields(cmd);
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.ACTIVATION_RULE, StringUtils.isNotBlank(activationRule));
QuotaTariffVO currentQuotaTariff = _quotaTariffDao.findByName(name);
@ -508,8 +504,6 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
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 class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
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));

View File

@ -40,6 +40,4 @@ public interface QuotaService extends PluggableService {
boolean saveQuotaAccount(AccountVO account, BigDecimal aggrUsage, Date endDate);
boolean isJsInterpretationEnabled();
}

View File

@ -64,7 +64,6 @@ import com.cloud.configuration.Config;
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 class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
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 class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
_quotaAcc.updateQuotaAccount(accountId, acc);
}
}
@Override
public boolean isJsInterpretationEnabled() {
return jsInterpretationEnabled;
}
}

View File

@ -78,6 +78,7 @@ import org.apache.cloudstack.framework.extensions.manager.ExtensionsManager;
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<? extends Discoverer> _discoverers;
@ -2818,9 +2821,6 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
@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 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
String url, List<String> hostTags, Boolean isTagARule, String annotation,
boolean isUpdateFromHostHealthCheck, Map<String, String> 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) {

View File

@ -1082,8 +1082,6 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
protected List<DeploymentPlanner> _planners;
private boolean jsInterpretationEnabled = false;
private final List<HypervisorType> supportedHypervisors = new ArrayList<>();
public List<DeploymentPlanner> getPlanners() {
@ -1164,8 +1162,6 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
supportedHypervisors.add(HypervisorType.KVM);
supportedHypervisors.add(HypervisorType.XenServer);
jsInterpretationEnabled = JsInterpretationEnabled.value();
return true;
}
@ -4374,10 +4370,8 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
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 class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
@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 class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
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));
}
}

View File

@ -111,6 +111,7 @@ import org.apache.cloudstack.framework.config.ConfigDepot;
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<StoragePoolDiscoverer> _discoverers;
@ -962,6 +965,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
@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<String,String> uriParams = extractUriParamsAsMap(cmd.getUrl());
boolean isFileScheme = "file".equals(uriParams.get("scheme"));
@ -1034,9 +1039,6 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
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<String, Object> params = new HashMap<>();
params.put("zoneId", zone.getId());
params.put("clusterId", clusterId);
@ -1218,11 +1220,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
@ActionEvent(eventType = EventTypes.EVENT_UPDATE_PRIMARY_STORAGE, eventDescription = "update storage pool")
public PrimaryDataStoreInfo updateStoragePool(UpdateStoragePoolCmd cmd) throws IllegalArgumentException {
// Input validation
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(cmd.isTagARule()));
Long id = cmd.getId();
managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE,
Boolean.TRUE.equals(cmd.isTagARule()));
StoragePoolVO pool = _storagePoolDao.findById(id);
if (pool == null) {
throw new IllegalArgumentException("Unable to find storage pool with ID: " + id);
@ -2751,6 +2751,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
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<ModifyStoragePoolAnswer> childDatastoreAnswerList, long hostId) {

View File

@ -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.Set;
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<Boolean> 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 class JsInterpreterHelper {
public void setVariables(Set<String> 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 };
}
}