mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-21 19:59:24 +00:00
feat(common,core,proprietary): remove unused injections, enhance type safety, and improve test mocks (#4213)
# Description of Changes This PR introduces several refactorings and minor enhancements across the `common`, `core`, and `proprietary` modules: - **Dependency Injection Cleanup** - Removed unused constructor-injected dependencies (e.g., `FileOrUploadService`, `ApplicationProperties`, redundant `@Autowired` annotations). - Simplified constructors to only require actively used dependencies. - **Model Enhancements** - Added `@NoArgsConstructor` to `FileInfo`, `PdfMetadata`, and `SignatureFile` to improve serialization/deserialization support. - **Service Improvements** - Improved `JobExecutorService` content type retrieval by assigning `MediaType` to a variable before conversion. - Enhanced `KeyPersistenceService` with type-safe `.filter(JwtVerificationKey.class::isInstance)`. - Annotated `decodePublicKey` in `KeyPersistenceService` with `@Override` for clarity. - **Controller & API Changes** - Updated `AdminSettingsController` to use `TypeReference<Map<String,Object>>` for safer conversion. - Improved long log and description strings with consistent formatting. - **Testing Updates** - Replaced `.lenient()` mock settings with `.defaultAnswer(RETURNS_DEFAULTS)` for `FileToPdf` static mocks. - Used `ArgumentMatchers.<TypeReference<List<BookmarkItem>>>any()` in `EditTableOfContentsControllerTest` for type safety. - Updated `UserServiceTest` default `AuthenticationType` from `SSO` to `OAUTH2`. - **Formatting** - Broke up long log/debug lines for better readability. - Removed redundant `@SuppressWarnings` where type safety was ensured. These changes aim to make the codebase leaner, more type-safe, and maintainable, while improving test reliability. --- ## 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.
This commit is contained in:
parent
c10474fd30
commit
ab7cef5a97
@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.annotations.AutoJobPostMapping;
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.FileOrUploadService;
|
||||
import stirling.software.common.service.FileStorage;
|
||||
import stirling.software.common.service.JobExecutorService;
|
||||
|
||||
@ -34,7 +33,6 @@ public class AutoJobAspect {
|
||||
|
||||
private final JobExecutorService jobExecutorService;
|
||||
private final HttpServletRequest request;
|
||||
private final FileOrUploadService fileOrUploadService;
|
||||
private final FileStorage fileStorage;
|
||||
|
||||
@Around("@annotation(autoJobPostMapping)")
|
||||
@ -53,7 +51,8 @@ public class AutoJobAspect {
|
||||
boolean trackProgress = autoJobPostMapping.trackProgress();
|
||||
|
||||
log.debug(
|
||||
"AutoJobPostMapping execution with async={}, timeout={}, retryCount={}, trackProgress={}",
|
||||
"AutoJobPostMapping execution with async={}, timeout={}, retryCount={},"
|
||||
+ " trackProgress={}",
|
||||
async,
|
||||
timeout > 0 ? timeout : "default",
|
||||
retryCount,
|
||||
@ -148,7 +147,8 @@ public class AutoJobAspect {
|
||||
} catch (Throwable ex) {
|
||||
lastException = ex;
|
||||
log.error(
|
||||
"AutoJobAspect caught exception during job execution (attempt {}/{}): {}",
|
||||
"AutoJobAspect caught exception during job execution (attempt"
|
||||
+ " {}/{}): {}",
|
||||
currentAttempt,
|
||||
maxRetries,
|
||||
ex.getMessage(),
|
||||
|
@ -8,9 +8,11 @@ import java.util.Locale;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileInfo {
|
||||
private static final DateTimeFormatter DATE_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
@ -2,11 +2,15 @@ package stirling.software.common.model;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PdfMetadata {
|
||||
private String author;
|
||||
private String producer;
|
||||
|
@ -252,8 +252,10 @@ public class JobExecutorService {
|
||||
}
|
||||
}
|
||||
|
||||
if (response.getHeaders().getContentType() != null) {
|
||||
contentType = response.getHeaders().getContentType().toString();
|
||||
MediaType mediaType = response.getHeaders().getContentType();
|
||||
|
||||
if (mediaType != null) {
|
||||
contentType = mediaType.toString();
|
||||
}
|
||||
|
||||
// Store byte array directly to disk
|
||||
|
@ -4,7 +4,6 @@ import org.owasp.html.AttributePolicy;
|
||||
import org.owasp.html.HtmlPolicyBuilder;
|
||||
import org.owasp.html.PolicyFactory;
|
||||
import org.owasp.html.Sanitizers;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
@ -16,7 +15,6 @@ public class CustomHtmlSanitizer {
|
||||
private final SsrfProtectionService ssrfProtectionService;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired
|
||||
public CustomHtmlSanitizer(
|
||||
SsrfProtectionService ssrfProtectionService,
|
||||
ApplicationProperties applicationProperties) {
|
||||
|
@ -29,7 +29,6 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import stirling.software.common.aop.AutoJobAspect;
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.FileOrUploadService;
|
||||
import stirling.software.common.service.FileStorage;
|
||||
import stirling.software.common.service.JobExecutorService;
|
||||
import stirling.software.common.service.JobQueue;
|
||||
@ -44,8 +43,6 @@ class AutoJobPostMappingIntegrationTest {
|
||||
|
||||
@Mock private HttpServletRequest request;
|
||||
|
||||
@Mock private FileOrUploadService fileOrUploadService;
|
||||
|
||||
@Mock private FileStorage fileStorage;
|
||||
|
||||
@Mock private ResourceMonitor resourceMonitor;
|
||||
@ -54,8 +51,7 @@ class AutoJobPostMappingIntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
autoJobAspect =
|
||||
new AutoJobAspect(jobExecutorService, request, fileOrUploadService, fileStorage);
|
||||
autoJobAspect = new AutoJobAspect(jobExecutorService, request, fileStorage);
|
||||
}
|
||||
|
||||
@Mock private ProceedingJoinPoint joinPoint;
|
||||
|
@ -586,7 +586,10 @@ class EmlToPdfTest {
|
||||
when(mockPdDocument.getNumberOfPages()).thenReturn(1);
|
||||
|
||||
try (MockedStatic<FileToPdf> fileToPdf =
|
||||
mockStatic(FileToPdf.class, org.mockito.Mockito.withSettings().lenient())) {
|
||||
mockStatic(
|
||||
FileToPdf.class,
|
||||
org.mockito.Mockito.withSettings()
|
||||
.defaultAnswer(org.mockito.Answers.RETURNS_DEFAULTS))) {
|
||||
fileToPdf
|
||||
.when(
|
||||
() ->
|
||||
@ -657,7 +660,10 @@ class EmlToPdfTest {
|
||||
when(mockPdDocument.getNumberOfPages()).thenReturn(1);
|
||||
|
||||
try (MockedStatic<FileToPdf> fileToPdf =
|
||||
mockStatic(FileToPdf.class, org.mockito.Mockito.withSettings().lenient())) {
|
||||
mockStatic(
|
||||
FileToPdf.class,
|
||||
org.mockito.Mockito.withSettings()
|
||||
.defaultAnswer(org.mockito.Answers.RETURNS_DEFAULTS))) {
|
||||
fileToPdf
|
||||
.when(
|
||||
() ->
|
||||
@ -724,7 +730,10 @@ class EmlToPdfTest {
|
||||
String errorMessage = "Conversion failed";
|
||||
|
||||
try (MockedStatic<FileToPdf> fileToPdf =
|
||||
mockStatic(FileToPdf.class, org.mockito.Mockito.withSettings().lenient())) {
|
||||
mockStatic(
|
||||
FileToPdf.class,
|
||||
org.mockito.Mockito.withSettings()
|
||||
.defaultAnswer(org.mockito.Answers.RETURNS_DEFAULTS))) {
|
||||
fileToPdf
|
||||
.when(
|
||||
() ->
|
||||
|
@ -27,7 +27,6 @@ import stirling.software.SPDF.UI.WebBrowser;
|
||||
import stirling.software.common.configuration.AppConfig;
|
||||
import stirling.software.common.configuration.ConfigInitializer;
|
||||
import stirling.software.common.configuration.InstallationPathConfig;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.util.UrlUtils;
|
||||
|
||||
@Slf4j
|
||||
@ -46,17 +45,14 @@ public class SPDFApplication {
|
||||
|
||||
private final AppConfig appConfig;
|
||||
private final Environment env;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final WebBrowser webBrowser;
|
||||
|
||||
public SPDFApplication(
|
||||
AppConfig appConfig,
|
||||
Environment env,
|
||||
ApplicationProperties applicationProperties,
|
||||
@Autowired(required = false) WebBrowser webBrowser) {
|
||||
this.appConfig = appConfig;
|
||||
this.env = env;
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.webBrowser = webBrowser;
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
@ -18,11 +16,12 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class EndpointInspector implements ApplicationListener<ContextRefreshedEvent> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(EndpointInspector.class);
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final Set<String> validGetEndpoints = new HashSet<>();
|
||||
@ -71,13 +70,13 @@ public class EndpointInspector implements ApplicationListener<ContextRefreshedEv
|
||||
}
|
||||
|
||||
if (validGetEndpoints.isEmpty()) {
|
||||
logger.warn("No endpoints discovered. Adding common endpoints as fallback.");
|
||||
log.warn("No endpoints discovered. Adding common endpoints as fallback.");
|
||||
validGetEndpoints.add("/");
|
||||
validGetEndpoints.add("/api/**");
|
||||
validGetEndpoints.add("/**");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Error discovering endpoints", e);
|
||||
log.error("Error discovering endpoints", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,10 +202,10 @@ public class EndpointInspector implements ApplicationListener<ContextRefreshedEv
|
||||
private void logAllEndpoints() {
|
||||
Set<String> sortedEndpoints = new TreeSet<>(validGetEndpoints);
|
||||
|
||||
logger.info("=== BEGIN: All discovered GET endpoints ===");
|
||||
log.info("=== BEGIN: All discovered GET endpoints ===");
|
||||
for (String endpoint : sortedEndpoints) {
|
||||
logger.info("Endpoint: {}", endpoint);
|
||||
log.info("Endpoint: {}", endpoint);
|
||||
}
|
||||
logger.info("=== END: All discovered GET endpoints ===");
|
||||
log.info("=== END: All discovered GET endpoints ===");
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package stirling.software.SPDF.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SignatureFile {
|
||||
private String fileName;
|
||||
|
@ -4,8 +4,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -13,15 +11,15 @@ import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.search.Search;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.EndpointInspector;
|
||||
import stirling.software.common.service.PostHogService;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MetricsAggregatorService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MetricsAggregatorService.class);
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final PostHogService postHogService;
|
||||
private final EndpointInspector endpointInspector;
|
||||
@ -66,7 +64,7 @@ public class MetricsAggregatorService {
|
||||
if ("GET".equals(method)
|
||||
&& validateGetEndpoints
|
||||
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||
logger.debug("Skipping invalid GET endpoint: {}", uri);
|
||||
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -77,7 +75,7 @@ public class MetricsAggregatorService {
|
||||
double lastCount = lastSentMetrics.getOrDefault(key, 0.0);
|
||||
double difference = currentCount - lastCount;
|
||||
if (difference > 0) {
|
||||
logger.debug("{}, {}", key, difference);
|
||||
log.debug("{}, {}", key, difference);
|
||||
metrics.put(key, difference);
|
||||
lastSentMetrics.put(key, currentCount);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@ -202,7 +203,9 @@ class EditTableOfContentsControllerTest {
|
||||
bookmarks.add(bookmark);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(objectMapper.readValue(eq(request.getBookmarkData()), any(TypeReference.class)))
|
||||
when(objectMapper.readValue(
|
||||
eq(request.getBookmarkData()),
|
||||
ArgumentMatchers.<TypeReference<List<BookmarkItem>>>any()))
|
||||
.thenReturn(bookmarks);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||
@ -242,7 +245,8 @@ class EditTableOfContentsControllerTest {
|
||||
request.setFileInput(mockFile);
|
||||
|
||||
String bookmarkJson =
|
||||
"[{\"title\":\"Chapter 1\",\"pageNumber\":1,\"children\":[{\"title\":\"Section 1.1\",\"pageNumber\":2,\"children\":[]}]}]";
|
||||
"[{\"title\":\"Chapter 1\",\"pageNumber\":1,\"children\":[{\"title\":\"Section"
|
||||
+ " 1.1\",\"pageNumber\":2,\"children\":[]}]}]";
|
||||
request.setBookmarkData(bookmarkJson);
|
||||
|
||||
List<BookmarkItem> bookmarks = new ArrayList<>();
|
||||
@ -261,7 +265,9 @@ class EditTableOfContentsControllerTest {
|
||||
bookmarks.add(parentBookmark);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(objectMapper.readValue(eq(bookmarkJson), any(TypeReference.class)))
|
||||
when(objectMapper.readValue(
|
||||
eq(bookmarkJson),
|
||||
ArgumentMatchers.<TypeReference<List<BookmarkItem>>>any()))
|
||||
.thenReturn(bookmarks);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||
@ -292,7 +298,8 @@ class EditTableOfContentsControllerTest {
|
||||
EditTableOfContentsRequest request = new EditTableOfContentsRequest();
|
||||
request.setFileInput(mockFile);
|
||||
request.setBookmarkData(
|
||||
"[{\"title\":\"Chapter 1\",\"pageNumber\":-5,\"children\":[]},{\"title\":\"Chapter 2\",\"pageNumber\":100,\"children\":[]}]");
|
||||
"[{\"title\":\"Chapter 1\",\"pageNumber\":-5,\"children\":[]},{\"title\":\"Chapter"
|
||||
+ " 2\",\"pageNumber\":100,\"children\":[]}]");
|
||||
|
||||
List<BookmarkItem> bookmarks = new ArrayList<>();
|
||||
|
||||
@ -310,7 +317,9 @@ class EditTableOfContentsControllerTest {
|
||||
bookmarks.add(bookmark2);
|
||||
|
||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
||||
when(objectMapper.readValue(eq(request.getBookmarkData()), any(TypeReference.class)))
|
||||
when(objectMapper.readValue(
|
||||
eq(request.getBookmarkData()),
|
||||
ArgumentMatchers.<TypeReference<List<BookmarkItem>>>any()))
|
||||
.thenReturn(bookmarks);
|
||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||
|
@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@ -81,7 +82,8 @@ public class AdminSettingsController {
|
||||
@Operation(
|
||||
summary = "Get all application settings",
|
||||
description =
|
||||
"Retrieve all current application settings. Use includePending=true to include settings that will take effect after restart. Admin access required.")
|
||||
"Retrieve all current application settings. Use includePending=true to include"
|
||||
+ " settings that will take effect after restart. Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "200", description = "Settings retrieved successfully"),
|
||||
@ -95,7 +97,9 @@ public class AdminSettingsController {
|
||||
log.debug("Admin requested all application settings (includePending={})", includePending);
|
||||
|
||||
// Convert ApplicationProperties to Map
|
||||
Map<String, Object> settings = objectMapper.convertValue(applicationProperties, Map.class);
|
||||
Map<String, Object> settings =
|
||||
objectMapper.convertValue(
|
||||
applicationProperties, new TypeReference<Map<String, Object>>() {});
|
||||
|
||||
if (includePending && !pendingChanges.isEmpty()) {
|
||||
// Merge pending changes into the settings map
|
||||
@ -112,7 +116,8 @@ public class AdminSettingsController {
|
||||
@Operation(
|
||||
summary = "Get pending settings changes",
|
||||
description =
|
||||
"Retrieve settings that have been modified but not yet applied (require restart). Admin access required.")
|
||||
"Retrieve settings that have been modified but not yet applied (require"
|
||||
+ " restart). Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
@ -137,7 +142,8 @@ public class AdminSettingsController {
|
||||
@Operation(
|
||||
summary = "Update application settings (delta updates)",
|
||||
description =
|
||||
"Update specific application settings using dot notation keys. Only sends changed values. Changes take effect on restart. Admin access required.")
|
||||
"Update specific application settings using dot notation keys. Only sends"
|
||||
+ " changed values. Changes take effect on restart. Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "200", description = "Settings updated successfully"),
|
||||
@ -178,7 +184,8 @@ public class AdminSettingsController {
|
||||
|
||||
return ResponseEntity.ok(
|
||||
String.format(
|
||||
"Successfully updated %d setting(s). Changes will take effect on application restart.",
|
||||
"Successfully updated %d setting(s). Changes will take effect on"
|
||||
+ " application restart.",
|
||||
updatedCount));
|
||||
|
||||
} catch (IOException e) {
|
||||
@ -199,7 +206,8 @@ public class AdminSettingsController {
|
||||
@Operation(
|
||||
summary = "Get specific settings section",
|
||||
description =
|
||||
"Retrieve settings for a specific section (e.g., security, system, ui). Admin access required.")
|
||||
"Retrieve settings for a specific section (e.g., security, system, ui). Admin"
|
||||
+ " access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
@ -288,7 +296,8 @@ public class AdminSettingsController {
|
||||
String escapedSectionName = HtmlUtils.htmlEscape(sectionName);
|
||||
return ResponseEntity.ok(
|
||||
String.format(
|
||||
"Successfully updated %d setting(s) in section '%s'. Changes will take effect on application restart.",
|
||||
"Successfully updated %d setting(s) in section '%s'. Changes will take"
|
||||
+ " effect on application restart.",
|
||||
updatedCount, escapedSectionName));
|
||||
|
||||
} catch (IOException e) {
|
||||
@ -308,7 +317,8 @@ public class AdminSettingsController {
|
||||
@Operation(
|
||||
summary = "Get specific setting value",
|
||||
description =
|
||||
"Retrieve value for a specific setting key using dot notation. Admin access required.")
|
||||
"Retrieve value for a specific setting key using dot notation. Admin access"
|
||||
+ " required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
@ -348,7 +358,8 @@ public class AdminSettingsController {
|
||||
@Operation(
|
||||
summary = "Update specific setting value",
|
||||
description =
|
||||
"Update value for a specific setting key using dot notation. Admin access required.")
|
||||
"Update value for a specific setting key using dot notation. Admin access"
|
||||
+ " required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "200", description = "Setting updated successfully"),
|
||||
@ -376,7 +387,8 @@ public class AdminSettingsController {
|
||||
String escapedKey = HtmlUtils.htmlEscape(key);
|
||||
return ResponseEntity.ok(
|
||||
String.format(
|
||||
"Successfully updated setting '%s'. Changes will take effect on application restart.",
|
||||
"Successfully updated setting '%s'. Changes will take effect on"
|
||||
+ " application restart.",
|
||||
escapedKey));
|
||||
|
||||
} catch (IOException e) {
|
||||
@ -532,7 +544,6 @@ public class AdminSettingsController {
|
||||
* Recursively mask sensitive fields in settings map. Sensitive fields are replaced with a
|
||||
* status indicator showing if they're configured.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> maskSensitiveFields(Map<String, Object> settings) {
|
||||
return maskSensitiveFieldsWithPath(settings, "");
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
// Check if the authenticated user is disabled and invalidate their session if so
|
||||
if (authentication != null && authentication.isAuthenticated()) {
|
||||
|
||||
LoginMethod loginMethod = LoginMethod.UNKNOWN;
|
||||
UserLoginType loginMethod = UserLoginType.UNKNOWN;
|
||||
|
||||
boolean blockRegistration = false;
|
||||
|
||||
@ -137,20 +137,20 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
String username = null;
|
||||
if (principal instanceof UserDetails detailsUser) {
|
||||
username = detailsUser.getUsername();
|
||||
loginMethod = LoginMethod.USERDETAILS;
|
||||
loginMethod = UserLoginType.USERDETAILS;
|
||||
} else if (principal instanceof OAuth2User oAuth2User) {
|
||||
username = oAuth2User.getName();
|
||||
loginMethod = LoginMethod.OAUTH2USER;
|
||||
loginMethod = UserLoginType.OAUTH2USER;
|
||||
OAUTH2 oAuth = securityProp.getOauth2();
|
||||
blockRegistration = oAuth != null && oAuth.getBlockRegistration();
|
||||
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
|
||||
username = saml2User.name();
|
||||
loginMethod = LoginMethod.SAML2USER;
|
||||
loginMethod = UserLoginType.SAML2USER;
|
||||
SAML2 saml2 = securityProp.getSaml2();
|
||||
blockRegistration = saml2 != null && saml2.getBlockRegistration();
|
||||
} else if (principal instanceof String stringUser) {
|
||||
username = stringUser;
|
||||
loginMethod = LoginMethod.STRINGUSER;
|
||||
loginMethod = UserLoginType.STRINGUSER;
|
||||
}
|
||||
|
||||
// Retrieve all active sessions for the user
|
||||
@ -164,8 +164,8 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
boolean isUserDisabled = userService.isUserDisabled(username);
|
||||
|
||||
boolean notSsoLogin =
|
||||
!LoginMethod.OAUTH2USER.equals(loginMethod)
|
||||
&& !LoginMethod.SAML2USER.equals(loginMethod);
|
||||
!UserLoginType.OAUTH2USER.equals(loginMethod)
|
||||
&& !UserLoginType.SAML2USER.equals(loginMethod);
|
||||
|
||||
// Block user registration if not allowed by configuration
|
||||
if (blockRegistration && !isUserExists) {
|
||||
@ -200,7 +200,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
private enum LoginMethod {
|
||||
private enum UserLoginType {
|
||||
USERDETAILS("UserDetails"),
|
||||
OAUTH2USER("OAuth2User"),
|
||||
STRINGUSER("StringUser"),
|
||||
@ -209,7 +209,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private String method;
|
||||
|
||||
LoginMethod(String method) {
|
||||
UserLoginType(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseCookie;
|
||||
@ -53,7 +52,6 @@ public class JwtService implements JwtServiceInterface {
|
||||
private final KeyPersistenceServiceInterface keyPersistenceService;
|
||||
private final boolean v2Enabled;
|
||||
|
||||
@Autowired
|
||||
public JwtService(
|
||||
@Qualifier("v2Enabled") boolean v2Enabled,
|
||||
KeyPersistenceServiceInterface keyPersistenceService) {
|
||||
@ -155,7 +153,8 @@ public class JwtService implements JwtServiceInterface {
|
||||
keyPair = specificKeyPair.get();
|
||||
} else {
|
||||
log.warn(
|
||||
"Key ID {} not found in keystore, token may have been signed with an expired key",
|
||||
"Key ID {} not found in keystore, token may have been signed with an"
|
||||
+ " expired key",
|
||||
keyId);
|
||||
|
||||
if (keyId.equals(keyPersistenceService.getActiveKey().getKeyId())) {
|
||||
|
@ -8,7 +8,6 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -30,7 +29,6 @@ public class KeyPairCleanupService {
|
||||
private final KeyPersistenceService keyPersistenceService;
|
||||
private final ApplicationProperties.Security.Jwt jwtProperties;
|
||||
|
||||
@Autowired
|
||||
public KeyPairCleanupService(
|
||||
KeyPersistenceService keyPersistenceService,
|
||||
ApplicationProperties applicationProperties) {
|
||||
|
@ -20,7 +20,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
@ -43,16 +42,13 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
public static final String KEY_SUFFIX = ".key";
|
||||
|
||||
private final ApplicationProperties.Security.Jwt jwtProperties;
|
||||
private final CacheManager cacheManager;
|
||||
private final Cache verifyingKeyCache;
|
||||
|
||||
private volatile JwtVerificationKey activeKey;
|
||||
|
||||
@Autowired
|
||||
public KeyPersistenceService(
|
||||
ApplicationProperties applicationProperties, CacheManager cacheManager) {
|
||||
this.jwtProperties = applicationProperties.getSecurity().getJwt();
|
||||
this.cacheManager = cacheManager;
|
||||
this.verifyingKeyCache = cacheManager.getCache("verifyingKeys");
|
||||
}
|
||||
|
||||
@ -159,7 +155,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
nativeCache.asMap().size());
|
||||
|
||||
return nativeCache.asMap().values().stream()
|
||||
.filter(value -> value instanceof JwtVerificationKey)
|
||||
.filter(JwtVerificationKey.class::isInstance)
|
||||
.map(value -> (JwtVerificationKey) value)
|
||||
.filter(
|
||||
key -> {
|
||||
@ -233,6 +229,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
||||
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey decodePublicKey(String encodedKey)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
|
||||
|
@ -189,7 +189,7 @@ class UserServiceTest {
|
||||
void testSaveUser_WithValidEmail_Success() throws Exception {
|
||||
// Given
|
||||
String emailUsername = "test@example.com";
|
||||
AuthenticationType authType = AuthenticationType.SSO;
|
||||
AuthenticationType authType = AuthenticationType.OAUTH2;
|
||||
|
||||
when(teamRepository.findByName("Default")).thenReturn(Optional.of(mockTeam));
|
||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||
|
Loading…
x
Reference in New Issue
Block a user