Builds custom Jar (#5029)

# Description of Changes

Change jar files to contain frontend if provided with param, else
doesnt... add release artifact -server version which wont have frontend

---

## 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.
This commit is contained in:
Anthony Stirling
2025-11-26 17:21:42 +00:00
committed by GitHub
parent a62c8b54cf
commit e47ed13be8
16 changed files with 406 additions and 87 deletions

View File

@@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
import stirling.software.common.model.ApplicationProperties.Security.SAML2;
import stirling.software.common.util.RequestUriUtils;
import stirling.software.proprietary.security.model.ApiKeyAuthenticationToken;
import stirling.software.proprietary.security.model.User;
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
@@ -110,7 +111,6 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// If we still don't have any authentication, check if it's a public endpoint. If not, deny
// the request
if (authentication == null || !authentication.isAuthenticated()) {
String method = request.getMethod();
String contextPath = request.getContextPath();
// Allow public auth endpoints to pass through without authentication
@@ -119,18 +119,18 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
return;
}
if ("GET".equalsIgnoreCase(method) && !requestURI.startsWith(contextPath + "/login")) {
response.sendRedirect(contextPath + "/login"); // redirect to the login page
} else {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter()
.write(
"""
Authentication required. Please provide a X-API-KEY in request header.
This is found in Settings -> Account Settings -> API Key
Alternatively you can disable authentication if this is unexpected.
""");
}
// For API requests, return 401 with JSON response (no redirects)
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json");
response.getWriter()
.write(
"""
{
"error": "Unauthorized",
"message": "Authentication required. Please provide valid credentials or X-API-KEY header.",
"status": 401
}
""");
return;
}
@@ -179,8 +179,18 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
// Block user registration if not allowed by configuration
if (blockRegistration && !isUserExists) {
log.warn("Blocked registration for OAuth2/SAML user: {}", username);
response.sendRedirect(
request.getContextPath() + "/logout?oAuth2AdminBlockedUser=true");
SecurityContextHolder.clearContext();
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json");
response.getWriter()
.write(
"""
{
"error": "Forbidden",
"message": "User registration is blocked by administrator",
"status": 403
}
""");
return;
}
@@ -194,13 +204,35 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
}
}
// Redirect to logout if credentials are invalid
// Return 401 if credentials are invalid (no redirects)
if (!isUserExists && notSsoLogin) {
response.sendRedirect(request.getContextPath() + "/logout?badCredentials=true");
SecurityContextHolder.clearContext();
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json");
response.getWriter()
.write(
"""
{
"error": "Unauthorized",
"message": "Invalid credentials",
"status": 401
}
""");
return;
}
if (isUserDisabled) {
response.sendRedirect(request.getContextPath() + "/logout?userIsDisabled=true");
SecurityContextHolder.clearContext();
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json");
response.getWriter()
.write(
"""
{
"error": "Forbidden",
"message": "User account is disabled",
"status": 403
}
""");
return;
}
}
@@ -250,33 +282,28 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
protected boolean shouldNotFilter(HttpServletRequest request) {
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String[] permitAllPatterns = {
contextPath + "/login",
contextPath + "/register",
contextPath + "/invite",
contextPath + "/error",
contextPath + "/images/",
contextPath + "/public/",
contextPath + "/css/",
contextPath + "/fonts/",
contextPath + "/js/",
contextPath + "/pdfjs/",
contextPath + "/pdfjs-legacy/",
// Allow unauthenticated access to static resources and SPA routes (GET/HEAD only)
if ("GET".equalsIgnoreCase(request.getMethod())
|| "HEAD".equalsIgnoreCase(request.getMethod())) {
if (RequestUriUtils.isStaticResource(contextPath, uri)
|| RequestUriUtils.isFrontendRoute(contextPath, uri)) {
return true;
}
}
// For API routes, only skip filter for these public endpoints
String[] publicApiPatterns = {
contextPath + "/api/v1/info/status",
contextPath + "/api/v1/auth/login",
contextPath + "/api/v1/auth/refresh",
contextPath + "/api/v1/auth/me",
contextPath + "/api/v1/invite/validate",
contextPath + "/api/v1/invite/accept",
contextPath + "/site.webmanifest"
contextPath + "/api/v1/invite/accept"
};
for (String pattern : permitAllPatterns) {
if (uri.startsWith(pattern)
|| uri.endsWith(".svg")
|| uri.endsWith(".mjs")
|| uri.endsWith(".png")
|| uri.endsWith(".ico")) {
for (String pattern : publicApiPatterns) {
if (uri.startsWith(pattern)) {
return true;
}
}