Pipeline changes and version bump (#6047)

# Description of Changes

<!--
Please provide a summary of the changes, including:

- What was changed
- Why the change was made
- Any challenges encountered

Closes #(issue_number)
-->

---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] 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)
- [ ] I have performed a self-review of my own code
- [ ] 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)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### 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: a <a>
This commit is contained in:
Anthony Stirling
2026-04-02 12:52:22 +01:00
committed by GitHub
parent da9327ab1c
commit de9625942b
8 changed files with 46 additions and 8 deletions

View File

@@ -5,6 +5,8 @@ public interface UserServiceInterface {
String getCurrentUsername();
String getCurrentUserApiKey();
long getTotalUsersCount();
boolean isCurrentUserAdmin();

View File

@@ -37,7 +37,6 @@ import stirling.software.SPDF.model.PipelineConfig;
import stirling.software.SPDF.model.PipelineOperation;
import stirling.software.SPDF.model.PipelineResult;
import stirling.software.SPDF.service.ApiDocService;
import stirling.software.common.model.enumeration.Role;
import stirling.software.common.service.UserServiceInterface;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
@@ -84,9 +83,35 @@ public class PipelineProcessor {
return name.substring(0, underscoreIndex) + extension;
}
// Allowlist of URL path prefixes permitted through the pipeline.
private static final List<String> ALLOWED_PIPELINE_PATH_PREFIXES =
List.of(
"/api/v1/general/",
"/api/v1/misc/",
"/api/v1/security/",
"/api/v1/convert/",
"/api/v1/filter/");
private void validatePipelineUrl(String url) {
// Strip scheme+host to get the path portion for comparison
String path = url;
int schemeEnd = url.indexOf("://");
if (schemeEnd != -1) {
int pathStart = url.indexOf('/', schemeEnd + 3);
path = pathStart != -1 ? url.substring(pathStart) : "/";
}
final String pathToCheck = path;
boolean allowed = ALLOWED_PIPELINE_PATH_PREFIXES.stream().anyMatch(pathToCheck::contains);
if (!allowed) {
log.warn("Blocked pipeline request to disallowed URL: {}", url);
throw new SecurityException(
"Pipeline operation not permitted for endpoint: " + pathToCheck);
}
}
private String getApiKeyForUser() {
if (userService == null) return "";
return userService.getApiKeyForUser(Role.INTERNAL_API_USER.getRoleId());
return userService.getCurrentUserApiKey();
}
private String getBaseUrl() {
@@ -283,6 +308,7 @@ public class PipelineProcessor {
/* package */ ResponseEntity<Resource> sendWebRequest(
String url, MultiValueMap<String, Object> body) {
validatePipelineUrl(url);
RestTemplate restTemplate = new RestTemplate();
// Set up headers, including API key
HttpHeaders headers = new HttpHeaders();

View File

@@ -205,7 +205,8 @@ class PipelineProcessorTest {
});
})) {
ResponseEntity<Resource> response =
pipelineProcessor.sendWebRequest("http://localhost/api", body);
pipelineProcessor.sendWebRequest(
"http://localhost/api/v1/general/merge-pdfs", body);
assertNotNull(response);
assertEquals(HttpStatus.OK, response.getStatusCode());

View File

@@ -198,6 +198,15 @@ public class UserService implements UserServiceInterface {
return user.getApiKey();
}
@Override
public String getCurrentUserApiKey() {
String username = getCurrentUsername();
if (username == null || username.isEmpty()) {
throw new IllegalStateException("Cannot determine calling user for API key lookup");
}
return getApiKeyForUser(username);
}
public boolean isValidApiKey(String apiKey) {
return userRepository.findByApiKey(apiKey).isPresent();
}

View File

@@ -28,7 +28,7 @@ ext {
springSecuritySamlVersion = "7.0.2"
openSamlVersion = "5.2.1"
commonmarkVersion = "0.27.1"
googleJavaFormatVersion = "1.35.0"
googleJavaFormatVersion = "1.28.0"
logback = "1.5.32"
junitPlatformVersion = "1.12.2"
modernJavaVersion = 21
@@ -78,7 +78,7 @@ springBoot {
allprojects {
group = 'stirling.software'
version = '2.8.0'
version = '2.9.0'
configurations.configureEach {
exclude group: "org.springframework.boot", module: "spring-boot-starter-tomcat"

View File

@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "Stirling-PDF",
"version": "2.8.0",
"version": "2.9.0",
"identifier": "stirling.pdf.dev",
"build": {
"frontendDist": "../dist",

View File

@@ -38,7 +38,7 @@ const FREE_LICENSE_INFO: LicenseInfo = {
const BASE_NO_LOGIN_CONFIG: AppConfig = {
enableAnalytics: true,
appVersion: '2.8.0',
appVersion: '2.9.0',
serverCertificateEnabled: false,
enableAlphaFunctionality: false,
serverPort: 8080,

View File

@@ -48,7 +48,7 @@ const FREE_LICENSE_INFO: LicenseInfo = {
const BASE_NO_LOGIN_CONFIG: AppConfig = {
enableAnalytics: true,
appVersion: '2.8.0',
appVersion: '2.9.0',
serverCertificateEnabled: false,
enableAlphaFunctionality: false,
enableDesktopInstallSlide: true,