Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
parent
d575ba8f9a
commit
fe9c5a7351
4 changed files with 141 additions and 143 deletions
|
@ -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') {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
Loading…
Reference in a new issue