From 3af93f0adb0eec9ef895ba2bb901f7ba812a5f8e Mon Sep 17 00:00:00 2001 From: Ludy Date: Sun, 24 Aug 2025 23:16:55 +0200 Subject: [PATCH] feat(database,Jwt): relocate backups and Jwt-keys to `config/backup` and add Enterprise cleanup endpoints (#4225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description of Changes - **What was changed** - Centralized installation paths: - Introduced `BACKUP_PATH`, `BACKUP_DB_PATH`, and `BACKUP_PRIVATE_KEY_PATH` in `InstallationPathConfig`; `getPrivateKeyPath()` now resolves to `backup/keys` and new `getBackupPath()` returns `backup/db`. - Removed old `PRIVATE_KEY_PATH` and switched all usages to the new locations. - Database service enhancements: - `DatabaseService` now uses `InstallationPathConfig.getBackupPath()` and includes a one-time migration to move existing backups from `config/db/backup` to `config/backup/db` (**@Deprecated(since = "2.0.0", forRemoval = true)**). - Added `deleteAllBackups()` and `deleteLastBackup()` methods and exposed them via a new Enterprise controller. - New Enterprise-only API: - Added `DatabaseControllerEnterprise` with: - `DELETE /api/v1/database/deleteAll` — delete all backup files. - `DELETE /api/v1/database/deleteLast` — delete the most recent backup. - Endpoints gated by `@EnterpriseEndpoint` and `@Conditional(H2SQLCondition.class)`. - Key persistence adjustments: - `KeyPersistenceService` now migrates keys from `config/db/keys` to `config/backup/keys` on startup (**@Deprecated(since = "2.0.0", forRemoval = true)**). - Miscellaneous refactors/fixes: - Switched driver resolution in `DatabaseConfig` to a switch expression. - Corrected HTTP status usage to `HttpStatus.SEE_OTHER`. - Removed constructor `runningEE` flag from `AccountWebController` and replaced EE checks with `@EnterpriseEndpoint`. - Minor test and annotation improvements (e.g., `@Deprecated(since = "0.45.0")`, method references, equals order). - **Why the change was made** - To standardize and future-proof storage locations for both backups and keys under a clear `config/backup` hierarchy. - To give Enterprise admins first-class, safe cleanup endpoints for managing backup retention without manual file operations. - To reduce conditional logic in controllers and rely on declarative EE gating. - To improve maintainability and correctness (status codes, switch expression, null-safety patterns). --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../configuration/InstallationPathConfig.java | 20 +++- .../model/ApplicationPropertiesLogicTest.java | 2 +- .../security/config/AccountWebController.java | 20 ++-- .../configuration/DatabaseConfig.java | 8 +- .../controller/api/DatabaseController.java | 4 +- .../DatabaseControllerEnterprise.java | 101 ++++++++++++++++++ .../security/service/DatabaseService.java | 85 ++++++++++++++- .../service/DatabaseServiceInterface.java | 6 ++ .../service/KeyPersistenceService.java | 31 ++++++ .../proprietary/service/AuditService.java | 4 +- 10 files changed, 252 insertions(+), 29 deletions(-) create mode 100644 app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/enterprise/DatabaseControllerEnterprise.java diff --git a/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java b/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java index 64fbc41b7..860e02806 100644 --- a/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java +++ b/app/common/src/main/java/stirling/software/common/configuration/InstallationPathConfig.java @@ -14,18 +14,22 @@ public class InstallationPathConfig { private static final String CONFIG_PATH; private static final String CUSTOM_FILES_PATH; private static final String CLIENT_WEBUI_PATH; - private static final String SCRIPTS_PATH; private static final String PIPELINE_PATH; // Config paths private static final String SETTINGS_PATH; private static final String CUSTOM_SETTINGS_PATH; + private static final String SCRIPTS_PATH; + private static final String BACKUP_PATH; + + // Backup paths + private static final String BACKUP_DB_PATH; + private static final String BACKUP_PRIVATE_KEY_PATH; // Custom file paths private static final String STATIC_PATH; private static final String TEMPLATES_PATH; private static final String SIGNATURES_PATH; - private static final String PRIVATE_KEY_PATH; static { BASE_PATH = initializeBasePath(); @@ -41,12 +45,16 @@ public class InstallationPathConfig { SETTINGS_PATH = CONFIG_PATH + "settings.yml"; CUSTOM_SETTINGS_PATH = CONFIG_PATH + "custom_settings.yml"; SCRIPTS_PATH = CONFIG_PATH + "scripts" + File.separator; + BACKUP_PATH = CONFIG_PATH + "backup" + File.separator; + + // Initialize backup paths + BACKUP_DB_PATH = BACKUP_PATH + "db" + File.separator; + BACKUP_PRIVATE_KEY_PATH = BACKUP_PATH + "keys" + File.separator; // Initialize custom file paths STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator; TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator; SIGNATURES_PATH = CUSTOM_FILES_PATH + "signatures" + File.separator; - PRIVATE_KEY_PATH = CONFIG_PATH + "db" + File.separator + "keys" + File.separator; } private static String initializeBasePath() { @@ -124,6 +132,10 @@ public class InstallationPathConfig { } public static String getPrivateKeyPath() { - return PRIVATE_KEY_PATH; + return BACKUP_PRIVATE_KEY_PATH; + } + + public static String getBackupPath() { + return BACKUP_DB_PATH; } } diff --git a/app/common/src/test/java/stirling/software/common/model/ApplicationPropertiesLogicTest.java b/app/common/src/test/java/stirling/software/common/model/ApplicationPropertiesLogicTest.java index da83fd462..c8d877dc9 100644 --- a/app/common/src/test/java/stirling/software/common/model/ApplicationPropertiesLogicTest.java +++ b/app/common/src/test/java/stirling/software/common/model/ApplicationPropertiesLogicTest.java @@ -179,7 +179,7 @@ class ApplicationPropertiesLogicTest { assertEquals(30, t.getOcrMyPdfTimeoutMinutes()); } - @Deprecated + @Deprecated(since = "0.45.0") @Test void enterprise_metadata_defaults() { ApplicationProperties.EnterpriseEdition ee = new ApplicationProperties.EnterpriseEdition(); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java index 46d0e7d3d..a61a7b0fa 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/config/AccountWebController.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -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; @@ -59,19 +58,16 @@ public class AccountWebController { private final SessionPersistentRegistry sessionPersistentRegistry; // Assuming you have a repository for user operations private final UserRepository userRepository; - private final boolean runningEE; private final TeamRepository teamRepository; public AccountWebController( ApplicationProperties applicationProperties, SessionPersistentRegistry sessionPersistentRegistry, UserRepository userRepository, - TeamRepository teamRepository, - @Qualifier("runningEE") boolean runningEE) { + TeamRepository teamRepository) { this.applicationProperties = applicationProperties; this.sessionPersistentRegistry = sessionPersistentRegistry; this.userRepository = userRepository; - this.runningEE = runningEE; this.teamRepository = teamRepository; } @@ -207,11 +203,9 @@ public class AccountWebController { } @PreAuthorize("hasRole('ROLE_ADMIN')") + @EnterpriseEndpoint @GetMapping("/usage") public String showUsage() { - if (!runningEE) { - return "error"; - } return "usage"; } @@ -243,7 +237,7 @@ public class AccountWebController { // Also check if user is part of the Internal team if (user.getTeam() != null - && user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) { + && TeamService.INTERNAL_TEAM_NAME.equals(user.getTeam().getName())) { shouldRemove = true; } @@ -362,11 +356,9 @@ public class AccountWebController { teamRepository.findAll().stream() .filter( team -> - !team.getName() - .equals( - stirling.software.proprietary.security - .service.TeamService - .INTERNAL_TEAM_NAME)) + !stirling.software.proprietary.security.service.TeamService + .INTERNAL_TEAM_NAME + .equals(team.getName())) .toList(); model.addAttribute("teams", allTeams); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/DatabaseConfig.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/DatabaseConfig.java index e6afa6e40..625dc041a 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/DatabaseConfig.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/configuration/DatabaseConfig.java @@ -134,21 +134,21 @@ public class DatabaseConfig { ApplicationProperties.Driver driver = ApplicationProperties.Driver.valueOf(driverName.toUpperCase()); - switch (driver) { + return switch (driver) { case H2 -> { log.debug("H2 driver selected"); - return DatabaseDriver.H2.getDriverClassName(); + yield DatabaseDriver.H2.getDriverClassName(); } case POSTGRESQL -> { log.debug("Postgres driver selected"); - return DatabaseDriver.POSTGRESQL.getDriverClassName(); + yield DatabaseDriver.POSTGRESQL.getDriverClassName(); } default -> { log.warn("{} driver selected", driverName); throw new UnsupportedProviderException( driverName + " is not currently supported"); } - } + }; } catch (IllegalArgumentException e) { log.warn("Unknown driver: {}", driverName); throw new UnsupportedProviderException(driverName + " is not currently supported"); diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/DatabaseController.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/DatabaseController.java index dec64c46f..ca520a20d 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/DatabaseController.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/DatabaseController.java @@ -7,10 +7,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import org.eclipse.jetty.http.HttpStatus; import org.springframework.context.annotation.Conditional; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; @@ -145,7 +145,7 @@ public class DatabaseController { .body(resource); } catch (IOException e) { log.error("Error downloading file: {}", e.getMessage()); - return ResponseEntity.status(HttpStatus.SEE_OTHER_303) + return ResponseEntity.status(HttpStatus.SEE_OTHER) .location(URI.create("/database?error=downloadFailed")) .build(); } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/enterprise/DatabaseControllerEnterprise.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/enterprise/DatabaseControllerEnterprise.java new file mode 100644 index 000000000..b1da460b0 --- /dev/null +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/controller/api/enterprise/DatabaseControllerEnterprise.java @@ -0,0 +1,101 @@ +package stirling.software.proprietary.security.controller.api.enterprise; + +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.context.annotation.Conditional; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import stirling.software.common.model.FileInfo; +import stirling.software.proprietary.security.config.EnterpriseEndpoint; +import stirling.software.proprietary.security.database.H2SQLCondition; +import stirling.software.proprietary.security.service.DatabaseService; + +@Slf4j +@Controller +@RequestMapping("/api/v1/database") +@PreAuthorize("hasRole('ROLE_ADMIN')") +@EnterpriseEndpoint +@Conditional(H2SQLCondition.class) +@Tag(name = "Database", description = "Database APIs for backup, import, and management") +@RequiredArgsConstructor +public class DatabaseControllerEnterprise { + + private final DatabaseService databaseService; + + @Operation( + summary = "Delete the last database backup file", + description = + "Only Enterprise - Deletes the last database backup file from the server.") + @DeleteMapping("/deleteLast") + public ResponseEntity deleteLastFile() { + log.info("Deleting last database backup file..."); + List> results = databaseService.deleteLastBackup(); + return getDeleteAllResults(results); + } + + @Operation( + summary = "Delete all database backup files", + description = "Only Enterprise - Deletes all database backup files from the server.") + @DeleteMapping("/deleteAll") + public ResponseEntity deleteAllFiles() { + log.info("Deleting all database backup files..."); + List> results = databaseService.deleteAllBackups(); + return getDeleteAllResults(results); + } + + private ResponseEntity getDeleteAllResults(List> results) { + if (results.isEmpty()) { + log.info("No backup files found to delete."); + return ResponseEntity.ok(new DeleteAllResult(List.of(), List.of(), "noContent")); + } + + List deleted = + results.stream() + .filter(p -> Boolean.TRUE.equals(p.getRight())) + .map(p -> p.getLeft().getFileName()) + .toList(); + + List failed = + results.stream() + .filter(p -> !Boolean.TRUE.equals(p.getRight())) + .map(p -> p.getLeft().getFileName()) + .toList(); + + log.info("Deleted backup files: {}", deleted); + if (!failed.isEmpty()) { + log.warn("Some backup files could not be deleted: {}", failed); + return ResponseEntity.status(HttpStatus.MULTI_STATUS) // 207 + .body(new DeleteAllResult(deleted, failed, "partialFailure")); + } + DeleteAllResult result = new DeleteAllResult(deleted, failed, "ok"); + log.debug( + "DeleteAllResult: deleted={}, failed={}, status={}", + result.deleted, + result.failed, + result.status); + return ResponseEntity.ok(result); // 200 + } + + private static final class DeleteAllResult { + public final List deleted; + public final List failed; + public final String status; + + public DeleteAllResult(List deleted, List failed, String status) { + this.deleted = deleted; + this.failed = failed; + this.status = status; + } + } +} diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java index 6474ae7ea..1a3f3ee9c 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseService.java @@ -5,6 +5,7 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.sql.Connection; import java.sql.PreparedStatement; @@ -21,6 +22,7 @@ import java.util.stream.Collectors; import javax.sql.DataSource; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.jdbc.datasource.init.CannotReadScriptException; import org.springframework.jdbc.datasource.init.ScriptException; import org.springframework.stereotype.Service; @@ -45,10 +47,39 @@ public class DatabaseService implements DatabaseServiceInterface { public DatabaseService( ApplicationProperties.Datasource datasourceProps, DataSource dataSource) { - this.BACKUP_DIR = - Paths.get(InstallationPathConfig.getConfigPath(), "db", "backup").normalize(); + this.BACKUP_DIR = Paths.get(InstallationPathConfig.getBackupPath()).normalize(); this.datasourceProps = datasourceProps; this.dataSource = dataSource; + moveBackupFiles(); + } + + /** Move all backup files from db/backup to backup/db */ + @Deprecated(since = "2.0.0", forRemoval = true) + private void moveBackupFiles() { + Path sourceDir = + Paths.get(InstallationPathConfig.getConfigPath(), "db", "backup").normalize(); + + if (!Files.exists(sourceDir)) { + log.info("Source directory does not exist: {}", sourceDir); + return; + } + + try { + Files.createDirectories(BACKUP_DIR); + try (DirectoryStream stream = Files.newDirectoryStream(sourceDir)) { + for (Path entry : stream) { + if (entry.getFileName().toString().startsWith(BACKUP_PREFIX) + && entry.getFileName().toString().endsWith(SQL_SUFFIX)) { + Files.move( + entry, + BACKUP_DIR.resolve(entry.getFileName()), + StandardCopyOption.REPLACE_EXISTING); + } + } + } + } catch (IOException e) { + log.error("Error moving backup files: {}", e.getMessage(), e); + } } /** @@ -198,6 +229,46 @@ public class DatabaseService implements DatabaseServiceInterface { } } + @Override + public List> deleteAllBackups() { + List backupList = this.getBackupList(); + List> deletedFiles = new ArrayList<>(); + + for (FileInfo backup : backupList) { + try { + Files.deleteIfExists(Paths.get(backup.getFilePath())); + deletedFiles.add(Pair.of(backup, true)); + } catch (IOException e) { + log.error("Error deleting backup file: {}", backup.getFileName(), e); + deletedFiles.add(Pair.of(backup, false)); + } + } + return deletedFiles; + } + + @Override + public List> deleteLastBackup() { + + List backupList = this.getBackupList(); + List> deletedFiles = new ArrayList<>(); + if (!backupList.isEmpty()) { + FileInfo lastBackup = backupList.get(backupList.size() - 1); + try { + Files.deleteIfExists(Paths.get(lastBackup.getFilePath())); + deletedFiles.add(Pair.of(lastBackup, true)); + } catch (IOException e) { + log.error("Error deleting last backup file: {}", lastBackup.getFileName(), e); + deletedFiles.add(Pair.of(lastBackup, false)); + } + } + return deletedFiles; + } + + /** + * Deletes the oldest backup file from the specified list. + * + * @param filteredBackupList the list of backup files + */ private static void deleteOldestBackup(List filteredBackupList) { try { filteredBackupList.sort( @@ -237,6 +308,11 @@ public class DatabaseService implements DatabaseServiceInterface { return version; } + /* + * Checks if the current datasource is H2. + * + * @return true if the datasource is H2, false otherwise + */ private boolean isH2Database() { boolean isTypeH2 = datasourceProps.getType().equalsIgnoreCase(ApplicationProperties.Driver.H2.name()); @@ -301,6 +377,11 @@ public class DatabaseService implements DatabaseServiceInterface { return filePath; } + /** + * Executes a database script. + * + * @param scriptPath the path to the script file + */ private void executeDatabaseScript(Path scriptPath) { if (isH2Database()) { String query = "RUNSCRIPT from ?;"; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseServiceInterface.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseServiceInterface.java index 613432f0a..d0ba033d6 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseServiceInterface.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/DatabaseServiceInterface.java @@ -3,6 +3,8 @@ package stirling.software.proprietary.security.service; import java.sql.SQLException; import java.util.List; +import org.apache.commons.lang3.tuple.Pair; + import stirling.software.common.model.FileInfo; import stirling.software.common.model.exception.UnsupportedProviderException; @@ -14,4 +16,8 @@ public interface DatabaseServiceInterface { boolean hasBackup(); List getBackupList(); + + List> deleteAllBackups(); + + List> deleteLastBackup(); } diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java index cc07fbbc7..eb02b6368 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/security/service/KeyPersistenceService.java @@ -1,9 +1,11 @@ package stirling.software.proprietary.security.service; import java.io.IOException; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -52,6 +54,34 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { this.verifyingKeyCache = cacheManager.getCache("verifyingKeys"); } + /** Move all key files from db/keys to backup/keys */ + @Deprecated(since = "2.0.0", forRemoval = true) + private void moveKeysToBackup() { + Path sourceDir = + Paths.get(InstallationPathConfig.getConfigPath(), "db", "keys").normalize(); + + if (!Files.exists(sourceDir)) { + log.info("Source directory does not exist: {}", sourceDir); + return; + } + + Path targetDir = Paths.get(InstallationPathConfig.getPrivateKeyPath()).normalize(); + + try { + Files.createDirectories(targetDir); + try (DirectoryStream stream = Files.newDirectoryStream(sourceDir)) { + for (Path entry : stream) { + Files.move( + entry, + targetDir.resolve(entry.getFileName()), + StandardCopyOption.REPLACE_EXISTING); + } + } + } catch (IOException e) { + log.error("Error moving key files to backup: {}", e.getMessage(), e); + } + } + @PostConstruct public void initializeKeystore() { if (!isKeystoreEnabled()) { @@ -59,6 +89,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface { } try { + moveKeysToBackup(); ensurePrivateKeyDirectoryExists(); loadKeyPair(); } catch (Exception e) { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/service/AuditService.java b/app/proprietary/src/main/java/stirling/software/proprietary/service/AuditService.java index 73b57286b..57ae13c18 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/service/AuditService.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/service/AuditService.java @@ -2,6 +2,7 @@ package stirling.software.proprietary.service; import java.util.Map; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.security.core.Authentication; @@ -29,8 +30,7 @@ public class AuditService { public AuditService( AuditEventRepository repository, AuditConfigurationProperties auditConfig, - @org.springframework.beans.factory.annotation.Qualifier("runningEE") - boolean runningEE) { + @Qualifier("runningEE") boolean runningEE) { this.repository = repository; this.auditConfig = auditConfig; this.runningEE = runningEE;