diff --git a/src/main/java/com/erudika/scoold/ScooldConfig.java b/src/main/java/com/erudika/scoold/ScooldConfig.java index f8af4331..09f1c820 100644 --- a/src/main/java/com/erudika/scoold/ScooldConfig.java +++ b/src/main/java/com/erudika/scoold/ScooldConfig.java @@ -3442,6 +3442,26 @@ public boolean automaticSpamProtectionEnabled() { return getConfigBoolean("automatic_spam_protection_enabled", true); } + @Documented(position = 3120, + identifier = "data_import_export_enabled", + value = "true", + type = Boolean.class, + category = "Miscellaneous", + description = "Enable/disable backup and restore features on the Administration page.") + public boolean dataImportExportEnabled() { + return getConfigBoolean("data_import_export_enabled", true); + } + + @Documented(position = 3130, + identifier = "config_editing_enabled", + value = "true", + type = Boolean.class, + category = "Miscellaneous", + description = "Enable/disable live configuration editing for admins on the Administration page.") + public boolean configEditingEnabled() { + return getConfigBoolean("config_editing_enabled", true); + } + /* **********************************************************************************************************/ public boolean inDevelopment() { diff --git a/src/main/java/com/erudika/scoold/controllers/AdminController.java b/src/main/java/com/erudika/scoold/controllers/AdminController.java index 9c654ff6..6a6513de 100755 --- a/src/main/java/com/erudika/scoold/controllers/AdminController.java +++ b/src/main/java/com/erudika/scoold/controllers/AdminController.java @@ -121,9 +121,12 @@ public String get(HttpServletRequest req, Model model) { return "redirect:" + SIGNINLINK + "?returnto=" + ADMINLINK; } Map configMetadata = new LinkedHashMap(); - try { - configMetadata = ParaObjectUtils.getJsonReader(Map.class).readValue(CONF.renderConfigDocumentation("json", true)); - } catch (IOException ex) { } + if (CONF.configEditingEnabled()) { + try { + configMetadata = ParaObjectUtils.getJsonReader(Map.class). + readValue(CONF.renderConfigDocumentation("json", true)); + } catch (IOException ex) { } + } Pager itemcount = utils.getPager("page", req); Pager itemcount1 = utils.getPager("page1", req); @@ -131,6 +134,7 @@ public String get(HttpServletRequest req, Model model) { model.addAttribute("path", "admin.vm"); model.addAttribute("title", utils.getLang(req).get("administration.title")); model.addAttribute("configMap", CONF); + model.addAttribute("configMetadata", configMetadata); model.addAttribute("version", pc.getServerVersion()); model.addAttribute("endpoint", CONF.redirectUri()); model.addAttribute("paraapp", CONF.paraAccessKey()); @@ -354,8 +358,8 @@ public String forceDelete(@RequestParam Boolean confirmdelete, @RequestParam Str @GetMapping(value = "/export", produces = "application/zip") public ResponseEntity backup(HttpServletRequest req, HttpServletResponse response) { Profile authUser = utils.getAuthUser(req); - if (!utils.isAdmin(authUser)) { - return new ResponseEntity(HttpStatus.UNAUTHORIZED); + if (!utils.isAdmin(authUser) || !CONF.dataImportExportEnabled()) { + return new ResponseEntity(HttpStatus.FORBIDDEN); } String fileName = App.identifier(CONF.paraAccessKey()) + "_" + Utils.formatDate("YYYYMMdd_HHmmss", Locale.US); response.setContentType("application/zip"); @@ -392,8 +396,8 @@ public String restore(@RequestParam("file") MultipartFile file, @RequestParam(required = false, defaultValue = "false") Boolean deleteall, HttpServletRequest req, HttpServletResponse res) { Profile authUser = utils.getAuthUser(req); - if (!utils.isAdmin(authUser)) { - res.setStatus(403); + if (!utils.isAdmin(authUser) || !CONF.dataImportExportEnabled()) { + res.setStatus(HttpStatus.FORBIDDEN.value()); return null; } ObjectReader reader = ParaObjectUtils.getJsonMapper().readerFor(new TypeReference>>() { }); @@ -535,7 +539,7 @@ public String reindex(HttpServletRequest req, Model model) { @PostMapping("/save-config") public String saveConfig(@RequestParam String key, @RequestParam(defaultValue = "") String value, HttpServletRequest req) { Profile authUser = utils.getAuthUser(req); - if (utils.isAdmin(authUser)) { + if (utils.isAdmin(authUser) && CONF.configEditingEnabled()) { if ("on".equals(value)) { value = "true"; } @@ -544,7 +548,7 @@ public String saveConfig(@RequestParam String key, @RequestParam(defaultValue = } else { System.clearProperty(key); } - logger.info("Configuration was changed: user {} modified property '{}'.", authUser.getId(), key); + logger.info("Configuration property '{}' was modified by user {}.", key, authUser.getCreatorid()); CONF.store(); if (CONF.getParaAppSettings().containsKey(key)) { pc.addAppSetting(key, value); diff --git a/src/main/resources/templates/admin.vm b/src/main/resources/templates/admin.vm index be967c40..29d230ca 100755 --- a/src/main/resources/templates/admin.vm +++ b/src/main/resources/templates/admin.vm @@ -24,10 +24,14 @@ @@ -139,6 +143,7 @@ #end + #if($scooldUtils.config.dataImportExportEnabled())
@@ -232,6 +237,7 @@
#end
+ #end
@@ -334,6 +340,7 @@ #end
+ #if($scooldUtils.config.configEditingEnabled())
    #foreach($key in $configMetadata.keySet()) @@ -396,6 +403,7 @@ #end
+ #end
diff --git a/src/main/resources/templates/api.yaml b/src/main/resources/templates/api.yaml index 9182f1fa..8d0bd9f3 100644 --- a/src/main/resources/templates/api.yaml +++ b/src/main/resources/templates/api.yaml @@ -1,7 +1,7 @@ openapi: "3.0.2" info: description: "This is the Scoold API. First, you need to generate an [API key](/admin) in order to access the API." - version: "1.6.0" + version: "1.6.1" title: "Scoold API documentation" externalDocs: description: "README" @@ -1504,6 +1504,8 @@ paths: schema: type: string format: binary + '403': + description: Forbidden - this feature is disabled or invalid request. /api/restore: put: summary: Restores (asynchronously) a backup ZIP archive, overwriting all existing data in Scoold @@ -1531,7 +1533,9 @@ paths: type: boolean responses: '200': - description: Restore operation hast started but is not necessarily complete. + description: Restore operation has started asynchronously but is not necessarily complete. + '403': + description: Forbidden - this feature is disabled or invalid request. /api/config: get: summary: Returns the current Scoold configuration as a list of properties in the HOCON or JSON formats.