mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-10-25 11:17:28 +02:00 
			
		
		
		
	Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									d575ba8f9a
								
							
						
					
					
						commit
						fe9c5a7351
					
				| @ -19,6 +19,7 @@ sourceCompatibility = '17' | ||||
| 
 | ||||
| repositories { | ||||
|     mavenCentral() | ||||
|     maven { url 'https://jitpack.io' } | ||||
| } | ||||
| 
 | ||||
| licenseReport { | ||||
| @ -74,7 +75,7 @@ spotless { | ||||
|     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') | ||||
|         toggleOffOn() | ||||
| @ -93,6 +94,7 @@ dependencies { | ||||
|     implementation("io.github.pixee:java-security-toolkit:1.1.3") | ||||
| 
 | ||||
|     implementation 'org.yaml:snakeyaml:2.2' | ||||
|     implementation 'com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4' | ||||
|      | ||||
|     // Exclude Tomcat and include Jetty | ||||
|     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.InputStream; | ||||
| import java.net.URISyntaxException; | ||||
| import java.net.URL; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.nio.file.StandardCopyOption; | ||||
| import java.util.Arrays; | ||||
| 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.ConfigurableApplicationContext; | ||||
| 
 | ||||
| public class ConfigInitializer | ||||
|         implements ApplicationContextInitializer<ConfigurableApplicationContext> { | ||||
| 
 | ||||
|     private static final Logger logger = LoggerFactory.getLogger(ConfigInitializer.class); | ||||
| 
 | ||||
|     @Override | ||||
|     public void initialize(ConfigurableApplicationContext applicationContext) { | ||||
|         try { | ||||
| @ -44,95 +53,89 @@ public class ConfigInitializer | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             //            Path templatePath = | ||||
|             //                    Paths.get( | ||||
|             //                            getClass() | ||||
|             //                                    .getClassLoader() | ||||
|             //                                    .getResource("settings.yml.template") | ||||
|             //                                    .toURI()); | ||||
|             //            Path userPath = Paths.get("configs", "settings.yml"); | ||||
|             // | ||||
|             //            List<String> templateLines = Files.readAllLines(templatePath); | ||||
|             //            List<String> userLines = | ||||
|             //                    Files.exists(userPath) ? Files.readAllLines(userPath) : new | ||||
|             // ArrayList<>(); | ||||
|             // | ||||
|             //            List<String> resultLines = new ArrayList<>(); | ||||
|             //            int position = 0; | ||||
|             //            for (String templateLine : templateLines) { | ||||
|             //                // Check if the line is a comment | ||||
|             //                if (templateLine.trim().startsWith("#")) { | ||||
|             //                    String entry = templateLine.trim().substring(1).trim(); | ||||
|             //                    if (!entry.isEmpty()) { | ||||
|             //                        // Check if this comment has been uncommented in userLines | ||||
|             //                        String key = entry.split(":")[0].trim(); | ||||
|             //                        addLine(resultLines, userLines, templateLine, key, position); | ||||
|             //                    } else { | ||||
|             //                        resultLines.add(templateLine); | ||||
|             //                    } | ||||
|             //                } | ||||
|             //                // Check if the line is a key-value pair | ||||
|             //                else if (templateLine.contains(":")) { | ||||
|             //                    String key = templateLine.split(":")[0].trim(); | ||||
|             //                    addLine(resultLines, userLines, templateLine, key, position); | ||||
|             //                } | ||||
|             //                // Handle empty lines | ||||
|             //                else if (templateLine.trim().length() == 0) { | ||||
|             //                    resultLines.add(""); | ||||
|             //                } | ||||
|             //                position++; | ||||
|             //            } | ||||
|             // | ||||
|             //            // Write the result to the user settings file | ||||
|             //            Files.write(userPath, resultLines); | ||||
| 
 | ||||
|             // Define the path to the config settings file | ||||
|             Path settingsPath = Paths.get("configs", "settings.yml"); | ||||
|             // Load the template resource | ||||
|             URL settingsTemplateResource = | ||||
|                     getClass().getClassLoader().getResource("settings.yml.template"); | ||||
|             if (settingsTemplateResource == null) { | ||||
|                 throw new IOException("Resource not found: settings.yml.template"); | ||||
|             } | ||||
| 
 | ||||
|             // Create a temporary file to copy the resource content | ||||
|             Path tempTemplatePath = Files.createTempFile("settings.yml", ".template"); | ||||
| 
 | ||||
|             try (InputStream in = settingsTemplateResource.openStream()) { | ||||
|                 Files.copy(in, tempTemplatePath, StandardCopyOption.REPLACE_EXISTING); | ||||
|             } | ||||
| 
 | ||||
|             final YamlFile settingsTemplateFile = new YamlFile(tempTemplatePath.toFile()); | ||||
|             settingsTemplateFile.loadWithComments(); | ||||
| 
 | ||||
|             final YamlFile settingsFile = new YamlFile(settingsPath.toFile()); | ||||
|             settingsFile.loadWithComments(); | ||||
| 
 | ||||
|             // Load headers and comments | ||||
|             String header = settingsTemplateFile.getHeader(); | ||||
| 
 | ||||
|             // Create a new file for temporary settings | ||||
|             final YamlFile tempSettingFile = new YamlFile(settingsPath.toFile()); | ||||
|             tempSettingFile.createNewFile(true); | ||||
|             tempSettingFile.setHeader(header); | ||||
| 
 | ||||
|             // Get all keys from the template | ||||
|             List<String> keys = | ||||
|                     Arrays.asList(settingsTemplateFile.getKeys(true).toArray(new String[0])); | ||||
| 
 | ||||
|             for (String key : keys) { | ||||
|                 if (!key.contains(".")) { | ||||
|                     // Add blank lines and comments to specific sections | ||||
|                     tempSettingFile | ||||
|                             .path(key) | ||||
|                             .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"); | ||||
|         if (!Files.exists(customSettingsPath)) { | ||||
|             Files.createFile(customSettingsPath); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO check parent value instead of just indent lines for duplicate keys (like enabled etc) | ||||
|     private static void addLine( | ||||
|             List<String> resultLines, | ||||
|             List<String> userLines, | ||||
|             String templateLine, | ||||
|             String key, | ||||
|             int position) { | ||||
|         boolean added = false; | ||||
|         int templateIndentationLevel = getIndentationLevel(templateLine); | ||||
|         int pos = 0; | ||||
|         for (String settingsLine : userLines) { | ||||
|             if (settingsLine.trim().startsWith(key + ":") && position == pos) { | ||||
|                 int settingsIndentationLevel = getIndentationLevel(settingsLine); | ||||
|                 // Check if it is correct settingsLine and has the same parent as templateLine | ||||
|                 if (settingsIndentationLevel == templateIndentationLevel) { | ||||
|                     resultLines.add(settingsLine); | ||||
|                     added = true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             pos++; | ||||
|     private void changeConfigItemFromCommentToKeyValue( | ||||
|             final YamlFile settingsTemplateFile, | ||||
|             final YamlFile settingsFile, | ||||
|             final YamlFile tempSettingFile, | ||||
|             String path) { | ||||
|         if (settingsFile.get(path) == null && settingsTemplateFile.get(path) != null) { | ||||
|             // If the key is only in the template, add it to the temporary settings with comments | ||||
|             tempSettingFile | ||||
|                     .path(path) | ||||
|                     .set(settingsTemplateFile.get(path)) | ||||
|                     .comment(settingsTemplateFile.getComment(path, CommentType.BLOCK)) | ||||
|                     .commentSide(settingsTemplateFile.getComment(path, CommentType.SIDE)); | ||||
|         } else if (settingsFile.get(path) != null && settingsTemplateFile.get(path) != null) { | ||||
|             // If the key is in both, update the temporary settings with the main settings' value | ||||
|             // and comments | ||||
|             tempSettingFile | ||||
|                     .path(path) | ||||
|                     .set(settingsFile.get(path)) | ||||
|                     .comment(settingsTemplateFile.getComment(path, CommentType.BLOCK)) | ||||
|                     .commentSide(settingsTemplateFile.getComment(path, CommentType.SIDE)); | ||||
|         } 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; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.List; | ||||
| import java.util.UUID; | ||||
| 
 | ||||
| import org.simpleyaml.configuration.file.YamlFile; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| @ -87,32 +86,16 @@ public class InitialSecuritySetup { | ||||
| 
 | ||||
|     private void saveKeyToConfig(String key) throws IOException { | ||||
|         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 | ||||
|         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; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         final YamlFile settingsYml = new YamlFile(path.toFile()); | ||||
| 
 | ||||
|         // If the section doesn't exist, append it | ||||
|         if (!keyFound) { | ||||
|             lines.add("# Automatically Generated Settings (Do Not Edit Directly)"); | ||||
|             lines.add("AutomaticallyGenerated:"); | ||||
|             lines.add("  key: " + key); | ||||
|         } | ||||
|         settingsYml.loadWithComments(); | ||||
| 
 | ||||
|         // Write back to the file | ||||
|         Files.write(path, lines); | ||||
|         settingsYml | ||||
|                 .path("AutomaticallyGenerated.key") | ||||
|                 .set(key) | ||||
|                 .comment("# Automatically Generated Settings (Do Not Edit Directly)"); | ||||
|         settingsYml.save(); | ||||
|     } | ||||
| 
 | ||||
|     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 | ||||
| # If you want to override with environment parameter follow parameter naming SECURITY_INITIALLOGIN_USERNAME | ||||
| ############################################################################################################# | ||||
| #                                         Welcome to settings file from                                     # | ||||
| #                      ____ _____ ___ ____  _     ___ _   _  ____       ____  ____  _____                   # | ||||
| #                     / ___|_   _|_ _|  _ \| |   |_ _| \ | |/ ___|     |  _ \|  _ \|  ___|                  # | ||||
| #                     \___ \ | |  | || |_) | |    | ||  \| | |  _ _____| |_) | | | | |_                     # | ||||
| #                      ___) || |  | ||  _ <| |___ | || |\  | |_| |_____|  __/| |_| |  _|                    # | ||||
| #                     |____/ |_| |___|_| \_\_____|___|_| \_|\____|     |_|   |____/|_|                      # | ||||
| #                                                                                                           # | ||||
| # 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: | ||||
|   enableLogin: false # set to 'true' to enable login | ||||
|   csrfDisabled: true # Set to 'true' to disable CSRF protection (not recommended for production) | ||||
|   loginAttemptCount: 5 # lock user account after 5 tries | ||||
|   loginResetTimeMinutes: 120 # lock account for 2 hours after x attempts | ||||
| #  initialLogin: | ||||
| #    username: "admin" # Initial username for the first login | ||||
| #    password: "stirling" # Initial password for the first login | ||||
| #  oauth2: | ||||
| #    enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work) | ||||
| #    issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point | ||||
| #    clientId: "" # Client ID from your provider | ||||
| #    clientSecret: "" # Client Secret from your provider | ||||
| #    autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users | ||||
| #    useAsUsername: "email" # Default is 'email'; custom fields can be used as the username | ||||
| #    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' | ||||
| #    client: | ||||
| #      google: | ||||
| #        clientId: "" # Client ID 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 | ||||
| #        useAsUsername: "email" # Field to use as the username for Google OAuth2 | ||||
| #      github: | ||||
| #        clientId: "" # Client ID for GitHub OAuth2 | ||||
| #        clientSecret: "" # Client Secret for GitHub OAuth2 | ||||
| #        scopes: "read:user" # Scope for GitHub OAuth2 | ||||
| #        useAsUsername: "login" # Field to use as the username for GitHub OAuth2 | ||||
| #      keycloak: | ||||
| #        issuer: "http://192.168.0.123:8888/realms/stirling-pdf" # URL of the Keycloak realm's OpenID Connect Discovery endpoint | ||||
| #        clientId: "stirling-pdf" # Client ID for Keycloak OAuth2 | ||||
| #        clientSecret: "" # Client Secret for Keycloak OAuth2 | ||||
| #        scopes: "openid, profile, email" # Scopes for Keycloak OAuth2 | ||||
| #        useAsUsername: "email" # Field to use as the username for Keycloak OAuth2 | ||||
|   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) | ||||
|   initialLogin: | ||||
|     username: '' # Initial username for the first login | ||||
|     password: '' # Initial password for the first login | ||||
|   oauth2: | ||||
|     enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work) | ||||
|     client: | ||||
|       keycloak: | ||||
|         issuer: '' # URL of the Keycloak realm's OpenID Connect Discovery endpoint | ||||
|         clientId: '' # Client ID for Keycloak OAuth2 | ||||
|         clientSecret: '' # Client Secret for Keycloak OAuth2 | ||||
|         scopes: openid, profile, email # Scopes for Keycloak OAuth2 | ||||
|         useAsUsername: preferred_username # Field to use as the username for Keycloak OAuth2 | ||||
|       google: | ||||
|         clientId: '' # Client ID 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 | ||||
|         useAsUsername: email # Field to use as the username for Google OAuth2 | ||||
|       github: | ||||
|         clientId: '' # Client ID for GitHub OAuth2 | ||||
|         clientSecret: '' # Client Secret for GitHub OAuth2 | ||||
|         scopes: read:user # Scope for GitHub OAuth2 | ||||
|         useAsUsername: login # Field to use as the username for GitHub OAuth2 | ||||
|     issuer: '' # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point | ||||
|     clientId: '' # Client ID from your provider | ||||
|     clientSecret: '' # Client Secret from your provider | ||||
|     autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users | ||||
|     useAsUsername: email # Default is 'email'; custom fields can be used as the username | ||||
|     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: | ||||
|   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 | ||||
|   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 | ||||
| @ -46,9 +56,9 @@ system: | ||||
|   customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template html files | ||||
| 
 | ||||
| ui: | ||||
|   appName: null # Application's visible name | ||||
|   homeDescription: null # Short description or tagline shown on homepage. | ||||
|   appNameNavbar: null # Name displayed on the navigation bar | ||||
|   appName: '' # Application's visible name | ||||
|   homeDescription: '' # Short description or tagline shown on homepage. | ||||
|   appNameNavbar: '' # Name displayed on the navigation bar | ||||
| 
 | ||||
| endpoints: | ||||
|   toRemove: [] # List endpoints to disable (e.g. ['img-to-pdf', 'remove-pages']) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user