Skip to content

Commit 6c9bc4c

Browse files
committed
Add support for new variables to the GUI whitelabel runtime system
1 parent 7ad68aa commit 6c9bc4c

File tree

11 files changed

+415
-111
lines changed

11 files changed

+415
-111
lines changed

server/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeServiceImpl.java

Lines changed: 5 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@
2727
import com.cloud.utils.db.Transaction;
2828
import com.cloud.utils.db.TransactionCallback;
2929
import com.cloud.utils.exception.CloudRuntimeException;
30-
import com.google.gson.JsonArray;
31-
import com.google.gson.JsonElement;
32-
import com.google.gson.JsonObject;
33-
import com.google.gson.JsonParser;
34-
import com.google.gson.JsonSyntaxException;
3530
import org.apache.cloudstack.api.ResponseGenerator;
3631
import org.apache.cloudstack.api.command.user.gui.theme.CreateGuiThemeCmd;
3732
import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd;
@@ -43,6 +38,7 @@
4338
import org.apache.cloudstack.gui.theme.dao.GuiThemeDao;
4439
import org.apache.cloudstack.gui.theme.dao.GuiThemeDetailsDao;
4540
import org.apache.cloudstack.gui.theme.dao.GuiThemeJoinDao;
41+
import org.apache.cloudstack.gui.theme.json.config.validator.JsonConfigValidator;
4642
import org.apache.commons.lang3.StringUtils;
4743
import org.apache.logging.log4j.LogManager;
4844
import org.apache.logging.log4j.Logger;
@@ -52,24 +48,12 @@
5248
import java.util.ArrayList;
5349
import java.util.Date;
5450
import java.util.List;
55-
import java.util.Map;
56-
import java.util.Set;
5751

5852
@Component
5953
public class GuiThemeServiceImpl implements GuiThemeService {
6054

6155
protected Logger logger = LogManager.getLogger(getClass());
6256

63-
private static final List<String> ALLOWED_PRIMITIVE_PROPERTIES = List.of("appTitle", "footer", "loginFooter", "logo", "minilogo", "banner");
64-
65-
private static final List<String> ALLOWED_ERROR_PROPERTIES = List.of("403", "404", "500");
66-
67-
private static final List<String> ALLOWED_PLUGIN_PROPERTIES = List.of("name", "path", "icon", "isExternalLink");
68-
69-
private static final String ERROR = "error";
70-
71-
private static final String PLUGINS = "plugins";
72-
7357
@Inject
7458
GuiThemeDao guiThemeDao;
7559

@@ -91,6 +75,9 @@ public class GuiThemeServiceImpl implements GuiThemeService {
9175
@Inject
9276
DomainDao domainDao;
9377

78+
@Inject
79+
JsonConfigValidator jsonConfigValidator;
80+
9481
@Override
9582
public ListResponse<GuiThemeResponse> listGuiThemes(ListGuiThemesCmd cmd) {
9683
ListResponse<GuiThemeResponse> response = new ListResponse<>();
@@ -244,94 +231,7 @@ protected void validateParameters(String jsonConfig, String domainIds, String ac
244231

245232
validateObjectUuids(accountIds, Account.class);
246233
validateObjectUuids(domainIds, Domain.class);
247-
validateJsonConfiguration(jsonConfig);
248-
}
249-
250-
protected void validateJsonConfiguration(String jsonConfig) {
251-
if (jsonConfig == null) {
252-
return;
253-
}
254-
255-
JsonObject jsonObject = new JsonObject();
256-
257-
try {
258-
JsonElement jsonElement = new JsonParser().parse(jsonConfig);
259-
Set<Map.Entry<String, JsonElement>> entries = jsonElement.getAsJsonObject().entrySet();
260-
entries.stream().forEach(entry -> validateJsonAttributes(entry, jsonObject));
261-
} catch (JsonSyntaxException exception) {
262-
logger.error("The following exception was thrown while parsing the JSON object: [{}].", exception.getMessage());
263-
throw new CloudRuntimeException("Specified JSON configuration is not a valid JSON object.");
264-
}
265-
}
266-
267-
/**
268-
* Validates the informed JSON attributes considering the allowed properties by the API, any invalid option is ignored.
269-
* All valid options are added to a {@link JsonObject} that will be considered as the final JSON configuration used by the GUI theme.
270-
*/
271-
private void validateJsonAttributes(Map.Entry<String, JsonElement> entry, JsonObject jsonObject) {
272-
JsonElement entryValue = entry.getValue();
273-
String entryKey = entry.getKey();
274-
275-
if (entryValue.isJsonPrimitive() && ALLOWED_PRIMITIVE_PROPERTIES.contains(entryKey)) {
276-
logger.trace("The JSON attribute [{}] is a valid option.", entryKey);
277-
jsonObject.add(entryKey, entryValue);
278-
} else if (entryValue.isJsonObject() && ERROR.equals(entryKey)) {
279-
validateErrorAttribute(entry, jsonObject);
280-
} else if (entryValue.isJsonArray() && PLUGINS.equals(entryKey)) {
281-
validatePluginsAttribute(entry, jsonObject);
282-
} else {
283-
warnOfInvalidJsonAttribute(entryKey);
284-
}
285-
}
286-
287-
/**
288-
* Creates a {@link JsonObject} with only the valid options for the Plugins' properties specified in the {@link #ALLOWED_PLUGIN_PROPERTIES}.
289-
*/
290-
protected void validatePluginsAttribute(Map.Entry<String, JsonElement> entry, JsonObject jsonObject) {
291-
Set<Map.Entry<String, JsonElement>> entries = entry.getValue().getAsJsonArray().get(0).getAsJsonObject().entrySet();
292-
JsonObject objectToBeAdded = createJsonObject(entries, ALLOWED_PLUGIN_PROPERTIES);
293-
JsonArray jsonArray = new JsonArray();
294-
295-
if (objectToBeAdded.entrySet().isEmpty()) {
296-
return;
297-
}
298-
299-
jsonArray.add(objectToBeAdded);
300-
jsonObject.add(entry.getKey(), jsonArray);
301-
}
302-
303-
/**
304-
* Creates a {@link JsonObject} with only the valid options for the Error's properties specified in the {@link #ALLOWED_ERROR_PROPERTIES}.
305-
*/
306-
protected void validateErrorAttribute(Map.Entry<String, JsonElement> entry, JsonObject jsonObject) {
307-
Set<Map.Entry<String, JsonElement>> entries = entry.getValue().getAsJsonObject().entrySet();
308-
JsonObject objectToBeAdded = createJsonObject(entries, ALLOWED_ERROR_PROPERTIES);
309-
310-
if (objectToBeAdded.entrySet().isEmpty()) {
311-
return;
312-
}
313-
314-
jsonObject.add(entry.getKey(), objectToBeAdded);
315-
}
316-
317-
protected JsonObject createJsonObject(Set<Map.Entry<String, JsonElement>> entries, List<String> allowedProperties) {
318-
JsonObject objectToBeAdded = new JsonObject();
319-
320-
for (Map.Entry<String, JsonElement> recursiveEntry : entries) {
321-
String entryKey = recursiveEntry.getKey();
322-
323-
if (!allowedProperties.contains(entryKey)) {
324-
warnOfInvalidJsonAttribute(entryKey);
325-
continue;
326-
}
327-
objectToBeAdded.add(entryKey, recursiveEntry.getValue());
328-
}
329-
330-
return objectToBeAdded;
331-
}
332-
333-
protected void warnOfInvalidJsonAttribute(String entryKey) {
334-
logger.warn("The JSON attribute [{}] is not a valid option, therefore, it will be ignored.", entryKey);
234+
jsonConfigValidator.validateJsonConfiguration(jsonConfig);
335235
}
336236

337237
/**
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.gui.theme.json.config.validator;
18+
19+
import com.google.gson.JsonElement;
20+
import com.google.gson.JsonObject;
21+
22+
import java.util.Map;
23+
24+
public interface JsonConfigAttributeValidator {
25+
26+
void validate(Map.Entry<String, JsonElement> entry, JsonObject jsonObject);
27+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.gui.theme.json.config.validator;
18+
19+
import com.cloud.utils.exception.CloudRuntimeException;
20+
import com.google.gson.JsonElement;
21+
import com.google.gson.JsonObject;
22+
import com.google.gson.JsonParser;
23+
import com.google.gson.JsonSyntaxException;
24+
import org.apache.commons.lang3.StringUtils;
25+
import org.apache.logging.log4j.LogManager;
26+
import org.apache.logging.log4j.Logger;
27+
28+
import javax.inject.Inject;
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.Set;
32+
33+
public class JsonConfigValidator {
34+
protected Logger logger = LogManager.getLogger(getClass());
35+
36+
private static final List<String> ALLOWED_PRIMITIVE_PROPERTIES = List.of("appTitle", "footer", "loginFooter", "logo", "minilogo", "banner", "docBase", "apidocs");
37+
private static final List<String> ALLOWED_DYNAMIC_PROPERTIES = List.of("error", "theme", "plugins", "keyboardOptions", "userCard", "docHelpMappings");
38+
39+
@Inject
40+
private List<JsonConfigAttributeValidator> attributes;
41+
42+
public void validateJsonConfiguration(String jsonConfig) {
43+
if (StringUtils.isBlank(jsonConfig)) {
44+
return;
45+
}
46+
47+
JsonObject jsonObject = new JsonObject();
48+
49+
try {
50+
JsonElement jsonElement = JsonParser.parseString(jsonConfig);
51+
Set<Map.Entry<String, JsonElement>> entries = jsonElement.getAsJsonObject().entrySet();
52+
entries.forEach(entry -> validateJsonAttributes(entry, jsonObject));
53+
} catch (JsonSyntaxException exception) {
54+
logger.error("The following exception was thrown while parsing the JSON object: [{}].", exception.getMessage());
55+
throw new CloudRuntimeException("Specified JSON configuration is not a valid JSON object.");
56+
}
57+
}
58+
59+
/**
60+
* Validates the informed JSON attributes considering the allowed properties by the API, any invalid option is ignored.
61+
* All valid options are added to a {@link JsonObject} that will be considered as the final JSON configuration used by the GUI theme.
62+
*/
63+
private void validateJsonAttributes(Map.Entry<String, JsonElement> entry, JsonObject jsonObject) {
64+
JsonElement entryValue = entry.getValue();
65+
String entryKey = entry.getKey();
66+
67+
if (entryValue.isJsonPrimitive() && ALLOWED_PRIMITIVE_PROPERTIES.contains(entryKey)) {
68+
logger.trace("The JSON attribute [{}] is a valid option.", entryKey);
69+
jsonObject.add(entryKey, entryValue);
70+
} else if (ALLOWED_DYNAMIC_PROPERTIES.contains(entryKey)) {
71+
attributes.forEach(attribute -> attribute.validate(entry, jsonObject));
72+
} else {
73+
logger.warn("The JSON attribute [{}] is not a valid option, therefore, it will be ignored.", entryKey);
74+
}
75+
}
76+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.gui.theme.json.config.validator.attributes;
18+
19+
import com.google.gson.JsonElement;
20+
import com.google.gson.JsonObject;
21+
import org.apache.cloudstack.gui.theme.json.config.validator.JsonConfigAttributeValidator;
22+
import org.apache.logging.log4j.LogManager;
23+
import org.apache.logging.log4j.Logger;
24+
25+
import java.util.List;
26+
import java.util.Map;
27+
import java.util.Set;
28+
29+
public abstract class AttributeBase implements JsonConfigAttributeValidator {
30+
protected Logger logger = LogManager.getLogger(getClass());
31+
32+
protected abstract String getAttributeName();
33+
protected abstract List<String> getAllowedProperties();
34+
35+
@Override
36+
public void validate(Map.Entry<String, JsonElement> entry, JsonObject jsonObject) {
37+
if (!getAttributeName().equals(entry.getKey())) {
38+
return;
39+
}
40+
41+
Set<Map.Entry<String, JsonElement>> entries = entry.getValue().getAsJsonObject().entrySet();
42+
JsonObject objectToBeAdded = createJsonObject(entries, getAllowedProperties());
43+
44+
if (!objectToBeAdded.entrySet().isEmpty()) {
45+
jsonObject.add(entry.getKey(), objectToBeAdded);
46+
}
47+
}
48+
49+
/**
50+
* Creates a {@link JsonObject} with only the valid options for the attribute properties specified in the allowedProperties parameter.
51+
*/
52+
public JsonObject createJsonObject(Set<Map.Entry<String, JsonElement>> entries, List<String> allowedProperties) {
53+
JsonObject objectToBeAdded = new JsonObject();
54+
55+
for (Map.Entry<String, JsonElement> recursiveEntry : entries) {
56+
String entryKey = recursiveEntry.getKey();
57+
58+
if (!allowedProperties.contains(entryKey)) {
59+
warnOfInvalidJsonAttribute(entryKey);
60+
continue;
61+
}
62+
objectToBeAdded.add(entryKey, recursiveEntry.getValue());
63+
}
64+
65+
logger.trace("JSON object with valid options: {}.", objectToBeAdded);
66+
return objectToBeAdded;
67+
}
68+
69+
protected void warnOfInvalidJsonAttribute(String entryKey) {
70+
logger.warn("The JSON attribute [{}] is not a valid option, therefore, it will be ignored.", entryKey);
71+
}
72+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.gui.theme.json.config.validator.attributes;
18+
19+
import java.util.List;
20+
21+
public class ErrorAttribute extends AttributeBase {
22+
23+
@Override
24+
protected String getAttributeName() {
25+
return "error";
26+
}
27+
28+
@Override
29+
protected List<String> getAllowedProperties() {
30+
return List.of("403", "404", "500");
31+
}
32+
}

0 commit comments

Comments
 (0)