mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-06 09:12:02 +00:00
Cleanups and making distinction between pro and enterprise (#3250)
# 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/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/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/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/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: a <a>
This commit is contained in:
parent
989c468db2
commit
3420a8633b
@ -25,7 +25,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "stirling.software"
|
group = "stirling.software"
|
||||||
version = "0.45.0"
|
version = "0.45.1"
|
||||||
|
|
||||||
java {
|
java {
|
||||||
// 17 is lowest but we support and recommend 21
|
// 17 is lowest but we support and recommend 21
|
||||||
|
@ -7,6 +7,7 @@ import org.springframework.core.annotation.Order;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.EnterpriseEdition;
|
import stirling.software.SPDF.model.ApplicationProperties.EnterpriseEdition;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties.Premium;
|
import stirling.software.SPDF.model.ApplicationProperties.Premium;
|
||||||
@ -27,9 +28,14 @@ public class EEAppConfig {
|
|||||||
migrateEnterpriseSettingsToPremium(this.applicationProperties);
|
migrateEnterpriseSettingsToPremium(this.applicationProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = "runningProOrHigher")
|
||||||
|
public boolean runningProOrHigher() {
|
||||||
|
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = "runningEE")
|
@Bean(name = "runningEE")
|
||||||
public boolean runningEnterpriseEdition() {
|
public boolean runningEnterprise() {
|
||||||
return licenseKeyChecker.getEnterpriseEnabledResult();
|
return licenseKeyChecker.getPremiumLicenseEnabledResult() == License.ENTERPRISE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "SSOAutoLogin")
|
@Bean(name = "SSOAutoLogin")
|
||||||
|
@ -25,6 +25,13 @@ import stirling.software.SPDF.utils.GeneralUtils;
|
|||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class KeygenLicenseVerifier {
|
public class KeygenLicenseVerifier {
|
||||||
|
|
||||||
|
enum License {
|
||||||
|
NORMAL,
|
||||||
|
PRO,
|
||||||
|
ENTERPRISE
|
||||||
|
}
|
||||||
|
|
||||||
// License verification configuration
|
// License verification configuration
|
||||||
private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
|
private static final String ACCOUNT_ID = "e5430f69-e834-4ae4-befd-b602aae5f372";
|
||||||
private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
|
private static final String BASE_URL = "https://api.keygen.sh/v1/accounts";
|
||||||
@ -45,19 +52,26 @@ public class KeygenLicenseVerifier {
|
|||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verifyLicense(String licenseKeyOrCert) {
|
public License verifyLicense(String licenseKeyOrCert) {
|
||||||
if (isCertificateLicense(licenseKeyOrCert)) {
|
if (isCertificateLicense(licenseKeyOrCert)) {
|
||||||
log.info("Detected certificate-based license. Processing...");
|
log.info("Detected certificate-based license. Processing...");
|
||||||
return verifyCertificateLicense(licenseKeyOrCert);
|
return resultToEnum(verifyCertificateLicense(licenseKeyOrCert), License.ENTERPRISE);
|
||||||
} else if (isJWTLicense(licenseKeyOrCert)) {
|
} else if (isJWTLicense(licenseKeyOrCert)) {
|
||||||
log.info("Detected JWT-style license key. Processing...");
|
log.info("Detected JWT-style license key. Processing...");
|
||||||
return verifyJWTLicense(licenseKeyOrCert);
|
return resultToEnum(verifyJWTLicense(licenseKeyOrCert), License.ENTERPRISE);
|
||||||
} else {
|
} else {
|
||||||
log.info("Detected standard license key. Processing...");
|
log.info("Detected standard license key. Processing...");
|
||||||
return verifyStandardLicense(licenseKeyOrCert);
|
return resultToEnum(verifyStandardLicense(licenseKeyOrCert), License.PRO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private License resultToEnum(boolean result, License option) {
|
||||||
|
if (result) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
return License.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isCertificateLicense(String license) {
|
private boolean isCertificateLicense(String license) {
|
||||||
return license != null && license.trim().startsWith(CERT_PREFIX);
|
return license != null && license.trim().startsWith(CERT_PREFIX);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import org.springframework.stereotype.Component;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.EE.KeygenLicenseVerifier.License;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ public class LicenseKeyChecker {
|
|||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private boolean premiumEnabledResult = false;
|
private License premiumEnabledResult = License.NORMAL;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LicenseKeyChecker(
|
public LicenseKeyChecker(
|
||||||
@ -41,19 +42,21 @@ public class LicenseKeyChecker {
|
|||||||
|
|
||||||
private void checkLicense() {
|
private void checkLicense() {
|
||||||
if (!applicationProperties.getPremium().isEnabled()) {
|
if (!applicationProperties.getPremium().isEnabled()) {
|
||||||
premiumEnabledResult = false;
|
premiumEnabledResult = License.NORMAL;
|
||||||
} else {
|
} else {
|
||||||
String licenseKey = getLicenseKeyContent(applicationProperties.getPremium().getKey());
|
String licenseKey = getLicenseKeyContent(applicationProperties.getPremium().getKey());
|
||||||
if (licenseKey != null) {
|
if (licenseKey != null) {
|
||||||
premiumEnabledResult = licenseService.verifyLicense(licenseKey);
|
premiumEnabledResult = licenseService.verifyLicense(licenseKey);
|
||||||
if (premiumEnabledResult) {
|
if (License.ENTERPRISE == premiumEnabledResult) {
|
||||||
log.info("License key is valid.");
|
log.info("License key is Enterprise.");
|
||||||
|
} else if (License.PRO == premiumEnabledResult) {
|
||||||
|
log.info("License key is Pro.");
|
||||||
} else {
|
} else {
|
||||||
log.info("License key is invalid.");
|
log.info("License key is invalid, defaulting to non pro license.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.error("Failed to obtain license key content.");
|
log.error("Failed to obtain license key content.");
|
||||||
premiumEnabledResult = false;
|
premiumEnabledResult = License.NORMAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +94,7 @@ public class LicenseKeyChecker {
|
|||||||
checkLicense();
|
checkLicense();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getEnterpriseEnabledResult() {
|
public License getPremiumLicenseEnabledResult() {
|
||||||
return premiumEnabledResult;
|
return premiumEnabledResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
@ -23,14 +22,14 @@ public class EndpointConfiguration {
|
|||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
||||||
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
|
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
|
||||||
private final boolean runningEE;
|
private final boolean runningProOrHigher;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public EndpointConfiguration(
|
public EndpointConfiguration(
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
@Qualifier("runningEE") boolean runningEE) {
|
@Qualifier("runningProOrHigher") boolean runningProOrHigher) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.runningEE = runningEE;
|
this.runningProOrHigher = runningProOrHigher;
|
||||||
init();
|
init();
|
||||||
processEnvironmentConfigs();
|
processEnvironmentConfigs();
|
||||||
}
|
}
|
||||||
@ -98,7 +97,7 @@ public class EndpointConfiguration {
|
|||||||
// is false)
|
// is false)
|
||||||
.map(Map.Entry::getKey)
|
.map(Map.Entry::getKey)
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
|
|
||||||
if (!disabledList.isEmpty()) {
|
if (!disabledList.isEmpty()) {
|
||||||
log.info(
|
log.info(
|
||||||
@ -286,7 +285,7 @@ public class EndpointConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!runningEE) {
|
if (!runningProOrHigher) {
|
||||||
disableGroup("enterprise");
|
disableGroup("enterprise");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class EnterpriseEndpointFilter extends OncePerRequestFilter {
|
public class EnterpriseEndpointFilter extends OncePerRequestFilter {
|
||||||
private final boolean runningEE;
|
private final boolean runningProOrHigher;
|
||||||
|
|
||||||
public EnterpriseEndpointFilter(@Qualifier("runningEE") boolean runningEE) {
|
public EnterpriseEndpointFilter(@Qualifier("runningProOrHigher") boolean runningProOrHigher) {
|
||||||
this.runningEE = runningEE;
|
this.runningProOrHigher = runningProOrHigher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -25,7 +25,7 @@ public class EnterpriseEndpointFilter extends OncePerRequestFilter {
|
|||||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
if (!runningEE && isPrometheusEndpointRequest(request)) {
|
if (!runningProOrHigher && isPrometheusEndpointRequest(request)) {
|
||||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ public class ExternalAppDepConfig {
|
|||||||
private List<String> getAffectedFeatures(String group) {
|
private List<String> getAffectedFeatures(String group) {
|
||||||
return endpointConfiguration.getEndpointsForGroup(group).stream()
|
return endpointConfiguration.getEndpointsForGroup(group).stream()
|
||||||
.map(endpoint -> formatEndpointAsFeature(endpoint))
|
.map(endpoint -> formatEndpointAsFeature(endpoint))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String formatEndpointAsFeature(String endpoint) {
|
private String formatEndpointAsFeature(String endpoint) {
|
||||||
|
@ -2,7 +2,6 @@ package stirling.software.SPDF.config.security;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
@ -58,6 +57,6 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|||||||
private Collection<? extends GrantedAuthority> getAuthorities(Set<Authority> authorities) {
|
private Collection<? extends GrantedAuthority> getAuthorities(Set<Authority> authorities) {
|
||||||
return authorities.stream()
|
return authorities.stream()
|
||||||
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
|
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,13 +46,13 @@ import stirling.software.SPDF.repository.PersistentLoginRepository;
|
|||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@EnableMethodSecurity
|
@EnableMethodSecurity
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@DependsOn("runningEE")
|
@DependsOn("runningProOrHigher")
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
private final CustomUserDetailsService userDetailsService;
|
private final CustomUserDetailsService userDetailsService;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final boolean loginEnabledValue;
|
private final boolean loginEnabledValue;
|
||||||
private final boolean runningEE;
|
private final boolean runningProOrHigher;
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private final UserAuthenticationFilter userAuthenticationFilter;
|
private final UserAuthenticationFilter userAuthenticationFilter;
|
||||||
@ -69,7 +69,7 @@ public class SecurityConfiguration {
|
|||||||
CustomUserDetailsService userDetailsService,
|
CustomUserDetailsService userDetailsService,
|
||||||
@Lazy UserService userService,
|
@Lazy UserService userService,
|
||||||
@Qualifier("loginEnabled") boolean loginEnabledValue,
|
@Qualifier("loginEnabled") boolean loginEnabledValue,
|
||||||
@Qualifier("runningEE") boolean runningEE,
|
@Qualifier("runningProOrHigher") boolean runningProOrHigher,
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
UserAuthenticationFilter userAuthenticationFilter,
|
UserAuthenticationFilter userAuthenticationFilter,
|
||||||
LoginAttemptService loginAttemptService,
|
LoginAttemptService loginAttemptService,
|
||||||
@ -83,7 +83,7 @@ public class SecurityConfiguration {
|
|||||||
this.userDetailsService = userDetailsService;
|
this.userDetailsService = userDetailsService;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.loginEnabledValue = loginEnabledValue;
|
this.loginEnabledValue = loginEnabledValue;
|
||||||
this.runningEE = runningEE;
|
this.runningProOrHigher = runningProOrHigher;
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.userAuthenticationFilter = userAuthenticationFilter;
|
this.userAuthenticationFilter = userAuthenticationFilter;
|
||||||
this.loginAttemptService = loginAttemptService;
|
this.loginAttemptService = loginAttemptService;
|
||||||
@ -254,7 +254,7 @@ public class SecurityConfiguration {
|
|||||||
.permitAll());
|
.permitAll());
|
||||||
}
|
}
|
||||||
// Handle SAML
|
// Handle SAML
|
||||||
if (applicationProperties.getSecurity().isSaml2Active() && runningEE) {
|
if (applicationProperties.getSecurity().isSaml2Active() && runningProOrHigher) {
|
||||||
// Configure the authentication provider
|
// Configure the authentication provider
|
||||||
OpenSaml4AuthenticationProvider authenticationProvider =
|
OpenSaml4AuthenticationProvider authenticationProvider =
|
||||||
new OpenSaml4AuthenticationProvider();
|
new OpenSaml4AuthenticationProvider();
|
||||||
|
@ -3,7 +3,6 @@ package stirling.software.SPDF.config.security;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
@ -99,7 +98,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
authority ->
|
authority ->
|
||||||
new SimpleGrantedAuthority(
|
new SimpleGrantedAuthority(
|
||||||
authority.getAuthority()))
|
authority.getAuthority()))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
authentication = new ApiKeyAuthenticationToken(user.get(), apiKey, authorities);
|
authentication = new ApiKeyAuthenticationToken(user.get(), apiKey, authorities);
|
||||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
} catch (AuthenticationException e) {
|
} catch (AuthenticationException e) {
|
||||||
|
@ -3,7 +3,6 @@ package stirling.software.SPDF.config.security;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
@ -108,7 +107,7 @@ public class UserService implements UserServiceInterface {
|
|||||||
// Convert each Authority object into a SimpleGrantedAuthority object.
|
// Convert each Authority object into a SimpleGrantedAuthority object.
|
||||||
return user.getAuthorities().stream()
|
return user.getAuthorities().stream()
|
||||||
.map((Authority authority) -> new SimpleGrantedAuthority(authority.getAuthority()))
|
.map((Authority authority) -> new SimpleGrantedAuthority(authority.getAuthority()))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateApiKey() {
|
private String generateApiKey() {
|
||||||
|
@ -27,18 +27,18 @@ public class DatabaseConfig {
|
|||||||
public static final String POSTGRES_DRIVER = "org.postgresql.Driver";
|
public static final String POSTGRES_DRIVER = "org.postgresql.Driver";
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private final boolean runningEE;
|
private final boolean runningProOrHigher;
|
||||||
|
|
||||||
public DatabaseConfig(
|
public DatabaseConfig(
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
@Qualifier("runningEE") boolean runningEE) {
|
@Qualifier("runningProOrHigher") boolean runningProOrHigher) {
|
||||||
DATASOURCE_DEFAULT_URL =
|
DATASOURCE_DEFAULT_URL =
|
||||||
"jdbc:h2:file:"
|
"jdbc:h2:file:"
|
||||||
+ InstallationPathConfig.getConfigPath()
|
+ InstallationPathConfig.getConfigPath()
|
||||||
+ "stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
+ "stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
||||||
log.debug("Database URL: {}", DATASOURCE_DEFAULT_URL);
|
log.debug("Database URL: {}", DATASOURCE_DEFAULT_URL);
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.runningEE = runningEE;
|
this.runningProOrHigher = runningProOrHigher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +54,7 @@ public class DatabaseConfig {
|
|||||||
public DataSource dataSource() throws UnsupportedProviderException {
|
public DataSource dataSource() throws UnsupportedProviderException {
|
||||||
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
|
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
|
||||||
|
|
||||||
if (!runningEE) {
|
if (!runningProOrHigher) {
|
||||||
return useDefaultDataSource(dataSourceBuilder);
|
return useDefaultDataSource(dataSourceBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ import java.time.format.DateTimeFormatter;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@ -170,7 +169,7 @@ public class DatabaseService implements DatabaseInterface {
|
|||||||
List<FileInfo> filteredBackupList =
|
List<FileInfo> filteredBackupList =
|
||||||
this.getBackupList().stream()
|
this.getBackupList().stream()
|
||||||
.filter(backup -> !backup.getFileName().startsWith(BACKUP_PREFIX + "user_"))
|
.filter(backup -> !backup.getFileName().startsWith(BACKUP_PREFIX + "user_"))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
|
|
||||||
if (filteredBackupList.size() > 5) {
|
if (filteredBackupList.size() > 5) {
|
||||||
deleteOldestBackup(filteredBackupList);
|
deleteOldestBackup(filteredBackupList);
|
||||||
|
@ -10,7 +10,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@ -165,7 +164,7 @@ public class MergeController {
|
|||||||
List<PDField> fieldsToRemove =
|
List<PDField> fieldsToRemove =
|
||||||
acroForm.getFields().stream()
|
acroForm.getFields().stream()
|
||||||
.filter(field -> field instanceof PDSignatureField)
|
.filter(field -> field instanceof PDSignatureField)
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
|
|
||||||
if (!fieldsToRemove.isEmpty()) {
|
if (!fieldsToRemove.isEmpty()) {
|
||||||
acroForm.flatten(
|
acroForm.flatten(
|
||||||
|
@ -8,7 +8,6 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
@ -146,7 +145,7 @@ public class ConvertImgPDFController {
|
|||||||
List<Path> webpFiles =
|
List<Path> webpFiles =
|
||||||
Files.walk(tempOutputDir)
|
Files.walk(tempOutputDir)
|
||||||
.filter(path -> path.toString().endsWith(".webp"))
|
.filter(path -> path.toString().endsWith(".webp"))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
|
|
||||||
if (webpFiles.isEmpty()) {
|
if (webpFiles.isEmpty()) {
|
||||||
log.error("No WebP files were created in: {}", tempOutputDir.toString());
|
log.error("No WebP files were created in: {}", tempOutputDir.toString());
|
||||||
|
@ -5,7 +5,6 @@ import java.io.*;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
@ -63,7 +62,7 @@ public class OCRController {
|
|||||||
.filter(file -> file.getName().endsWith(".traineddata"))
|
.filter(file -> file.getName().endsWith(".traineddata"))
|
||||||
.map(file -> file.getName().replace(".traineddata", ""))
|
.map(file -> file.getName().replace(".traineddata", ""))
|
||||||
.filter(lang -> !lang.equalsIgnoreCase("osd"))
|
.filter(lang -> !lang.equalsIgnoreCase("osd"))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
|
@PostMapping(consumes = "multipart/form-data", value = "/ocr-pdf")
|
||||||
|
@ -5,7 +5,6 @@ import java.io.InputStream;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
@ -64,9 +63,7 @@ public class PipelineController {
|
|||||||
log.info("Received POST request to /handleData with {} files", files.length);
|
log.info("Received POST request to /handleData with {} files", files.length);
|
||||||
|
|
||||||
List<String> operationNames =
|
List<String> operationNames =
|
||||||
config.getOperations().stream()
|
config.getOperations().stream().map(PipelineOperation::getOperation).toList();
|
||||||
.map(PipelineOperation::getOperation)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
Map<String, Object> properties = new HashMap<>();
|
Map<String, Object> properties = new HashMap<>();
|
||||||
properties.put("operations", operationNames);
|
properties.put("operations", operationNames);
|
||||||
|
@ -11,7 +11,6 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
@ -168,7 +167,7 @@ public class PipelineProcessor {
|
|||||||
file ->
|
file ->
|
||||||
finalinputFileTypes.stream()
|
finalinputFileTypes.stream()
|
||||||
.anyMatch(file.getFilename()::endsWith))
|
.anyMatch(file.getFilename()::endsWith))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
// Check if there are matching files
|
// Check if there are matching files
|
||||||
if (!matchingFiles.isEmpty()) {
|
if (!matchingFiles.isEmpty()) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package stirling.software.SPDF.controller.api.security;
|
package stirling.software.SPDF.controller.api.security;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
|
||||||
@ -59,7 +58,7 @@ public class RemoveCertSignController {
|
|||||||
List<PDField> fieldsToRemove =
|
List<PDField> fieldsToRemove =
|
||||||
acroForm.getFields().stream()
|
acroForm.getFields().stream()
|
||||||
.filter(field -> field instanceof PDSignatureField)
|
.filter(field -> field instanceof PDSignatureField)
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
|
|
||||||
if (!fieldsToRemove.isEmpty()) {
|
if (!fieldsToRemove.isEmpty()) {
|
||||||
acroForm.flatten(fieldsToRemove, false);
|
acroForm.flatten(fieldsToRemove, false);
|
||||||
|
@ -66,13 +66,13 @@ public class SanitizeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (removeXMPMetadata) {
|
if (removeXMPMetadata) {
|
||||||
sanitizeXMPMetadata(document);
|
sanitizeXMPMetadata(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeMetadata) {
|
if (removeMetadata) {
|
||||||
sanitizeDocumentInfoMetadata(document);
|
sanitizeDocumentInfoMetadata(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeLinks) {
|
if (removeLinks) {
|
||||||
sanitizeLinks(document);
|
sanitizeLinks(document);
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ public class SanitizeController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sanitizeDocumentInfoMetadata(PDDocument document) {
|
private void sanitizeDocumentInfoMetadata(PDDocument document) {
|
||||||
PDDocumentInformation docInfo = document.getDocumentInformation();
|
PDDocumentInformation docInfo = document.getDocumentInformation();
|
||||||
if (docInfo != null) {
|
if (docInfo != null) {
|
||||||
@ -167,8 +167,6 @@ public class SanitizeController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void sanitizeLinks(PDDocument document) throws IOException {
|
private void sanitizeLinks(PDDocument document) throws IOException {
|
||||||
for (PDPage page : document.getPages()) {
|
for (PDPage page : document.getPages()) {
|
||||||
for (PDAnnotation annotation : page.getAnnotations()) {
|
for (PDAnnotation annotation : page.getAnnotations()) {
|
||||||
|
@ -10,8 +10,8 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@ -56,14 +56,17 @@ public class AccountWebController {
|
|||||||
private final SessionPersistentRegistry sessionPersistentRegistry;
|
private final SessionPersistentRegistry sessionPersistentRegistry;
|
||||||
// Assuming you have a repository for user operations
|
// Assuming you have a repository for user operations
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
|
private final boolean runningEE;
|
||||||
|
|
||||||
public AccountWebController(
|
public AccountWebController(
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
SessionPersistentRegistry sessionPersistentRegistry,
|
SessionPersistentRegistry sessionPersistentRegistry,
|
||||||
UserRepository userRepository) {
|
UserRepository userRepository,
|
||||||
|
@Qualifier("runningEE") boolean runningEE) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
|
this.runningEE = runningEE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/login")
|
@GetMapping("/login")
|
||||||
@ -197,6 +200,9 @@ public class AccountWebController {
|
|||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@GetMapping("/usage")
|
@GetMapping("/usage")
|
||||||
public String showUsage() {
|
public String showUsage() {
|
||||||
|
if (!runningEE) {
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
return "usage";
|
return "usage";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +285,7 @@ public class AccountWebController {
|
|||||||
return u2LastRequest.compareTo(u1LastRequest);
|
return u2LastRequest.compareTo(u1LastRequest);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
String messageType = request.getParameter("messageType");
|
String messageType = request.getParameter("messageType");
|
||||||
|
|
||||||
String deleteMessage;
|
String deleteMessage;
|
||||||
@ -325,7 +331,7 @@ public class AccountWebController {
|
|||||||
model.addAttribute("activeUsers", activeUsers);
|
model.addAttribute("activeUsers", activeUsers);
|
||||||
model.addAttribute("disabledUsers", disabledUsers);
|
model.addAttribute("disabledUsers", disabledUsers);
|
||||||
|
|
||||||
model.addAttribute("maxEnterpriseUsers", applicationProperties.getPremium().getMaxUsers());
|
model.addAttribute("maxPaidUsers", applicationProperties.getPremium().getMaxUsers());
|
||||||
return "adminSettings";
|
return "adminSettings";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -65,7 +64,7 @@ public class GeneralWebController {
|
|||||||
List<Path> jsonFiles =
|
List<Path> jsonFiles =
|
||||||
paths.filter(Files::isRegularFile)
|
paths.filter(Files::isRegularFile)
|
||||||
.filter(p -> p.toString().endsWith(".json"))
|
.filter(p -> p.toString().endsWith(".json"))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
for (Path jsonFile : jsonFiles) {
|
for (Path jsonFile : jsonFiles) {
|
||||||
String content = Files.readString(jsonFile, StandardCharsets.UTF_8);
|
String content = Files.readString(jsonFile, StandardCharsets.UTF_8);
|
||||||
pipelineConfigs.add(content);
|
pipelineConfigs.add(content);
|
||||||
@ -261,7 +260,7 @@ public class GeneralWebController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to read font directory from " + locationPattern, e);
|
throw new RuntimeException("Failed to read font directory from " + locationPattern, e);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package stirling.software.SPDF.controller.web;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -287,7 +286,7 @@ public class MetricsController {
|
|||||||
return counts.entrySet().stream()
|
return counts.entrySet().stream()
|
||||||
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
|
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
|
||||||
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getUniqueUserCount(String method, Optional<String> endpoint) {
|
private double getUniqueUserCount(String method, Optional<String> endpoint) {
|
||||||
@ -349,7 +348,7 @@ public class MetricsController {
|
|||||||
return uniqueUsers.entrySet().stream()
|
return uniqueUsers.entrySet().stream()
|
||||||
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue().size()))
|
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue().size()))
|
||||||
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/uptime")
|
@GetMapping("/uptime")
|
||||||
|
@ -4,7 +4,6 @@ import java.io.File;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
@ -123,8 +122,8 @@ public class OtherWebController {
|
|||||||
return Arrays.stream(files)
|
return Arrays.stream(files)
|
||||||
.filter(file -> file.getName().endsWith(".traineddata"))
|
.filter(file -> file.getName().endsWith(".traineddata"))
|
||||||
.map(file -> file.getName().replace(".traineddata", ""))
|
.map(file -> file.getName().replace(".traineddata", ""))
|
||||||
.filter(lang -> !lang.equalsIgnoreCase("osd"))
|
.filter(lang -> !lang.equalsIgnoreCase("osd")).sorted()
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/ocr-pdf")
|
@GetMapping("/ocr-pdf")
|
||||||
@ -132,7 +131,6 @@ public class OtherWebController {
|
|||||||
public ModelAndView ocrPdfPage() {
|
public ModelAndView ocrPdfPage() {
|
||||||
ModelAndView modelAndView = new ModelAndView("misc/ocr-pdf");
|
ModelAndView modelAndView = new ModelAndView("misc/ocr-pdf");
|
||||||
List<String> languages = getAvailableTesseractLanguages();
|
List<String> languages = getAvailableTesseractLanguages();
|
||||||
Collections.sort(languages);
|
|
||||||
modelAndView.addObject("languages", languages);
|
modelAndView.addObject("languages", languages);
|
||||||
modelAndView.addObject("currentPage", "ocr-pdf");
|
modelAndView.addObject("currentPage", "ocr-pdf");
|
||||||
return modelAndView;
|
return modelAndView;
|
||||||
|
@ -22,7 +22,7 @@ public class SanitizePdfRequest extends PDFFile {
|
|||||||
|
|
||||||
@Schema(description = "Remove document info metadata from the PDF", defaultValue = "false")
|
@Schema(description = "Remove document info metadata from the PDF", defaultValue = "false")
|
||||||
private boolean removeMetadata;
|
private boolean removeMetadata;
|
||||||
|
|
||||||
@Schema(description = "Remove links from the PDF", defaultValue = "false")
|
@Schema(description = "Remove links from the PDF", defaultValue = "false")
|
||||||
private boolean removeLinks;
|
private boolean removeLinks;
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import static stirling.software.SPDF.model.UsernameAttribute.EMAIL;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@ -59,8 +58,7 @@ public class Provider {
|
|||||||
|
|
||||||
public void setScopes(String scopes) {
|
public void setScopes(String scopes) {
|
||||||
if (scopes != null && !scopes.isBlank()) {
|
if (scopes != null && !scopes.isBlank()) {
|
||||||
this.scopes =
|
this.scopes = Arrays.stream(scopes.split(",")).map(String::trim).toList();
|
||||||
Arrays.stream(scopes.split(",")).map(String::trim).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,17 +68,17 @@ public class CustomPDFDocumentFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for loading a PDF document from a file. Automatically selects the most
|
* Main entry point for loading a PDF document from a file. Automatically selects the most
|
||||||
* appropriate loading strategy.
|
* appropriate loading strategy.
|
||||||
*/
|
*/
|
||||||
public PDDocument load(File file) throws IOException {
|
public PDDocument load(File file) throws IOException {
|
||||||
return load(file, false);
|
return load(file, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for loading a PDF document from a file with read-only option.
|
* Main entry point for loading a PDF document from a file with read-only option. Automatically
|
||||||
* Automatically selects the most appropriate loading strategy.
|
* selects the most appropriate loading strategy.
|
||||||
*/
|
*/
|
||||||
public PDDocument load(File file, boolean readOnly) throws IOException {
|
public PDDocument load(File file, boolean readOnly) throws IOException {
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
throw new IllegalArgumentException("File cannot be null");
|
throw new IllegalArgumentException("File cannot be null");
|
||||||
@ -95,17 +95,17 @@ public class CustomPDFDocumentFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for loading a PDF document from a Path. Automatically selects the most
|
* Main entry point for loading a PDF document from a Path. Automatically selects the most
|
||||||
* appropriate loading strategy.
|
* appropriate loading strategy.
|
||||||
*/
|
*/
|
||||||
public PDDocument load(Path path) throws IOException {
|
public PDDocument load(Path path) throws IOException {
|
||||||
return load(path, false);
|
return load(path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point for loading a PDF document from a Path with read-only option.
|
* Main entry point for loading a PDF document from a Path with read-only option. Automatically
|
||||||
* Automatically selects the most appropriate loading strategy.
|
* selects the most appropriate loading strategy.
|
||||||
*/
|
*/
|
||||||
public PDDocument load(Path path, boolean readOnly) throws IOException {
|
public PDDocument load(Path path, boolean readOnly) throws IOException {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
throw new IllegalArgumentException("File cannot be null");
|
throw new IllegalArgumentException("File cannot be null");
|
||||||
@ -170,7 +170,8 @@ public class CustomPDFDocumentFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Load with password from InputStream and read-only option */
|
/** Load with password from InputStream and read-only option */
|
||||||
public PDDocument load(InputStream input, String password, boolean readOnly) throws IOException {
|
public PDDocument load(InputStream input, String password, boolean readOnly)
|
||||||
|
throws IOException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
throw new IllegalArgumentException("InputStream cannot be null");
|
throw new IllegalArgumentException("InputStream cannot be null");
|
||||||
}
|
}
|
||||||
@ -179,7 +180,8 @@ public class CustomPDFDocumentFactory {
|
|||||||
Path tempFile = createTempFile("pdf-stream-");
|
Path tempFile = createTempFile("pdf-stream-");
|
||||||
|
|
||||||
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
PDDocument doc = loadAdaptivelyWithPassword(tempFile.toFile(), Files.size(tempFile), password);
|
PDDocument doc =
|
||||||
|
loadAdaptivelyWithPassword(tempFile.toFile(), Files.size(tempFile), password);
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
postProcessDocument(doc);
|
postProcessDocument(doc);
|
||||||
}
|
}
|
||||||
@ -203,7 +205,7 @@ public class CustomPDFDocumentFactory {
|
|||||||
|
|
||||||
/** Load from a PDFFile object with read-only option */
|
/** Load from a PDFFile object with read-only option */
|
||||||
public PDDocument load(PDFFile pdfFile, boolean readOnly) throws IOException {
|
public PDDocument load(PDFFile pdfFile, boolean readOnly) throws IOException {
|
||||||
return load(pdfFile.getFileInput(), readOnly);
|
return load(pdfFile.getFileInput(), readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load from a MultipartFile */
|
/** Load from a MultipartFile */
|
||||||
@ -213,8 +215,7 @@ public class CustomPDFDocumentFactory {
|
|||||||
|
|
||||||
/** Load from a MultipartFile with read-only option */
|
/** Load from a MultipartFile with read-only option */
|
||||||
public PDDocument load(MultipartFile pdfFile, boolean readOnly) throws IOException {
|
public PDDocument load(MultipartFile pdfFile, boolean readOnly) throws IOException {
|
||||||
return load(pdfFile.getInputStream(), readOnly);
|
return load(pdfFile.getInputStream(), readOnly);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load with password from MultipartFile */
|
/** Load with password from MultipartFile */
|
||||||
@ -223,10 +224,11 @@ public class CustomPDFDocumentFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Load with password from MultipartFile with read-only option */
|
/** Load with password from MultipartFile with read-only option */
|
||||||
public PDDocument load(MultipartFile fileInput, String password, boolean readOnly) throws IOException {
|
public PDDocument load(MultipartFile fileInput, String password, boolean readOnly)
|
||||||
return load(fileInput.getInputStream(), password, readOnly);
|
throws IOException {
|
||||||
|
return load(fileInput.getInputStream(), password, readOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the appropriate caching strategy based on file size and available memory. This
|
* Determine the appropriate caching strategy based on file size and available memory. This
|
||||||
* common method is used by both password and non-password loading paths.
|
* common method is used by both password and non-password loading paths.
|
||||||
@ -471,5 +473,4 @@ public class CustomPDFDocumentFactory {
|
|||||||
return saveToBytes(document);
|
return saveToBytes(document);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,18 +17,18 @@ public class PdfMetadataService {
|
|||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private final String stirlingPDFLabel;
|
private final String stirlingPDFLabel;
|
||||||
private final UserServiceInterface userService;
|
private final UserServiceInterface userService;
|
||||||
private final boolean runningEE;
|
private final boolean runningProOrHigher;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public PdfMetadataService(
|
public PdfMetadataService(
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
@Qualifier("StirlingPDFLabel") String stirlingPDFLabel,
|
@Qualifier("StirlingPDFLabel") String stirlingPDFLabel,
|
||||||
@Qualifier("runningEE") boolean runningEE,
|
@Qualifier("runningProOrHigher") boolean runningProOrHigher,
|
||||||
@Autowired(required = false) UserServiceInterface userService) {
|
@Autowired(required = false) UserServiceInterface userService) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.stirlingPDFLabel = stirlingPDFLabel;
|
this.stirlingPDFLabel = stirlingPDFLabel;
|
||||||
this.userService = userService;
|
this.userService = userService;
|
||||||
this.runningEE = runningEE;
|
this.runningProOrHigher = runningProOrHigher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
|
public PdfMetadata extractMetadataFromPdf(PDDocument pdf) {
|
||||||
@ -69,7 +69,7 @@ public class PdfMetadataService {
|
|||||||
.getProFeatures()
|
.getProFeatures()
|
||||||
.getCustomMetadata()
|
.getCustomMetadata()
|
||||||
.isAutoUpdateMetadata()
|
.isAutoUpdateMetadata()
|
||||||
&& runningEE) {
|
&& runningProOrHigher) {
|
||||||
|
|
||||||
creator =
|
creator =
|
||||||
applicationProperties
|
applicationProperties
|
||||||
@ -98,7 +98,7 @@ public class PdfMetadataService {
|
|||||||
.getProFeatures()
|
.getProFeatures()
|
||||||
.getCustomMetadata()
|
.getCustomMetadata()
|
||||||
.isAutoUpdateMetadata()
|
.isAutoUpdateMetadata()
|
||||||
&& runningEE) {
|
&& runningProOrHigher) {
|
||||||
author =
|
author =
|
||||||
applicationProperties
|
applicationProperties
|
||||||
.getPremium()
|
.getPremium()
|
||||||
|
@ -7,7 +7,6 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thymeleaf.util.StringUtils;
|
import org.thymeleaf.util.StringUtils;
|
||||||
@ -70,7 +69,7 @@ public class SignatureService {
|
|||||||
return Files.list(folder)
|
return Files.list(folder)
|
||||||
.filter(path -> isImageFile(path))
|
.filter(path -> isImageFile(path))
|
||||||
.map(path -> new SignatureFile(path.getFileName().toString(), category))
|
.map(path -> new SignatureFile(path.getFileName().toString(), category))
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getSignatureBytes(String username, String fileName) throws IOException {
|
public byte[] getSignatureBytes(String username, String fileName) throws IOException {
|
||||||
|
@ -9,7 +9,6 @@ import java.nio.file.SimpleFileVisitor;
|
|||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
@ -172,9 +171,7 @@ public class FileToPdf {
|
|||||||
|
|
||||||
// Search for the main HTML file.
|
// Search for the main HTML file.
|
||||||
try (Stream<Path> walk = Files.walk(tempDirectory)) {
|
try (Stream<Path> walk = Files.walk(tempDirectory)) {
|
||||||
List<Path> htmlFiles =
|
List<Path> htmlFiles = walk.filter(file -> file.toString().endsWith(".html")).toList();
|
||||||
walk.filter(file -> file.toString().endsWith(".html"))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
if (htmlFiles.isEmpty()) {
|
if (htmlFiles.isEmpty()) {
|
||||||
throw new IOException("No HTML files found in the unzipped directory.");
|
throw new IOException("No HTML files found in the unzipped directory.");
|
||||||
|
@ -34,10 +34,10 @@
|
|||||||
<!-- User Settings Title -->
|
<!-- User Settings Title -->
|
||||||
<div style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
|
<div style="background: var(--md-sys-color-outline-variant);padding: .8rem; margin: 10px 0; border-radius: 2rem; text-align: center;">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
th:data-bs-toggle="${@runningEE && totalUsers >= maxEnterpriseUsers} ? null : 'modal'"
|
th:data-bs-toggle="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : 'modal'"
|
||||||
th:data-bs-target="${@runningEE && totalUsers >= maxEnterpriseUsers} ? null : '#addUserModal'"
|
th:data-bs-target="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? null : '#addUserModal'"
|
||||||
th:class="${@runningEE && totalUsers >= maxEnterpriseUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
|
th:class="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? 'btn btn-danger' : 'btn btn-outline-success'"
|
||||||
th:title="${@runningEE && totalUsers >= maxEnterpriseUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
|
th:title="${@runningProOrHigher && totalUsers >= maxPaidUsers} ? #{adminUserSettings.maxUsersReached} : #{adminUserSettings.addUser}">
|
||||||
<span class="material-symbols-rounded">person_add</span>
|
<span class="material-symbols-rounded">person_add</span>
|
||||||
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
|
<span th:text="#{adminUserSettings.addUser}">Add New User</span>
|
||||||
</a>
|
</a>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
|
<span th:text="#{adminUserSettings.changeUserRole}">Change User's Role</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="/usage"
|
<a href="/usage" th:if="${@runningEE}"
|
||||||
class="btn btn-outline-success"
|
class="btn btn-outline-success"
|
||||||
th:title="#{adminUserSettings.usage}">
|
th:title="#{adminUserSettings.usage}">
|
||||||
<span class="material-symbols-rounded">analytics</span>
|
<span class="material-symbols-rounded">analytics</span>
|
||||||
@ -61,7 +61,7 @@
|
|||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong>
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.totalUsers}">Total Users:</strong>
|
||||||
<span th:text="${totalUsers}"></span>
|
<span th:text="${totalUsers}"></span>
|
||||||
<span th:if="${@runningEE}" th:text="'/'+${maxEnterpriseUsers}"></span>
|
<span th:if="${@runningProOrHigher}" th:text="'/'+${maxPaidUsers}"></span>
|
||||||
|
|
||||||
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
|
<strong style="margin-left: 20px;" th:text="#{adminUserSettings.activeUsers}">Active Users:</strong>
|
||||||
<span th:text="${activeUsers}"></span>
|
<span th:text="${activeUsers}"></span>
|
||||||
@ -126,7 +126,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<p th:if="${!@runningEE}" th:text="#{enterpriseEdition.ssoAdvert}"></p>
|
<p th:if="${!@runningProOrHigher}" th:text="#{enterpriseEdition.ssoAdvert}"></p>
|
||||||
|
|
||||||
<script th:inline="javascript">
|
<script th:inline="javascript">
|
||||||
const delete_confirm_text = /*[[#{adminUserSettings.confirmDeleteUser}]]*/ 'Should the user be deleted?';
|
const delete_confirm_text = /*[[#{adminUserSettings.confirmDeleteUser}]]*/ 'Should the user be deleted?';
|
||||||
|
@ -170,7 +170,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item" th:if="${!@runningEE}">
|
<li class="nav-item" th:if="${!@runningProOrHigher}">
|
||||||
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank"
|
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank"
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer">
|
||||||
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
|
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
const runningEE = /*[[${@runningEE}]]*/ false;
|
const runningProOrHigher = /*[[${@runningProOrHigher}]]*/ false;
|
||||||
const SSOAutoLogin = /*[[${@SSOAutoLogin}]]*/ false;
|
const SSOAutoLogin = /*[[${@SSOAutoLogin}]]*/ false;
|
||||||
const loginMethod = /*[[${loginMethod}]]*/ 'normal';
|
const loginMethod = /*[[${loginMethod}]]*/ 'normal';
|
||||||
const providerList = /*[[${providerList}]]*/ {};
|
const providerList = /*[[${providerList}]]*/ {};
|
||||||
@ -47,7 +47,7 @@
|
|||||||
!hasLogout &&
|
!hasLogout &&
|
||||||
!hasMessage &&
|
!hasMessage &&
|
||||||
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
redirectAttempts < MAX_REDIRECT_ATTEMPTS &&
|
||||||
loginMethod !== 'normal' && runningEE && SSOAutoLogin;
|
loginMethod !== 'normal' && runningProOrHigher && SSOAutoLogin;
|
||||||
|
|
||||||
console.log('Should redirect:', shouldAutoRedirect, {
|
console.log('Should redirect:', shouldAutoRedirect, {
|
||||||
'No error': !hasRedirectError,
|
'No error': !hasRedirectError,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user