From 890163053b554484362f443f4d9ab72aa65e0556 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Fri, 3 May 2024 20:43:48 +0100 Subject: [PATCH] introduces custom settings file (#1158) * Introducing a custom settings file * formats * chnages * Update README.md --- README.md | 2 + .../software/SPDF/SPdfApplication.java | 30 +- .../SPDF/config/ConfigInitializer.java | 272 ++++++++---------- .../config/FileFallbackTemplateResolver.java | 2 +- .../security/CustomLogoutSuccessHandler.java | 34 +-- .../config/security/InitialSecuritySetup.java | 14 +- .../security/SecurityConfiguration.java | 155 +++++----- .../SPDF/config/security/UserService.java | 2 +- .../converters/ConvertImgPDFController.java | 2 +- .../controller/web/AccountWebController.java | 4 +- .../SPDF/model/ApplicationProperties.java | 2 +- .../SPDF/repository/AuthorityRepository.java | 1 - src/main/resources/settings.yml.template | 5 +- 13 files changed, 272 insertions(+), 253 deletions(-) diff --git a/README.md b/README.md index e47b0bde..82f0dcb7 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,8 @@ metrics: enabled: true # 'true' to enable Info APIs endpoints (view http://localhost:8080/swagger-ui/index.html#/API to learn more), 'false' to disable ``` +There is an additional config file ``/configs/custom_settings.yml`` were users familiar with java and spring application.properties can input their own settings on-top of Stirling-PDFs existing ones + ### Extra notes - Endpoints. Currently, the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma separate lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image-to-pdf and remove pages, GROUPS_TO_REMOVE=LibreOffice Would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md) diff --git a/src/main/java/stirling/software/SPDF/SPdfApplication.java b/src/main/java/stirling/software/SPDF/SPdfApplication.java index e21dadb1..fb682998 100644 --- a/src/main/java/stirling/software/SPDF/SPdfApplication.java +++ b/src/main/java/stirling/software/SPDF/SPdfApplication.java @@ -5,6 +5,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,14 +67,36 @@ public class SPdfApplication { SpringApplication app = new SpringApplication(SPdfApplication.class); app.addInitializers(new ConfigInitializer()); + Map propertyFiles = new HashMap<>(); + + // stirling pdf settings file if (Files.exists(Paths.get("configs/settings.yml"))) { - app.setDefaultProperties( - Collections.singletonMap( - "spring.config.additional-location", "file:configs/settings.yml")); + propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml"); } else { logger.warn( "External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead."); } + + // custom javs settings file + if (Files.exists(Paths.get("configs/custom_settings.yml"))) { + String existing = propertyFiles.getOrDefault("spring.config.additional-location", ""); + if (!existing.isEmpty()) { + existing += ","; + } + propertyFiles.put( + "spring.config.additional-location", + existing + "file:configs/custom_settings.yml"); + } else { + logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist."); + } + + if (!propertyFiles.isEmpty()) { + app.setDefaultProperties( + Collections.singletonMap( + "spring.config.additional-location", + propertyFiles.get("spring.config.additional-location"))); + } + app.run(args); try { diff --git a/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java b/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java index 7bceaf8f..177a91ad 100644 --- a/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java +++ b/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java @@ -1,20 +1,20 @@ package stirling.software.SPDF.config; -import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; +import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Optional; +import java.util.Map; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; @@ -26,12 +26,12 @@ public class ConfigInitializer public void initialize(ConfigurableApplicationContext applicationContext) { try { ensureConfigExists(); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException("Failed to initialize application configuration", e); } } - public void ensureConfigExists() throws IOException { + public void ensureConfigExists() throws IOException, URISyntaxException { // Define the path to the external config directory Path destPath = Paths.get("configs", "settings.yml"); @@ -51,170 +51,132 @@ public class ConfigInitializer } } } else { - // If user file exists, we need to merge it with the template from the classpath - List templateLines; - try (InputStream in = - getClass().getClassLoader().getResourceAsStream("settings.yml.template")) { - templateLines = - new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)) - .lines() - .collect(Collectors.toList()); - } + Path templatePath = + Paths.get( + getClass() + .getClassLoader() + .getResource("settings.yml.template") + .toURI()); + Path userPath = Paths.get("configs", "settings.yml"); - mergeYamlFiles(templateLines, destPath, destPath); + List templateLines = Files.readAllLines(templatePath); + List userLines = + Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>(); + + Map templateEntries = extractEntries(templateLines); + Map userEntries = extractEntries(userLines); + + List mergedLines = mergeConfigs(templateLines, templateEntries, userEntries); + mergedLines = cleanInvalidYamlEntries(mergedLines); + Files.write(userPath, mergedLines); + } + + Path customSettingsPath = Paths.get("configs", "custom_settings.yml"); + if (!Files.exists(customSettingsPath)) { + Files.createFile(customSettingsPath); } } - public void mergeYamlFiles(List templateLines, Path userFilePath, Path outputPath) - throws IOException { - List userLines = Files.readAllLines(userFilePath); + private static Map extractEntries(List lines) { + Map entries = new HashMap<>(); + String keyRegex = "^\\s*(\\w+)\\s*:\\s*(.*)"; // Capture key and value + Pattern pattern = Pattern.compile(keyRegex); + + for (String line : lines) { + Matcher matcher = pattern.matcher(line); + if (matcher.find() && !line.trim().startsWith("#")) { + String key = matcher.group(1).trim(); + String value = matcher.group(2).trim(); // Capture the value directly + entries.put(key, line); + } + } + return entries; + } + + + private static List mergeConfigs( + List templateLines, + Map templateEntries, + Map userEntries) { List mergedLines = new ArrayList<>(); - boolean insideAutoGenerated = false; - boolean beforeFirstKey = true; - - Function isCommented = line -> line.trim().startsWith("#"); - Function extractKey = - line -> { - String[] parts = line.split(":"); - return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : ""; - }; - - Function getIndentationLevel = - line -> { - int count = 0; - for (char ch : line.toCharArray()) { - if (ch == ' ') count++; - else break; - } - return count; - }; - - Set userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet()); + Set handledKeys = new HashSet<>(); for (String line : templateLines) { - String key = extractKey.apply(line); - - if ("AutomaticallyGenerated:".equalsIgnoreCase(line.trim())) { - insideAutoGenerated = true; - mergedLines.add(line); - continue; - } else if (insideAutoGenerated && line.trim().isEmpty()) { - insideAutoGenerated = false; - mergedLines.add(line); - continue; - } - - if (beforeFirstKey && (isCommented.apply(line) || line.trim().isEmpty())) { - // Handle top comments and empty lines before the first key. - mergedLines.add(line); - continue; - } - - if (!key.isEmpty()) beforeFirstKey = false; - - if (userKeys.contains(key)) { - // If user has any version (commented or uncommented) of this key, skip the - // template line - Optional userValue = - userLines.stream() - .filter( - l -> - extractKey.apply(l).equalsIgnoreCase(key) - && !isCommented.apply(l)) - .findFirst(); - if (userValue.isPresent()) mergedLines.add(userValue.get()); - continue; - } - - if (isCommented.apply(line) || line.trim().isEmpty() || !userKeys.contains(key)) { - mergedLines.add( - line); // If line is commented, empty or key not present in user's file, - // retain the - // template line - continue; - } - } - - // Add any additional uncommented user lines that are not present in the - // template - for (String userLine : userLines) { - String userKey = extractKey.apply(userLine); - boolean isPresentInTemplate = - templateLines.stream() - .map(extractKey) - .anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey)); - if (!isPresentInTemplate && !isCommented.apply(userLine)) { - if (!childOfTemplateEntry( - isCommented, - extractKey, - getIndentationLevel, - userLines, - userLine, - templateLines)) { - // check if userLine is a child of a entry within templateLines or not, if child - // of parent in templateLines then dont add to mergedLines, if anything else - // then add - mergedLines.add(userLine); + String cleanLine = line.split("#")[0].trim(); + if (!cleanLine.isEmpty() && cleanLine.contains(":")) { + String key = cleanLine.split(":")[0].trim(); + if (userEntries.containsKey(key)) { + // Always use user's entry if exists + mergedLines.add(userEntries.get(key)); + handledKeys.add(key); + } else { + // Use template's entry if no user entry + mergedLines.add(line); } + } else { + // Add comments and other lines directly + mergedLines.add(line); } } - Files.write(outputPath, mergedLines, StandardCharsets.UTF_8); + // Add user entries not present in the template at the end + for (String key : userEntries.keySet()) { + if (!handledKeys.contains(key)) { + mergedLines.add(userEntries.get(key)); + } + } + + return mergedLines; } - // New method to check if a userLine is a child of an entry in templateLines - boolean childOfTemplateEntry( - Function isCommented, - Function extractKey, - Function getIndentationLevel, - List userLines, - String userLine, - List templateLines) { - String userKey = extractKey.apply(userLine).trim(); - int userIndentation = getIndentationLevel.apply(userLine); - // Start by assuming the line is not a child of an entry in templateLines - boolean isChild = false; + private static List cleanInvalidYamlEntries(List lines) { + List cleanedLines = new ArrayList<>(); + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + String trimmedLine = line.trim(); - // Iterate backwards through userLines from the current line to find any parent - for (int i = userLines.indexOf(userLine) - 1; i >= 0; i--) { - String potentialParentLine = userLines.get(i); - int parentIndentation = getIndentationLevel.apply(potentialParentLine); - - // Check if we've reached a potential parent based on indentation - if (parentIndentation < userIndentation) { - String parentKey = extractKey.apply(potentialParentLine).trim(); - - // Now, check if this potential parent or any of its parents exist in templateLines - boolean parentExistsInTemplate = - templateLines.stream() - .filter(line -> !isCommented.apply(line)) // Skip commented lines - .anyMatch( - templateLine -> { - String templateKey = - extractKey.apply(templateLine).trim(); - return parentKey.equalsIgnoreCase(templateKey); - }); - - if (!parentExistsInTemplate) { - // If the parent does not exist in template, check the next level parent - userIndentation = - parentIndentation; // Update userIndentation to the parent's indentation - // for next iteration - if (parentIndentation == 0) { - // If we've reached the top-level parent and it's not in template, the - // original line is considered not a child - isChild = false; - break; - } - } else { - // If any parent exists in template, the original line is considered a child - isChild = true; - break; - } + // Ignore commented lines and lines that don't look like key-only entries + if (trimmedLine.startsWith("#") + || !trimmedLine.endsWith(":") + || trimmedLine.contains(" ")) { + cleanedLines.add(line); + continue; } + + // For potential key-only lines, check the next line to determine context + if (isKeyWithoutChildrenOrValue(i, lines)) { + // Skip adding the current line since it's a key without any following value or + // children + continue; + } + + cleanedLines.add(line); + } + return cleanedLines; + } + + private static boolean isKeyWithoutChildrenOrValue(int currentIndex, List lines) { + if (currentIndex + 1 < lines.size()) { + String currentLine = lines.get(currentIndex); + String nextLine = lines.get(currentIndex + 1); + int currentIndentation = getIndentationLevel(currentLine); + int nextIndentation = getIndentationLevel(nextLine); + + // If the next line is less or equally indented, it's not a child or value + return nextIndentation <= currentIndentation; } - return isChild; // Return true if the line is not a child of any entry in templateLines + // If it's the last line, then it definitely has no children or value + return true; + } + + private static int getIndentationLevel(String line) { + int count = 0; + for (char ch : line.toCharArray()) { + if (ch == ' ') count++; + else break; + } + return count; } } diff --git a/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java b/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java index 10ea9488..be6e28a7 100644 --- a/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java +++ b/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java @@ -37,7 +37,7 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe return new FileTemplateResource(resource.getFile().getPath(), characterEncoding); } } catch (IOException e) { - + } return new ClassLoaderTemplateResource( diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java index 8926814e..300eee00 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java @@ -2,42 +2,38 @@ package stirling.software.SPDF.config.security; import java.io.IOException; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; -import jakarta.servlet.ServletException; import org.springframework.context.annotation.Bean; import org.springframework.security.core.Authentication; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; -public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler -{ +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { @Bean public SessionRegistry sessionRegistry() { return new SessionRegistryImpl(); } @Override - public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException - { + public void onLogoutSuccess( + HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { HttpSession session = request.getSession(false); if (session != null) { String sessionId = session.getId(); - sessionRegistry() - .removeSessionInformation( - sessionId); + sessionRegistry().removeSessionInformation(sessionId); } - if(request.getParameter("oauth2AutoCreateDisabled") != null) - { - response.sendRedirect(request.getContextPath()+"/login?error=oauth2AutoCreateDisabled"); - } - else - { + if (request.getParameter("oauth2AutoCreateDisabled") != null) { + response.sendRedirect( + request.getContextPath() + "/login?error=oauth2AutoCreateDisabled"); + } else { response.sendRedirect(request.getContextPath() + "/login?logout=true"); } } -} \ No newline at end of file +} diff --git a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java index 0d87ca48..b472622b 100644 --- a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java +++ b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java @@ -50,7 +50,7 @@ public class InitialSecuritySetup { @PostConstruct public void initSecretKey() throws IOException { String secretKey = applicationProperties.getAutomaticallyGenerated().getKey(); - if (secretKey == null || secretKey.isEmpty()) { + if (!isValidUUID(secretKey)) { secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key saveKeyToConfig(secretKey); } @@ -85,4 +85,16 @@ public class InitialSecuritySetup { // Write back to the file Files.write(path, lines); } + private boolean isValidUUID(String uuid) { + if (uuid == null) { + return false; + } + try { + UUID.fromString(uuid); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + } diff --git a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java index 3c612bf8..8eb80cdc 100644 --- a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java @@ -1,8 +1,8 @@ package stirling.software.SPDF.config.security; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.*; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -24,6 +24,8 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.registration.ClientRegistrations; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; @@ -33,17 +35,15 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.savedrequest.NullRequestCache; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.oauth2.client.registration.ClientRegistrations; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.User; import stirling.software.SPDF.repository.JPATokenRepositoryImpl; -import java.io.IOException; -import java.util.*; - @Configuration @EnableWebSecurity() @EnableMethodSecurity @@ -109,7 +109,7 @@ public class SecurityConfiguration { logout -> logout.logoutRequestMatcher( new AntPathRequestMatcher("/logout")) - .logoutSuccessHandler(new CustomLogoutSuccessHandler()) // Use a Custom Logout Handler to handle custom error message if OAUTH2 Auto Create is disabled + .logoutSuccessHandler(new CustomLogoutSuccessHandler()) .invalidateHttpSession(true) // Invalidate session .deleteCookies("JSESSIONID", "remember-me") .addLogoutHandler( @@ -167,33 +167,45 @@ public class SecurityConfiguration { // Handle OAUTH2 Logins if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) { - http.oauth2Login( oauth2 -> oauth2 - .loginPage("/oauth2") - /* - This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database. - If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser' - is set as true, else login fails with an error message advising the same. - */ - .successHandler(new AuthenticationSuccessHandler() { - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws ServletException , IOException{ - OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal(); - if (userService.processOAuth2PostLogin(oauthUser.getAttribute("email"), applicationProperties.getSecurity().getOAUTH2().getAutoCreateUser())) { - response.sendRedirect("/"); - } - else{ - response.sendRedirect("/logout?oauth2AutoCreateDisabled=true"); - } - } - } - ) - // Add existing Authorities from the database - .userInfoEndpoint( userInfoEndpoint -> - userInfoEndpoint.userAuthoritiesMapper(userAuthoritiesMapper()) - ) - ); - } + http.oauth2Login( + oauth2 -> + oauth2.loginPage("/oauth2") + /* + This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database. + If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser' + is set as true, else login fails with an error message advising the same. + */ + .successHandler( + new AuthenticationSuccessHandler() { + @Override + public void onAuthenticationSuccess( + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) + throws ServletException, IOException { + OAuth2User oauthUser = + (OAuth2User) + authentication + .getPrincipal(); + if (userService.processOAuth2PostLogin( + oauthUser.getAttribute("email"), + applicationProperties + .getSecurity() + .getOAUTH2() + .getAutoCreateUser())) { + response.sendRedirect("/"); + } else { + response.sendRedirect( + "/logout?oauth2AutoCreateDisabled=true"); + } + } + }) + // Add existing Authorities from the database + .userInfoEndpoint( + userInfoEndpoint -> + userInfoEndpoint.userAuthoritiesMapper( + userAuthoritiesMapper()))); + } } else { http.csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authz -> authz.anyRequest().permitAll()); @@ -204,50 +216,59 @@ public class SecurityConfiguration { // Client Registration Repository for OAUTH2 OIDC Login @Bean - @ConditionalOnProperty(value = "security.oauth2.enabled" , havingValue = "true", matchIfMissing = false) - public ClientRegistrationRepository clientRegistrationRepository() { - return new InMemoryClientRegistrationRepository(this.oidcClientRegistration()); - } + @ConditionalOnProperty( + value = "security.oauth2.enabled", + havingValue = "true", + matchIfMissing = false) + public ClientRegistrationRepository clientRegistrationRepository() { + return new InMemoryClientRegistrationRepository(this.oidcClientRegistration()); + } - private ClientRegistration oidcClientRegistration() { - return ClientRegistrations.fromOidcIssuerLocation(applicationProperties.getSecurity().getOAUTH2().getIssuer()) - .registrationId("oidc") - .clientId(applicationProperties.getSecurity().getOAUTH2().getClientId()) - .clientSecret(applicationProperties.getSecurity().getOAUTH2().getClientSecret()) - .scope("openid", "profile", "email") - .userNameAttributeName("email") - .clientName("OIDC") - .build(); - } + private ClientRegistration oidcClientRegistration() { + return ClientRegistrations.fromOidcIssuerLocation( + applicationProperties.getSecurity().getOAUTH2().getIssuer()) + .registrationId("oidc") + .clientId(applicationProperties.getSecurity().getOAUTH2().getClientId()) + .clientSecret(applicationProperties.getSecurity().getOAUTH2().getClientSecret()) + .scope("openid", "profile", "email") + .userNameAttributeName("email") + .clientName("OIDC") + .build(); + } /* This following function is to grant Authorities to the OAUTH2 user from the values stored in the database. This is required for the internal; 'hasRole()' function to give out the correct role. */ @Bean - @ConditionalOnProperty(value = "security.oauth2.enabled" , havingValue = "true", matchIfMissing = false) + @ConditionalOnProperty( + value = "security.oauth2.enabled", + havingValue = "true", + matchIfMissing = false) GrantedAuthoritiesMapper userAuthoritiesMapper() { return (authorities) -> { Set mappedAuthorities = new HashSet<>(); - authorities.forEach(authority -> { - // Add existing OAUTH2 Authorities - mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority())); + authorities.forEach( + authority -> { + // Add existing OAUTH2 Authorities + mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority())); - // Add Authorities from database for existing user, if user is present. - if (authority instanceof OAuth2UserAuthority oauth2Auth) { - Optional userOpt = userService.findByUsernameIgnoreCase((String)oauth2Auth.getAttributes().get("email")); - if (userOpt.isPresent()) { - User user = userOpt.get(); - if (user != null){ - mappedAuthorities.add(new SimpleGrantedAuthority( - userService - .findRole(user) - .getAuthority())); + // Add Authorities from database for existing user, if user is present. + if (authority instanceof OAuth2UserAuthority oauth2Auth) { + Optional userOpt = + userService.findByUsernameIgnoreCase( + (String) oauth2Auth.getAttributes().get("email")); + if (userOpt.isPresent()) { + User user = userOpt.get(); + if (user != null) { + mappedAuthorities.add( + new SimpleGrantedAuthority( + userService.findRole(user).getAuthority())); + } + } } - } - } - }); + }); return mappedAuthorities; }; } diff --git a/src/main/java/stirling/software/SPDF/config/security/UserService.java b/src/main/java/stirling/software/SPDF/config/security/UserService.java index f45db76a..7d57f8f6 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserService.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserService.java @@ -44,7 +44,7 @@ public class UserService implements UserServiceInterface { user.setUsername(username); user.setEnabled(true); user.setFirstLogin(false); - user.addAuthority(new Authority( Role.USER.getRoleId(), user)); + user.addAuthority(new Authority(Role.USER.getRoleId(), user)); userRepository.save(user); return true; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java index eeb4db08..9fa357f7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java @@ -74,7 +74,7 @@ public class ConvertImgPDFController { } if (singleImage) { - String docName = filename + "." + imageFormat; + String docName = filename + "." + imageFormat; MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat)); return WebResponseUtils.bytesToWebResponse(result, docName, mediaType); } else { diff --git a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java index 03ba2020..bc43bd4b 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java @@ -6,7 +6,6 @@ import java.util.Map; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; @@ -39,7 +38,8 @@ public class AccountWebController { return "redirect:/"; } - model.addAttribute("oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled()); + model.addAttribute( + "oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled()); model.addAttribute("currentPage", "login"); diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java index 72de4094..ce109299 100644 --- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java +++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java @@ -271,7 +271,7 @@ public class ApplicationProperties { + ", clientId=" + clientId + ", clientSecret=" - + (clientSecret!= null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + + (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + ", autoCreateUser=" + autoCreateUser + "]"; diff --git a/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java b/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java index 5e14a326..41c251e1 100644 --- a/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java +++ b/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java @@ -11,5 +11,4 @@ public interface AuthorityRepository extends JpaRepository { Set findByUser_Username(String username); Authority findByUserId(long user_id); - } diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template index f6a68b5f..7d2047c3 100644 --- a/src/main/resources/settings.yml.template +++ b/src/main/resources/settings.yml.template @@ -20,7 +20,6 @@ system: enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes) showUpdate: true # see when a new update is available showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true' - customHTMLFiles: false # Enable to have files placed in /customFiles/templates override the existing template html files #ui: # appName: exampleAppName # Application's visible name @@ -33,3 +32,7 @@ endpoints: metrics: enabled: true # 'true' to enable Info APIs (`/api/*`) endpoints, 'false' to disable + +# Automatically Generated Settings (Do Not Edit Directly) +AutomaticallyGenerated: + key: example \ No newline at end of file