Skip to content

Commit 1fe7c32

Browse files
committed
api,server: normalize string empty value on config update
Fixes #10823 Signed-off-by: Abhishek Kumar <[email protected]>
1 parent d60f455 commit 1fe7c32

File tree

4 files changed

+122
-12
lines changed

4 files changed

+122
-12
lines changed

api/src/main/java/org/apache/cloudstack/api/command/admin/config/UpdateCfgCmd.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void execute() {
150150
ConfigurationResponse response = _responseGenerator.createConfigurationResponse(cfg);
151151
response.setResponseName(getCommandName());
152152
response = setResponseScopes(response);
153-
response = setResponseValue(response, cfg);
153+
setResponseValue(response, cfg);
154154
this.setResponseObject(response);
155155
} else {
156156
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update config");
@@ -161,15 +161,13 @@ public void execute() {
161161
* Sets the configuration value in the response. If the configuration is in the `Hidden` or `Secure` categories, the value is encrypted before being set in the response.
162162
* @param response to be set with the configuration `cfg` value
163163
* @param cfg to be used in setting the response value
164-
* @return the response with the configuration's value
165164
*/
166-
public ConfigurationResponse setResponseValue(ConfigurationResponse response, Configuration cfg) {
165+
public void setResponseValue(ConfigurationResponse response, Configuration cfg) {
166+
String value = cfg.getValue();
167167
if (cfg.isEncrypted()) {
168-
response.setValue(DBEncryptionUtil.encrypt(getValue()));
169-
} else {
170-
response.setValue(getValue());
168+
value = DBEncryptionUtil.encrypt(value);
171169
}
172-
return response;
170+
response.setValue(value);
173171
}
174172

175173
/**
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.apache.cloudstack.api.command.admin.config;
2+
3+
import org.apache.cloudstack.api.response.ConfigurationResponse;
4+
import org.apache.cloudstack.config.Configuration;
5+
import org.junit.After;
6+
import org.junit.Assert;
7+
import org.junit.Before;
8+
import org.junit.Test;
9+
import org.junit.runner.RunWith;
10+
import org.mockito.MockedStatic;
11+
import org.mockito.Mockito;
12+
import org.mockito.junit.MockitoJUnitRunner;
13+
14+
import com.cloud.utils.crypt.DBEncryptionUtil;
15+
16+
@RunWith(MockitoJUnitRunner.class)
17+
public class UpdateCfgCmdTest {
18+
19+
private UpdateCfgCmd updateCfgCmd;
20+
21+
private MockedStatic<DBEncryptionUtil> mockedStatic;
22+
23+
@Before
24+
public void setUp() {
25+
updateCfgCmd = new UpdateCfgCmd();
26+
mockedStatic = Mockito.mockStatic(DBEncryptionUtil.class);
27+
}
28+
29+
@After
30+
public void tearDown() {
31+
mockedStatic.close();
32+
}
33+
34+
@Test
35+
public void setResponseValueSetsEncryptedValueWhenConfigurationIsEncrypted() {
36+
ConfigurationResponse response = new ConfigurationResponse();
37+
Configuration cfg = Mockito.mock(Configuration.class);
38+
Mockito.when(cfg.isEncrypted()).thenReturn(true);
39+
Mockito.when(cfg.getValue()).thenReturn("testValue");
40+
Mockito.when(DBEncryptionUtil.encrypt("testValue")).thenReturn("encryptedValue");
41+
updateCfgCmd.setResponseValue(response, cfg);
42+
Assert.assertEquals("encryptedValue", response.getValue());
43+
}
44+
45+
@Test
46+
public void setResponseValueSetsPlainValueWhenConfigurationIsNotEncrypted() {
47+
ConfigurationResponse response = new ConfigurationResponse();
48+
Configuration cfg = Mockito.mock(Configuration.class);
49+
Mockito.when(cfg.isEncrypted()).thenReturn(false);
50+
Mockito.when(cfg.getValue()).thenReturn("testValue");
51+
updateCfgCmd.setResponseValue(response, cfg);
52+
Assert.assertEquals("testValue", response.getValue());
53+
}
54+
55+
@Test
56+
public void setResponseValueHandlesNullConfigurationValueGracefully() {
57+
ConfigurationResponse response = new ConfigurationResponse();
58+
Configuration cfg = Mockito.mock(Configuration.class);
59+
Mockito.when(cfg.isEncrypted()).thenReturn(false);
60+
Mockito.when(cfg.getValue()).thenReturn(null);
61+
updateCfgCmd.setResponseValue(response, cfg);
62+
Assert.assertNull(response.getValue());
63+
}
64+
65+
}

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,19 @@ private void updateCustomDisplayNameOnHypervisorsList(String previousValue, Stri
982982
}
983983
}
984984

985+
protected String getNormalizedEmptyValueForConfig(final String name, final String inputValue,
986+
final Long configStorageId) {
987+
String value = inputValue.trim();
988+
if (!value.isEmpty() && !value.equals("null")) {
989+
return value;
990+
}
991+
if (configStorageId != null) {
992+
return "";
993+
}
994+
ConfigKey<?> key = _configDepot.get(name);
995+
return (key != null && key.type() == String.class) ? "" : null;
996+
}
997+
985998
@Override
986999
@ActionEvent(eventType = EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, eventDescription = "updating configuration")
9871000
public Configuration updateConfiguration(final UpdateCfgCmd cmd) throws InvalidParameterValueException {
@@ -1076,11 +1089,7 @@ public Configuration updateConfiguration(final UpdateCfgCmd cmd) throws InvalidP
10761089
throw new InvalidParameterValueException("cannot handle multiple IDs, provide only one ID corresponding to the scope");
10771090
}
10781091

1079-
value = value.trim();
1080-
1081-
if (value.isEmpty() || value.equals("null")) {
1082-
value = (id == null) ? null : "";
1083-
}
1092+
value = getNormalizedEmptyValueForConfig(name, value, id);
10841093

10851094
String currentValueInScope = getConfigurationValueInScope(config, name, scope, id);
10861095
final String updatedValue = updateConfiguration(userId, name, category, value, scope, id);

server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,4 +1072,42 @@ public void serviceOfferingExternalDetailsNeedUpdateReturnsFalseWhenDetailsMatch
10721072

10731073
Assert.assertFalse(result);
10741074
}
1075+
1076+
@Test
1077+
public void normalizedEmptyValueForConfigReturnsTrimmedValueWhenInputIsValid() {
1078+
String result = configurationManagerImplSpy.getNormalizedEmptyValueForConfig("someConfig", " validValue ", null);
1079+
Assert.assertEquals("validValue", result);
1080+
}
1081+
1082+
@Test
1083+
public void normalizedEmptyValueForConfigReturnsNullWhenInputIsNullAndNoConfigStorageId() {
1084+
String result = configurationManagerImplSpy.getNormalizedEmptyValueForConfig("someConfig", "null", null);
1085+
Assert.assertNull(result);
1086+
}
1087+
1088+
@Test
1089+
public void normalizedEmptyValueForConfigReturnsEmptyStringWhenInputIsNullAndConfigStorageIdProvided() {
1090+
String result = configurationManagerImplSpy.getNormalizedEmptyValueForConfig("someConfig", "null", 123L);
1091+
Assert.assertEquals("", result);
1092+
}
1093+
1094+
@Test
1095+
public void normalizedEmptyValueForConfigReturnsEmptyStringWhenKeyTypeIsStringAndInputIsEmpty() {
1096+
ConfigKey<String> mockKey = Mockito.mock(ConfigKey.class);
1097+
Mockito.when(mockKey.type()).thenReturn(String.class);
1098+
Mockito.doReturn(mockKey).when(configDepot).get("someConfig");
1099+
1100+
String result = configurationManagerImplSpy.getNormalizedEmptyValueForConfig("someConfig", "", null);
1101+
Assert.assertEquals("", result);
1102+
}
1103+
1104+
@Test
1105+
public void normalizedEmptyValueForConfigReturnsNullWhenKeyTypeIsNotStringAndInputIsEmpty() {
1106+
ConfigKey<Integer> mockKey = Mockito.mock(ConfigKey.class);
1107+
Mockito.when(mockKey.type()).thenReturn(Integer.class);
1108+
Mockito.doReturn(mockKey).when(configDepot).get("someConfig");
1109+
1110+
String result = configurationManagerImplSpy.getNormalizedEmptyValueForConfig("someConfig", "", null);
1111+
Assert.assertNull(result);
1112+
}
10751113
}

0 commit comments

Comments
 (0)