multi module build running

This commit is contained in:
Dario Ghunney Ware 2025-05-19 09:50:39 +01:00
parent ea04596e35
commit a328ea9d25
33 changed files with 195 additions and 1134 deletions

View File

@ -31,6 +31,7 @@ import stirling.software.common.configuration.AppConfig;
import stirling.software.common.model.ApplicationProperties; import stirling.software.common.model.ApplicationProperties;
import stirling.software.proprietary.security.CustomAuthenticationFailureHandler; import stirling.software.proprietary.security.CustomAuthenticationFailureHandler;
import stirling.software.proprietary.security.CustomAuthenticationSuccessHandler; import stirling.software.proprietary.security.CustomAuthenticationSuccessHandler;
import stirling.software.proprietary.security.CustomLogoutSuccessHandler;
import stirling.software.proprietary.security.database.repository.JPATokenRepositoryImpl; import stirling.software.proprietary.security.database.repository.JPATokenRepositoryImpl;
import stirling.software.proprietary.security.database.repository.PersistentLoginRepository; import stirling.software.proprietary.security.database.repository.PersistentLoginRepository;
import stirling.software.proprietary.security.filter.FirstLoginFilter; import stirling.software.proprietary.security.filter.FirstLoginFilter;
@ -168,7 +169,7 @@ public class SecurityConfiguration {
logout -> logout ->
logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout")) logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessHandler( .logoutSuccessHandler(
new stirling.software.proprietary.security.CustomLogoutSuccessHandler(applicationProperties, appConfig)) new CustomLogoutSuccessHandler(applicationProperties, appConfig))
.clearAuthentication(true) .clearAuthentication(true)
.invalidateHttpSession(true) .invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me")); .deleteCookies("JSESSIONID", "remember-me"));
@ -293,7 +294,7 @@ public class SecurityConfiguration {
}); });
} }
} else { } else {
log.debug("SAML 2 login is not enabled. Using default."); log.debug("Login is not enabled.");
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll()); http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
} }
return http.build(); return http.build();

View File

@ -1,317 +0,0 @@
package stirling.software.SPDF.config.security;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationFailureHandler;
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationSuccessHandler;
import stirling.software.SPDF.config.security.saml2.CustomSaml2ResponseAuthenticationConverter;
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.User;
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
import stirling.software.SPDF.repository.PersistentLoginRepository;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@Slf4j
@DependsOn("runningProOrHigher")
public class SecurityConfiguration {
private final CustomUserDetailsService userDetailsService;
private final UserService userService;
private final boolean loginEnabledValue;
private final boolean runningProOrHigher;
private final ApplicationProperties applicationProperties;
private final UserAuthenticationFilter userAuthenticationFilter;
private final LoginAttemptService loginAttemptService;
private final FirstLoginFilter firstLoginFilter;
private final SessionPersistentRegistry sessionRegistry;
private final PersistentLoginRepository persistentLoginRepository;
private final GrantedAuthoritiesMapper oAuth2userAuthoritiesMapper;
private final RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations;
private final OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver;
public SecurityConfiguration(
PersistentLoginRepository persistentLoginRepository,
CustomUserDetailsService userDetailsService,
@Lazy UserService userService,
@Qualifier("loginEnabled") boolean loginEnabledValue,
@Qualifier("runningProOrHigher") boolean runningProOrHigher,
ApplicationProperties applicationProperties,
UserAuthenticationFilter userAuthenticationFilter,
LoginAttemptService loginAttemptService,
FirstLoginFilter firstLoginFilter,
SessionPersistentRegistry sessionRegistry,
@Autowired(required = false) GrantedAuthoritiesMapper oAuth2userAuthoritiesMapper,
@Autowired(required = false)
RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations,
@Autowired(required = false)
OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver) {
this.userDetailsService = userDetailsService;
this.userService = userService;
this.loginEnabledValue = loginEnabledValue;
this.runningProOrHigher = runningProOrHigher;
this.applicationProperties = applicationProperties;
this.userAuthenticationFilter = userAuthenticationFilter;
this.loginAttemptService = loginAttemptService;
this.firstLoginFilter = firstLoginFilter;
this.sessionRegistry = sessionRegistry;
this.persistentLoginRepository = persistentLoginRepository;
this.oAuth2userAuthoritiesMapper = oAuth2userAuthoritiesMapper;
this.saml2RelyingPartyRegistrations = saml2RelyingPartyRegistrations;
this.saml2AuthenticationRequestResolver = saml2AuthenticationRequestResolver;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
if (applicationProperties.getSecurity().getCsrfDisabled() || !loginEnabledValue) {
http.csrf(csrf -> csrf.disable());
}
if (loginEnabledValue) {
http.addFilterBefore(
userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
if (!applicationProperties.getSecurity().getCsrfDisabled()) {
CookieCsrfTokenRepository cookieRepo =
CookieCsrfTokenRepository.withHttpOnlyFalse();
CsrfTokenRequestAttributeHandler requestHandler =
new CsrfTokenRequestAttributeHandler();
requestHandler.setCsrfRequestAttributeName(null);
http.csrf(
csrf ->
csrf.ignoringRequestMatchers(
request -> {
String apiKey = request.getHeader("X-API-KEY");
// If there's no API key, don't ignore CSRF
// (return false)
if (apiKey == null || apiKey.trim().isEmpty()) {
return false;
}
// Validate API key using existing UserService
try {
Optional<User> user =
userService.getUserByApiKey(apiKey);
// If API key is valid, ignore CSRF (return
// true)
// If API key is invalid, don't ignore CSRF
// (return false)
return user.isPresent();
} catch (Exception e) {
// If there's any error validating the API
// key, don't ignore CSRF
return false;
}
})
.csrfTokenRepository(cookieRepo)
.csrfTokenRequestHandler(requestHandler));
}
http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class);
http.sessionManagement(
sessionManagement ->
sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(10)
.maxSessionsPreventsLogin(false)
.sessionRegistry(sessionRegistry)
.expiredUrl("/login?logout=true"));
http.authenticationProvider(daoAuthenticationProvider());
http.requestCache(requestCache -> requestCache.requestCache(new NullRequestCache()));
http.logout(
logout ->
logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessHandler(
new CustomLogoutSuccessHandler(applicationProperties))
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me"));
http.rememberMe(
rememberMeConfigurer -> // Use the configurator directly
rememberMeConfigurer
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds( // 14 days
14 * 24 * 60 * 60)
.userDetailsService( // Your existing UserDetailsService
userDetailsService)
.useSecureCookie( // Enable secure cookie
true)
.rememberMeParameter( // Form parameter name
"remember-me")
.rememberMeCookieName( // Cookie name
"remember-me")
.alwaysRemember(false));
http.authorizeHttpRequests(
authz ->
authz.requestMatchers(
req -> {
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
// Remove the context path from the URI
String trimmedUri =
uri.startsWith(contextPath)
? uri.substring(
contextPath.length())
: uri;
return trimmedUri.startsWith("/login")
|| trimmedUri.startsWith("/oauth")
|| trimmedUri.startsWith("/saml2")
|| trimmedUri.endsWith(".svg")
|| trimmedUri.startsWith("/register")
|| trimmedUri.startsWith("/error")
|| trimmedUri.startsWith("/images/")
|| trimmedUri.startsWith("/public/")
|| trimmedUri.startsWith("/css/")
|| trimmedUri.startsWith("/fonts/")
|| trimmedUri.startsWith("/js/")
|| trimmedUri.startsWith(
"/api/v1/info/status");
})
.permitAll()
.anyRequest()
.authenticated());
// Handle User/Password Logins
if (applicationProperties.getSecurity().isUserPass()) {
http.formLogin(
formLogin ->
formLogin
.loginPage("/login")
.successHandler(
new CustomAuthenticationSuccessHandler(
loginAttemptService, userService))
.failureHandler(
new CustomAuthenticationFailureHandler(
loginAttemptService, userService))
.defaultSuccessUrl("/")
.permitAll());
}
// Handle OAUTH2 Logins
if (applicationProperties.getSecurity().isOauth2Active()) {
http.oauth2Login(
oauth2 ->
oauth2.loginPage("/oauth2")
.
/*
This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database.
If user exists, login proceeds as usual. If user does not exist, then it is auto-created but only if 'OAUTH2AutoCreateUser'
is set as true, else login fails with an error message advising the same.
*/
successHandler(
new CustomOAuth2AuthenticationSuccessHandler(
loginAttemptService,
applicationProperties,
userService))
.failureHandler(
new CustomOAuth2AuthenticationFailureHandler())
. // Add existing Authorities from the database
userInfoEndpoint(
userInfoEndpoint ->
userInfoEndpoint
.oidcUserService(
new CustomOAuth2UserService(
applicationProperties,
userService,
loginAttemptService))
.userAuthoritiesMapper(
oAuth2userAuthoritiesMapper))
.permitAll());
}
// Handle SAML
if (applicationProperties.getSecurity().isSaml2Active() && runningProOrHigher) {
// Configure the authentication provider
OpenSaml4AuthenticationProvider authenticationProvider =
new OpenSaml4AuthenticationProvider();
authenticationProvider.setResponseAuthenticationConverter(
new CustomSaml2ResponseAuthenticationConverter(userService));
http.authenticationProvider(authenticationProvider)
.saml2Login(
saml2 -> {
try {
saml2.loginPage("/saml2")
.relyingPartyRegistrationRepository(
saml2RelyingPartyRegistrations)
.authenticationManager(
new ProviderManager(authenticationProvider))
.successHandler(
new CustomSaml2AuthenticationSuccessHandler(
loginAttemptService,
applicationProperties,
userService))
.failureHandler(
new CustomSaml2AuthenticationFailureHandler())
.authenticationRequestResolver(
saml2AuthenticationRequestResolver);
} catch (Exception e) {
log.error("Error configuring SAML 2 login", e);
throw new RuntimeException(e);
}
});
}
} else {
log.debug("SAML 2 login is not enabled. Using default.");
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
}
return http.build();
}
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
@Bean
public IPRateLimitingFilter rateLimitingFilter() {
// Example limit TODO add config level
int maxRequestsPerIp = 1000000;
return new IPRateLimitingFilter(maxRequestsPerIp, maxRequestsPerIp);
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
return new JPATokenRepositoryImpl(persistentLoginRepository);
}
@Bean
public boolean activeSecurity() {
return true;
}
}

View File

@ -1,31 +0,0 @@
package stirling.software.SPDF.model.api;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = false)
public class SplitPdfByChaptersRequest extends PDFFile {
@Schema(
description = "Whether to include Metadata or not",
defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean includeMetadata;
@Schema(
description = "Whether to allow duplicates or not",
defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean allowDuplicates;
@Schema(
description = "Maximum bookmark level required",
minimum = "0",
defaultValue = "2",
requiredMode = Schema.RequiredMode.REQUIRED)
private Integer bookmarkLevel;
}

View File

@ -1,42 +0,0 @@
package stirling.software.SPDF.model.api.converters;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@Data
@EqualsAndHashCode(callSuper = true)
public class ConvertToImageRequest extends PDFWithPageNums {
@Schema(
description = "The output image format",
defaultValue = "png",
allowableValues = {"png", "jpeg", "jpg", "gif", "webp"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String imageFormat;
@Schema(
description =
"Choose between a single image containing all pages or separate images for each"
+ " page",
defaultValue = "multiple",
allowableValues = {"single", "multiple"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String singleOrMultiple;
@Schema(
description = "The color type of the output image(s)",
defaultValue = "color",
allowableValues = {"color", "greyscale", "blackwhite"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String colorType;
@Schema(
description = "The DPI (dots per inch) for the output image(s)",
defaultValue = "300",
requiredMode = Schema.RequiredMode.REQUIRED)
private Integer dpi;
}

View File

@ -1,19 +0,0 @@
package stirling.software.SPDF.model.api.filter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@Data
@EqualsAndHashCode(callSuper = true)
public class ContainsTextRequest extends PDFWithPageNums {
@Schema(
description = "The text to check for",
defaultValue = "text",
requiredMode = Schema.RequiredMode.REQUIRED)
private String text;
}

View File

@ -1,19 +0,0 @@
package stirling.software.SPDF.model.api.filter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFComparison;
@Data
@EqualsAndHashCode(callSuper = true)
public class FileSizeRequest extends PDFComparison {
@Schema(
description = "Size of the file in bytes",
requiredMode = Schema.RequiredMode.REQUIRED,
defaultValue = "0")
private long fileSize;
}

View File

@ -1,19 +0,0 @@
package stirling.software.SPDF.model.api.filter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFComparison;
@Data
@EqualsAndHashCode(callSuper = true)
public class PageRotationRequest extends PDFComparison {
@Schema(
description = "Rotation in degrees",
requiredMode = Schema.RequiredMode.REQUIRED,
defaultValue = "0")
private int rotation;
}

View File

@ -1,20 +0,0 @@
package stirling.software.SPDF.model.api.filter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFComparison;
@Data
@EqualsAndHashCode(callSuper = true)
public class PageSizeRequest extends PDFComparison {
@Schema(
description = "Standard Page Size",
allowableValues = {"A0", "A1", "A2", "A3", "A4", "A5", "A6", "LETTER", "LEGAL"},
defaultValue = "A4",
requiredMode = Schema.RequiredMode.REQUIRED)
private String standardPageSize;
}

View File

@ -1,46 +0,0 @@
package stirling.software.SPDF.model.api.general;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class OverlayPdfsRequest extends PDFFile {
@Schema(
description =
"An array of PDF files to be used as overlays on the base PDF. The order in"
+ " these files is applied based on the selected mode.",
requiredMode = Schema.RequiredMode.REQUIRED)
private MultipartFile[] overlayFiles;
@Schema(
description =
"The mode of overlaying: 'SequentialOverlay' for sequential application,"
+ " 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay'"
+ " for fixed repetition based on provided counts",
allowableValues = {"SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String overlayMode;
@Schema(
description =
"An array of integers specifying the number of times each corresponding overlay"
+ " file should be applied in the 'FixedRepeatOverlay' mode. This should"
+ " match the length of the overlayFiles array.",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private int[] counts;
@Schema(
description = "Overlay position 0 is Foregound, 1 is Background",
allowableValues = {"0", "1"},
requiredMode = Schema.RequiredMode.REQUIRED,
type = "number")
private int overlayPosition;
}

View File

@ -1,87 +0,0 @@
package stirling.software.SPDF.model.api.misc;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@Data
@EqualsAndHashCode(callSuper = true)
public class AddStampRequest extends PDFWithPageNums {
@Schema(
description = "The stamp type (text or image)",
allowableValues = {"text", "image"},
requiredMode = Schema.RequiredMode.REQUIRED)
private String stampType;
@Schema(description = "The stamp text", defaultValue = "Stirling Software")
private String stampText;
@Schema(description = "The stamp image")
private MultipartFile stampImage;
@Schema(
description = "The selected alphabet of the stamp text",
allowableValues = {"roman", "arabic", "japanese", "korean", "chinese"},
defaultValue = "roman")
private String alphabet = "roman";
@Schema(
description = "The font size of the stamp text and image",
defaultValue = "30",
requiredMode = Schema.RequiredMode.REQUIRED)
private float fontSize;
@Schema(
description = "The rotation of the stamp in degrees",
defaultValue = "0",
requiredMode = Schema.RequiredMode.REQUIRED)
private float rotation;
@Schema(
description = "The opacity of the stamp (0.0 - 1.0)",
defaultValue = "0.5",
requiredMode = Schema.RequiredMode.REQUIRED)
private float opacity;
@Schema(
description =
"Position for stamp placement based on a 1-9 grid (1: bottom-left, 2: bottom-center,"
+ " 3: bottom-right, 4: middle-left, 5: middle-center, 6: middle-right,"
+ " 7: top-left, 8: top-center, 9: top-right)",
allowableValues = {"1", "2", "3", "4", "5", "6", "7", "8", "9"},
defaultValue = "5",
requiredMode = Schema.RequiredMode.REQUIRED)
private int position;
@Schema(
description =
"Override X coordinate for stamp placement. If set, it will override the"
+ " position-based calculation. Negative value means no override.",
defaultValue = "-1",
requiredMode = Schema.RequiredMode.REQUIRED)
private float overrideX; // Default to -1 indicating no override
@Schema(
description =
"Override Y coordinate for stamp placement. If set, it will override the"
+ " position-based calculation. Negative value means no override.",
defaultValue = "-1",
requiredMode = Schema.RequiredMode.REQUIRED)
private float overrideY; // Default to -1 indicating no override
@Schema(
description = "Specifies the margin size for the stamp.",
allowableValues = {"small", "medium", "large", "x-large"},
defaultValue = "medium",
requiredMode = Schema.RequiredMode.REQUIRED)
private String customMargin;
@Schema(description = "The color of the stamp text", defaultValue = "#d3d3d3")
private String customColor;
}

View File

@ -1,84 +0,0 @@
package stirling.software.SPDF.model.api.misc;
import java.util.Map;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class MetadataRequest extends PDFFile {
@Schema(
description = "Delete all metadata if set to true",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean deleteAll;
@Schema(
description = "The author of the document",
defaultValue = "author",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String author;
@Schema(
description = "The creation date of the document (format: yyyy/MM/dd HH:mm:ss)",
pattern = "yyyy/MM/dd HH:mm:ss",
defaultValue = "2023/10/01 12:00:00",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String creationDate;
@Schema(
description = "The creator of the document",
defaultValue = "creator",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String creator;
@Schema(
description = "The keywords for the document",
defaultValue = "keywords",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String keywords;
@Schema(
description = "The modification date of the document (format: yyyy/MM/dd HH:mm:ss)",
pattern = "yyyy/MM/dd HH:mm:ss",
defaultValue = "2023/10/01 12:00:00",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String modificationDate;
@Schema(
description = "The producer of the document",
defaultValue = "producer",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String producer;
@Schema(
description = "The subject of the document",
defaultValue = "subject",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String subject;
@Schema(
description = "The title of the document",
defaultValue = "title",
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String title;
@Schema(
description = "The trapped status of the document",
defaultValue = "False",
allowableValues = {"True", "False", "Unknown"},
requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String trapped;
@Schema(
description =
"Map list of key and value of custom parameters. Note these must start with"
+ " customKey and customValue if they are non-standard")
private Map<String, String> allRequestParams;
}

View File

@ -1,62 +0,0 @@
package stirling.software.SPDF.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class AddPasswordRequest extends PDFFile {
@Schema(
description =
"The owner password to be added to the PDF file (Restricts what can be done"
+ " with the document once it is opened)",
format = "password")
private String ownerPassword;
@Schema(
description =
"The password to be added to the PDF file (Restricts the opening of the"
+ " document itself.)",
format = "password")
private String password;
@Schema(
description = "The length of the encryption key",
allowableValues = {"40", "128", "256"},
defaultValue = "256",
requiredMode = Schema.RequiredMode.REQUIRED)
private int keyLength = 256;
@Schema(description = "Whether document assembly is prevented", defaultValue = "false")
private Boolean preventAssembly;
@Schema(description = "Whether content extraction is prevented", defaultValue = "false")
private Boolean preventExtractContent;
@Schema(
description = "Whether content extraction for accessibility is prevented",
defaultValue = "false")
private Boolean preventExtractForAccessibility;
@Schema(description = "Whether form filling is prevented", defaultValue = "false")
private Boolean preventFillInForm;
@Schema(description = "Whether document modification is prevented", defaultValue = "false")
private Boolean preventModify;
@Schema(
description = "Whether modification of annotations is prevented",
defaultValue = "false")
private Boolean preventModifyAnnotations;
@Schema(description = "Whether printing of the document is prevented", defaultValue = "false")
private Boolean preventPrinting;
@Schema(description = "Whether faithful printing is prevented", defaultValue = "false")
private Boolean preventPrintingFaithful;
}

View File

@ -1,31 +0,0 @@
package stirling.software.SPDF.model.api.security;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFWithPageNums;
@Data
@EqualsAndHashCode(callSuper = true)
public class ManualRedactPdfRequest extends PDFWithPageNums {
@Schema(
description = "A list of areas that should be redacted",
requiredMode = Schema.RequiredMode.REQUIRED)
private List<RedactionArea> redactions;
@Schema(
description = "Convert the redacted PDF to an image",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean convertPDFToImage;
@Schema(
description = "The color used to fully redact certain pages",
defaultValue = "#000000",
requiredMode = Schema.RequiredMode.REQUIRED)
private String pageRedactionColor;
}

View File

@ -1,49 +0,0 @@
package stirling.software.SPDF.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.common.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class RedactPdfRequest extends PDFFile {
@Schema(
description = "List of text to redact from the PDF",
defaultValue = "text,text2",
requiredMode = Schema.RequiredMode.REQUIRED)
private String listOfText;
@Schema(
description = "Whether to use regex for the listOfText",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean useRegex;
@Schema(
description = "Whether to use whole word search",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean wholeWordSearch;
@Schema(
description = "The color for redaction",
defaultValue = "#000000",
requiredMode = Schema.RequiredMode.REQUIRED)
private String redactColor;
@Schema(
description = "Custom padding for redaction",
type = "number",
requiredMode = Schema.RequiredMode.REQUIRED)
private float customPadding;
@Schema(
description = "Convert the redacted PDF to an image",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean convertPDFToImage;
}

View File

@ -1,49 +0,0 @@
package stirling.software.SPDF.model.api.security;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import stirling.software.SPDF.model.api.PDFFile;
@Data
@EqualsAndHashCode(callSuper = true)
public class SanitizePdfRequest extends PDFFile {
@Schema(
description = "Remove JavaScript actions from the PDF",
defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeJavaScript;
@Schema(
description = "Remove embedded files from the PDF",
defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeEmbeddedFiles;
@Schema(
description = "Remove XMP metadata from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeXMPMetadata;
@Schema(
description = "Remove document info metadata from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeMetadata;
@Schema(
description = "Remove links from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeLinks;
@Schema(
description = "Remove fonts from the PDF",
defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeFonts;
}

View File

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
<head>
<th:block th:insert="~{fragments/common :: head(title=#{removeBlanks.title}, header=#{removeBlanks.header})}"></th:block>
</head>
<body>
<th:block th:insert="~{fragments/common :: game}"></th:block>
<div id="page-container">
<div id="content-wrap">
<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
<br><br>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6 bg-card">
<div class="tool-header">
<span class="material-symbols-rounded tool-header-icon other">scan_delete</span>
<span class="tool-header-text" th:text="#{removeBlanks.header}"></span>
</div>
<form id="multiPdfForm" th:action="@{'/api/v1/misc/remove-blanks'}" method="post" enctype="multipart/form-data">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3">
<label for="threshold" th:text="#{removeBlanks.threshold}"></label>
<input type="number" class="form-control" id="threshold" name="threshold" value="10" min="0" max="255" step="1">
<small id="thresholdHelp" class="form-text text-muted" th:text="#{removeBlanks.thresholdDesc}"></small>
</div>
<div class="mb-3">
<label for="whitePercent" th:text="#{removeBlanks.whitePercent}"></label>
<input type="number" class="form-control" id="whitePercent" name="whitePercent" value="99.9" step="0.1" min="0.1" max="100">
<small id="whitePercentHelp" class="form-text text-muted" th:text="#{removeBlanks.whitePercentDesc}"></small>
</div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeBlanks.submit}"></button>
</form>
</div>
</div>
</div>
</div>
<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
</div>
</body>
</html>

View File

@ -1,26 +0,0 @@
package stirling.software.SPDF.utils;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class RequestUriUtilsTest {
@Test
public void testIsStaticResource() {
assertTrue(RequestUriUtils.isStaticResource("/css/styles.css"));
assertTrue(RequestUriUtils.isStaticResource("/js/script.js"));
assertTrue(RequestUriUtils.isStaticResource("/images/logo.png"));
assertTrue(RequestUriUtils.isStaticResource("/public/index.html"));
assertTrue(RequestUriUtils.isStaticResource("/pdfjs/pdf.worker.js"));
assertTrue(RequestUriUtils.isStaticResource("/api/v1/info/status"));
assertTrue(RequestUriUtils.isStaticResource("/some-path/icon.svg"));
assertFalse(RequestUriUtils.isStaticResource("/api/v1/users"));
assertFalse(RequestUriUtils.isStaticResource("/api/v1/orders"));
assertFalse(RequestUriUtils.isStaticResource("/"));
assertTrue(RequestUriUtils.isStaticResource("/login"));
assertFalse(RequestUriUtils.isStaticResource("/register"));
assertFalse(RequestUriUtils.isStaticResource("/api/v1/products"));
}
}

View File

@ -14,9 +14,9 @@ server.error.whitelabel.enabled=false
server.error.include-stacktrace=always server.error.include-stacktrace=always
server.error.include-exception=true server.error.include-exception=true
server.error.include-message=always server.error.include-message=always
logging.level.org.springframework.web=DEBUG #logging.level.org.springframework.web=DEBUG
logging.level.org.springframework=DEBUG #logging.level.org.springframework=DEBUG
logging.level.org.springframework.security=DEBUG #logging.level.org.springframework.security=DEBUG
spring.servlet.multipart.max-file-size=2000MB spring.servlet.multipart.max-file-size=2000MB
spring.servlet.multipart.max-request-size=2000MB spring.servlet.multipart.max-request-size=2000MB
server.servlet.session.tracking-modes=cookie server.servlet.session.tracking-modes=cookie

View File

@ -124,7 +124,7 @@ pipelineOptions.validateButton=Validar
# ENTERPRISE EDITION # # ENTERPRISE EDITION #
######################## ########################
enterpriseEdition.button=Actualiza a Pro enterpriseEdition.button=Actualiza a Pro
enterpriseEdition.warning=Esta característica está disponible solamente para usuarios Pro. enterpriseEdition.warning=Esta característica está únicamente disponible para usuarios Pro.
enterpriseEdition.yamlAdvert=Stirling PDF Pro soporta configuración de ficheros YAML y otras características SSO. enterpriseEdition.yamlAdvert=Stirling PDF Pro soporta configuración de ficheros YAML y otras características SSO.
enterpriseEdition.ssoAdvert=¿Busca más funciones de administración de usuarios? Consulte Stirling PDF Pro enterpriseEdition.ssoAdvert=¿Busca más funciones de administración de usuarios? Consulte Stirling PDF Pro
@ -364,9 +364,9 @@ home.compressPdfs.title=Comprimir
home.compressPdfs.desc=Comprimir PDFs para reducir el tamaño del archivo home.compressPdfs.desc=Comprimir PDFs para reducir el tamaño del archivo
compressPdfs.tags=aplastar,pequeño,diminuto compressPdfs.tags=aplastar,pequeño,diminuto
home.unlockPDFForms.title=Desbloquear formularios PDF home.unlockPDFForms.title=Unlock PDF Forms
home.unlockPDFForms.desc=Eliminar la propiedad de solo-lectura de los campos de formulario en un documento PDF. home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document.
unlockPDFForms.tags=quitar,eliminar,formulario,campo,solo-lectura unlockPDFForms.tags=remove,delete,form,field,readonly
home.changeMetadata.title=Cambiar metadatos home.changeMetadata.title=Cambiar metadatos
home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF
@ -609,7 +609,7 @@ login.userIsDisabled=El usuario está desactivado, actualmente el acceso está b
login.alreadyLoggedIn=Ya ha iniciado sesión en login.alreadyLoggedIn=Ya ha iniciado sesión en
login.alreadyLoggedIn2=dispositivos. Cierre sesión en los dispositivos y vuelva a intentarlo. login.alreadyLoggedIn2=dispositivos. Cierre sesión en los dispositivos y vuelva a intentarlo.
login.toManySessions=Tiene demasiadas sesiones activas login.toManySessions=Tiene demasiadas sesiones activas
login.logoutMessage=Se ha cerrado su sesión. login.logoutMessage=You have been logged out.
#auto-redact #auto-redact
autoRedact.title=Auto Censurar Texto autoRedact.title=Auto Censurar Texto
@ -1023,7 +1023,7 @@ multiTool.deleteSelected=Borrar selecionado(s)
multiTool.downloadAll=Exportar multiTool.downloadAll=Exportar
multiTool.downloadSelected=Exportar selecionado(s) multiTool.downloadSelected=Exportar selecionado(s)
multiTool.insertPageBreak=Insertar salto de página multiTool.insertPageBreak=Insertar salto página
multiTool.addFile=Agregar Archivo multiTool.addFile=Agregar Archivo
multiTool.rotateLeft=Rotar a la izquierda multiTool.rotateLeft=Rotar a la izquierda
multiTool.rotateRight=Rotar a la derecha multiTool.rotateRight=Rotar a la derecha
@ -1061,10 +1061,10 @@ pageRemover.placeholder=(por ejemplo 1,2,6 o 1-10,15-30)
#rotate #rotate
rotate.title=Girar PDF rotate.title=Rotar PDF
rotate.header=Girar PDF rotate.header=Rotar PDF
rotate.selectAngle=Seleccionar ángulo de giro (en múltiplos de 90 grados): rotate.selectAngle=Seleccionar ángulo de rotación (en múltiplos de 90 grados):
rotate.submit=Girar rotate.submit=Rotar
#split-pdfs #split-pdfs
@ -1197,9 +1197,9 @@ changeMetadata.selectText.5=Agregar entrada de metadatos personalizados
changeMetadata.submit=Cambiar changeMetadata.submit=Cambiar
#unlockPDFForms #unlockPDFForms
unlockPDFForms.title=Eliminar solo-lectura de los campos de formulario unlockPDFForms.title=Remove Read-Only from Form Fields
unlockPDFForms.header=Desbloquear los formularios PDF unlockPDFForms.header=Unlock PDF Forms
unlockPDFForms.submit=Eliminar unlockPDFForms.submit=Remove
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF a PDF/A pdfToPDFA.title=PDF a PDF/A

View File

@ -364,9 +364,9 @@ home.compressPdfs.title=壓縮
home.compressPdfs.desc=壓縮 PDF 以減少其檔案大小。 home.compressPdfs.desc=壓縮 PDF 以減少其檔案大小。
compressPdfs.tags=壓縮,小,微小 compressPdfs.tags=壓縮,小,微小
home.unlockPDFForms.title=解鎖 PDF 表單 home.unlockPDFForms.title=Unlock PDF Forms
home.unlockPDFForms.desc=移除 PDF 文件中表單欄位的唯讀屬性 home.unlockPDFForms.desc=Remove read-only property of form fields in a PDF document.
unlockPDFForms.tags=移除,刪除,表格,欄位,唯讀 unlockPDFForms.tags=remove,delete,form,field,readonly
home.changeMetadata.title=變更中繼資料 home.changeMetadata.title=變更中繼資料
home.changeMetadata.desc=從 PDF 檔案中變更/移除/新增中繼資料 home.changeMetadata.desc=從 PDF 檔案中變更/移除/新增中繼資料
@ -1197,9 +1197,9 @@ changeMetadata.selectText.5=新增自訂中繼資料項目
changeMetadata.submit=變更 changeMetadata.submit=變更
#unlockPDFForms #unlockPDFForms
unlockPDFForms.title=移除表單欄位的唯讀限制 unlockPDFForms.title=Remove Read-Only from Form Fields
unlockPDFForms.header=解鎖 PDF 表單 unlockPDFForms.header=Unlock PDF Forms
unlockPDFForms.submit=移除 unlockPDFForms.submit=Remove
#pdfToPDFA #pdfToPDFA
pdfToPDFA.title=PDF 轉 PDF/A pdfToPDFA.title=PDF 轉 PDF/A

View File

@ -4,39 +4,38 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import stirling.software.spdf.model.api.PDFWithPageNums;
import stirling.software.common.model.api.PDFWithPageNums;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ConvertToImageRequest extends PDFWithPageNums { public class ConvertToImageRequest extends PDFWithPageNums {
@Schema( @Schema(
description = "The output image format", description = "The output image format",
defaultValue = "png", defaultValue = "png",
allowableValues = {"png", "jpeg", "jpg", "gif", "webp"}, allowableValues = {"png", "jpeg", "jpg", "gif", "webp"},
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String imageFormat; private String imageFormat;
@Schema( @Schema(
description = description =
"Choose between a single image containing all pages or separate images for each" "Choose between a single image containing all pages or separate images for each"
+ " page", + " page",
defaultValue = "multiple", defaultValue = "multiple",
allowableValues = {"single", "multiple"}, allowableValues = {"single", "multiple"},
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String singleOrMultiple; private String singleOrMultiple;
@Schema( @Schema(
description = "The color type of the output image(s)", description = "The color type of the output image(s)",
defaultValue = "color", defaultValue = "color",
allowableValues = {"color", "greyscale", "blackwhite"}, allowableValues = {"color", "greyscale", "blackwhite"},
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String colorType; private String colorType;
@Schema( @Schema(
description = "The DPI (dots per inch) for the output image(s)", description = "The DPI (dots per inch) for the output image(s)",
defaultValue = "300", defaultValue = "300",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Integer dpi; private Integer dpi;
} }

View File

@ -11,6 +11,9 @@ import stirling.software.spdf.model.api.PDFWithPageNums;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ContainsTextRequest extends PDFWithPageNums { public class ContainsTextRequest extends PDFWithPageNums {
@Schema(description = "The text to check for", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(
description = "The text to check for",
defaultValue = "text",
requiredMode = Schema.RequiredMode.REQUIRED)
private String text; private String text;
} }

View File

@ -12,8 +12,8 @@ import stirling.software.spdf.model.api.PDFComparison;
public class FileSizeRequest extends PDFComparison { public class FileSizeRequest extends PDFComparison {
@Schema( @Schema(
description = "Size of the file in bytes", description = "Size of the file in bytes",
requiredMode = Schema.RequiredMode.REQUIRED, requiredMode = Schema.RequiredMode.REQUIRED,
defaultValue = "0") defaultValue = "0")
private long fileSize; private long fileSize;
} }

View File

@ -12,8 +12,8 @@ import stirling.software.spdf.model.api.PDFComparison;
public class PageRotationRequest extends PDFComparison { public class PageRotationRequest extends PDFComparison {
@Schema( @Schema(
description = "Rotation in degrees", description = "Rotation in degrees",
requiredMode = Schema.RequiredMode.REQUIRED, requiredMode = Schema.RequiredMode.REQUIRED,
defaultValue = "0") defaultValue = "0")
private int rotation; private int rotation;
} }

View File

@ -12,9 +12,9 @@ import stirling.software.spdf.model.api.PDFComparison;
public class PageSizeRequest extends PDFComparison { public class PageSizeRequest extends PDFComparison {
@Schema( @Schema(
description = "Standard Page Size", description = "Standard Page Size",
allowableValues = {"A0", "A1", "A2", "A3", "A4", "A5", "A6", "LETTER", "LEGAL"}, allowableValues = {"A0", "A1", "A2", "A3", "A4", "A5", "A6", "LETTER", "LEGAL"},
defaultValue = "A4", defaultValue = "A4",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String standardPageSize; private String standardPageSize;
} }

View File

@ -14,33 +14,33 @@ import stirling.software.common.model.api.PDFFile;
public class OverlayPdfsRequest extends PDFFile { public class OverlayPdfsRequest extends PDFFile {
@Schema( @Schema(
description = description =
"An array of PDF files to be used as overlays on the base PDF. The order in" "An array of PDF files to be used as overlays on the base PDF. The order in"
+ " these files is applied based on the selected mode.", + " these files is applied based on the selected mode.",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private MultipartFile[] overlayFiles; private MultipartFile[] overlayFiles;
@Schema( @Schema(
description = description =
"The mode of overlaying: 'SequentialOverlay' for sequential application," "The mode of overlaying: 'SequentialOverlay' for sequential application,"
+ " 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay'" + " 'InterleavedOverlay' for round-robin application, 'FixedRepeatOverlay'"
+ " for fixed repetition based on provided counts", + " for fixed repetition based on provided counts",
allowableValues = {"SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay"}, allowableValues = {"SequentialOverlay", "InterleavedOverlay", "FixedRepeatOverlay"},
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String overlayMode; private String overlayMode;
@Schema( @Schema(
description = description =
"An array of integers specifying the number of times each corresponding overlay" "An array of integers specifying the number of times each corresponding overlay"
+ " file should be applied in the 'FixedRepeatOverlay' mode. This should" + " file should be applied in the 'FixedRepeatOverlay' mode. This should"
+ " match the length of the overlayFiles array.", + " match the length of the overlayFiles array.",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private int[] counts; private int[] counts;
@Schema( @Schema(
description = "Overlay position 0 is Foregound, 1 is Background", description = "Overlay position 0 is Foregound, 1 is Background",
allowableValues = {"0", "1"}, allowableValues = {"0", "1"},
requiredMode = Schema.RequiredMode.REQUIRED, requiredMode = Schema.RequiredMode.REQUIRED,
type = "number") type = "number")
private int overlayPosition; private int overlayPosition;
} }

View File

@ -14,9 +14,9 @@ import stirling.software.spdf.model.api.PDFWithPageNums;
public class AddStampRequest extends PDFWithPageNums { public class AddStampRequest extends PDFWithPageNums {
@Schema( @Schema(
description = "The stamp type (text or image)", description = "The stamp type (text or image)",
allowableValues = {"text", "image"}, allowableValues = {"text", "image"},
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String stampType; private String stampType;
@Schema(description = "The stamp text", defaultValue = "Stirling Software") @Schema(description = "The stamp text", defaultValue = "Stirling Software")
@ -26,60 +26,60 @@ public class AddStampRequest extends PDFWithPageNums {
private MultipartFile stampImage; private MultipartFile stampImage;
@Schema( @Schema(
description = "The selected alphabet of the stamp text", description = "The selected alphabet of the stamp text",
allowableValues = {"roman", "arabic", "japanese", "korean", "chinese"}, allowableValues = {"roman", "arabic", "japanese", "korean", "chinese"},
defaultValue = "roman") defaultValue = "roman")
private String alphabet = "roman"; private String alphabet = "roman";
@Schema( @Schema(
description = "The font size of the stamp text and image", description = "The font size of the stamp text and image",
defaultValue = "30", defaultValue = "30",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private float fontSize; private float fontSize;
@Schema( @Schema(
description = "The rotation of the stamp in degrees", description = "The rotation of the stamp in degrees",
defaultValue = "0", defaultValue = "0",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private float rotation; private float rotation;
@Schema( @Schema(
description = "The opacity of the stamp (0.0 - 1.0)", description = "The opacity of the stamp (0.0 - 1.0)",
defaultValue = "0.5", defaultValue = "0.5",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private float opacity; private float opacity;
@Schema( @Schema(
description = description =
"Position for stamp placement based on a 1-9 grid (1: bottom-left, 2: bottom-center," "Position for stamp placement based on a 1-9 grid (1: bottom-left, 2: bottom-center,"
+ " 3: bottom-right, 4: middle-left, 5: middle-center, 6: middle-right," + " 3: bottom-right, 4: middle-left, 5: middle-center, 6: middle-right,"
+ " 7: top-left, 8: top-center, 9: top-right)", + " 7: top-left, 8: top-center, 9: top-right)",
allowableValues = {"1", "2", "3", "4", "5", "6", "7", "8", "9"}, allowableValues = {"1", "2", "3", "4", "5", "6", "7", "8", "9"},
defaultValue = "5", defaultValue = "5",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private int position; private int position;
@Schema( @Schema(
description = description =
"Override X coordinate for stamp placement. If set, it will override the" "Override X coordinate for stamp placement. If set, it will override the"
+ " position-based calculation. Negative value means no override.", + " position-based calculation. Negative value means no override.",
defaultValue = "-1", defaultValue = "-1",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private float overrideX; // Default to -1 indicating no override private float overrideX; // Default to -1 indicating no override
@Schema( @Schema(
description = description =
"Override Y coordinate for stamp placement. If set, it will override the" "Override Y coordinate for stamp placement. If set, it will override the"
+ " position-based calculation. Negative value means no override.", + " position-based calculation. Negative value means no override.",
defaultValue = "-1", defaultValue = "-1",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private float overrideY; // Default to -1 indicating no override private float overrideY; // Default to -1 indicating no override
@Schema( @Schema(
description = "Specifies the margin size for the stamp.", description = "Specifies the margin size for the stamp.",
allowableValues = {"small", "medium", "large", "x-large"}, allowableValues = {"small", "medium", "large", "x-large"},
defaultValue = "medium", defaultValue = "medium",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String customMargin; private String customMargin;
@Schema(description = "The color of the stamp text", defaultValue = "#d3d3d3") @Schema(description = "The color of the stamp text", defaultValue = "#d3d3d3")

View File

@ -14,71 +14,71 @@ import stirling.software.common.model.api.PDFFile;
public class MetadataRequest extends PDFFile { public class MetadataRequest extends PDFFile {
@Schema( @Schema(
description = "Delete all metadata if set to true", description = "Delete all metadata if set to true",
defaultValue = "false", defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean deleteAll; private Boolean deleteAll;
@Schema( @Schema(
description = "The author of the document", description = "The author of the document",
defaultValue = "author", defaultValue = "author",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String author; private String author;
@Schema( @Schema(
description = "The creation date of the document (format: yyyy/MM/dd HH:mm:ss)", description = "The creation date of the document (format: yyyy/MM/dd HH:mm:ss)",
pattern = "yyyy/MM/dd HH:mm:ss", pattern = "yyyy/MM/dd HH:mm:ss",
defaultValue = "2023/10/01 12:00:00", defaultValue = "2023/10/01 12:00:00",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String creationDate; private String creationDate;
@Schema( @Schema(
description = "The creator of the document", description = "The creator of the document",
defaultValue = "creator", defaultValue = "creator",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String creator; private String creator;
@Schema( @Schema(
description = "The keywords for the document", description = "The keywords for the document",
defaultValue = "keywords", defaultValue = "keywords",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String keywords; private String keywords;
@Schema( @Schema(
description = "The modification date of the document (format: yyyy/MM/dd HH:mm:ss)", description = "The modification date of the document (format: yyyy/MM/dd HH:mm:ss)",
pattern = "yyyy/MM/dd HH:mm:ss", pattern = "yyyy/MM/dd HH:mm:ss",
defaultValue = "2023/10/01 12:00:00", defaultValue = "2023/10/01 12:00:00",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String modificationDate; private String modificationDate;
@Schema( @Schema(
description = "The producer of the document", description = "The producer of the document",
defaultValue = "producer", defaultValue = "producer",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String producer; private String producer;
@Schema( @Schema(
description = "The subject of the document", description = "The subject of the document",
defaultValue = "subject", defaultValue = "subject",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String subject; private String subject;
@Schema( @Schema(
description = "The title of the document", description = "The title of the document",
defaultValue = "title", defaultValue = "title",
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String title; private String title;
@Schema( @Schema(
description = "The trapped status of the document", description = "The trapped status of the document",
defaultValue = "False", defaultValue = "False",
allowableValues = {"True", "False", "Unknown"}, allowableValues = {"True", "False", "Unknown"},
requiredMode = Schema.RequiredMode.NOT_REQUIRED) requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String trapped; private String trapped;
@Schema( @Schema(
description = description =
"Map list of key and value of custom parameters. Note these must start with" "Map list of key and value of custom parameters. Note these must start with"
+ " customKey and customValue if they are non-standard") + " customKey and customValue if they are non-standard")
private Map<String, String> allRequestParams; private Map<String, String> allRequestParams;
} }

View File

@ -12,24 +12,24 @@ import stirling.software.common.model.api.PDFFile;
public class AddPasswordRequest extends PDFFile { public class AddPasswordRequest extends PDFFile {
@Schema( @Schema(
description = description =
"The owner password to be added to the PDF file (Restricts what can be done" "The owner password to be added to the PDF file (Restricts what can be done"
+ " with the document once it is opened)", + " with the document once it is opened)",
format = "password") format = "password")
private String ownerPassword; private String ownerPassword;
@Schema( @Schema(
description = description =
"The password to be added to the PDF file (Restricts the opening of the" "The password to be added to the PDF file (Restricts the opening of the"
+ " document itself.)", + " document itself.)",
format = "password") format = "password")
private String password; private String password;
@Schema( @Schema(
description = "The length of the encryption key", description = "The length of the encryption key",
allowableValues = {"40", "128", "256"}, allowableValues = {"40", "128", "256"},
defaultValue = "256", defaultValue = "256",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private int keyLength = 256; private int keyLength = 256;
@Schema(description = "Whether document assembly is prevented", defaultValue = "false") @Schema(description = "Whether document assembly is prevented", defaultValue = "false")
@ -39,8 +39,8 @@ public class AddPasswordRequest extends PDFFile {
private Boolean preventExtractContent; private Boolean preventExtractContent;
@Schema( @Schema(
description = "Whether content extraction for accessibility is prevented", description = "Whether content extraction for accessibility is prevented",
defaultValue = "false") defaultValue = "false")
private Boolean preventExtractForAccessibility; private Boolean preventExtractForAccessibility;
@Schema(description = "Whether form filling is prevented", defaultValue = "false") @Schema(description = "Whether form filling is prevented", defaultValue = "false")
@ -50,8 +50,8 @@ public class AddPasswordRequest extends PDFFile {
private Boolean preventModify; private Boolean preventModify;
@Schema( @Schema(
description = "Whether modification of annotations is prevented", description = "Whether modification of annotations is prevented",
defaultValue = "false") defaultValue = "false")
private Boolean preventModifyAnnotations; private Boolean preventModifyAnnotations;
@Schema(description = "Whether printing of the document is prevented", defaultValue = "false") @Schema(description = "Whether printing of the document is prevented", defaultValue = "false")

View File

@ -14,19 +14,19 @@ import stirling.software.common.model.api.security.RedactionArea;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ManualRedactPdfRequest extends PDFWithPageNums { public class ManualRedactPdfRequest extends PDFWithPageNums {
@Schema( @Schema(
description = "A list of areas that should be redacted", description = "A list of areas that should be redacted",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private List<RedactionArea> redactions; private List<RedactionArea> redactions;
@Schema( @Schema(
description = "Convert the redacted PDF to an image", description = "Convert the redacted PDF to an image",
defaultValue = "false", defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean convertPDFToImage; private Boolean convertPDFToImage;
@Schema( @Schema(
description = "The color used to fully redact certain pages", description = "The color used to fully redact certain pages",
defaultValue = "#000000", defaultValue = "#000000",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private String pageRedactionColor; private String pageRedactionColor;
} }

View File

@ -12,38 +12,38 @@ import stirling.software.common.model.api.PDFFile;
public class SanitizePdfRequest extends PDFFile { public class SanitizePdfRequest extends PDFFile {
@Schema( @Schema(
description = "Remove JavaScript actions from the PDF", description = "Remove JavaScript actions from the PDF",
defaultValue = "true", defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeJavaScript; private Boolean removeJavaScript;
@Schema( @Schema(
description = "Remove embedded files from the PDF", description = "Remove embedded files from the PDF",
defaultValue = "true", defaultValue = "true",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeEmbeddedFiles; private Boolean removeEmbeddedFiles;
@Schema( @Schema(
description = "Remove XMP metadata from the PDF", description = "Remove XMP metadata from the PDF",
defaultValue = "false", defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeXMPMetadata; private Boolean removeXMPMetadata;
@Schema( @Schema(
description = "Remove document info metadata from the PDF", description = "Remove document info metadata from the PDF",
defaultValue = "false", defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeMetadata; private Boolean removeMetadata;
@Schema( @Schema(
description = "Remove links from the PDF", description = "Remove links from the PDF",
defaultValue = "false", defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeLinks; private Boolean removeLinks;
@Schema( @Schema(
description = "Remove fonts from the PDF", description = "Remove fonts from the PDF",
defaultValue = "false", defaultValue = "false",
requiredMode = Schema.RequiredMode.REQUIRED) requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean removeFonts; private Boolean removeFonts;
} }

View File

@ -14,9 +14,9 @@ server.error.whitelabel.enabled=false
server.error.include-stacktrace=always server.error.include-stacktrace=always
server.error.include-exception=true server.error.include-exception=true
server.error.include-message=always server.error.include-message=always
logging.level.org.springframework.web=DEBUG #logging.level.org.springframework.web=DEBUG
logging.level.org.springframework=DEBUG #logging.level.org.springframework=DEBUG
logging.level.org.springframework.security=DEBUG #logging.level.org.springframework.security=DEBUG
spring.servlet.multipart.max-file-size=2000MB spring.servlet.multipart.max-file-size=2000MB
spring.servlet.multipart.max-request-size=2000MB spring.servlet.multipart.max-request-size=2000MB
server.servlet.session.tracking-modes=cookie server.servlet.session.tracking-modes=cookie

View File

@ -21,12 +21,12 @@
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div> <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
<div class="mb-3"> <div class="mb-3">
<label for="threshold" th:text="#{removeBlanks.threshold}"></label> <label for="threshold" th:text="#{removeBlanks.threshold}"></label>
<input type="number" class="form-control" id="threshold" name="threshold" value="10"> <input type="number" class="form-control" id="threshold" name="threshold" value="10" min="0" max="255" step="1">
<small id="thresholdHelp" class="form-text text-muted" th:text="#{removeBlanks.thresholdDesc}"></small> <small id="thresholdHelp" class="form-text text-muted" th:text="#{removeBlanks.thresholdDesc}"></small>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="whitePercent" th:text="#{removeBlanks.whitePercent}"></label> <label for="whitePercent" th:text="#{removeBlanks.whitePercent}"></label>
<input type="number" class="form-control" id="whitePercent" name="whitePercent" value="99.9" step="0.1"> <input type="number" class="form-control" id="whitePercent" name="whitePercent" value="99.9" step="0.1" min="0.1" max="100">
<small id="whitePercentHelp" class="form-text text-muted" th:text="#{removeBlanks.whitePercentDesc}"></small> <small id="whitePercentHelp" class="form-text text-muted" th:text="#{removeBlanks.whitePercentDesc}"></small>
</div> </div>
<button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeBlanks.submit}"></button> <button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{removeBlanks.submit}"></button>