Change: method write and read settings.yml #1441 (#1463)

Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
Ludy 2024-06-15 10:05:31 +02:00 committed by GitHub
parent d575ba8f9a
commit fe9c5a7351
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 141 additions and 143 deletions

View file

@ -19,6 +19,7 @@ sourceCompatibility = '17'
repositories { repositories {
mavenCentral() mavenCentral()
maven { url 'https://jitpack.io' }
} }
licenseReport { licenseReport {
@ -74,7 +75,7 @@ spotless {
java { java {
target project.fileTree('src/main/java') target project.fileTree('src/main/java')
googleJavaFormat('1.19.1').aosp().reorderImports(false) googleJavaFormat('1.22.0').aosp().reorderImports(false)
importOrder('java', 'javax', 'org', 'com', 'net', 'io') importOrder('java', 'javax', 'org', 'com', 'net', 'io')
toggleOffOn() toggleOffOn()
@ -93,6 +94,7 @@ dependencies {
implementation("io.github.pixee:java-security-toolkit:1.1.3") implementation("io.github.pixee:java-security-toolkit:1.1.3")
implementation 'org.yaml:snakeyaml:2.2' implementation 'org.yaml:snakeyaml:2.2'
implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4'
// Exclude Tomcat and include Jetty // Exclude Tomcat and include Jetty
implementation('org.springframework.boot:spring-boot-starter-web:3.2.4') { implementation('org.springframework.boot:spring-boot-starter-web:3.2.4') {

View file

@ -4,17 +4,26 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List; import java.util.List;
import org.simpleyaml.configuration.comments.CommentType;
import org.simpleyaml.configuration.file.YamlFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
public class ConfigInitializer public class ConfigInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> { implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger logger = LoggerFactory.getLogger(ConfigInitializer.class);
@Override @Override
public void initialize(ConfigurableApplicationContext applicationContext) { public void initialize(ConfigurableApplicationContext applicationContext) {
try { try {
@ -44,95 +53,89 @@ public class ConfigInitializer
} }
} }
} else { } else {
// Path templatePath =
// Paths.get( // Define the path to the config settings file
// getClass() Path settingsPath = Paths.get("configs", "settings.yml");
// .getClassLoader() // Load the template resource
// .getResource("settings.yml.template") URL settingsTemplateResource =
// .toURI()); getClass().getClassLoader().getResource("settings.yml.template");
// Path userPath = Paths.get("configs", "settings.yml"); if (settingsTemplateResource == null) {
// throw new IOException("Resource not found: settings.yml.template");
// List<String> templateLines = Files.readAllLines(templatePath); }
// List<String> userLines =
// Files.exists(userPath) ? Files.readAllLines(userPath) : new // Create a temporary file to copy the resource content
// ArrayList<>(); Path tempTemplatePath = Files.createTempFile("settings.yml", ".template");
//
// List<String> resultLines = new ArrayList<>(); try (InputStream in = settingsTemplateResource.openStream()) {
// int position = 0; Files.copy(in, tempTemplatePath, StandardCopyOption.REPLACE_EXISTING);
// for (String templateLine : templateLines) { }
// // Check if the line is a comment
// if (templateLine.trim().startsWith("#")) { final YamlFile settingsTemplateFile = new YamlFile(tempTemplatePath.toFile());
// String entry = templateLine.trim().substring(1).trim(); settingsTemplateFile.loadWithComments();
// if (!entry.isEmpty()) {
// // Check if this comment has been uncommented in userLines final YamlFile settingsFile = new YamlFile(settingsPath.toFile());
// String key = entry.split(":")[0].trim(); settingsFile.loadWithComments();
// addLine(resultLines, userLines, templateLine, key, position);
// } else { // Load headers and comments
// resultLines.add(templateLine); String header = settingsTemplateFile.getHeader();
// }
// } // Create a new file for temporary settings
// // Check if the line is a key-value pair final YamlFile tempSettingFile = new YamlFile(settingsPath.toFile());
// else if (templateLine.contains(":")) { tempSettingFile.createNewFile(true);
// String key = templateLine.split(":")[0].trim(); tempSettingFile.setHeader(header);
// addLine(resultLines, userLines, templateLine, key, position);
// } // Get all keys from the template
// // Handle empty lines List<String> keys =
// else if (templateLine.trim().length() == 0) { Arrays.asList(settingsTemplateFile.getKeys(true).toArray(new String[0]));
// resultLines.add("");
// } for (String key : keys) {
// position++; if (!key.contains(".")) {
// } // Add blank lines and comments to specific sections
// tempSettingFile
// // Write the result to the user settings file .path(key)
// Files.write(userPath, resultLines); .comment(settingsTemplateFile.getComment(key))
.blankLine();
continue;
}
// Copy settings from the template to the settings.yml file
changeConfigItemFromCommentToKeyValue(
settingsTemplateFile, settingsFile, tempSettingFile, key);
}
// Save the settings.yml file
tempSettingFile.save();
} }
// Create custom settings file if it doesn't exist
Path customSettingsPath = Paths.get("configs", "custom_settings.yml"); Path customSettingsPath = Paths.get("configs", "custom_settings.yml");
if (!Files.exists(customSettingsPath)) { if (!Files.exists(customSettingsPath)) {
Files.createFile(customSettingsPath); Files.createFile(customSettingsPath);
} }
} }
// TODO check parent value instead of just indent lines for duplicate keys (like enabled etc) private void changeConfigItemFromCommentToKeyValue(
private static void addLine( final YamlFile settingsTemplateFile,
List<String> resultLines, final YamlFile settingsFile,
List<String> userLines, final YamlFile tempSettingFile,
String templateLine, String path) {
String key, if (settingsFile.get(path) == null && settingsTemplateFile.get(path) != null) {
int position) { // If the key is only in the template, add it to the temporary settings with comments
boolean added = false; tempSettingFile
int templateIndentationLevel = getIndentationLevel(templateLine); .path(path)
int pos = 0; .set(settingsTemplateFile.get(path))
for (String settingsLine : userLines) { .comment(settingsTemplateFile.getComment(path, CommentType.BLOCK))
if (settingsLine.trim().startsWith(key + ":") && position == pos) { .commentSide(settingsTemplateFile.getComment(path, CommentType.SIDE));
int settingsIndentationLevel = getIndentationLevel(settingsLine); } else if (settingsFile.get(path) != null && settingsTemplateFile.get(path) != null) {
// Check if it is correct settingsLine and has the same parent as templateLine // If the key is in both, update the temporary settings with the main settings' value
if (settingsIndentationLevel == templateIndentationLevel) { // and comments
resultLines.add(settingsLine); tempSettingFile
added = true; .path(path)
break; .set(settingsFile.get(path))
} .comment(settingsTemplateFile.getComment(path, CommentType.BLOCK))
} .commentSide(settingsTemplateFile.getComment(path, CommentType.SIDE));
pos++; } else {
// Log if the key is not found in both YAML files
logger.info("Key not found in both YAML files: " + path);
} }
if (!added) {
resultLines.add(templateLine);
}
}
private static int getIndentationLevel(String line) {
int indentationLevel = 0;
String trimmedLine = line.trim();
if (trimmedLine.startsWith("#")) {
line = trimmedLine.substring(1);
}
for (char c : line.toCharArray()) {
if (c == ' ') {
indentationLevel++;
} else {
break;
}
}
return indentationLevel;
} }
} }

View file

@ -1,12 +1,11 @@
package stirling.software.SPDF.config.security; package stirling.software.SPDF.config.security;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.simpleyaml.configuration.file.YamlFile;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -87,32 +86,16 @@ public class InitialSecuritySetup {
private void saveKeyToConfig(String key) throws IOException { private void saveKeyToConfig(String key) throws IOException {
Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml Path path = Paths.get("configs", "settings.yml"); // Target the configs/settings.yml
List<String> lines = Files.readAllLines(path);
boolean keyFound = false;
// Search for the existing key to replace it or place to add it final YamlFile settingsYml = new YamlFile(path.toFile());
for (int i = 0; i < lines.size(); i++) {
if (lines.get(i).startsWith("AutomaticallyGenerated:")) {
keyFound = true;
if (i + 1 < lines.size() && lines.get(i + 1).trim().startsWith("key:")) {
lines.set(i + 1, " key: " + key);
break;
} else {
lines.add(i + 1, " key: " + key);
break;
}
}
}
// If the section doesn't exist, append it settingsYml.loadWithComments();
if (!keyFound) {
lines.add("# Automatically Generated Settings (Do Not Edit Directly)");
lines.add("AutomaticallyGenerated:");
lines.add(" key: " + key);
}
// Write back to the file settingsYml
Files.write(path, lines); .path("AutomaticallyGenerated.key")
.set(key)
.comment("# Automatically Generated Settings (Do Not Edit Directly)");
settingsYml.save();
} }
private boolean isValidUUID(String uuid) { private boolean isValidUUID(String uuid) {

View file

@ -1,44 +1,54 @@
# Welcome to settings file #############################################################################################################
# Remove comment marker # if on start of line to enable the configuration # Welcome to settings file from #
# If you want to override with environment parameter follow parameter naming SECURITY_INITIALLOGIN_USERNAME # ____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____ #
# / ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___| #
# \___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_ #
# ___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _| #
# |____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_| #
# #
# Do not comment out any entry, it will be removed on next startup #
# If you want to override with environment parameter follow parameter naming SECURITY_INITIALLOGIN_USERNAME #
#############################################################################################################
security: security:
enableLogin: false # set to 'true' to enable login enableLogin: false # set to 'true' to enable login
csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production) csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production)
loginAttemptCount: 5 # lock user account after 5 tries loginAttemptCount: 5 # lock user account after 5 tries
loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts
# initialLogin: loginMethod: all # 'all' (Login Username/Password and OAuth2[must be enabled and configured]), 'normal'(only Login with Username/Password) or 'oauth2'(only Login with OAuth2)
# username: "admin" # Initial username for the first login initialLogin:
# password: "stirling" # Initial password for the first login username: '' # Initial username for the first login
# oauth2: password: '' # Initial password for the first login
# enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work) oauth2:
# issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work)
# clientId: "" # Client ID from your provider client:
# clientSecret: "" # Client Secret from your provider keycloak:
# autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users issuer: '' # URL of the Keycloak realm's OpenID Connect Discovery endpoint
# useAsUsername: "email" # Default is 'email'; custom fields can be used as the username clientId: '' # Client ID for Keycloak OAuth2
# scopes: "openid, profile, email" # Specify the scopes for which the application will request permissions clientSecret: '' # Client Secret for Keycloak OAuth2
# provider: "google" # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak' scopes: openid, profile, email # Scopes for Keycloak OAuth2
# client: useAsUsername: preferred_username # Field to use as the username for Keycloak OAuth2
# google: google:
# clientId: "" # Client ID for Google OAuth2 clientId: '' # Client ID for Google OAuth2
# clientSecret: "" # Client Secret for Google OAuth2 clientSecret: '' # Client Secret for Google OAuth2
# scopes: "https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile" # Scopes for Google OAuth2 scopes: https://www.googleapis.com/auth/userinfo.email, https://www.googleapis.com/auth/userinfo.profile # Scopes for Google OAuth2
# useAsUsername: "email" # Field to use as the username for Google OAuth2 useAsUsername: email # Field to use as the username for Google OAuth2
# github: github:
# clientId: "" # Client ID for GitHub OAuth2 clientId: '' # Client ID for GitHub OAuth2
# clientSecret: "" # Client Secret for GitHub OAuth2 clientSecret: '' # Client Secret for GitHub OAuth2
# scopes: "read:user" # Scope for GitHub OAuth2 scopes: read:user # Scope for GitHub OAuth2
# useAsUsername: "login" # Field to use as the username for GitHub OAuth2 useAsUsername: login # Field to use as the username for GitHub OAuth2
# keycloak: issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point
# issuer: "http://192.168.0.123:8888/realms/stirling-pdf" # URL of the Keycloak realm's OpenID Connect Discovery endpoint clientId: '' # Client ID from your provider
# clientId: "stirling-pdf" # Client ID for Keycloak OAuth2 clientSecret: '' # Client Secret from your provider
# clientSecret: "" # Client Secret for Keycloak OAuth2 autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users
# scopes: "openid, profile, email" # Scopes for Keycloak OAuth2 useAsUsername: email # Default is 'email'; custom fields can be used as the username
# useAsUsername: "email" # Field to use as the username for Keycloak OAuth2 scopes: openid, profile, email # Specify the scopes for which the application will request permissions
provider: google # Set this to your OAuth provider's name, e.g., 'google' or 'keycloak'
system: system:
defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc) defaultLocale: en-US # Set the default language (e.g. 'de-DE', 'fr-FR', etc)
googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow
enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes) enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes)
showUpdate: false # see when a new update is available showUpdate: false # see when a new update is available
@ -46,9 +56,9 @@ system:
customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template html files customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template html files
ui: ui:
appName: null # Application's visible name appName: '' # Application's visible name
homeDescription: null # Short description or tagline shown on homepage. homeDescription: '' # Short description or tagline shown on homepage.
appNameNavbar: null # Name displayed on the navigation bar appNameNavbar: '' # Name displayed on the navigation bar
endpoints: endpoints:
toRemove: [] # List endpoints to disable (e.g. ['img-to-pdf', 'remove-pages']) toRemove: [] # List endpoints to disable (e.g. ['img-to-pdf', 'remove-pages'])