mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-22 12:19: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.annotations.AutoJobPostMapping;
|
||||||
import stirling.software.common.model.api.PDFFile;
|
import stirling.software.common.model.api.PDFFile;
|
||||||
import stirling.software.common.service.FileOrUploadService;
|
|
||||||
import stirling.software.common.service.FileStorage;
|
import stirling.software.common.service.FileStorage;
|
||||||
import stirling.software.common.service.JobExecutorService;
|
import stirling.software.common.service.JobExecutorService;
|
||||||
|
|
||||||
@ -34,7 +33,6 @@ public class AutoJobAspect {
|
|||||||
|
|
||||||
private final JobExecutorService jobExecutorService;
|
private final JobExecutorService jobExecutorService;
|
||||||
private final HttpServletRequest request;
|
private final HttpServletRequest request;
|
||||||
private final FileOrUploadService fileOrUploadService;
|
|
||||||
private final FileStorage fileStorage;
|
private final FileStorage fileStorage;
|
||||||
|
|
||||||
@Around("@annotation(autoJobPostMapping)")
|
@Around("@annotation(autoJobPostMapping)")
|
||||||
@ -53,7 +51,8 @@ public class AutoJobAspect {
|
|||||||
boolean trackProgress = autoJobPostMapping.trackProgress();
|
boolean trackProgress = autoJobPostMapping.trackProgress();
|
||||||
|
|
||||||
log.debug(
|
log.debug(
|
||||||
"AutoJobPostMapping execution with async={}, timeout={}, retryCount={}, trackProgress={}",
|
"AutoJobPostMapping execution with async={}, timeout={}, retryCount={},"
|
||||||
|
+ " trackProgress={}",
|
||||||
async,
|
async,
|
||||||
timeout > 0 ? timeout : "default",
|
timeout > 0 ? timeout : "default",
|
||||||
retryCount,
|
retryCount,
|
||||||
@ -148,7 +147,8 @@ public class AutoJobAspect {
|
|||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
lastException = ex;
|
lastException = ex;
|
||||||
log.error(
|
log.error(
|
||||||
"AutoJobAspect caught exception during job execution (attempt {}/{}): {}",
|
"AutoJobAspect caught exception during job execution (attempt"
|
||||||
|
+ " {}/{}): {}",
|
||||||
currentAttempt,
|
currentAttempt,
|
||||||
maxRetries,
|
maxRetries,
|
||||||
ex.getMessage(),
|
ex.getMessage(),
|
||||||
|
@ -8,9 +8,11 @@ import java.util.Locale;
|
|||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class FileInfo {
|
public class FileInfo {
|
||||||
private static final DateTimeFormatter DATE_FORMATTER =
|
private static final DateTimeFormatter DATE_FORMATTER =
|
||||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
@ -2,11 +2,15 @@ package stirling.software.common.model;
|
|||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class PdfMetadata {
|
public class PdfMetadata {
|
||||||
private String author;
|
private String author;
|
||||||
private String producer;
|
private String producer;
|
||||||
|
@ -252,8 +252,10 @@ public class JobExecutorService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.getHeaders().getContentType() != null) {
|
MediaType mediaType = response.getHeaders().getContentType();
|
||||||
contentType = response.getHeaders().getContentType().toString();
|
|
||||||
|
if (mediaType != null) {
|
||||||
|
contentType = mediaType.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store byte array directly to disk
|
// Store byte array directly to disk
|
||||||
|
@ -4,7 +4,6 @@ import org.owasp.html.AttributePolicy;
|
|||||||
import org.owasp.html.HtmlPolicyBuilder;
|
import org.owasp.html.HtmlPolicyBuilder;
|
||||||
import org.owasp.html.PolicyFactory;
|
import org.owasp.html.PolicyFactory;
|
||||||
import org.owasp.html.Sanitizers;
|
import org.owasp.html.Sanitizers;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
@ -16,7 +15,6 @@ public class CustomHtmlSanitizer {
|
|||||||
private final SsrfProtectionService ssrfProtectionService;
|
private final SsrfProtectionService ssrfProtectionService;
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public CustomHtmlSanitizer(
|
public CustomHtmlSanitizer(
|
||||||
SsrfProtectionService ssrfProtectionService,
|
SsrfProtectionService ssrfProtectionService,
|
||||||
ApplicationProperties applicationProperties) {
|
ApplicationProperties applicationProperties) {
|
||||||
|
@ -29,7 +29,6 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
|
|
||||||
import stirling.software.common.aop.AutoJobAspect;
|
import stirling.software.common.aop.AutoJobAspect;
|
||||||
import stirling.software.common.model.api.PDFFile;
|
import stirling.software.common.model.api.PDFFile;
|
||||||
import stirling.software.common.service.FileOrUploadService;
|
|
||||||
import stirling.software.common.service.FileStorage;
|
import stirling.software.common.service.FileStorage;
|
||||||
import stirling.software.common.service.JobExecutorService;
|
import stirling.software.common.service.JobExecutorService;
|
||||||
import stirling.software.common.service.JobQueue;
|
import stirling.software.common.service.JobQueue;
|
||||||
@ -44,8 +43,6 @@ class AutoJobPostMappingIntegrationTest {
|
|||||||
|
|
||||||
@Mock private HttpServletRequest request;
|
@Mock private HttpServletRequest request;
|
||||||
|
|
||||||
@Mock private FileOrUploadService fileOrUploadService;
|
|
||||||
|
|
||||||
@Mock private FileStorage fileStorage;
|
@Mock private FileStorage fileStorage;
|
||||||
|
|
||||||
@Mock private ResourceMonitor resourceMonitor;
|
@Mock private ResourceMonitor resourceMonitor;
|
||||||
@ -54,8 +51,7 @@ class AutoJobPostMappingIntegrationTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
autoJobAspect =
|
autoJobAspect = new AutoJobAspect(jobExecutorService, request, fileStorage);
|
||||||
new AutoJobAspect(jobExecutorService, request, fileOrUploadService, fileStorage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mock private ProceedingJoinPoint joinPoint;
|
@Mock private ProceedingJoinPoint joinPoint;
|
||||||
|
@ -586,7 +586,10 @@ class EmlToPdfTest {
|
|||||||
when(mockPdDocument.getNumberOfPages()).thenReturn(1);
|
when(mockPdDocument.getNumberOfPages()).thenReturn(1);
|
||||||
|
|
||||||
try (MockedStatic<FileToPdf> fileToPdf =
|
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
|
fileToPdf
|
||||||
.when(
|
.when(
|
||||||
() ->
|
() ->
|
||||||
@ -657,7 +660,10 @@ class EmlToPdfTest {
|
|||||||
when(mockPdDocument.getNumberOfPages()).thenReturn(1);
|
when(mockPdDocument.getNumberOfPages()).thenReturn(1);
|
||||||
|
|
||||||
try (MockedStatic<FileToPdf> fileToPdf =
|
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
|
fileToPdf
|
||||||
.when(
|
.when(
|
||||||
() ->
|
() ->
|
||||||
@ -724,7 +730,10 @@ class EmlToPdfTest {
|
|||||||
String errorMessage = "Conversion failed";
|
String errorMessage = "Conversion failed";
|
||||||
|
|
||||||
try (MockedStatic<FileToPdf> fileToPdf =
|
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
|
fileToPdf
|
||||||
.when(
|
.when(
|
||||||
() ->
|
() ->
|
||||||
|
@ -27,7 +27,6 @@ import stirling.software.SPDF.UI.WebBrowser;
|
|||||||
import stirling.software.common.configuration.AppConfig;
|
import stirling.software.common.configuration.AppConfig;
|
||||||
import stirling.software.common.configuration.ConfigInitializer;
|
import stirling.software.common.configuration.ConfigInitializer;
|
||||||
import stirling.software.common.configuration.InstallationPathConfig;
|
import stirling.software.common.configuration.InstallationPathConfig;
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
|
||||||
import stirling.software.common.util.UrlUtils;
|
import stirling.software.common.util.UrlUtils;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -46,17 +45,14 @@ public class SPDFApplication {
|
|||||||
|
|
||||||
private final AppConfig appConfig;
|
private final AppConfig appConfig;
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
private final ApplicationProperties applicationProperties;
|
|
||||||
private final WebBrowser webBrowser;
|
private final WebBrowser webBrowser;
|
||||||
|
|
||||||
public SPDFApplication(
|
public SPDFApplication(
|
||||||
AppConfig appConfig,
|
AppConfig appConfig,
|
||||||
Environment env,
|
Environment env,
|
||||||
ApplicationProperties applicationProperties,
|
|
||||||
@Autowired(required = false) WebBrowser webBrowser) {
|
@Autowired(required = false) WebBrowser webBrowser) {
|
||||||
this.appConfig = appConfig;
|
this.appConfig = appConfig;
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.applicationProperties = applicationProperties;
|
|
||||||
this.webBrowser = webBrowser;
|
this.webBrowser = webBrowser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.context.event.ContextRefreshedEvent;
|
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 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class EndpointInspector implements ApplicationListener<ContextRefreshedEvent> {
|
public class EndpointInspector implements ApplicationListener<ContextRefreshedEvent> {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(EndpointInspector.class);
|
|
||||||
|
|
||||||
private final ApplicationContext applicationContext;
|
private final ApplicationContext applicationContext;
|
||||||
private final Set<String> validGetEndpoints = new HashSet<>();
|
private final Set<String> validGetEndpoints = new HashSet<>();
|
||||||
@ -71,13 +70,13 @@ public class EndpointInspector implements ApplicationListener<ContextRefreshedEv
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (validGetEndpoints.isEmpty()) {
|
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("/");
|
||||||
validGetEndpoints.add("/api/**");
|
validGetEndpoints.add("/api/**");
|
||||||
validGetEndpoints.add("/**");
|
validGetEndpoints.add("/**");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} 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() {
|
private void logAllEndpoints() {
|
||||||
Set<String> sortedEndpoints = new TreeSet<>(validGetEndpoints);
|
Set<String> sortedEndpoints = new TreeSet<>(validGetEndpoints);
|
||||||
|
|
||||||
logger.info("=== BEGIN: All discovered GET endpoints ===");
|
log.info("=== BEGIN: All discovered GET endpoints ===");
|
||||||
for (String endpoint : sortedEndpoints) {
|
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.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class SignatureFile {
|
public class SignatureFile {
|
||||||
private String fileName;
|
private String fileName;
|
||||||
|
@ -4,8 +4,6 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@ -13,15 +11,15 @@ import io.micrometer.core.instrument.MeterRegistry;
|
|||||||
import io.micrometer.core.instrument.search.Search;
|
import io.micrometer.core.instrument.search.Search;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.SPDF.config.EndpointInspector;
|
import stirling.software.SPDF.config.EndpointInspector;
|
||||||
import stirling.software.common.service.PostHogService;
|
import stirling.software.common.service.PostHogService;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
public class MetricsAggregatorService {
|
public class MetricsAggregatorService {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MetricsAggregatorService.class);
|
|
||||||
|
|
||||||
private final MeterRegistry meterRegistry;
|
private final MeterRegistry meterRegistry;
|
||||||
private final PostHogService postHogService;
|
private final PostHogService postHogService;
|
||||||
private final EndpointInspector endpointInspector;
|
private final EndpointInspector endpointInspector;
|
||||||
@ -66,7 +64,7 @@ public class MetricsAggregatorService {
|
|||||||
if ("GET".equals(method)
|
if ("GET".equals(method)
|
||||||
&& validateGetEndpoints
|
&& validateGetEndpoints
|
||||||
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||||
logger.debug("Skipping invalid GET endpoint: {}", uri);
|
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +75,7 @@ public class MetricsAggregatorService {
|
|||||||
double lastCount = lastSentMetrics.getOrDefault(key, 0.0);
|
double lastCount = lastSentMetrics.getOrDefault(key, 0.0);
|
||||||
double difference = currentCount - lastCount;
|
double difference = currentCount - lastCount;
|
||||||
if (difference > 0) {
|
if (difference > 0) {
|
||||||
logger.debug("{}, {}", key, difference);
|
log.debug("{}, {}", key, difference);
|
||||||
metrics.put(key, difference);
|
metrics.put(key, difference);
|
||||||
lastSentMetrics.put(key, currentCount);
|
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.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.ArgumentMatchers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
@ -202,7 +203,9 @@ class EditTableOfContentsControllerTest {
|
|||||||
bookmarks.add(bookmark);
|
bookmarks.add(bookmark);
|
||||||
|
|
||||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
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);
|
.thenReturn(bookmarks);
|
||||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||||
@ -242,7 +245,8 @@ class EditTableOfContentsControllerTest {
|
|||||||
request.setFileInput(mockFile);
|
request.setFileInput(mockFile);
|
||||||
|
|
||||||
String bookmarkJson =
|
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);
|
request.setBookmarkData(bookmarkJson);
|
||||||
|
|
||||||
List<BookmarkItem> bookmarks = new ArrayList<>();
|
List<BookmarkItem> bookmarks = new ArrayList<>();
|
||||||
@ -261,7 +265,9 @@ class EditTableOfContentsControllerTest {
|
|||||||
bookmarks.add(parentBookmark);
|
bookmarks.add(parentBookmark);
|
||||||
|
|
||||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
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);
|
.thenReturn(bookmarks);
|
||||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
||||||
@ -292,7 +298,8 @@ class EditTableOfContentsControllerTest {
|
|||||||
EditTableOfContentsRequest request = new EditTableOfContentsRequest();
|
EditTableOfContentsRequest request = new EditTableOfContentsRequest();
|
||||||
request.setFileInput(mockFile);
|
request.setFileInput(mockFile);
|
||||||
request.setBookmarkData(
|
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<>();
|
List<BookmarkItem> bookmarks = new ArrayList<>();
|
||||||
|
|
||||||
@ -310,7 +317,9 @@ class EditTableOfContentsControllerTest {
|
|||||||
bookmarks.add(bookmark2);
|
bookmarks.add(bookmark2);
|
||||||
|
|
||||||
when(pdfDocumentFactory.load(mockFile)).thenReturn(mockDocument);
|
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);
|
.thenReturn(bookmarks);
|
||||||
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
when(mockDocument.getDocumentCatalog()).thenReturn(mockCatalog);
|
||||||
when(mockDocument.getNumberOfPages()).thenReturn(5);
|
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.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.util.HtmlUtils;
|
import org.springframework.web.util.HtmlUtils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@ -81,7 +82,8 @@ public class AdminSettingsController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Get all application settings",
|
summary = "Get all application settings",
|
||||||
description =
|
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(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(responseCode = "200", description = "Settings retrieved successfully"),
|
@ApiResponse(responseCode = "200", description = "Settings retrieved successfully"),
|
||||||
@ -95,7 +97,9 @@ public class AdminSettingsController {
|
|||||||
log.debug("Admin requested all application settings (includePending={})", includePending);
|
log.debug("Admin requested all application settings (includePending={})", includePending);
|
||||||
|
|
||||||
// Convert ApplicationProperties to Map
|
// 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()) {
|
if (includePending && !pendingChanges.isEmpty()) {
|
||||||
// Merge pending changes into the settings map
|
// Merge pending changes into the settings map
|
||||||
@ -112,7 +116,8 @@ public class AdminSettingsController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Get pending settings changes",
|
summary = "Get pending settings changes",
|
||||||
description =
|
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(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@ -137,7 +142,8 @@ public class AdminSettingsController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Update application settings (delta updates)",
|
summary = "Update application settings (delta updates)",
|
||||||
description =
|
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(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(responseCode = "200", description = "Settings updated successfully"),
|
@ApiResponse(responseCode = "200", description = "Settings updated successfully"),
|
||||||
@ -178,7 +184,8 @@ public class AdminSettingsController {
|
|||||||
|
|
||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
String.format(
|
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));
|
updatedCount));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -199,7 +206,8 @@ public class AdminSettingsController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Get specific settings section",
|
summary = "Get specific settings section",
|
||||||
description =
|
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(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@ -288,7 +296,8 @@ public class AdminSettingsController {
|
|||||||
String escapedSectionName = HtmlUtils.htmlEscape(sectionName);
|
String escapedSectionName = HtmlUtils.htmlEscape(sectionName);
|
||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
String.format(
|
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));
|
updatedCount, escapedSectionName));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -308,7 +317,8 @@ public class AdminSettingsController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Get specific setting value",
|
summary = "Get specific setting value",
|
||||||
description =
|
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(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
@ -348,7 +358,8 @@ public class AdminSettingsController {
|
|||||||
@Operation(
|
@Operation(
|
||||||
summary = "Update specific setting value",
|
summary = "Update specific setting value",
|
||||||
description =
|
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(
|
@ApiResponses(
|
||||||
value = {
|
value = {
|
||||||
@ApiResponse(responseCode = "200", description = "Setting updated successfully"),
|
@ApiResponse(responseCode = "200", description = "Setting updated successfully"),
|
||||||
@ -376,7 +387,8 @@ public class AdminSettingsController {
|
|||||||
String escapedKey = HtmlUtils.htmlEscape(key);
|
String escapedKey = HtmlUtils.htmlEscape(key);
|
||||||
return ResponseEntity.ok(
|
return ResponseEntity.ok(
|
||||||
String.format(
|
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));
|
escapedKey));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -532,7 +544,6 @@ public class AdminSettingsController {
|
|||||||
* Recursively mask sensitive fields in settings map. Sensitive fields are replaced with a
|
* Recursively mask sensitive fields in settings map. Sensitive fields are replaced with a
|
||||||
* status indicator showing if they're configured.
|
* status indicator showing if they're configured.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private Map<String, Object> maskSensitiveFields(Map<String, Object> settings) {
|
private Map<String, Object> maskSensitiveFields(Map<String, Object> settings) {
|
||||||
return maskSensitiveFieldsWithPath(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
|
// Check if the authenticated user is disabled and invalidate their session if so
|
||||||
if (authentication != null && authentication.isAuthenticated()) {
|
if (authentication != null && authentication.isAuthenticated()) {
|
||||||
|
|
||||||
LoginMethod loginMethod = LoginMethod.UNKNOWN;
|
UserLoginType loginMethod = UserLoginType.UNKNOWN;
|
||||||
|
|
||||||
boolean blockRegistration = false;
|
boolean blockRegistration = false;
|
||||||
|
|
||||||
@ -137,20 +137,20 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
String username = null;
|
String username = null;
|
||||||
if (principal instanceof UserDetails detailsUser) {
|
if (principal instanceof UserDetails detailsUser) {
|
||||||
username = detailsUser.getUsername();
|
username = detailsUser.getUsername();
|
||||||
loginMethod = LoginMethod.USERDETAILS;
|
loginMethod = UserLoginType.USERDETAILS;
|
||||||
} else if (principal instanceof OAuth2User oAuth2User) {
|
} else if (principal instanceof OAuth2User oAuth2User) {
|
||||||
username = oAuth2User.getName();
|
username = oAuth2User.getName();
|
||||||
loginMethod = LoginMethod.OAUTH2USER;
|
loginMethod = UserLoginType.OAUTH2USER;
|
||||||
OAUTH2 oAuth = securityProp.getOauth2();
|
OAUTH2 oAuth = securityProp.getOauth2();
|
||||||
blockRegistration = oAuth != null && oAuth.getBlockRegistration();
|
blockRegistration = oAuth != null && oAuth.getBlockRegistration();
|
||||||
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
|
} else if (principal instanceof CustomSaml2AuthenticatedPrincipal saml2User) {
|
||||||
username = saml2User.name();
|
username = saml2User.name();
|
||||||
loginMethod = LoginMethod.SAML2USER;
|
loginMethod = UserLoginType.SAML2USER;
|
||||||
SAML2 saml2 = securityProp.getSaml2();
|
SAML2 saml2 = securityProp.getSaml2();
|
||||||
blockRegistration = saml2 != null && saml2.getBlockRegistration();
|
blockRegistration = saml2 != null && saml2.getBlockRegistration();
|
||||||
} else if (principal instanceof String stringUser) {
|
} else if (principal instanceof String stringUser) {
|
||||||
username = stringUser;
|
username = stringUser;
|
||||||
loginMethod = LoginMethod.STRINGUSER;
|
loginMethod = UserLoginType.STRINGUSER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve all active sessions for the user
|
// Retrieve all active sessions for the user
|
||||||
@ -164,8 +164,8 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
boolean isUserDisabled = userService.isUserDisabled(username);
|
boolean isUserDisabled = userService.isUserDisabled(username);
|
||||||
|
|
||||||
boolean notSsoLogin =
|
boolean notSsoLogin =
|
||||||
!LoginMethod.OAUTH2USER.equals(loginMethod)
|
!UserLoginType.OAUTH2USER.equals(loginMethod)
|
||||||
&& !LoginMethod.SAML2USER.equals(loginMethod);
|
&& !UserLoginType.SAML2USER.equals(loginMethod);
|
||||||
|
|
||||||
// Block user registration if not allowed by configuration
|
// Block user registration if not allowed by configuration
|
||||||
if (blockRegistration && !isUserExists) {
|
if (blockRegistration && !isUserExists) {
|
||||||
@ -200,7 +200,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum LoginMethod {
|
private enum UserLoginType {
|
||||||
USERDETAILS("UserDetails"),
|
USERDETAILS("UserDetails"),
|
||||||
OAUTH2USER("OAuth2User"),
|
OAUTH2USER("OAuth2User"),
|
||||||
STRINGUSER("StringUser"),
|
STRINGUSER("StringUser"),
|
||||||
@ -209,7 +209,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
private String method;
|
private String method;
|
||||||
|
|
||||||
LoginMethod(String method) {
|
UserLoginType(String method) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
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.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.ResponseCookie;
|
import org.springframework.http.ResponseCookie;
|
||||||
@ -53,7 +52,6 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
private final KeyPersistenceServiceInterface keyPersistenceService;
|
private final KeyPersistenceServiceInterface keyPersistenceService;
|
||||||
private final boolean v2Enabled;
|
private final boolean v2Enabled;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public JwtService(
|
public JwtService(
|
||||||
@Qualifier("v2Enabled") boolean v2Enabled,
|
@Qualifier("v2Enabled") boolean v2Enabled,
|
||||||
KeyPersistenceServiceInterface keyPersistenceService) {
|
KeyPersistenceServiceInterface keyPersistenceService) {
|
||||||
@ -155,7 +153,8 @@ public class JwtService implements JwtServiceInterface {
|
|||||||
keyPair = specificKeyPair.get();
|
keyPair = specificKeyPair.get();
|
||||||
} else {
|
} else {
|
||||||
log.warn(
|
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);
|
keyId);
|
||||||
|
|
||||||
if (keyId.equals(keyPersistenceService.getActiveKey().getKeyId())) {
|
if (keyId.equals(keyPersistenceService.getActiveKey().getKeyId())) {
|
||||||
|
@ -8,7 +8,6 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -30,7 +29,6 @@ public class KeyPairCleanupService {
|
|||||||
private final KeyPersistenceService keyPersistenceService;
|
private final KeyPersistenceService keyPersistenceService;
|
||||||
private final ApplicationProperties.Security.Jwt jwtProperties;
|
private final ApplicationProperties.Security.Jwt jwtProperties;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public KeyPairCleanupService(
|
public KeyPairCleanupService(
|
||||||
KeyPersistenceService keyPersistenceService,
|
KeyPersistenceService keyPersistenceService,
|
||||||
ApplicationProperties applicationProperties) {
|
ApplicationProperties applicationProperties) {
|
||||||
|
@ -20,7 +20,6 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.cache.Cache;
|
import org.springframework.cache.Cache;
|
||||||
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
@ -43,16 +42,13 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
|||||||
public static final String KEY_SUFFIX = ".key";
|
public static final String KEY_SUFFIX = ".key";
|
||||||
|
|
||||||
private final ApplicationProperties.Security.Jwt jwtProperties;
|
private final ApplicationProperties.Security.Jwt jwtProperties;
|
||||||
private final CacheManager cacheManager;
|
|
||||||
private final Cache verifyingKeyCache;
|
private final Cache verifyingKeyCache;
|
||||||
|
|
||||||
private volatile JwtVerificationKey activeKey;
|
private volatile JwtVerificationKey activeKey;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public KeyPersistenceService(
|
public KeyPersistenceService(
|
||||||
ApplicationProperties applicationProperties, CacheManager cacheManager) {
|
ApplicationProperties applicationProperties, CacheManager cacheManager) {
|
||||||
this.jwtProperties = applicationProperties.getSecurity().getJwt();
|
this.jwtProperties = applicationProperties.getSecurity().getJwt();
|
||||||
this.cacheManager = cacheManager;
|
|
||||||
this.verifyingKeyCache = cacheManager.getCache("verifyingKeys");
|
this.verifyingKeyCache = cacheManager.getCache("verifyingKeys");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +155,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
|||||||
nativeCache.asMap().size());
|
nativeCache.asMap().size());
|
||||||
|
|
||||||
return nativeCache.asMap().values().stream()
|
return nativeCache.asMap().values().stream()
|
||||||
.filter(value -> value instanceof JwtVerificationKey)
|
.filter(JwtVerificationKey.class::isInstance)
|
||||||
.map(value -> (JwtVerificationKey) value)
|
.map(value -> (JwtVerificationKey) value)
|
||||||
.filter(
|
.filter(
|
||||||
key -> {
|
key -> {
|
||||||
@ -233,6 +229,7 @@ public class KeyPersistenceService implements KeyPersistenceServiceInterface {
|
|||||||
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public PublicKey decodePublicKey(String encodedKey)
|
public PublicKey decodePublicKey(String encodedKey)
|
||||||
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
|
byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
|
||||||
|
@ -189,7 +189,7 @@ class UserServiceTest {
|
|||||||
void testSaveUser_WithValidEmail_Success() throws Exception {
|
void testSaveUser_WithValidEmail_Success() throws Exception {
|
||||||
// Given
|
// Given
|
||||||
String emailUsername = "test@example.com";
|
String emailUsername = "test@example.com";
|
||||||
AuthenticationType authType = AuthenticationType.SSO;
|
AuthenticationType authType = AuthenticationType.OAUTH2;
|
||||||
|
|
||||||
when(teamRepository.findByName("Default")).thenReturn(Optional.of(mockTeam));
|
when(teamRepository.findByName("Default")).thenReturn(Optional.of(mockTeam));
|
||||||
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
when(userRepository.save(any(User.class))).thenReturn(mockUser);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user