wip - making db and sessions conditional

This commit is contained in:
Dario Ghunney Ware 2025-04-16 16:56:18 +01:00
parent b9129a6f8c
commit e8430fb351
14 changed files with 103 additions and 49 deletions

View File

@ -53,24 +53,37 @@ public class KeygenLicenseVerifier {
} }
public License verifyLicense(String licenseKeyOrCert) { public License verifyLicense(String licenseKeyOrCert) {
License license;
if (isCertificateLicense(licenseKeyOrCert)) { if (isCertificateLicense(licenseKeyOrCert)) {
log.info("Detected certificate-based license. Processing..."); log.info("Detected certificate-based license. Processing...");
return resultToEnum(verifyCertificateLicense(licenseKeyOrCert), License.ENTERPRISE); boolean isValid = verifyCertificateLicense(licenseKeyOrCert);
if (isValid) {
license = isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
} else {
license = License.NORMAL;
}
} 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 resultToEnum(verifyJWTLicense(licenseKeyOrCert), License.ENTERPRISE); boolean isValid = verifyJWTLicense(licenseKeyOrCert);
if (isValid) {
license = isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
} else {
license = License.NORMAL;
}
} else { } else {
log.info("Detected standard license key. Processing..."); log.info("Detected standard license key. Processing...");
return resultToEnum(verifyStandardLicense(licenseKeyOrCert), License.PRO); boolean isValid = verifyStandardLicense(licenseKeyOrCert);
if (isValid) {
license = isEnterpriseLicense ? License.ENTERPRISE : License.PRO;
} else {
license = License.NORMAL;
}
} }
return license;
} }
private License resultToEnum(boolean result, License option) { private boolean isEnterpriseLicense = false;
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);
@ -82,8 +95,6 @@ public class KeygenLicenseVerifier {
private boolean verifyCertificateLicense(String licenseFile) { private boolean verifyCertificateLicense(String licenseFile) {
try { try {
log.info("Verifying certificate-based license");
String encodedPayload = licenseFile; String encodedPayload = licenseFile;
// Remove the header // Remove the header
encodedPayload = encodedPayload.replace(CERT_PREFIX, ""); encodedPayload = encodedPayload.replace(CERT_PREFIX, "");
@ -106,8 +117,6 @@ public class KeygenLicenseVerifier {
encryptedData = (String) attrs.get("enc"); encryptedData = (String) attrs.get("enc");
encodedSignature = (String) attrs.get("sig"); encodedSignature = (String) attrs.get("sig");
algorithm = (String) attrs.get("alg"); algorithm = (String) attrs.get("alg");
log.info("Certificate algorithm: {}", algorithm);
} catch (JSONException e) { } catch (JSONException e) {
log.error("Failed to parse license file: {}", e.getMessage()); log.error("Failed to parse license file: {}", e.getMessage());
return false; return false;
@ -151,7 +160,6 @@ public class KeygenLicenseVerifier {
private boolean verifyEd25519Signature(String encryptedData, String encodedSignature) { private boolean verifyEd25519Signature(String encryptedData, String encodedSignature) {
try { try {
log.info("Signature to verify: {}", encodedSignature); log.info("Signature to verify: {}", encodedSignature);
log.info("Public key being used: {}", PUBLIC_KEY);
byte[] signatureBytes = Base64.getDecoder().decode(encodedSignature); byte[] signatureBytes = Base64.getDecoder().decode(encodedSignature);
@ -185,8 +193,6 @@ public class KeygenLicenseVerifier {
private boolean processCertificateData(String certData) { private boolean processCertificateData(String certData) {
try { try {
log.info("Processing certificate data: {}", certData);
JSONObject licenseData = new JSONObject(certData); JSONObject licenseData = new JSONObject(certData);
JSONObject metaObj = licenseData.optJSONObject("meta"); JSONObject metaObj = licenseData.optJSONObject("meta");
if (metaObj != null) { if (metaObj != null) {
@ -234,18 +240,9 @@ public class KeygenLicenseVerifier {
applicationProperties.getPremium().setMaxUsers(users); applicationProperties.getPremium().setMaxUsers(users);
log.info("License allows for {} users", users); log.info("License allows for {} users", users);
} }
isEnterpriseLicense = metadataObj.optBoolean("isEnterprise", false);
} }
// Check maxUsers directly in attributes if present from policy definition
// if (attributesObj.has("maxUsers")) {
// int maxUsers = attributesObj.optInt("maxUsers", 0);
// if (maxUsers > 0) {
// applicationProperties.getPremium().setMaxUsers(maxUsers);
// log.info("License directly specifies {} max users",
// maxUsers);
// }
// }
// Check license status if available // Check license status if available
String status = attributesObj.optString("status", null); String status = attributesObj.optString("status", null);
if (status != null if (status != null
@ -388,9 +385,10 @@ public class KeygenLicenseVerifier {
String policyId = policyObj.optString("id", "unknown"); String policyId = policyObj.optString("id", "unknown");
log.info("License uses policy: {}", policyId); log.info("License uses policy: {}", policyId);
// Extract max users from policy if available (customize based on your policy // Extract max users and isEnterprise from policy or metadata
// structure)
int users = policyObj.optInt("users", 0); int users = policyObj.optInt("users", 0);
isEnterpriseLicense = policyObj.optBoolean("isEnterprise", false);
if (users > 0) { if (users > 0) {
applicationProperties.getPremium().setMaxUsers(users); applicationProperties.getPremium().setMaxUsers(users);
log.info("License allows for {} users", users); log.info("License allows for {} users", users);
@ -402,6 +400,9 @@ public class KeygenLicenseVerifier {
users = metadata.optInt("users", 1); users = metadata.optInt("users", 1);
applicationProperties.getPremium().setMaxUsers(users); applicationProperties.getPremium().setMaxUsers(users);
log.info("License allows for {} users (from metadata)", users); log.info("License allows for {} users (from metadata)", users);
// Check for isEnterprise flag in metadata
isEnterpriseLicense = metadata.optBoolean("isEnterprise", false);
} else { } else {
// Default value // Default value
applicationProperties.getPremium().setMaxUsers(1); applicationProperties.getPremium().setMaxUsers(1);
@ -494,6 +495,7 @@ public class KeygenLicenseVerifier {
log.info("Validation detail: " + detail); log.info("Validation detail: " + detail);
log.info("Validation code: " + code); log.info("Validation code: " + code);
// Extract user count
int users = int users =
jsonResponse jsonResponse
.path("data") .path("data")
@ -502,6 +504,16 @@ public class KeygenLicenseVerifier {
.path("users") .path("users")
.asInt(0); .asInt(0);
applicationProperties.getPremium().setMaxUsers(users); applicationProperties.getPremium().setMaxUsers(users);
// Extract isEnterprise flag
isEnterpriseLicense =
jsonResponse
.path("data")
.path("attributes")
.path("metadata")
.path("isEnterprise")
.asBoolean(false);
log.info(applicationProperties.toString()); log.info(applicationProperties.toString());
} else { } else {

View File

@ -14,6 +14,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
@ -32,7 +34,11 @@ import stirling.software.SPDF.utils.UrlUtils;
@Slf4j @Slf4j
@EnableScheduling @EnableScheduling
@SpringBootApplication @SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class
})
public class SPDFApplication { public class SPDFApplication {
private static String serverPortStatic; private static String serverPortStatic;

View File

@ -3,10 +3,10 @@ package stirling.software.SPDF.config.security;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.UUID; import java.util.UUID;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.interfaces.DatabaseInterface; import stirling.software.SPDF.config.interfaces.DatabaseInterface;
@ -14,15 +14,25 @@ import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.Role;
import stirling.software.SPDF.model.exception.UnsupportedProviderException; import stirling.software.SPDF.model.exception.UnsupportedProviderException;
/**
* This class is responsible for the initial security setup of the application. It checks if there
* are any existing users and initializes the admin user if none exist. It also migrates OAuth2
* users to SSO and initializes an internal API user.
*/
@Slf4j @Slf4j
@Component @Component
/*
todo: add @ConditionOnProperty to check if the application is running in a specific environment
add @Profile for enterprise/pro or higher
*/
// @Profile({"pro", "enterprise"})
public class InitialSecuritySetup { public class InitialSecuritySetup {
private final UserService userService; private final UserService userService;
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private final DatabaseInterface databaseService; @Lazy private final DatabaseInterface databaseService;
public InitialSecuritySetup( public InitialSecuritySetup(
UserService userService, UserService userService,
@ -33,19 +43,10 @@ public class InitialSecuritySetup {
this.databaseService = databaseService; this.databaseService = databaseService;
} }
@PostConstruct // @PostConstruct
public void init() { public void init() {
try { try {
initialiseDB();
if (!userService.hasUsers()) {
if (databaseService.hasBackup()) {
databaseService.importDatabase();
} else {
initializeAdminUser();
}
}
userService.migrateOauth2ToSSO();
initializeInternalApiUser(); initializeInternalApiUser();
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) { } catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
log.error("Failed to initialize security setup.", e); log.error("Failed to initialize security setup.", e);
@ -53,6 +54,19 @@ public class InitialSecuritySetup {
} }
} }
@ConditionalOnProperty(name = "premium.proFeatures.database", havingValue = "true")
private void initialiseDB() throws SQLException, UnsupportedProviderException {
if (!userService.hasUsers()) {
if (databaseService.hasBackup()) {
databaseService.importDatabase();
} else {
initializeAdminUser();
}
}
userService.migrateOauth2ToSSO();
}
private void initializeAdminUser() throws SQLException, UnsupportedProviderException { private void initializeAdminUser() throws SQLException, UnsupportedProviderException {
String initialUsername = String initialUsername =
applicationProperties.getSecurity().getInitialLogin().getUsername(); applicationProperties.getSecurity().getInitialLogin().getUsername();

View File

@ -5,6 +5,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -35,6 +36,7 @@ import stirling.software.SPDF.model.User;
@Slf4j @Slf4j
@Component @Component
@ConditionalOnProperty(name = "premium.enabled", havingValue = "true")
public class UserAuthenticationFilter extends OncePerRequestFilter { public class UserAuthenticationFilter extends OncePerRequestFilter {
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;

View File

@ -5,6 +5,7 @@ import java.sql.SQLException;
import java.util.*; import java.util.*;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@ -44,7 +45,7 @@ public class UserService implements UserServiceInterface {
private final SessionPersistentRegistry sessionRegistry; private final SessionPersistentRegistry sessionRegistry;
private final DatabaseInterface databaseService; @Lazy private final DatabaseInterface databaseService;
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;

View File

@ -3,9 +3,11 @@ package stirling.software.SPDF.config.security.database;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -16,7 +18,9 @@ import stirling.software.SPDF.model.exception.UnsupportedProviderException;
@Slf4j @Slf4j
@Getter @Getter
@Lazy
@Configuration @Configuration
@ConditionalOnProperty(name = "premium.proFeatures.database", havingValue = "true")
public class DatabaseConfig { public class DatabaseConfig {
public final String DATASOURCE_DEFAULT_URL; public final String DATASOURCE_DEFAULT_URL;
@ -35,7 +39,7 @@ public class DatabaseConfig {
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;MODE=PostgreSQL";
log.debug("Database URL: {}", DATASOURCE_DEFAULT_URL); log.debug("Database URL: {}", DATASOURCE_DEFAULT_URL);
this.applicationProperties = applicationProperties; this.applicationProperties = applicationProperties;
this.runningProOrHigher = runningProOrHigher; this.runningProOrHigher = runningProOrHigher;

View File

@ -21,6 +21,7 @@ import java.util.stream.Collectors;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.context.annotation.Lazy;
import org.springframework.jdbc.datasource.init.CannotReadScriptException; import org.springframework.jdbc.datasource.init.CannotReadScriptException;
import org.springframework.jdbc.datasource.init.ScriptException; import org.springframework.jdbc.datasource.init.ScriptException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -33,6 +34,7 @@ import stirling.software.SPDF.model.ApplicationProperties;
import stirling.software.SPDF.model.exception.BackupNotFoundException; import stirling.software.SPDF.model.exception.BackupNotFoundException;
import stirling.software.SPDF.utils.FileInfo; import stirling.software.SPDF.utils.FileInfo;
@Lazy
@Slf4j @Slf4j
@Service @Service
public class DatabaseService implements DatabaseInterface { public class DatabaseService implements DatabaseInterface {
@ -42,7 +44,7 @@ public class DatabaseService implements DatabaseInterface {
private final Path BACKUP_DIR; private final Path BACKUP_DIR;
private final ApplicationProperties applicationProperties; private final ApplicationProperties applicationProperties;
private final DataSource dataSource; @Lazy private final DataSource dataSource;
public DatabaseService(ApplicationProperties applicationProperties, DataSource dataSource) { public DatabaseService(ApplicationProperties applicationProperties, DataSource dataSource) {
this.BACKUP_DIR = this.BACKUP_DIR =

View File

@ -1,6 +1,7 @@
package stirling.software.SPDF.config.security.session; package stirling.software.SPDF.config.security.session;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpSessionEvent; import jakarta.servlet.http.HttpSessionEvent;
@ -8,8 +9,9 @@ import jakarta.servlet.http.HttpSessionListener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j @Slf4j
@Component
@ConditionalOnProperty(name = "premium.enabled", havingValue = "true")
public class CustomHttpSessionListener implements HttpSessionListener { public class CustomHttpSessionListener implements HttpSessionListener {
private SessionPersistentRegistry sessionPersistentRegistry; private SessionPersistentRegistry sessionPersistentRegistry;

View File

@ -4,6 +4,7 @@ import java.time.Duration;
import java.util.*; import java.util.*;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
@ -16,6 +17,7 @@ import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrin
import stirling.software.SPDF.model.SessionEntity; import stirling.software.SPDF.model.SessionEntity;
@Component @Component
@ConditionalOnProperty(name = "premium.enabled", havingValue = "true")
public class SessionPersistentRegistry implements SessionRegistry { public class SessionPersistentRegistry implements SessionRegistry {
private final SessionRepository sessionRepository; private final SessionRepository sessionRepository;

View File

@ -1,10 +1,12 @@
package stirling.software.SPDF.config.security.session; package stirling.software.SPDF.config.security.session;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.core.session.SessionRegistryImpl;
@Configuration @Configuration
@ConditionalOnProperty(name = "premium.enabled", havingValue = "true")
public class SessionRegistryConfig { public class SessionRegistryConfig {
@Bean @Bean

View File

@ -3,6 +3,7 @@ package stirling.software.SPDF.config.security.session;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
@ -14,6 +15,7 @@ import jakarta.transaction.Transactional;
import stirling.software.SPDF.model.SessionEntity; import stirling.software.SPDF.model.SessionEntity;
@Repository @Repository
@ConditionalOnProperty(name = "premium.enabled", havingValue = "true")
public interface SessionRepository extends JpaRepository<SessionEntity, String> { public interface SessionRepository extends JpaRepository<SessionEntity, String> {
List<SessionEntity> findByPrincipalName(String principalName); List<SessionEntity> findByPrincipalName(String principalName);

View File

@ -5,11 +5,13 @@ import java.time.temporal.ChronoUnit;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.core.session.SessionInformation; import org.springframework.security.core.session.SessionInformation;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@ConditionalOnProperty(name = "premium.enabled", havingValue = "true")
public class SessionScheduled { public class SessionScheduled {
private final SessionPersistentRegistry sessionPersistentRegistry; private final SessionPersistentRegistry sessionPersistentRegistry;

View File

@ -28,11 +28,12 @@ spring.thymeleaf.encoding=UTF-8
spring.web.resources.mime-mappings.webmanifest=application/manifest+json spring.web.resources.mime-mappings.webmanifest=application/manifest+json
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000} spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
management.endpoints.web.exposure.include=beans
spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
spring.datasource.driver-class-name=org.h2.Driver spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa spring.datasource.username=sa
spring.datasource.password= spring.datasource.password=
spring.h2.console.enabled=false spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
server.servlet.session.timeout:30m server.servlet.session.timeout:30m
# Change the default URL path for OpenAPI JSON # Change the default URL path for OpenAPI JSON
@ -40,4 +41,4 @@ springdoc.api-docs.path=/v1/api-docs
# Set the URL of the OpenAPI JSON for the Swagger UI # Set the URL of the OpenAPI JSON for the Swagger UI
springdoc.swagger-ui.url=/v1/api-docs springdoc.swagger-ui.url=/v1/api-docs
posthog.api.key=phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq posthog.api.key=phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq
posthog.host=https://eu.i.posthog.com posthog.host=https://eu.i.posthog.com

View File

@ -64,6 +64,7 @@ premium:
key: 00000000-0000-0000-0000-000000000000 key: 00000000-0000-0000-0000-000000000000
enabled: false # Enable license key checks for pro/enterprise features enabled: false # Enable license key checks for pro/enterprise features
proFeatures: proFeatures:
database: false # Enable database features
SSOAutoLogin: false SSOAutoLogin: false
CustomMetadata: CustomMetadata:
autoUpdateMetadata: false autoUpdateMetadata: false
@ -95,6 +96,7 @@ system:
enableUrlToPDF: false # Set to 'true' to enable URL to PDF, INTERNAL ONLY, known security issues, should not be used externally enableUrlToPDF: false # Set to 'true' to enable URL to PDF, INTERNAL ONLY, known security issues, should not be used externally
disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML) disableSanitize: false # set to true to disable Sanitize HTML; (can lead to injections in HTML)
datasource: datasource:
enabled: true # set to 'true' to enable the database connection
enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration enableCustomDatabase: false # Enterprise users ONLY, set this property to 'true' if you would like to use your own custom database configuration
customDatabaseUrl: '' # eg jdbc:postgresql://localhost:5432/postgres, set the url for your own custom database connection. If provided, the type, hostName, port and name are not necessary and will not be used customDatabaseUrl: '' # eg jdbc:postgresql://localhost:5432/postgres, set the url for your own custom database connection. If provided, the type, hostName, port and name are not necessary and will not be used
username: postgres # set the database username username: postgres # set the database username