mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-19 05:55:03 +00:00
style: improve formatting and import order consistency across codebase (#3761)
# Description of Changes This pull request applies consistent formatting and import ordering across the codebase. Specifically: - Reordered imports according to the configured Spotless `importOrder()` directive. - Enabled formatting flags such as `trimTrailingWhitespace`, `leadingTabsToSpaces`, and `endWithNewline`. - Resolved inconsistencies in blank lines and spacing between imports and annotations. - Applied consistent formatting to annotations and method declarations. - Removed unused or redundant import statements. This change improves code readability, enforces a consistent style, and prepares the codebase for future automated formatting checks. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] 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) - [x] I have performed a self-review of my own code - [x] 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.
This commit is contained in:
parent
a208d55525
commit
8632ccb870
@ -170,16 +170,16 @@ subprojects {
|
|||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all packaging tasks depend on writeVersion from root project
|
// Ensure all packaging tasks depend on writeVersion from root project
|
||||||
tasks.withType(org.springframework.boot.gradle.tasks.bundling.BootJar) {
|
tasks.withType(org.springframework.boot.gradle.tasks.bundling.BootJar) {
|
||||||
dependsOn(rootProject.tasks.writeVersion)
|
dependsOn(rootProject.tasks.writeVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Jar) {
|
tasks.withType(Jar) {
|
||||||
dependsOn(rootProject.tasks.writeVersion)
|
dependsOn(rootProject.tasks.writeVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(org.gradle.api.tasks.bundling.Zip) {
|
tasks.withType(org.gradle.api.tasks.bundling.Zip) {
|
||||||
dependsOn(rootProject.tasks.writeVersion)
|
dependsOn(rootProject.tasks.writeVersion)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,13 @@ bootRun {
|
|||||||
spotless {
|
spotless {
|
||||||
java {
|
java {
|
||||||
target sourceSets.main.allJava
|
target sourceSets.main.allJava
|
||||||
googleJavaFormat(googleJavaFormatVersion).aosp()
|
googleJavaFormat(googleJavaFormatVersion).aosp().reorderImports(false)
|
||||||
|
|
||||||
|
importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling")
|
||||||
|
toggleOffOn()
|
||||||
|
trimTrailingWhitespace()
|
||||||
|
leadingTabsToSpaces()
|
||||||
|
endWithNewline()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -4,9 +4,11 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
|
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/** A custom RandomAccessRead implementation that deletes the file when closed */
|
/** A custom RandomAccessRead implementation that deletes the file when closed */
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DeletingRandomAccessFile extends RandomAccessReadBufferedFile {
|
public class DeletingRandomAccessFile extends RandomAccessReadBufferedFile {
|
||||||
|
@ -8,9 +8,7 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
@ -24,6 +22,11 @@ import org.springframework.core.io.ClassPathResource;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.thymeleaf.spring6.SpringTemplateEngine;
|
import org.thymeleaf.spring6.SpringTemplateEngine;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
|
@ -10,7 +10,9 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.util.YamlHelper;
|
import stirling.software.common.util.YamlHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,13 +3,16 @@ package stirling.software.common.configuration;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.thymeleaf.IEngineConfiguration;
|
import org.thymeleaf.IEngineConfiguration;
|
||||||
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
|
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
|
||||||
import org.thymeleaf.templateresource.FileTemplateResource;
|
import org.thymeleaf.templateresource.FileTemplateResource;
|
||||||
import org.thymeleaf.templateresource.ITemplateResource;
|
import org.thymeleaf.templateresource.ITemplateResource;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.InputStreamTemplateResource;
|
import stirling.software.common.model.InputStreamTemplateResource;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -2,6 +2,7 @@ package stirling.software.common.configuration;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package stirling.software.common.configuration;
|
package stirling.software.common.configuration;
|
||||||
|
|
||||||
import com.posthog.java.PostHog;
|
|
||||||
import jakarta.annotation.PreDestroy;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
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 com.posthog.java.PostHog;
|
||||||
|
|
||||||
|
import jakarta.annotation.PreDestroy;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PostHogConfig {
|
public class PostHogConfig {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package stirling.software.common.configuration;
|
package stirling.software.common.configuration;
|
||||||
|
|
||||||
import com.posthog.java.PostHogLogger;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.posthog.java.PostHogLogger;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class PostHogLoggerImpl implements PostHogLogger {
|
public class PostHogLoggerImpl implements PostHogLogger {
|
||||||
|
@ -2,10 +2,13 @@ package stirling.software.common.configuration;
|
|||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.ApplicationProperties.CustomPaths.Operations;
|
import stirling.software.common.model.ApplicationProperties.CustomPaths.Operations;
|
||||||
import stirling.software.common.model.ApplicationProperties.CustomPaths.Pipeline;
|
import stirling.software.common.model.ApplicationProperties.CustomPaths.Pipeline;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.common.configuration;
|
package stirling.software.common.configuration;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||||
import org.springframework.core.env.PropertiesPropertySource;
|
import org.springframework.core.env.PropertiesPropertySource;
|
||||||
import org.springframework.core.env.PropertySource;
|
import org.springframework.core.env.PropertySource;
|
||||||
|
@ -12,11 +12,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.ToString;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
@ -28,6 +24,13 @@ import org.springframework.core.io.FileSystemResource;
|
|||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.support.EncodedResource;
|
import org.springframework.core.io.support.EncodedResource;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.configuration.InstallationPathConfig;
|
import stirling.software.common.configuration.InstallationPathConfig;
|
||||||
import stirling.software.common.configuration.YamlPropertySourceFactory;
|
import stirling.software.common.configuration.YamlPropertySourceFactory;
|
||||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||||
@ -486,14 +489,14 @@ public class ApplicationProperties {
|
|||||||
public static class EnterpriseFeatures {
|
public static class EnterpriseFeatures {
|
||||||
private PersistentMetrics persistentMetrics = new PersistentMetrics();
|
private PersistentMetrics persistentMetrics = new PersistentMetrics();
|
||||||
private Audit audit = new Audit();
|
private Audit audit = new Audit();
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class Audit {
|
public static class Audit {
|
||||||
private boolean enabled = true;
|
private boolean enabled = true;
|
||||||
private int level = 2; // 0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE
|
private int level = 2; // 0=OFF, 1=BASIC, 2=STANDARD, 3=VERBOSE
|
||||||
private int retentionDays = 90;
|
private int retentionDays = 90;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class PersistentMetrics {
|
public static class PersistentMetrics {
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
@ -5,6 +5,7 @@ import java.nio.file.Paths;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
|
||||||
import org.thymeleaf.templateresource.ITemplateResource;
|
import org.thymeleaf.templateresource.ITemplateResource;
|
||||||
|
|
||||||
public class InputStreamTemplateResource implements ITemplateResource {
|
public class InputStreamTemplateResource implements ITemplateResource {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.common.model;
|
package stirling.software.common.model;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package stirling.software.common.model.api;
|
package stirling.software.common.model.api;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package stirling.software.common.model.api;
|
package stirling.software.common.model.api;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package stirling.software.common.model.api.converters;
|
package stirling.software.common.model.api.converters;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import stirling.software.common.model.api.PDFFile;
|
import stirling.software.common.model.api.PDFFile;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package stirling.software.common.model.api.converters;
|
package stirling.software.common.model.api.converters;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import stirling.software.common.model.api.PDFFile;
|
import stirling.software.common.model.api.PDFFile;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.common.model.api.security;
|
package stirling.software.common.model.api.security;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package stirling.software.common.model.enumeration;
|
|||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@ package stirling.software.common.model.oauth2;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.common.model.enumeration.UsernameAttribute;
|
import stirling.software.common.model.enumeration.UsernameAttribute;
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@ -2,7 +2,9 @@ package stirling.software.common.model.oauth2;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.common.model.enumeration.UsernameAttribute;
|
import stirling.software.common.model.enumeration.UsernameAttribute;
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@ -2,7 +2,9 @@ package stirling.software.common.model.oauth2;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.common.model.enumeration.UsernameAttribute;
|
import stirling.software.common.model.enumeration.UsernameAttribute;
|
||||||
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@ -5,8 +5,10 @@ import static stirling.software.common.model.enumeration.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 lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.common.model.enumeration.UsernameAttribute;
|
import stirling.software.common.model.enumeration.UsernameAttribute;
|
||||||
import stirling.software.common.model.exception.UnsupportedClaimException;
|
import stirling.software.common.model.exception.UnsupportedClaimException;
|
||||||
|
|
||||||
|
@ -8,8 +8,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.examples.util.DeletingRandomAccessFile;
|
import org.apache.pdfbox.examples.util.DeletingRandomAccessFile;
|
||||||
import org.apache.pdfbox.io.IOUtils;
|
import org.apache.pdfbox.io.IOUtils;
|
||||||
@ -19,6 +18,10 @@ import org.apache.pdfbox.io.ScratchFile;
|
|||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.api.PDFFile;
|
import stirling.software.common.model.api.PDFFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package stirling.software.common.service;
|
package stirling.software.common.service;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
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;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.PdfMetadata;
|
import stirling.software.common.model.PdfMetadata;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package stirling.software.common.service;
|
package stirling.software.common.service;
|
||||||
|
|
||||||
import com.posthog.java.PostHog;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.management.GarbageCollectorMXBean;
|
import java.lang.management.GarbageCollectorMXBean;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
@ -17,11 +16,15 @@ import java.util.HashMap;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
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;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.posthog.java.PostHog;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@ -3,6 +3,7 @@ package stirling.software.common.util;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||||
|
|
||||||
public class CheckProgramInstall {
|
public class CheckProgramInstall {
|
||||||
|
@ -19,10 +19,7 @@ import java.util.Map;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.experimental.UtilityClass;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.pdfbox.cos.COSDictionary;
|
import org.apache.pdfbox.cos.COSDictionary;
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
@ -38,6 +35,12 @@ import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
|
|||||||
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
|
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.api.converters.EmlToPdfRequest;
|
import stirling.software.common.model.api.converters.EmlToPdfRequest;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -2,6 +2,7 @@ package stirling.software.common.util;
|
|||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
@ -11,10 +11,13 @@ import java.util.*;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.configuration.RuntimePathConfig;
|
import stirling.software.common.configuration.RuntimePathConfig;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
import io.github.pixee.security.ZipSecurity;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.FileVisitResult;
|
import java.nio.file.FileVisitResult;
|
||||||
@ -14,6 +13,9 @@ 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;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import io.github.pixee.security.ZipSecurity;
|
||||||
|
|
||||||
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
|
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
|
||||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
import com.fathzer.soft.javaluator.DoubleEvaluator;
|
|
||||||
import io.github.pixee.security.HostValidator;
|
|
||||||
import io.github.pixee.security.Urls;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -17,11 +14,19 @@ import java.util.Arrays;
|
|||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import com.fathzer.soft.javaluator.DoubleEvaluator;
|
||||||
|
|
||||||
|
import io.github.pixee.security.HostValidator;
|
||||||
|
import io.github.pixee.security.Urls;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.configuration.InstallationPathConfig;
|
import stirling.software.common.configuration.InstallationPathConfig;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import com.drew.imaging.ImageMetadataReader;
|
import com.drew.imaging.ImageMetadataReader;
|
||||||
import com.drew.imaging.ImageProcessingException;
|
import com.drew.imaging.ImageProcessingException;
|
||||||
import com.drew.metadata.Metadata;
|
import com.drew.metadata.Metadata;
|
||||||
import com.drew.metadata.MetadataException;
|
import com.drew.metadata.MetadataException;
|
||||||
import com.drew.metadata.exif.ExifSubIFDDirectory;
|
import com.drew.metadata.exif.ExifSubIFDDirectory;
|
||||||
import java.awt.geom.AffineTransform;
|
|
||||||
import java.awt.image.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ImageProcessingUtils {
|
public class ImageProcessingUtils {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
|
|
||||||
import com.vladsch.flexmark.util.data.MutableDataSet;
|
|
||||||
import io.github.pixee.security.Filenames;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
@ -15,14 +12,22 @@ import java.util.List;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
|
||||||
|
import com.vladsch.flexmark.util.data.MutableDataSet;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
import io.github.pixee.security.Filenames;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.RenderedImage;
|
import java.awt.image.RenderedImage;
|
||||||
@ -11,9 +10,10 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import javax.imageio.*;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.pdfbox.cos.COSName;
|
import org.apache.pdfbox.cos.COSName;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@ -30,6 +30,11 @@ import org.apache.pdfbox.rendering.ImageType;
|
|||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.apache.pdfbox.text.PDFTextStripper;
|
import org.apache.pdfbox.text.PDFTextStripper;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
import io.github.pixee.security.BoundedLineReader;
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -13,7 +12,11 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import io.github.pixee.security.BoundedLineReader;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
public class UrlUtils {
|
public class UrlUtils {
|
||||||
|
|
||||||
public static String getOrigin(HttpServletRequest request) {
|
public static String getOrigin(HttpServletRequest request) {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package stirling.software.common.util;
|
package stirling.software.common.util;
|
||||||
|
|
||||||
import io.github.pixee.security.Filenames;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@ -12,6 +12,8 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import io.github.pixee.security.Filenames;
|
||||||
|
|
||||||
public class WebResponseUtils {
|
public class WebResponseUtils {
|
||||||
|
|
||||||
public static ResponseEntity<byte[]> boasToWebResponse(
|
public static ResponseEntity<byte[]> boasToWebResponse(
|
||||||
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.snakeyaml.engine.v2.api.Dump;
|
import org.snakeyaml.engine.v2.api.Dump;
|
||||||
import org.snakeyaml.engine.v2.api.DumpSettings;
|
import org.snakeyaml.engine.v2.api.DumpSettings;
|
||||||
import org.snakeyaml.engine.v2.api.LoadSettings;
|
import org.snakeyaml.engine.v2.api.LoadSettings;
|
||||||
@ -30,6 +30,8 @@ import org.snakeyaml.engine.v2.nodes.Tag;
|
|||||||
import org.snakeyaml.engine.v2.parser.ParserImpl;
|
import org.snakeyaml.engine.v2.parser.ParserImpl;
|
||||||
import org.snakeyaml.engine.v2.scanner.StreamReader;
|
import org.snakeyaml.engine.v2.scanner.StreamReader;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class YamlHelper {
|
public class YamlHelper {
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@ -21,6 +21,9 @@ import org.apache.pdfbox.pdmodel.font.Standard14Fonts;
|
|||||||
import org.apache.pdfbox.text.TextPosition;
|
import org.apache.pdfbox.text.TextPosition;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.api.misc.HighContrastColorCombination;
|
import stirling.software.common.model.api.misc.HighContrastColorCombination;
|
||||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||||
|
|
||||||
|
@ -7,7 +7,9 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
@ -16,6 +18,7 @@ import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
|||||||
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||||
|
|
||||||
public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
|
public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
|
||||||
|
@ -3,6 +3,7 @@ package stirling.software.common.util.misc;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.text.PDFTextStripperByArea;
|
import org.apache.pdfbox.text.PDFTextStripperByArea;
|
||||||
import org.apache.pdfbox.text.TextPosition;
|
import org.apache.pdfbox.text.TextPosition;
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package stirling.software.common.util.misc;
|
package stirling.software.common.util.misc;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import stirling.software.common.model.api.PDFFile;
|
import stirling.software.common.model.api.PDFFile;
|
||||||
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
import stirling.software.common.model.api.misc.ReplaceAndInvert;
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package stirling.software.common.util.propertyeditor;
|
package stirling.software.common.util.propertyeditor;
|
||||||
|
|
||||||
|
import java.beans.PropertyEditorSupport;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import java.beans.PropertyEditorSupport;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.api.security.RedactionArea;
|
import stirling.software.common.model.api.security.RedactionArea;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package stirling.software.common.util.propertyeditor;
|
package stirling.software.common.util.propertyeditor;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import java.beans.PropertyEditorSupport;
|
import java.beans.PropertyEditorSupport;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
public class StringToMapPropertyEditor extends PropertyEditorSupport {
|
public class StringToMapPropertyEditor extends PropertyEditorSupport {
|
||||||
|
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
@ -7,7 +7,13 @@ bootRun {
|
|||||||
spotless {
|
spotless {
|
||||||
java {
|
java {
|
||||||
target sourceSets.main.allJava
|
target sourceSets.main.allJava
|
||||||
googleJavaFormat(googleJavaFormatVersion).aosp()
|
googleJavaFormat(googleJavaFormatVersion).aosp().reorderImports(false)
|
||||||
|
|
||||||
|
importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling")
|
||||||
|
toggleOffOn()
|
||||||
|
trimTrailingWhitespace()
|
||||||
|
leadingTabsToSpaces()
|
||||||
|
endWithNewline()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package stirling.software.proprietary.audit;
|
package stirling.software.proprietary.audit;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import java.lang.reflect.Method;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
@ -10,18 +11,17 @@ import org.aspectj.lang.reflect.MethodSignature;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
|
||||||
import stirling.software.proprietary.service.AuditService;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
import lombok.RequiredArgsConstructor;
|
||||||
* Aspect for processing {@link Audited} annotations.
|
import lombok.extern.slf4j.Slf4j;
|
||||||
*/
|
|
||||||
|
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
||||||
|
import stirling.software.proprietary.service.AuditService;
|
||||||
|
|
||||||
|
/** Aspect for processing {@link Audited} annotations. */
|
||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -36,18 +36,20 @@ public class AuditAspect {
|
|||||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
Method method = signature.getMethod();
|
Method method = signature.getMethod();
|
||||||
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
||||||
|
|
||||||
// Fast path: use unified check to determine if we should audit
|
// Fast path: use unified check to determine if we should audit
|
||||||
// This avoids all data collection if auditing is disabled
|
// This avoids all data collection if auditing is disabled
|
||||||
if (!AuditUtils.shouldAudit(method, auditConfig)) {
|
if (!AuditUtils.shouldAudit(method, auditConfig)) {
|
||||||
return joinPoint.proceed();
|
return joinPoint.proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only create the map once we know we'll use it
|
// Only create the map once we know we'll use it
|
||||||
Map<String, Object> auditData = AuditUtils.createBaseAuditData(joinPoint, auditedAnnotation.level());
|
Map<String, Object> auditData =
|
||||||
|
AuditUtils.createBaseAuditData(joinPoint, auditedAnnotation.level());
|
||||||
|
|
||||||
// Add HTTP information if we're in a web context
|
// Add HTTP information if we're in a web context
|
||||||
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
ServletRequestAttributes attrs =
|
||||||
|
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
if (attrs != null) {
|
if (attrs != null) {
|
||||||
HttpServletRequest req = attrs.getRequest();
|
HttpServletRequest req = attrs.getRequest();
|
||||||
String path = req.getRequestURI();
|
String path = req.getRequestURI();
|
||||||
@ -55,51 +57,55 @@ public class AuditAspect {
|
|||||||
AuditUtils.addHttpData(auditData, httpMethod, path, auditedAnnotation.level());
|
AuditUtils.addHttpData(auditData, httpMethod, path, auditedAnnotation.level());
|
||||||
AuditUtils.addFileData(auditData, joinPoint, auditedAnnotation.level());
|
AuditUtils.addFileData(auditData, joinPoint, auditedAnnotation.level());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add arguments if requested and if at VERBOSE level, or if specifically requested
|
// Add arguments if requested and if at VERBOSE level, or if specifically requested
|
||||||
boolean includeArgs = auditedAnnotation.includeArgs() &&
|
boolean includeArgs =
|
||||||
(auditedAnnotation.level() == AuditLevel.VERBOSE ||
|
auditedAnnotation.includeArgs()
|
||||||
auditConfig.getAuditLevel() == AuditLevel.VERBOSE);
|
&& (auditedAnnotation.level() == AuditLevel.VERBOSE
|
||||||
|
|| auditConfig.getAuditLevel() == AuditLevel.VERBOSE);
|
||||||
|
|
||||||
if (includeArgs) {
|
if (includeArgs) {
|
||||||
AuditUtils.addMethodArguments(auditData, joinPoint, AuditLevel.VERBOSE);
|
AuditUtils.addMethodArguments(auditData, joinPoint, AuditLevel.VERBOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record start time for latency calculation
|
// Record start time for latency calculation
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
Object result;
|
Object result;
|
||||||
try {
|
try {
|
||||||
// Execute the method
|
// Execute the method
|
||||||
result = joinPoint.proceed();
|
result = joinPoint.proceed();
|
||||||
|
|
||||||
// Add success status
|
// Add success status
|
||||||
auditData.put("status", "success");
|
auditData.put("status", "success");
|
||||||
|
|
||||||
// Add result if requested and if at VERBOSE level
|
// Add result if requested and if at VERBOSE level
|
||||||
boolean includeResult = auditedAnnotation.includeResult() &&
|
boolean includeResult =
|
||||||
(auditedAnnotation.level() == AuditLevel.VERBOSE ||
|
auditedAnnotation.includeResult()
|
||||||
auditConfig.getAuditLevel() == AuditLevel.VERBOSE);
|
&& (auditedAnnotation.level() == AuditLevel.VERBOSE
|
||||||
|
|| auditConfig.getAuditLevel() == AuditLevel.VERBOSE);
|
||||||
|
|
||||||
if (includeResult && result != null) {
|
if (includeResult && result != null) {
|
||||||
// Use safe string conversion with size limiting
|
// Use safe string conversion with size limiting
|
||||||
auditData.put("result", AuditUtils.safeToString(result, 1000));
|
auditData.put("result", AuditUtils.safeToString(result, 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
// Always add failure information regardless of level
|
// Always add failure information regardless of level
|
||||||
auditData.put("status", "failure");
|
auditData.put("status", "failure");
|
||||||
auditData.put("errorType", ex.getClass().getName());
|
auditData.put("errorType", ex.getClass().getName());
|
||||||
auditData.put("errorMessage", ex.getMessage());
|
auditData.put("errorMessage", ex.getMessage());
|
||||||
|
|
||||||
// Re-throw the exception
|
// Re-throw the exception
|
||||||
throw ex;
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
// Add timing information - use isHttpRequest=false to ensure we get timing for non-HTTP methods
|
// Add timing information - use isHttpRequest=false to ensure we get timing for non-HTTP
|
||||||
|
// methods
|
||||||
HttpServletResponse resp = attrs != null ? attrs.getResponse() : null;
|
HttpServletResponse resp = attrs != null ? attrs.getResponse() : null;
|
||||||
boolean isHttpRequest = attrs != null;
|
boolean isHttpRequest = attrs != null;
|
||||||
AuditUtils.addTimingData(auditData, startTime, resp, auditedAnnotation.level(), isHttpRequest);
|
AuditUtils.addTimingData(
|
||||||
|
auditData, startTime, resp, auditedAnnotation.level(), isHttpRequest);
|
||||||
|
|
||||||
// Resolve the event type based on annotation and context
|
// Resolve the event type based on annotation and context
|
||||||
String httpMethod = null;
|
String httpMethod = null;
|
||||||
String path = null;
|
String path = null;
|
||||||
@ -108,15 +114,15 @@ public class AuditAspect {
|
|||||||
httpMethod = req.getMethod();
|
httpMethod = req.getMethod();
|
||||||
path = req.getRequestURI();
|
path = req.getRequestURI();
|
||||||
}
|
}
|
||||||
|
|
||||||
AuditEventType eventType = AuditUtils.resolveEventType(
|
AuditEventType eventType =
|
||||||
method,
|
AuditUtils.resolveEventType(
|
||||||
joinPoint.getTarget().getClass(),
|
method,
|
||||||
path,
|
joinPoint.getTarget().getClass(),
|
||||||
httpMethod,
|
path,
|
||||||
auditedAnnotation
|
httpMethod,
|
||||||
);
|
auditedAnnotation);
|
||||||
|
|
||||||
// Check if we should use string type instead
|
// Check if we should use string type instead
|
||||||
String typeString = auditedAnnotation.typeString();
|
String typeString = auditedAnnotation.typeString();
|
||||||
if (eventType == AuditEventType.HTTP_REQUEST && StringUtils.isNotEmpty(typeString)) {
|
if (eventType == AuditEventType.HTTP_REQUEST && StringUtils.isNotEmpty(typeString)) {
|
||||||
@ -128,4 +134,4 @@ public class AuditAspect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,41 @@
|
|||||||
package stirling.software.proprietary.audit;
|
package stirling.software.proprietary.audit;
|
||||||
|
|
||||||
/**
|
/** Standardized audit event types for the application. */
|
||||||
* Standardized audit event types for the application.
|
|
||||||
*/
|
|
||||||
public enum AuditEventType {
|
public enum AuditEventType {
|
||||||
// Authentication events - BASIC level
|
// Authentication events - BASIC level
|
||||||
USER_LOGIN("User login"),
|
USER_LOGIN("User login"),
|
||||||
USER_LOGOUT("User logout"),
|
USER_LOGOUT("User logout"),
|
||||||
USER_FAILED_LOGIN("Failed login attempt"),
|
USER_FAILED_LOGIN("Failed login attempt"),
|
||||||
|
|
||||||
// User/admin events - BASIC level
|
// User/admin events - BASIC level
|
||||||
USER_PROFILE_UPDATE("User or profile operation"),
|
USER_PROFILE_UPDATE("User or profile operation"),
|
||||||
|
|
||||||
// System configuration events - STANDARD level
|
// System configuration events - STANDARD level
|
||||||
SETTINGS_CHANGED("System or admin settings operation"),
|
SETTINGS_CHANGED("System or admin settings operation"),
|
||||||
|
|
||||||
// File operations - STANDARD level
|
// File operations - STANDARD level
|
||||||
FILE_OPERATION("File operation"),
|
FILE_OPERATION("File operation"),
|
||||||
|
|
||||||
// PDF operations - STANDARD level
|
// PDF operations - STANDARD level
|
||||||
PDF_PROCESS("PDF processing operation"),
|
PDF_PROCESS("PDF processing operation"),
|
||||||
|
|
||||||
// HTTP requests - STANDARD level
|
// HTTP requests - STANDARD level
|
||||||
HTTP_REQUEST("HTTP request");
|
HTTP_REQUEST("HTTP request");
|
||||||
|
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|
||||||
AuditEventType(String description) {
|
AuditEventType(String description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the enum value from a string representation.
|
* Get the enum value from a string representation. Useful for backward compatibility with
|
||||||
* Useful for backward compatibility with string-based event types.
|
* string-based event types.
|
||||||
*
|
*
|
||||||
* @param type The string representation of the event type
|
* @param type The string representation of the event type
|
||||||
* @return The corresponding enum value or null if not found
|
* @return The corresponding enum value or null if not found
|
||||||
*/
|
*/
|
||||||
@ -45,18 +43,18 @@ public enum AuditEventType {
|
|||||||
if (type == null) {
|
if (type == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return AuditEventType.valueOf(type);
|
return AuditEventType.valueOf(type);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// If the exact enum name doesn't match, try finding a similar one
|
// If the exact enum name doesn't match, try finding a similar one
|
||||||
for (AuditEventType eventType : values()) {
|
for (AuditEventType eventType : values()) {
|
||||||
if (eventType.name().equalsIgnoreCase(type) ||
|
if (eventType.name().equalsIgnoreCase(type)
|
||||||
eventType.getDescription().equalsIgnoreCase(type)) {
|
|| eventType.getDescription().equalsIgnoreCase(type)) {
|
||||||
return eventType;
|
return eventType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,80 +1,69 @@
|
|||||||
package stirling.software.proprietary.audit;
|
package stirling.software.proprietary.audit;
|
||||||
|
|
||||||
/**
|
/** Defines the different levels of audit logging available in the application. */
|
||||||
* Defines the different levels of audit logging available in the application.
|
|
||||||
*/
|
|
||||||
public enum AuditLevel {
|
public enum AuditLevel {
|
||||||
/**
|
/**
|
||||||
* OFF - No audit logging (level 0)
|
* OFF - No audit logging (level 0) Disables all audit logging except for critical security
|
||||||
* Disables all audit logging except for critical security events
|
* events
|
||||||
*/
|
*/
|
||||||
OFF(0),
|
OFF(0),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BASIC - Minimal audit logging (level 1)
|
* BASIC - Minimal audit logging (level 1) Includes: - Authentication events (login, logout,
|
||||||
* Includes:
|
* failed logins) - Password changes - User/role changes - System configuration changes
|
||||||
* - Authentication events (login, logout, failed logins)
|
|
||||||
* - Password changes
|
|
||||||
* - User/role changes
|
|
||||||
* - System configuration changes
|
|
||||||
*/
|
*/
|
||||||
BASIC(1),
|
BASIC(1),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* STANDARD - Standard audit logging (level 2)
|
* STANDARD - Standard audit logging (level 2) Includes everything in BASIC plus: - All HTTP
|
||||||
* Includes everything in BASIC plus:
|
* requests (basic info: URL, method, status) - File operations (upload, download, process) -
|
||||||
* - All HTTP requests (basic info: URL, method, status)
|
* PDF operations (view, edit, etc.) - User operations
|
||||||
* - File operations (upload, download, process)
|
|
||||||
* - PDF operations (view, edit, etc.)
|
|
||||||
* - User operations
|
|
||||||
*/
|
*/
|
||||||
STANDARD(2),
|
STANDARD(2),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* VERBOSE - Detailed audit logging (level 3)
|
* VERBOSE - Detailed audit logging (level 3) Includes everything in STANDARD plus: - Request
|
||||||
* Includes everything in STANDARD plus:
|
* headers and parameters - Method parameters - Operation results - Detailed timing information
|
||||||
* - Request headers and parameters
|
|
||||||
* - Method parameters
|
|
||||||
* - Operation results
|
|
||||||
* - Detailed timing information
|
|
||||||
*/
|
*/
|
||||||
VERBOSE(3);
|
VERBOSE(3);
|
||||||
|
|
||||||
private final int level;
|
private final int level;
|
||||||
|
|
||||||
AuditLevel(int level) {
|
AuditLevel(int level) {
|
||||||
this.level = level;
|
this.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLevel() {
|
public int getLevel() {
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if this audit level includes the specified level
|
* Checks if this audit level includes the specified level
|
||||||
|
*
|
||||||
* @param otherLevel The level to check against
|
* @param otherLevel The level to check against
|
||||||
* @return true if this level is equal to or greater than the specified level
|
* @return true if this level is equal to or greater than the specified level
|
||||||
*/
|
*/
|
||||||
public boolean includes(AuditLevel otherLevel) {
|
public boolean includes(AuditLevel otherLevel) {
|
||||||
return this.level >= otherLevel.level;
|
return this.level >= otherLevel.level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an AuditLevel from an integer value
|
* Get an AuditLevel from an integer value
|
||||||
|
*
|
||||||
* @param level The integer level (0-3)
|
* @param level The integer level (0-3)
|
||||||
* @return The corresponding AuditLevel
|
* @return The corresponding AuditLevel
|
||||||
*/
|
*/
|
||||||
public static AuditLevel fromInt(int level) {
|
public static AuditLevel fromInt(int level) {
|
||||||
// Ensure level is within valid bounds
|
// Ensure level is within valid bounds
|
||||||
int boundedLevel = Math.min(Math.max(level, 0), 3);
|
int boundedLevel = Math.min(Math.max(level, 0), 3);
|
||||||
|
|
||||||
for (AuditLevel auditLevel : values()) {
|
for (AuditLevel auditLevel : values()) {
|
||||||
if (auditLevel.level == boundedLevel) {
|
if (auditLevel.level == boundedLevel) {
|
||||||
return auditLevel;
|
return auditLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to STANDARD if somehow we didn't match
|
// Default to STANDARD if somehow we didn't match
|
||||||
return STANDARD;
|
return STANDARD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,5 @@
|
|||||||
package stirling.software.proprietary.audit;
|
package stirling.software.proprietary.audit;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
|
||||||
import org.slf4j.MDC;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
import stirling.software.common.util.RequestUriUtils;
|
|
||||||
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -24,10 +10,26 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.common.util.RequestUriUtils;
|
||||||
|
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared utilities for audit aspects to ensure consistent behavior
|
* Shared utilities for audit aspects to ensure consistent behavior across different audit
|
||||||
* across different audit mechanisms.
|
* mechanisms.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AuditUtils {
|
public class AuditUtils {
|
||||||
@ -39,12 +41,13 @@ public class AuditUtils {
|
|||||||
* @param auditLevel The current audit level
|
* @param auditLevel The current audit level
|
||||||
* @return A map with standard audit data
|
* @return A map with standard audit data
|
||||||
*/
|
*/
|
||||||
public static Map<String, Object> createBaseAuditData(ProceedingJoinPoint joinPoint, AuditLevel auditLevel) {
|
public static Map<String, Object> createBaseAuditData(
|
||||||
|
ProceedingJoinPoint joinPoint, AuditLevel auditLevel) {
|
||||||
Map<String, Object> data = new HashMap<>();
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
|
||||||
// Common data for all levels
|
// Common data for all levels
|
||||||
data.put("timestamp", Instant.now().toString());
|
data.put("timestamp", Instant.now().toString());
|
||||||
|
|
||||||
// Add principal if available
|
// Add principal if available
|
||||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
if (auth != null && auth.getName() != null) {
|
if (auth != null && auth.getName() != null) {
|
||||||
@ -52,16 +55,18 @@ public class AuditUtils {
|
|||||||
} else {
|
} else {
|
||||||
data.put("principal", "system");
|
data.put("principal", "system");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add class name and method name only at VERBOSE level
|
// Add class name and method name only at VERBOSE level
|
||||||
if (auditLevel.includes(AuditLevel.VERBOSE)) {
|
if (auditLevel.includes(AuditLevel.VERBOSE)) {
|
||||||
data.put("className", joinPoint.getTarget().getClass().getName());
|
data.put("className", joinPoint.getTarget().getClass().getName());
|
||||||
data.put("methodName", ((MethodSignature) joinPoint.getSignature()).getMethod().getName());
|
data.put(
|
||||||
|
"methodName",
|
||||||
|
((MethodSignature) joinPoint.getSignature()).getMethod().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add HTTP-specific information to the audit data if available
|
* Add HTTP-specific information to the audit data if available
|
||||||
*
|
*
|
||||||
@ -70,45 +75,50 @@ public class AuditUtils {
|
|||||||
* @param path The request path
|
* @param path The request path
|
||||||
* @param auditLevel The current audit level
|
* @param auditLevel The current audit level
|
||||||
*/
|
*/
|
||||||
public static void addHttpData(Map<String, Object> data, String httpMethod, String path, AuditLevel auditLevel) {
|
public static void addHttpData(
|
||||||
|
Map<String, Object> data, String httpMethod, String path, AuditLevel auditLevel) {
|
||||||
if (httpMethod == null || path == null) {
|
if (httpMethod == null || path == null) {
|
||||||
return; // Skip if we don't have basic HTTP info
|
return; // Skip if we don't have basic HTTP info
|
||||||
}
|
}
|
||||||
|
|
||||||
// BASIC level HTTP data
|
// BASIC level HTTP data
|
||||||
data.put("httpMethod", httpMethod);
|
data.put("httpMethod", httpMethod);
|
||||||
data.put("path", path);
|
data.put("path", path);
|
||||||
|
|
||||||
// Get request attributes safely
|
// Get request attributes safely
|
||||||
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
ServletRequestAttributes attrs =
|
||||||
|
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
if (attrs == null) {
|
if (attrs == null) {
|
||||||
return; // No request context available
|
return; // No request context available
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpServletRequest req = attrs.getRequest();
|
HttpServletRequest req = attrs.getRequest();
|
||||||
if (req == null) {
|
if (req == null) {
|
||||||
return; // No request available
|
return; // No request available
|
||||||
}
|
}
|
||||||
|
|
||||||
// STANDARD level HTTP data
|
// STANDARD level HTTP data
|
||||||
if (auditLevel.includes(AuditLevel.STANDARD)) {
|
if (auditLevel.includes(AuditLevel.STANDARD)) {
|
||||||
data.put("clientIp", req.getRemoteAddr());
|
data.put("clientIp", req.getRemoteAddr());
|
||||||
data.put("sessionId", req.getSession(false) != null ? req.getSession(false).getId() : null);
|
data.put(
|
||||||
|
"sessionId",
|
||||||
|
req.getSession(false) != null ? req.getSession(false).getId() : null);
|
||||||
data.put("requestId", MDC.get("requestId"));
|
data.put("requestId", MDC.get("requestId"));
|
||||||
|
|
||||||
// Form data for POST/PUT/PATCH
|
// Form data for POST/PUT/PATCH
|
||||||
if (("POST".equalsIgnoreCase(httpMethod) ||
|
if (("POST".equalsIgnoreCase(httpMethod)
|
||||||
"PUT".equalsIgnoreCase(httpMethod) ||
|
|| "PUT".equalsIgnoreCase(httpMethod)
|
||||||
"PATCH".equalsIgnoreCase(httpMethod)) && req.getContentType() != null) {
|
|| "PATCH".equalsIgnoreCase(httpMethod))
|
||||||
|
&& req.getContentType() != null) {
|
||||||
|
|
||||||
String contentType = req.getContentType();
|
String contentType = req.getContentType();
|
||||||
if (contentType.contains("application/x-www-form-urlencoded") ||
|
if (contentType.contains("application/x-www-form-urlencoded")
|
||||||
contentType.contains("multipart/form-data")) {
|
|| contentType.contains("multipart/form-data")) {
|
||||||
|
|
||||||
Map<String, String[]> params = new HashMap<>(req.getParameterMap());
|
Map<String, String[]> params = new HashMap<>(req.getParameterMap());
|
||||||
// Remove CSRF token from logged parameters
|
// Remove CSRF token from logged parameters
|
||||||
params.remove("_csrf");
|
params.remove("_csrf");
|
||||||
|
|
||||||
if (!params.isEmpty()) {
|
if (!params.isEmpty()) {
|
||||||
data.put("formParams", params);
|
data.put("formParams", params);
|
||||||
}
|
}
|
||||||
@ -116,7 +126,7 @@ public class AuditUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add file information to the audit data if available
|
* Add file information to the audit data if available
|
||||||
*
|
*
|
||||||
@ -124,27 +134,33 @@ public class AuditUtils {
|
|||||||
* @param joinPoint The AspectJ join point
|
* @param joinPoint The AspectJ join point
|
||||||
* @param auditLevel The current audit level
|
* @param auditLevel The current audit level
|
||||||
*/
|
*/
|
||||||
public static void addFileData(Map<String, Object> data, ProceedingJoinPoint joinPoint, AuditLevel auditLevel) {
|
public static void addFileData(
|
||||||
|
Map<String, Object> data, ProceedingJoinPoint joinPoint, AuditLevel auditLevel) {
|
||||||
if (auditLevel.includes(AuditLevel.STANDARD)) {
|
if (auditLevel.includes(AuditLevel.STANDARD)) {
|
||||||
List<MultipartFile> files = Arrays.stream(joinPoint.getArgs())
|
List<MultipartFile> files =
|
||||||
.filter(a -> a instanceof MultipartFile)
|
Arrays.stream(joinPoint.getArgs())
|
||||||
.map(a -> (MultipartFile)a)
|
.filter(a -> a instanceof MultipartFile)
|
||||||
.collect(Collectors.toList());
|
.map(a -> (MultipartFile) a)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (!files.isEmpty()) {
|
if (!files.isEmpty()) {
|
||||||
List<Map<String,Object>> fileInfos = files.stream().map(f -> {
|
List<Map<String, Object>> fileInfos =
|
||||||
Map<String,Object> m = new HashMap<>();
|
files.stream()
|
||||||
m.put("name", f.getOriginalFilename());
|
.map(
|
||||||
m.put("size", f.getSize());
|
f -> {
|
||||||
m.put("type", f.getContentType());
|
Map<String, Object> m = new HashMap<>();
|
||||||
return m;
|
m.put("name", f.getOriginalFilename());
|
||||||
}).collect(Collectors.toList());
|
m.put("size", f.getSize());
|
||||||
|
m.put("type", f.getContentType());
|
||||||
|
return m;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
data.put("files", fileInfos);
|
data.put("files", fileInfos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add method arguments to the audit data
|
* Add method arguments to the audit data
|
||||||
*
|
*
|
||||||
@ -152,28 +168,30 @@ public class AuditUtils {
|
|||||||
* @param joinPoint The AspectJ join point
|
* @param joinPoint The AspectJ join point
|
||||||
* @param auditLevel The current audit level
|
* @param auditLevel The current audit level
|
||||||
*/
|
*/
|
||||||
public static void addMethodArguments(Map<String, Object> data, ProceedingJoinPoint joinPoint, AuditLevel auditLevel) {
|
public static void addMethodArguments(
|
||||||
|
Map<String, Object> data, ProceedingJoinPoint joinPoint, AuditLevel auditLevel) {
|
||||||
if (auditLevel.includes(AuditLevel.VERBOSE)) {
|
if (auditLevel.includes(AuditLevel.VERBOSE)) {
|
||||||
MethodSignature sig = (MethodSignature) joinPoint.getSignature();
|
MethodSignature sig = (MethodSignature) joinPoint.getSignature();
|
||||||
String[] names = sig.getParameterNames();
|
String[] names = sig.getParameterNames();
|
||||||
Object[] vals = joinPoint.getArgs();
|
Object[] vals = joinPoint.getArgs();
|
||||||
if (names != null && vals != null) {
|
if (names != null && vals != null) {
|
||||||
IntStream.range(0, names.length)
|
IntStream.range(0, names.length)
|
||||||
.forEach(i -> {
|
.forEach(
|
||||||
if (vals[i] != null) {
|
i -> {
|
||||||
// Convert objects to safe string representation
|
if (vals[i] != null) {
|
||||||
data.put("arg_" + names[i], safeToString(vals[i], 500));
|
// Convert objects to safe string representation
|
||||||
} else {
|
data.put("arg_" + names[i], safeToString(vals[i], 500));
|
||||||
data.put("arg_" + names[i], null);
|
} else {
|
||||||
}
|
data.put("arg_" + names[i], null);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safely convert an object to string with size limiting
|
* Safely convert an object to string with size limiting
|
||||||
*
|
*
|
||||||
* @param obj The object to convert
|
* @param obj The object to convert
|
||||||
* @param maxLength Maximum length of the resulting string
|
* @param maxLength Maximum length of the resulting string
|
||||||
* @return A safe string representation, truncated if needed
|
* @return A safe string representation, truncated if needed
|
||||||
@ -182,7 +200,7 @@ public class AuditUtils {
|
|||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
String result;
|
String result;
|
||||||
try {
|
try {
|
||||||
// Handle common types directly to avoid toString() overhead
|
// Handle common types directly to avoid toString() overhead
|
||||||
@ -196,19 +214,19 @@ public class AuditUtils {
|
|||||||
// For complex objects, use toString but handle exceptions
|
// For complex objects, use toString but handle exceptions
|
||||||
result = obj.toString();
|
result = obj.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate if necessary
|
// Truncate if necessary
|
||||||
if (result != null && result.length() > maxLength) {
|
if (result != null && result.length() > maxLength) {
|
||||||
return StringUtils.truncate(result, maxLength - 3) + "...";
|
return StringUtils.truncate(result, maxLength - 3) + "...";
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If toString() fails, return the class name
|
// If toString() fails, return the class name
|
||||||
return "[" + obj.getClass().getName() + " - toString() failed]";
|
return "[" + obj.getClass().getName() + " - toString() failed]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a method should be audited based on config and annotation
|
* Determine if a method should be audited based on config and annotation
|
||||||
*
|
*
|
||||||
@ -221,17 +239,16 @@ public class AuditUtils {
|
|||||||
if (!auditConfig.isEnabled()) {
|
if (!auditConfig.isEnabled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for annotation override
|
// Check for annotation override
|
||||||
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
||||||
AuditLevel requiredLevel = (auditedAnnotation != null)
|
AuditLevel requiredLevel =
|
||||||
? auditedAnnotation.level()
|
(auditedAnnotation != null) ? auditedAnnotation.level() : AuditLevel.BASIC;
|
||||||
: AuditLevel.BASIC;
|
|
||||||
|
|
||||||
// Check if the required level is enabled
|
// Check if the required level is enabled
|
||||||
return auditConfig.getAuditLevel().includes(requiredLevel);
|
return auditConfig.getAuditLevel().includes(requiredLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add timing and response status data to the audit record
|
* Add timing and response status data to the audit record
|
||||||
*
|
*
|
||||||
@ -241,14 +258,19 @@ public class AuditUtils {
|
|||||||
* @param level The current audit level
|
* @param level The current audit level
|
||||||
* @param isHttpRequest Whether this is an HTTP request (controller) or a regular method call
|
* @param isHttpRequest Whether this is an HTTP request (controller) or a regular method call
|
||||||
*/
|
*/
|
||||||
public static void addTimingData(Map<String, Object> data, long startTime, HttpServletResponse response, AuditLevel level, boolean isHttpRequest) {
|
public static void addTimingData(
|
||||||
|
Map<String, Object> data,
|
||||||
|
long startTime,
|
||||||
|
HttpServletResponse response,
|
||||||
|
AuditLevel level,
|
||||||
|
boolean isHttpRequest) {
|
||||||
if (level.includes(AuditLevel.STANDARD)) {
|
if (level.includes(AuditLevel.STANDARD)) {
|
||||||
// For HTTP requests, let ControllerAuditAspect handle timing separately
|
// For HTTP requests, let ControllerAuditAspect handle timing separately
|
||||||
// For non-HTTP methods, add execution time here
|
// For non-HTTP methods, add execution time here
|
||||||
if (!isHttpRequest) {
|
if (!isHttpRequest) {
|
||||||
data.put("latencyMs", System.currentTimeMillis() - startTime);
|
data.put("latencyMs", System.currentTimeMillis() - startTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add HTTP status code if available
|
// Add HTTP status code if available
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
try {
|
try {
|
||||||
@ -259,7 +281,7 @@ public class AuditUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the event type to use for auditing, considering annotations and context
|
* Resolve the event type to use for auditing, considering annotations and context
|
||||||
*
|
*
|
||||||
@ -270,34 +292,45 @@ public class AuditUtils {
|
|||||||
* @param annotation The @Audited annotation (may be null)
|
* @param annotation The @Audited annotation (may be null)
|
||||||
* @return The resolved event type (never null)
|
* @return The resolved event type (never null)
|
||||||
*/
|
*/
|
||||||
public static AuditEventType resolveEventType(Method method, Class<?> controller, String path, String httpMethod, Audited annotation) {
|
public static AuditEventType resolveEventType(
|
||||||
|
Method method,
|
||||||
|
Class<?> controller,
|
||||||
|
String path,
|
||||||
|
String httpMethod,
|
||||||
|
Audited annotation) {
|
||||||
// First check if we have an explicit annotation
|
// First check if we have an explicit annotation
|
||||||
if (annotation != null && annotation.type() != AuditEventType.HTTP_REQUEST) {
|
if (annotation != null && annotation.type() != AuditEventType.HTTP_REQUEST) {
|
||||||
return annotation.type();
|
return annotation.type();
|
||||||
}
|
}
|
||||||
|
|
||||||
// For HTTP methods, infer based on controller and path
|
// For HTTP methods, infer based on controller and path
|
||||||
if (httpMethod != null && path != null) {
|
if (httpMethod != null && path != null) {
|
||||||
String cls = controller.getSimpleName().toLowerCase();
|
String cls = controller.getSimpleName().toLowerCase();
|
||||||
String pkg = controller.getPackage().getName().toLowerCase();
|
String pkg = controller.getPackage().getName().toLowerCase();
|
||||||
|
|
||||||
if ("GET".equals(httpMethod)) return AuditEventType.HTTP_REQUEST;
|
if ("GET".equals(httpMethod)) return AuditEventType.HTTP_REQUEST;
|
||||||
|
|
||||||
if (cls.contains("user") || cls.contains("auth") || pkg.contains("auth")
|
if (cls.contains("user")
|
||||||
|| path.startsWith("/user") || path.startsWith("/login")) {
|
|| cls.contains("auth")
|
||||||
|
|| pkg.contains("auth")
|
||||||
|
|| path.startsWith("/user")
|
||||||
|
|| path.startsWith("/login")) {
|
||||||
return AuditEventType.USER_PROFILE_UPDATE;
|
return AuditEventType.USER_PROFILE_UPDATE;
|
||||||
} else if (cls.contains("admin") || path.startsWith("/admin") || path.startsWith("/settings")) {
|
} else if (cls.contains("admin")
|
||||||
|
|| path.startsWith("/admin")
|
||||||
|
|| path.startsWith("/settings")) {
|
||||||
return AuditEventType.SETTINGS_CHANGED;
|
return AuditEventType.SETTINGS_CHANGED;
|
||||||
} else if (cls.contains("file") || path.startsWith("/file")
|
} else if (cls.contains("file")
|
||||||
|
|| path.startsWith("/file")
|
||||||
|| path.matches("(?i).*/(upload|download)/.*")) {
|
|| path.matches("(?i).*/(upload|download)/.*")) {
|
||||||
return AuditEventType.FILE_OPERATION;
|
return AuditEventType.FILE_OPERATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default for non-HTTP methods or when no specific match
|
// Default for non-HTTP methods or when no specific match
|
||||||
return AuditEventType.PDF_PROCESS;
|
return AuditEventType.PDF_PROCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the appropriate audit level to use
|
* Determine the appropriate audit level to use
|
||||||
*
|
*
|
||||||
@ -306,17 +339,18 @@ public class AuditUtils {
|
|||||||
* @param auditConfig The audit configuration
|
* @param auditConfig The audit configuration
|
||||||
* @return The audit level to use
|
* @return The audit level to use
|
||||||
*/
|
*/
|
||||||
public static AuditLevel getEffectiveAuditLevel(Method method, AuditLevel defaultLevel, AuditConfigurationProperties auditConfig) {
|
public static AuditLevel getEffectiveAuditLevel(
|
||||||
|
Method method, AuditLevel defaultLevel, AuditConfigurationProperties auditConfig) {
|
||||||
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
||||||
if (auditedAnnotation != null) {
|
if (auditedAnnotation != null) {
|
||||||
// Method has @Audited - use its level
|
// Method has @Audited - use its level
|
||||||
return auditedAnnotation.level();
|
return auditedAnnotation.level();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use default level (typically from global config)
|
// Use default level (typically from global config)
|
||||||
return defaultLevel;
|
return defaultLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the appropriate audit event type to use
|
* Determine the appropriate audit event type to use
|
||||||
*
|
*
|
||||||
@ -326,42 +360,50 @@ public class AuditUtils {
|
|||||||
* @param httpMethod The HTTP method
|
* @param httpMethod The HTTP method
|
||||||
* @return The determined audit event type
|
* @return The determined audit event type
|
||||||
*/
|
*/
|
||||||
public static AuditEventType determineAuditEventType(Method method, Class<?> controller, String path, String httpMethod) {
|
public static AuditEventType determineAuditEventType(
|
||||||
|
Method method, Class<?> controller, String path, String httpMethod) {
|
||||||
// First check for explicit annotation
|
// First check for explicit annotation
|
||||||
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
||||||
if (auditedAnnotation != null) {
|
if (auditedAnnotation != null) {
|
||||||
return auditedAnnotation.type();
|
return auditedAnnotation.type();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise infer from controller and path
|
// Otherwise infer from controller and path
|
||||||
String cls = controller.getSimpleName().toLowerCase();
|
String cls = controller.getSimpleName().toLowerCase();
|
||||||
String pkg = controller.getPackage().getName().toLowerCase();
|
String pkg = controller.getPackage().getName().toLowerCase();
|
||||||
|
|
||||||
if ("GET".equals(httpMethod)) return AuditEventType.HTTP_REQUEST;
|
if ("GET".equals(httpMethod)) return AuditEventType.HTTP_REQUEST;
|
||||||
|
|
||||||
if (cls.contains("user") || cls.contains("auth") || pkg.contains("auth")
|
if (cls.contains("user")
|
||||||
|| path.startsWith("/user") || path.startsWith("/login")) {
|
|| cls.contains("auth")
|
||||||
|
|| pkg.contains("auth")
|
||||||
|
|| path.startsWith("/user")
|
||||||
|
|| path.startsWith("/login")) {
|
||||||
return AuditEventType.USER_PROFILE_UPDATE;
|
return AuditEventType.USER_PROFILE_UPDATE;
|
||||||
} else if (cls.contains("admin") || path.startsWith("/admin") || path.startsWith("/settings")) {
|
} else if (cls.contains("admin")
|
||||||
|
|| path.startsWith("/admin")
|
||||||
|
|| path.startsWith("/settings")) {
|
||||||
return AuditEventType.SETTINGS_CHANGED;
|
return AuditEventType.SETTINGS_CHANGED;
|
||||||
} else if (cls.contains("file") || path.startsWith("/file")
|
} else if (cls.contains("file")
|
||||||
|
|| path.startsWith("/file")
|
||||||
|| path.matches("(?i).*/(upload|download)/.*")) {
|
|| path.matches("(?i).*/(upload|download)/.*")) {
|
||||||
return AuditEventType.FILE_OPERATION;
|
return AuditEventType.FILE_OPERATION;
|
||||||
} else {
|
} else {
|
||||||
return AuditEventType.PDF_PROCESS;
|
return AuditEventType.PDF_PROCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current HTTP request if available
|
* Get the current HTTP request if available
|
||||||
*
|
*
|
||||||
* @return The current request or null if not in a request context
|
* @return The current request or null if not in a request context
|
||||||
*/
|
*/
|
||||||
public static HttpServletRequest getCurrentRequest() {
|
public static HttpServletRequest getCurrentRequest() {
|
||||||
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
ServletRequestAttributes attrs =
|
||||||
|
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
return attrs != null ? attrs.getRequest() : null;
|
return attrs != null ? attrs.getRequest() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a GET request is for a static resource
|
* Check if a GET request is for a static resource
|
||||||
*
|
*
|
||||||
@ -369,7 +411,8 @@ public class AuditUtils {
|
|||||||
* @return true if this is a static resource request
|
* @return true if this is a static resource request
|
||||||
*/
|
*/
|
||||||
public static boolean isStaticResourceRequest(HttpServletRequest request) {
|
public static boolean isStaticResourceRequest(HttpServletRequest request) {
|
||||||
return request != null && !RequestUriUtils.isTrackableResource(
|
return request != null
|
||||||
request.getContextPath(), request.getRequestURI());
|
&& !RequestUriUtils.isTrackableResource(
|
||||||
|
request.getContextPath(), request.getRequestURI());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,61 +7,51 @@ import java.lang.annotation.Target;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation for methods that should be audited.
|
* Annotation for methods that should be audited.
|
||||||
*
|
*
|
||||||
* Usage:
|
* <p>Usage:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>{@code
|
||||||
* {@code
|
|
||||||
* @Audited(type = AuditEventType.USER_REGISTRATION, level = AuditLevel.BASIC)
|
* @Audited(type = AuditEventType.USER_REGISTRATION, level = AuditLevel.BASIC)
|
||||||
* public void registerUser(String username) {
|
* public void registerUser(String username) {
|
||||||
* // Method implementation
|
* // Method implementation
|
||||||
* }
|
* }
|
||||||
* }
|
* }</pre>
|
||||||
* </pre>
|
*
|
||||||
*
|
|
||||||
* For backward compatibility, string-based event types are still supported:
|
* For backward compatibility, string-based event types are still supported:
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>{@code
|
||||||
* {@code
|
|
||||||
* @Audited(typeString = "CUSTOM_EVENT_TYPE", level = AuditLevel.BASIC)
|
* @Audited(typeString = "CUSTOM_EVENT_TYPE", level = AuditLevel.BASIC)
|
||||||
* public void customOperation() {
|
* public void customOperation() {
|
||||||
* // Method implementation
|
* // Method implementation
|
||||||
* }
|
* }
|
||||||
* }
|
* }</pre>
|
||||||
* </pre>
|
|
||||||
*/
|
*/
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface Audited {
|
public @interface Audited {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of audit event using the standardized AuditEventType enum.
|
* The type of audit event using the standardized AuditEventType enum. This is the preferred way
|
||||||
* This is the preferred way to specify the event type.
|
* to specify the event type.
|
||||||
*
|
*
|
||||||
* If both type() and typeString() are specified, type() takes precedence.
|
* <p>If both type() and typeString() are specified, type() takes precedence.
|
||||||
*/
|
*/
|
||||||
AuditEventType type() default AuditEventType.HTTP_REQUEST;
|
AuditEventType type() default AuditEventType.HTTP_REQUEST;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of audit event as a string (e.g., "FILE_UPLOAD", "USER_REGISTRATION").
|
* The type of audit event as a string (e.g., "FILE_UPLOAD", "USER_REGISTRATION"). Provided for
|
||||||
* Provided for backward compatibility and custom event types not in the enum.
|
* backward compatibility and custom event types not in the enum.
|
||||||
*
|
*
|
||||||
* If both type() and typeString() are specified, type() takes precedence.
|
* <p>If both type() and typeString() are specified, type() takes precedence.
|
||||||
*/
|
*/
|
||||||
String typeString() default "";
|
String typeString() default "";
|
||||||
|
|
||||||
/**
|
/** The audit level at which this event should be logged */
|
||||||
* The audit level at which this event should be logged
|
|
||||||
*/
|
|
||||||
AuditLevel level() default AuditLevel.STANDARD;
|
AuditLevel level() default AuditLevel.STANDARD;
|
||||||
|
|
||||||
/**
|
/** Should method arguments be included in the audit event */
|
||||||
* Should method arguments be included in the audit event
|
|
||||||
*/
|
|
||||||
boolean includeArgs() default true;
|
boolean includeArgs() default true;
|
||||||
|
|
||||||
/**
|
/** Should the method return value be included in the audit event */
|
||||||
* Should the method return value be included in the audit event
|
|
||||||
*/
|
|
||||||
boolean includeResult() default false;
|
boolean includeResult() default false;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package stirling.software.proprietary.audit;
|
package stirling.software.proprietary.audit;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import java.lang.annotation.Annotation;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
@ -19,17 +21,16 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
|||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
||||||
import stirling.software.proprietary.service.AuditService;
|
import stirling.software.proprietary.service.AuditService;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aspect for automatically auditing controller methods with web mappings
|
* Aspect for automatically auditing controller methods with web mappings (GetMapping, PostMapping,
|
||||||
* (GetMapping, PostMapping, etc.)
|
* etc.)
|
||||||
*/
|
*/
|
||||||
@Aspect
|
@Aspect
|
||||||
@Component
|
@Component
|
||||||
@ -40,65 +41,57 @@ public class ControllerAuditAspect {
|
|||||||
private final AuditService auditService;
|
private final AuditService auditService;
|
||||||
private final AuditConfigurationProperties auditConfig;
|
private final AuditConfigurationProperties auditConfig;
|
||||||
|
|
||||||
|
@Around(
|
||||||
@Around("execution(* org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(..))")
|
"execution(* org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(..))")
|
||||||
public Object auditStaticResource(ProceedingJoinPoint jp) throws Throwable {
|
public Object auditStaticResource(ProceedingJoinPoint jp) throws Throwable {
|
||||||
return auditController(jp, "GET");
|
return auditController(jp, "GET");
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Intercept all methods with GetMapping annotation
|
/** Intercept all methods with GetMapping annotation */
|
||||||
*/
|
|
||||||
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping)")
|
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping)")
|
||||||
public Object auditGetMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object auditGetMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
return auditController(joinPoint, "GET");
|
return auditController(joinPoint, "GET");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Intercept all methods with PostMapping annotation */
|
||||||
* Intercept all methods with PostMapping annotation
|
|
||||||
*/
|
|
||||||
@Around("@annotation(org.springframework.web.bind.annotation.PostMapping)")
|
@Around("@annotation(org.springframework.web.bind.annotation.PostMapping)")
|
||||||
public Object auditPostMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object auditPostMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
return auditController(joinPoint, "POST");
|
return auditController(joinPoint, "POST");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Intercept all methods with PutMapping annotation */
|
||||||
* Intercept all methods with PutMapping annotation
|
|
||||||
*/
|
|
||||||
@Around("@annotation(org.springframework.web.bind.annotation.PutMapping)")
|
@Around("@annotation(org.springframework.web.bind.annotation.PutMapping)")
|
||||||
public Object auditPutMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object auditPutMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
return auditController(joinPoint, "PUT");
|
return auditController(joinPoint, "PUT");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Intercept all methods with DeleteMapping annotation */
|
||||||
* Intercept all methods with DeleteMapping annotation
|
|
||||||
*/
|
|
||||||
@Around("@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
|
@Around("@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
|
||||||
public Object auditDeleteMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object auditDeleteMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
return auditController(joinPoint, "DELETE");
|
return auditController(joinPoint, "DELETE");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Intercept all methods with PatchMapping annotation */
|
||||||
* Intercept all methods with PatchMapping annotation
|
|
||||||
*/
|
|
||||||
@Around("@annotation(org.springframework.web.bind.annotation.PatchMapping)")
|
@Around("@annotation(org.springframework.web.bind.annotation.PatchMapping)")
|
||||||
public Object auditPatchMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object auditPatchMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
return auditController(joinPoint, "PATCH");
|
return auditController(joinPoint, "PATCH");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object auditController(ProceedingJoinPoint joinPoint, String httpMethod) throws Throwable {
|
private Object auditController(ProceedingJoinPoint joinPoint, String httpMethod)
|
||||||
|
throws Throwable {
|
||||||
MethodSignature sig = (MethodSignature) joinPoint.getSignature();
|
MethodSignature sig = (MethodSignature) joinPoint.getSignature();
|
||||||
Method method = sig.getMethod();
|
Method method = sig.getMethod();
|
||||||
|
|
||||||
// Fast path: check if auditing is enabled before doing any work
|
// Fast path: check if auditing is enabled before doing any work
|
||||||
// This avoids all data collection if auditing is disabled
|
// This avoids all data collection if auditing is disabled
|
||||||
if (!AuditUtils.shouldAudit(method, auditConfig)) {
|
if (!AuditUtils.shouldAudit(method, auditConfig)) {
|
||||||
return joinPoint.proceed();
|
return joinPoint.proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if method is explicitly annotated with @Audited
|
// Check if method is explicitly annotated with @Audited
|
||||||
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
Audited auditedAnnotation = method.getAnnotation(Audited.class);
|
||||||
AuditLevel level = auditConfig.getAuditLevel();
|
AuditLevel level = auditConfig.getAuditLevel();
|
||||||
|
|
||||||
// If @Audited annotation is present, respect its level setting
|
// If @Audited annotation is present, respect its level setting
|
||||||
if (auditedAnnotation != null) {
|
if (auditedAnnotation != null) {
|
||||||
// Use the level from annotation if it's stricter than global level
|
// Use the level from annotation if it's stricter than global level
|
||||||
@ -115,21 +108,22 @@ public class ControllerAuditAspect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
ServletRequestAttributes attrs =
|
||||||
|
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
HttpServletRequest req = attrs != null ? attrs.getRequest() : null;
|
HttpServletRequest req = attrs != null ? attrs.getRequest() : null;
|
||||||
HttpServletResponse resp = attrs != null ? attrs.getResponse() : null;
|
HttpServletResponse resp = attrs != null ? attrs.getResponse() : null;
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
// Use AuditUtils to create the base audit data
|
// Use AuditUtils to create the base audit data
|
||||||
Map<String, Object> data = AuditUtils.createBaseAuditData(joinPoint, level);
|
Map<String, Object> data = AuditUtils.createBaseAuditData(joinPoint, level);
|
||||||
|
|
||||||
// Add HTTP-specific information
|
// Add HTTP-specific information
|
||||||
AuditUtils.addHttpData(data, httpMethod, path, level);
|
AuditUtils.addHttpData(data, httpMethod, path, level);
|
||||||
|
|
||||||
// Add file information if present
|
// Add file information if present
|
||||||
AuditUtils.addFileData(data, joinPoint, level);
|
AuditUtils.addFileData(data, joinPoint, level);
|
||||||
|
|
||||||
// Add method arguments if at VERBOSE level
|
// Add method arguments if at VERBOSE level
|
||||||
if (level.includes(AuditLevel.VERBOSE)) {
|
if (level.includes(AuditLevel.VERBOSE)) {
|
||||||
AuditUtils.addMethodArguments(data, joinPoint, level);
|
AuditUtils.addMethodArguments(data, joinPoint, level);
|
||||||
@ -150,34 +144,35 @@ public class ControllerAuditAspect {
|
|||||||
data.put("latencyMs", System.currentTimeMillis() - start);
|
data.put("latencyMs", System.currentTimeMillis() - start);
|
||||||
if (resp != null) data.put("statusCode", resp.getStatus());
|
if (resp != null) data.put("statusCode", resp.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call AuditUtils but with isHttpRequest=true to skip additional timing
|
// Call AuditUtils but with isHttpRequest=true to skip additional timing
|
||||||
AuditUtils.addTimingData(data, start, resp, level, true);
|
AuditUtils.addTimingData(data, start, resp, level, true);
|
||||||
|
|
||||||
// Add result for VERBOSE level
|
// Add result for VERBOSE level
|
||||||
if (level.includes(AuditLevel.VERBOSE) && result != null) {
|
if (level.includes(AuditLevel.VERBOSE) && result != null) {
|
||||||
// Use safe string conversion with size limiting
|
// Use safe string conversion with size limiting
|
||||||
data.put("result", AuditUtils.safeToString(result, 1000));
|
data.put("result", AuditUtils.safeToString(result, 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve the event type using the unified method
|
// Resolve the event type using the unified method
|
||||||
AuditEventType eventType = AuditUtils.resolveEventType(
|
AuditEventType eventType =
|
||||||
method,
|
AuditUtils.resolveEventType(
|
||||||
joinPoint.getTarget().getClass(),
|
method,
|
||||||
path,
|
joinPoint.getTarget().getClass(),
|
||||||
httpMethod,
|
path,
|
||||||
auditedAnnotation
|
httpMethod,
|
||||||
);
|
auditedAnnotation);
|
||||||
|
|
||||||
// Check if we should use string type instead (for backward compatibility)
|
// Check if we should use string type instead (for backward compatibility)
|
||||||
if (auditedAnnotation != null) {
|
if (auditedAnnotation != null) {
|
||||||
String typeString = auditedAnnotation.typeString();
|
String typeString = auditedAnnotation.typeString();
|
||||||
if (eventType == AuditEventType.HTTP_REQUEST && StringUtils.isNotEmpty(typeString)) {
|
if (eventType == AuditEventType.HTTP_REQUEST
|
||||||
|
&& StringUtils.isNotEmpty(typeString)) {
|
||||||
auditService.audit(typeString, data, level);
|
auditService.audit(typeString, data, level);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the enum type
|
// Use the enum type
|
||||||
auditService.audit(eventType, data, level);
|
auditService.audit(eventType, data, level);
|
||||||
}
|
}
|
||||||
@ -191,14 +186,15 @@ public class ControllerAuditAspect {
|
|||||||
RequestMapping cm = method.getDeclaringClass().getAnnotation(RequestMapping.class);
|
RequestMapping cm = method.getDeclaringClass().getAnnotation(RequestMapping.class);
|
||||||
if (cm != null && cm.value().length > 0) base = cm.value()[0];
|
if (cm != null && cm.value().length > 0) base = cm.value()[0];
|
||||||
String mp = "";
|
String mp = "";
|
||||||
Annotation ann = switch (httpMethod) {
|
Annotation ann =
|
||||||
case "GET" -> method.getAnnotation(GetMapping.class);
|
switch (httpMethod) {
|
||||||
case "POST" -> method.getAnnotation(PostMapping.class);
|
case "GET" -> method.getAnnotation(GetMapping.class);
|
||||||
case "PUT" -> method.getAnnotation(PutMapping.class);
|
case "POST" -> method.getAnnotation(PostMapping.class);
|
||||||
case "DELETE" -> method.getAnnotation(DeleteMapping.class);
|
case "PUT" -> method.getAnnotation(PutMapping.class);
|
||||||
case "PATCH" -> method.getAnnotation(PatchMapping.class);
|
case "DELETE" -> method.getAnnotation(DeleteMapping.class);
|
||||||
default -> null;
|
case "PATCH" -> method.getAnnotation(PatchMapping.class);
|
||||||
};
|
default -> null;
|
||||||
|
};
|
||||||
if (ann instanceof GetMapping gm && gm.value().length > 0) mp = gm.value()[0];
|
if (ann instanceof GetMapping gm && gm.value().length > 0) mp = gm.value()[0];
|
||||||
if (ann instanceof PostMapping pm && pm.value().length > 0) mp = pm.value()[0];
|
if (ann instanceof PostMapping pm && pm.value().length > 0) mp = pm.value()[0];
|
||||||
if (ann instanceof PutMapping pum && pum.value().length > 0) mp = pum.value()[0];
|
if (ann instanceof PutMapping pum && pum.value().length > 0) mp = pum.value()[0];
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package stirling.software.proprietary.config;
|
package stirling.software.proprietary.config;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import org.slf4j.MDC;
|
import org.slf4j.MDC;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -7,23 +10,20 @@ import org.springframework.core.task.TaskDecorator;
|
|||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
public class AsyncConfig {
|
public class AsyncConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MDC context-propagating task decorator
|
* MDC context-propagating task decorator Copies MDC context from the caller thread to the async
|
||||||
* Copies MDC context from the caller thread to the async executor thread
|
* executor thread
|
||||||
*/
|
*/
|
||||||
static class MDCContextTaskDecorator implements TaskDecorator {
|
static class MDCContextTaskDecorator implements TaskDecorator {
|
||||||
@Override
|
@Override
|
||||||
public Runnable decorate(Runnable runnable) {
|
public Runnable decorate(Runnable runnable) {
|
||||||
// Capture the MDC context from the current thread
|
// Capture the MDC context from the current thread
|
||||||
Map<String, String> contextMap = MDC.getCopyOfContextMap();
|
Map<String, String> contextMap = MDC.getCopyOfContextMap();
|
||||||
|
|
||||||
return () -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
// Set the captured context on the worker thread
|
// Set the captured context on the worker thread
|
||||||
@ -47,11 +47,11 @@ public class AsyncConfig {
|
|||||||
exec.setMaxPoolSize(8);
|
exec.setMaxPoolSize(8);
|
||||||
exec.setQueueCapacity(1_000);
|
exec.setQueueCapacity(1_000);
|
||||||
exec.setThreadNamePrefix("audit-");
|
exec.setThreadNamePrefix("audit-");
|
||||||
|
|
||||||
// Set the task decorator to propagate MDC context
|
// Set the task decorator to propagate MDC context
|
||||||
exec.setTaskDecorator(new MDCContextTaskDecorator());
|
exec.setTaskDecorator(new MDCContextTaskDecorator());
|
||||||
|
|
||||||
exec.initialize();
|
exec.initialize();
|
||||||
return exec;
|
return exec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
package stirling.software.proprietary.config;
|
package stirling.software.proprietary.config;
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.proprietary.audit.AuditLevel;
|
import stirling.software.proprietary.audit.AuditLevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration properties for the audit system.
|
* Configuration properties for the audit system. Reads values from the ApplicationProperties under
|
||||||
* Reads values from the ApplicationProperties under premium.enterpriseFeatures.audit
|
* premium.enterpriseFeatures.audit
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Getter
|
@Getter
|
||||||
@ -22,47 +23,53 @@ public class AuditConfigurationProperties {
|
|||||||
private final boolean enabled;
|
private final boolean enabled;
|
||||||
private final int level;
|
private final int level;
|
||||||
private final int retentionDays;
|
private final int retentionDays;
|
||||||
|
|
||||||
public AuditConfigurationProperties(ApplicationProperties applicationProperties) {
|
public AuditConfigurationProperties(ApplicationProperties applicationProperties) {
|
||||||
ApplicationProperties.Premium.EnterpriseFeatures.Audit auditConfig =
|
ApplicationProperties.Premium.EnterpriseFeatures.Audit auditConfig =
|
||||||
applicationProperties.getPremium().getEnterpriseFeatures().getAudit();
|
applicationProperties.getPremium().getEnterpriseFeatures().getAudit();
|
||||||
// Read values directly from configuration
|
// Read values directly from configuration
|
||||||
this.enabled = auditConfig.isEnabled();
|
this.enabled = auditConfig.isEnabled();
|
||||||
|
|
||||||
// Ensure level is within valid bounds (0-3)
|
// Ensure level is within valid bounds (0-3)
|
||||||
int configLevel = auditConfig.getLevel();
|
int configLevel = auditConfig.getLevel();
|
||||||
this.level = Math.min(Math.max(configLevel, 0), 3);
|
this.level = Math.min(Math.max(configLevel, 0), 3);
|
||||||
|
|
||||||
// Retention days (0 means infinite)
|
// Retention days (0 means infinite)
|
||||||
this.retentionDays = auditConfig.getRetentionDays();
|
this.retentionDays = auditConfig.getRetentionDays();
|
||||||
|
|
||||||
log.debug("Initialized audit configuration: enabled={}, level={}, retentionDays={} (0=infinite)",
|
log.debug(
|
||||||
this.enabled, this.level, this.retentionDays);
|
"Initialized audit configuration: enabled={}, level={}, retentionDays={} (0=infinite)",
|
||||||
|
this.enabled,
|
||||||
|
this.level,
|
||||||
|
this.retentionDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the audit level as an enum
|
* Get the audit level as an enum
|
||||||
|
*
|
||||||
* @return The current AuditLevel
|
* @return The current AuditLevel
|
||||||
*/
|
*/
|
||||||
public AuditLevel getAuditLevel() {
|
public AuditLevel getAuditLevel() {
|
||||||
return AuditLevel.fromInt(level);
|
return AuditLevel.fromInt(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current audit level includes the specified level
|
* Check if the current audit level includes the specified level
|
||||||
|
*
|
||||||
* @param requiredLevel The level to check against
|
* @param requiredLevel The level to check against
|
||||||
* @return true if auditing is enabled and the current level includes the required level
|
* @return true if auditing is enabled and the current level includes the required level
|
||||||
*/
|
*/
|
||||||
public boolean isLevelEnabled(AuditLevel requiredLevel) {
|
public boolean isLevelEnabled(AuditLevel requiredLevel) {
|
||||||
return enabled && getAuditLevel().includes(requiredLevel);
|
return enabled && getAuditLevel().includes(requiredLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the effective retention period in days
|
* Get the effective retention period in days
|
||||||
|
*
|
||||||
* @return The number of days to retain audit records, or -1 for infinite retention
|
* @return The number of days to retain audit records, or -1 for infinite retention
|
||||||
*/
|
*/
|
||||||
public int getEffectiveRetentionDays() {
|
public int getEffectiveRetentionDays() {
|
||||||
// 0 means infinite retention
|
// 0 means infinite retention
|
||||||
return retentionDays <= 0 ? -1 : retentionDays;
|
return retentionDays <= 0 ? -1 : retentionDays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
|||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
/**
|
/** Configuration to explicitly enable JPA repositories and scheduling for the audit system. */
|
||||||
* Configuration to explicitly enable JPA repositories and scheduling for the audit system.
|
|
||||||
*/
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
@EnableJpaRepositories(basePackages = "stirling.software.proprietary.repository")
|
@EnableJpaRepositories(basePackages = "stirling.software.proprietary.repository")
|
||||||
@ -16,4 +14,4 @@ public class AuditJpaConfig {
|
|||||||
// This configuration enables JPA repositories in the specified package
|
// This configuration enables JPA repositories in the specified package
|
||||||
// and enables scheduling for audit cleanup tasks
|
// and enables scheduling for audit cleanup tasks
|
||||||
// No additional beans or methods needed
|
// No additional beans or methods needed
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package stirling.software.proprietary.config;
|
package stirling.software.proprietary.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import java.time.Instant;
|
||||||
import lombok.RequiredArgsConstructor;
|
import java.util.List;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.slf4j.MDC;
|
import org.slf4j.MDC;
|
||||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||||
@ -11,14 +11,16 @@ import org.springframework.context.annotation.Primary;
|
|||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.model.security.PersistentAuditEvent;
|
import stirling.software.proprietary.model.security.PersistentAuditEvent;
|
||||||
import stirling.software.proprietary.repository.PersistentAuditEventRepository;
|
import stirling.software.proprietary.repository.PersistentAuditEventRepository;
|
||||||
import stirling.software.proprietary.util.SecretMasker;
|
import stirling.software.proprietary.util.SecretMasker;
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Primary
|
@Primary
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@ -40,35 +42,33 @@ public class CustomAuditEventRepository implements AuditEventRepository {
|
|||||||
public void add(AuditEvent ev) {
|
public void add(AuditEvent ev) {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> clean =
|
Map<String, Object> clean =
|
||||||
CollectionUtils.isEmpty(ev.getData())
|
CollectionUtils.isEmpty(ev.getData())
|
||||||
? Map.of()
|
? Map.of()
|
||||||
: SecretMasker.mask(ev.getData());
|
: SecretMasker.mask(ev.getData());
|
||||||
|
|
||||||
|
if (clean.isEmpty() || (clean.size() == 1 && clean.containsKey("details"))) {
|
||||||
if (clean.isEmpty() ||
|
return;
|
||||||
(clean.size() == 1 && clean.containsKey("details"))) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
String rid = MDC.get("requestId");
|
String rid = MDC.get("requestId");
|
||||||
|
|
||||||
|
|
||||||
if (rid != null) {
|
if (rid != null) {
|
||||||
clean = new java.util.HashMap<>(clean);
|
clean = new java.util.HashMap<>(clean);
|
||||||
clean.put("requestId", rid);
|
clean.put("requestId", rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
String auditEventData = mapper.writeValueAsString(clean);
|
String auditEventData = mapper.writeValueAsString(clean);
|
||||||
log.debug("AuditEvent data (JSON): {}",auditEventData);
|
log.debug("AuditEvent data (JSON): {}", auditEventData);
|
||||||
|
|
||||||
PersistentAuditEvent ent = PersistentAuditEvent.builder()
|
PersistentAuditEvent ent =
|
||||||
.principal(ev.getPrincipal())
|
PersistentAuditEvent.builder()
|
||||||
.type(ev.getType())
|
.principal(ev.getPrincipal())
|
||||||
.data(auditEventData)
|
.type(ev.getType())
|
||||||
.timestamp(ev.getTimestamp())
|
.data(auditEventData)
|
||||||
.build();
|
.timestamp(ev.getTimestamp())
|
||||||
|
.build();
|
||||||
repo.save(ent);
|
repo.save(ent);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace(); // fail-open
|
e.printStackTrace(); // fail-open
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
|
@ -33,8 +33,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.audit.AuditEventType;
|
import stirling.software.proprietary.audit.AuditEventType;
|
||||||
import stirling.software.proprietary.audit.AuditLevel;
|
import stirling.software.proprietary.audit.AuditLevel;
|
||||||
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
import stirling.software.proprietary.config.AuditConfigurationProperties;
|
||||||
@ -42,10 +44,7 @@ import stirling.software.proprietary.model.security.PersistentAuditEvent;
|
|||||||
import stirling.software.proprietary.repository.PersistentAuditEventRepository;
|
import stirling.software.proprietary.repository.PersistentAuditEventRepository;
|
||||||
import stirling.software.proprietary.security.config.EnterpriseEndpoint;
|
import stirling.software.proprietary.security.config.EnterpriseEndpoint;
|
||||||
|
|
||||||
/**
|
/** Controller for the audit dashboard. Admin-only access. */
|
||||||
* Controller for the audit dashboard.
|
|
||||||
* Admin-only access.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/audit")
|
@RequestMapping("/audit")
|
||||||
@ -58,28 +57,24 @@ public class AuditDashboardController {
|
|||||||
private final AuditConfigurationProperties auditConfig;
|
private final AuditConfigurationProperties auditConfig;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
/**
|
/** Display the audit dashboard. */
|
||||||
* Display the audit dashboard.
|
|
||||||
*/
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public String showDashboard(Model model) {
|
public String showDashboard(Model model) {
|
||||||
model.addAttribute("auditEnabled", auditConfig.isEnabled());
|
model.addAttribute("auditEnabled", auditConfig.isEnabled());
|
||||||
model.addAttribute("auditLevel", auditConfig.getAuditLevel());
|
model.addAttribute("auditLevel", auditConfig.getAuditLevel());
|
||||||
model.addAttribute("auditLevelInt", auditConfig.getLevel());
|
model.addAttribute("auditLevelInt", auditConfig.getLevel());
|
||||||
model.addAttribute("retentionDays", auditConfig.getRetentionDays());
|
model.addAttribute("retentionDays", auditConfig.getRetentionDays());
|
||||||
|
|
||||||
// Add audit level enum values for display
|
// Add audit level enum values for display
|
||||||
model.addAttribute("auditLevels", AuditLevel.values());
|
model.addAttribute("auditLevels", AuditLevel.values());
|
||||||
|
|
||||||
// Add audit event types for the dropdown
|
// Add audit event types for the dropdown
|
||||||
model.addAttribute("auditEventTypes", AuditEventType.values());
|
model.addAttribute("auditEventTypes", AuditEventType.values());
|
||||||
|
|
||||||
return "audit/dashboard";
|
return "audit/dashboard";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Get audit events data for the dashboard tables. */
|
||||||
* Get audit events data for the dashboard tables.
|
|
||||||
*/
|
|
||||||
@GetMapping("/data")
|
@GetMapping("/data")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Map<String, Object> getAuditData(
|
public Map<String, Object> getAuditData(
|
||||||
@ -87,11 +82,13 @@ public class AuditDashboardController {
|
|||||||
@RequestParam(value = "size", defaultValue = "30") int size,
|
@RequestParam(value = "size", defaultValue = "30") int size,
|
||||||
@RequestParam(value = "type", required = false) String type,
|
@RequestParam(value = "type", required = false) String type,
|
||||||
@RequestParam(value = "principal", required = false) String principal,
|
@RequestParam(value = "principal", required = false) String principal,
|
||||||
@RequestParam(value = "startDate", required = false)
|
@RequestParam(value = "startDate", required = false)
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
@RequestParam(value = "endDate", required = false)
|
LocalDate startDate,
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate, HttpServletRequest request) {
|
@RequestParam(value = "endDate", required = false)
|
||||||
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
|
LocalDate endDate,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
Pageable pageable = PageRequest.of(page, size, Sort.by("timestamp").descending());
|
Pageable pageable = PageRequest.of(page, size, Sort.by("timestamp").descending());
|
||||||
Page<PersistentAuditEvent> events;
|
Page<PersistentAuditEvent> events;
|
||||||
@ -102,7 +99,9 @@ public class AuditDashboardController {
|
|||||||
mode = "principal + type + startDate + endDate";
|
mode = "principal + type + startDate + endDate";
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
events = auditRepository.findByPrincipalAndTypeAndTimestampBetween(principal, type, start, end, pageable);
|
events =
|
||||||
|
auditRepository.findByPrincipalAndTypeAndTimestampBetween(
|
||||||
|
principal, type, start, end, pageable);
|
||||||
} else if (type != null && principal != null) {
|
} else if (type != null && principal != null) {
|
||||||
mode = "principal + type";
|
mode = "principal + type";
|
||||||
events = auditRepository.findByPrincipalAndType(principal, type, pageable);
|
events = auditRepository.findByPrincipalAndType(principal, type, pageable);
|
||||||
@ -115,7 +114,9 @@ public class AuditDashboardController {
|
|||||||
mode = "principal + startDate + endDate";
|
mode = "principal + startDate + endDate";
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
events = auditRepository.findByPrincipalAndTimestampBetween(principal, start, end, pageable);
|
events =
|
||||||
|
auditRepository.findByPrincipalAndTimestampBetween(
|
||||||
|
principal, start, end, pageable);
|
||||||
} else if (startDate != null && endDate != null) {
|
} else if (startDate != null && endDate != null) {
|
||||||
mode = "startDate + endDate";
|
mode = "startDate + endDate";
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
@ -144,85 +145,93 @@ public class AuditDashboardController {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Get statistics for charts. */
|
||||||
/**
|
|
||||||
* Get statistics for charts.
|
|
||||||
*/
|
|
||||||
@GetMapping("/stats")
|
@GetMapping("/stats")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Map<String, Object> getAuditStats(
|
public Map<String, Object> getAuditStats(
|
||||||
@RequestParam(value = "days", defaultValue = "7") int days) {
|
@RequestParam(value = "days", defaultValue = "7") int days) {
|
||||||
|
|
||||||
// Get events from the last X days
|
// Get events from the last X days
|
||||||
Instant startDate = Instant.now().minus(java.time.Duration.ofDays(days));
|
Instant startDate = Instant.now().minus(java.time.Duration.ofDays(days));
|
||||||
List<PersistentAuditEvent> events = auditRepository.findByTimestampAfter(startDate);
|
List<PersistentAuditEvent> events = auditRepository.findByTimestampAfter(startDate);
|
||||||
|
|
||||||
// Count events by type
|
// Count events by type
|
||||||
Map<String, Long> eventsByType = events.stream()
|
Map<String, Long> eventsByType =
|
||||||
.collect(Collectors.groupingBy(PersistentAuditEvent::getType, Collectors.counting()));
|
events.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.groupingBy(
|
||||||
|
PersistentAuditEvent::getType, Collectors.counting()));
|
||||||
|
|
||||||
// Count events by principal
|
// Count events by principal
|
||||||
Map<String, Long> eventsByPrincipal = events.stream()
|
Map<String, Long> eventsByPrincipal =
|
||||||
.collect(Collectors.groupingBy(PersistentAuditEvent::getPrincipal, Collectors.counting()));
|
events.stream()
|
||||||
|
.collect(
|
||||||
|
Collectors.groupingBy(
|
||||||
|
PersistentAuditEvent::getPrincipal, Collectors.counting()));
|
||||||
|
|
||||||
// Count events by day
|
// Count events by day
|
||||||
Map<String, Long> eventsByDay = events.stream()
|
Map<String, Long> eventsByDay =
|
||||||
.collect(Collectors.groupingBy(
|
events.stream()
|
||||||
e -> LocalDateTime.ofInstant(e.getTimestamp(), ZoneId.systemDefault())
|
.collect(
|
||||||
.format(DateTimeFormatter.ISO_LOCAL_DATE),
|
Collectors.groupingBy(
|
||||||
Collectors.counting()));
|
e ->
|
||||||
|
LocalDateTime.ofInstant(
|
||||||
|
e.getTimestamp(),
|
||||||
|
ZoneId.systemDefault())
|
||||||
|
.format(DateTimeFormatter.ISO_LOCAL_DATE),
|
||||||
|
Collectors.counting()));
|
||||||
|
|
||||||
Map<String, Object> stats = new HashMap<>();
|
Map<String, Object> stats = new HashMap<>();
|
||||||
stats.put("eventsByType", eventsByType);
|
stats.put("eventsByType", eventsByType);
|
||||||
stats.put("eventsByPrincipal", eventsByPrincipal);
|
stats.put("eventsByPrincipal", eventsByPrincipal);
|
||||||
stats.put("eventsByDay", eventsByDay);
|
stats.put("eventsByDay", eventsByDay);
|
||||||
stats.put("totalEvents", events.size());
|
stats.put("totalEvents", events.size());
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Get all unique event types from the database for filtering. */
|
||||||
* Get all unique event types from the database for filtering.
|
|
||||||
*/
|
|
||||||
@GetMapping("/types")
|
@GetMapping("/types")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public List<String> getAuditTypes() {
|
public List<String> getAuditTypes() {
|
||||||
// Get distinct event types from the database
|
// Get distinct event types from the database
|
||||||
List<String> dbTypes = auditRepository.findDistinctEventTypes();
|
List<String> dbTypes = auditRepository.findDistinctEventTypes();
|
||||||
|
|
||||||
// Include standard enum types in case they're not in the database yet
|
// Include standard enum types in case they're not in the database yet
|
||||||
List<String> enumTypes = Arrays.stream(AuditEventType.values())
|
List<String> enumTypes =
|
||||||
.map(AuditEventType::name)
|
Arrays.stream(AuditEventType.values())
|
||||||
.collect(Collectors.toList());
|
.map(AuditEventType::name)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// Combine both sources, remove duplicates, and sort
|
// Combine both sources, remove duplicates, and sort
|
||||||
Set<String> combinedTypes = new HashSet<>();
|
Set<String> combinedTypes = new HashSet<>();
|
||||||
combinedTypes.addAll(dbTypes);
|
combinedTypes.addAll(dbTypes);
|
||||||
combinedTypes.addAll(enumTypes);
|
combinedTypes.addAll(enumTypes);
|
||||||
|
|
||||||
return combinedTypes.stream().sorted().collect(Collectors.toList());
|
return combinedTypes.stream().sorted().collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Export audit data as CSV. */
|
||||||
* Export audit data as CSV.
|
|
||||||
*/
|
|
||||||
@GetMapping("/export")
|
@GetMapping("/export")
|
||||||
public ResponseEntity<byte[]> exportAuditData(
|
public ResponseEntity<byte[]> exportAuditData(
|
||||||
@RequestParam(value = "type", required = false) String type,
|
@RequestParam(value = "type", required = false) String type,
|
||||||
@RequestParam(value = "principal", required = false) String principal,
|
@RequestParam(value = "principal", required = false) String principal,
|
||||||
@RequestParam(value = "startDate", required = false)
|
@RequestParam(value = "startDate", required = false)
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
@RequestParam(value = "endDate", required = false)
|
LocalDate startDate,
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
@RequestParam(value = "endDate", required = false)
|
||||||
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
|
LocalDate endDate) {
|
||||||
|
|
||||||
// Get data with same filtering as getAuditData
|
// Get data with same filtering as getAuditData
|
||||||
List<PersistentAuditEvent> events;
|
List<PersistentAuditEvent> events;
|
||||||
|
|
||||||
if (type != null && principal != null && startDate != null && endDate != null) {
|
if (type != null && principal != null && startDate != null && endDate != null) {
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
events = auditRepository.findAllByPrincipalAndTypeAndTimestampBetweenForExport(
|
events =
|
||||||
principal, type, start, end);
|
auditRepository.findAllByPrincipalAndTypeAndTimestampBetweenForExport(
|
||||||
|
principal, type, start, end);
|
||||||
} else if (type != null && principal != null) {
|
} else if (type != null && principal != null) {
|
||||||
events = auditRepository.findAllByPrincipalAndTypeForExport(principal, type);
|
events = auditRepository.findAllByPrincipalAndTypeForExport(principal, type);
|
||||||
} else if (type != null && startDate != null && endDate != null) {
|
} else if (type != null && startDate != null && endDate != null) {
|
||||||
@ -232,7 +241,9 @@ public class AuditDashboardController {
|
|||||||
} else if (principal != null && startDate != null && endDate != null) {
|
} else if (principal != null && startDate != null && endDate != null) {
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
events = auditRepository.findAllByPrincipalAndTimestampBetweenForExport(principal, start, end);
|
events =
|
||||||
|
auditRepository.findAllByPrincipalAndTimestampBetweenForExport(
|
||||||
|
principal, start, end);
|
||||||
} else if (startDate != null && endDate != null) {
|
} else if (startDate != null && endDate != null) {
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
@ -244,13 +255,13 @@ public class AuditDashboardController {
|
|||||||
} else {
|
} else {
|
||||||
events = auditRepository.findAll();
|
events = auditRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to CSV
|
// Convert to CSV
|
||||||
StringBuilder csv = new StringBuilder();
|
StringBuilder csv = new StringBuilder();
|
||||||
csv.append("ID,Principal,Type,Timestamp,Data\n");
|
csv.append("ID,Principal,Type,Timestamp,Data\n");
|
||||||
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
|
DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
|
||||||
|
|
||||||
for (PersistentAuditEvent event : events) {
|
for (PersistentAuditEvent event : events) {
|
||||||
csv.append(event.getId()).append(",");
|
csv.append(event.getId()).append(",");
|
||||||
csv.append(escapeCSV(event.getPrincipal())).append(",");
|
csv.append(escapeCSV(event.getPrincipal())).append(",");
|
||||||
@ -258,39 +269,38 @@ public class AuditDashboardController {
|
|||||||
csv.append(formatter.format(event.getTimestamp())).append(",");
|
csv.append(formatter.format(event.getTimestamp())).append(",");
|
||||||
csv.append(escapeCSV(event.getData())).append("\n");
|
csv.append(escapeCSV(event.getData())).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] csvBytes = csv.toString().getBytes();
|
byte[] csvBytes = csv.toString().getBytes();
|
||||||
|
|
||||||
// Set up HTTP headers for download
|
// Set up HTTP headers for download
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
||||||
headers.setContentDispositionFormData("attachment", "audit_export.csv");
|
headers.setContentDispositionFormData("attachment", "audit_export.csv");
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok().headers(headers).body(csvBytes);
|
||||||
.headers(headers)
|
|
||||||
.body(csvBytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Export audit data as JSON. */
|
||||||
* Export audit data as JSON.
|
|
||||||
*/
|
|
||||||
@GetMapping("/export/json")
|
@GetMapping("/export/json")
|
||||||
public ResponseEntity<byte[]> exportAuditDataJson(
|
public ResponseEntity<byte[]> exportAuditDataJson(
|
||||||
@RequestParam(value = "type", required = false) String type,
|
@RequestParam(value = "type", required = false) String type,
|
||||||
@RequestParam(value = "principal", required = false) String principal,
|
@RequestParam(value = "principal", required = false) String principal,
|
||||||
@RequestParam(value = "startDate", required = false)
|
@RequestParam(value = "startDate", required = false)
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
@RequestParam(value = "endDate", required = false)
|
LocalDate startDate,
|
||||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
|
@RequestParam(value = "endDate", required = false)
|
||||||
|
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
|
||||||
|
LocalDate endDate) {
|
||||||
|
|
||||||
// Get data with same filtering as getAuditData
|
// Get data with same filtering as getAuditData
|
||||||
List<PersistentAuditEvent> events;
|
List<PersistentAuditEvent> events;
|
||||||
|
|
||||||
if (type != null && principal != null && startDate != null && endDate != null) {
|
if (type != null && principal != null && startDate != null && endDate != null) {
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
events = auditRepository.findAllByPrincipalAndTypeAndTimestampBetweenForExport(
|
events =
|
||||||
principal, type, start, end);
|
auditRepository.findAllByPrincipalAndTypeAndTimestampBetweenForExport(
|
||||||
|
principal, type, start, end);
|
||||||
} else if (type != null && principal != null) {
|
} else if (type != null && principal != null) {
|
||||||
events = auditRepository.findAllByPrincipalAndTypeForExport(principal, type);
|
events = auditRepository.findAllByPrincipalAndTypeForExport(principal, type);
|
||||||
} else if (type != null && startDate != null && endDate != null) {
|
} else if (type != null && startDate != null && endDate != null) {
|
||||||
@ -300,7 +310,9 @@ public class AuditDashboardController {
|
|||||||
} else if (principal != null && startDate != null && endDate != null) {
|
} else if (principal != null && startDate != null && endDate != null) {
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
events = auditRepository.findAllByPrincipalAndTimestampBetweenForExport(principal, start, end);
|
events =
|
||||||
|
auditRepository.findAllByPrincipalAndTimestampBetweenForExport(
|
||||||
|
principal, start, end);
|
||||||
} else if (startDate != null && endDate != null) {
|
} else if (startDate != null && endDate != null) {
|
||||||
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant start = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
Instant end = endDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant();
|
||||||
@ -312,28 +324,24 @@ public class AuditDashboardController {
|
|||||||
} else {
|
} else {
|
||||||
events = auditRepository.findAll();
|
events = auditRepository.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to JSON
|
// Convert to JSON
|
||||||
try {
|
try {
|
||||||
byte[] jsonBytes = objectMapper.writeValueAsBytes(events);
|
byte[] jsonBytes = objectMapper.writeValueAsBytes(events);
|
||||||
|
|
||||||
// Set up HTTP headers for download
|
// Set up HTTP headers for download
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
headers.setContentDispositionFormData("attachment", "audit_export.json");
|
headers.setContentDispositionFormData("attachment", "audit_export.json");
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok().headers(headers).body(jsonBytes);
|
||||||
.headers(headers)
|
|
||||||
.body(jsonBytes);
|
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
log.error("Error serializing audit events to JSON", e);
|
log.error("Error serializing audit events to JSON", e);
|
||||||
return ResponseEntity.internalServerError().build();
|
return ResponseEntity.internalServerError().build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Helper method to escape CSV fields. */
|
||||||
* Helper method to escape CSV fields.
|
|
||||||
*/
|
|
||||||
private String escapeCSV(String field) {
|
private String escapeCSV(String field) {
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
return "";
|
return "";
|
||||||
@ -341,4 +349,4 @@ public class AuditDashboardController {
|
|||||||
// Replace double quotes with two double quotes and wrap in quotes
|
// Replace double quotes with two double quotes and wrap in quotes
|
||||||
return "\"" + field.replace("\"", "\"\"") + "\"";
|
return "\"" + field.replace("\"", "\"\"") + "\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package stirling.software.proprietary.model;
|
package stirling.software.proprietary.model;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.model.User;
|
import stirling.software.proprietary.security.model.User;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -1,23 +1,29 @@
|
|||||||
package stirling.software.proprietary.model.security;
|
package stirling.software.proprietary.model.security;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import lombok.*;
|
|
||||||
import org.hibernate.annotations.Index;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(
|
@Table(
|
||||||
name = "audit_events",
|
name = "audit_events",
|
||||||
indexes = {
|
indexes = {
|
||||||
@jakarta.persistence.Index(name = "idx_audit_timestamp", columnList = "timestamp"),
|
@jakarta.persistence.Index(name = "idx_audit_timestamp", columnList = "timestamp"),
|
||||||
@jakarta.persistence.Index(name = "idx_audit_principal", columnList = "principal"),
|
@jakarta.persistence.Index(name = "idx_audit_principal", columnList = "principal"),
|
||||||
@jakarta.persistence.Index(name = "idx_audit_type", columnList = "type"),
|
@jakarta.persistence.Index(name = "idx_audit_type", columnList = "type"),
|
||||||
@jakarta.persistence.Index(name = "idx_audit_principal_type", columnList = "principal,type"),
|
@jakarta.persistence.Index(
|
||||||
@jakarta.persistence.Index(name = "idx_audit_type_timestamp", columnList = "type,timestamp")
|
name = "idx_audit_principal_type",
|
||||||
}
|
columnList = "principal,type"),
|
||||||
)
|
@jakarta.persistence.Index(
|
||||||
@Data @Builder @NoArgsConstructor @AllArgsConstructor
|
name = "idx_audit_type_timestamp",
|
||||||
|
columnList = "type,timestamp")
|
||||||
|
})
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class PersistentAuditEvent {
|
public class PersistentAuditEvent {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@ -27,8 +33,7 @@ public class PersistentAuditEvent {
|
|||||||
private String principal;
|
private String principal;
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
@Lob
|
@Lob private String data; // JSON blob
|
||||||
private String data; // JSON blob
|
|
||||||
|
|
||||||
private Instant timestamp;
|
private Instant timestamp;
|
||||||
}
|
}
|
||||||
|
@ -15,58 +15,104 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import stirling.software.proprietary.model.security.PersistentAuditEvent;
|
import stirling.software.proprietary.model.security.PersistentAuditEvent;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface PersistentAuditEventRepository
|
public interface PersistentAuditEventRepository extends JpaRepository<PersistentAuditEvent, Long> {
|
||||||
extends JpaRepository<PersistentAuditEvent, Long> {
|
|
||||||
|
|
||||||
// Basic queries
|
// Basic queries
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%'))")
|
@Query(
|
||||||
Page<PersistentAuditEvent> findByPrincipal(@Param("principal") String principal, Pageable pageable);
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%'))")
|
||||||
|
Page<PersistentAuditEvent> findByPrincipal(
|
||||||
|
@Param("principal") String principal, Pageable pageable);
|
||||||
|
|
||||||
Page<PersistentAuditEvent> findByType(String type, Pageable pageable);
|
Page<PersistentAuditEvent> findByType(String type, Pageable pageable);
|
||||||
Page<PersistentAuditEvent> findByTimestampBetween(Instant startDate, Instant endDate, Pageable pageable);
|
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type")
|
Page<PersistentAuditEvent> findByTimestampBetween(
|
||||||
Page<PersistentAuditEvent> findByPrincipalAndType(@Param("principal") String principal, @Param("type") String type, Pageable pageable);
|
Instant startDate, Instant endDate, Pageable pageable);
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.timestamp BETWEEN :startDate AND :endDate")
|
|
||||||
Page<PersistentAuditEvent> findByPrincipalAndTimestampBetween(@Param("principal") String principal, @Param("startDate") Instant startDate, @Param("endDate") Instant endDate, Pageable pageable);
|
@Query(
|
||||||
Page<PersistentAuditEvent> findByTypeAndTimestampBetween(String type, Instant startDate, Instant endDate, Pageable pageable);
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type")
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type AND e.timestamp BETWEEN :startDate AND :endDate")
|
Page<PersistentAuditEvent> findByPrincipalAndType(
|
||||||
Page<PersistentAuditEvent> findByPrincipalAndTypeAndTimestampBetween(@Param("principal") String principal, @Param("type") String type, @Param("startDate") Instant startDate, @Param("endDate") Instant endDate, Pageable pageable);
|
@Param("principal") String principal, @Param("type") String type, Pageable pageable);
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.timestamp BETWEEN :startDate AND :endDate")
|
||||||
|
Page<PersistentAuditEvent> findByPrincipalAndTimestampBetween(
|
||||||
|
@Param("principal") String principal,
|
||||||
|
@Param("startDate") Instant startDate,
|
||||||
|
@Param("endDate") Instant endDate,
|
||||||
|
Pageable pageable);
|
||||||
|
|
||||||
|
Page<PersistentAuditEvent> findByTypeAndTimestampBetween(
|
||||||
|
String type, Instant startDate, Instant endDate, Pageable pageable);
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type AND e.timestamp BETWEEN :startDate AND :endDate")
|
||||||
|
Page<PersistentAuditEvent> findByPrincipalAndTypeAndTimestampBetween(
|
||||||
|
@Param("principal") String principal,
|
||||||
|
@Param("type") String type,
|
||||||
|
@Param("startDate") Instant startDate,
|
||||||
|
@Param("endDate") Instant endDate,
|
||||||
|
Pageable pageable);
|
||||||
|
|
||||||
// Non-paged versions for export
|
// Non-paged versions for export
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%'))")
|
@Query(
|
||||||
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%'))")
|
||||||
List<PersistentAuditEvent> findAllByPrincipalForExport(@Param("principal") String principal);
|
List<PersistentAuditEvent> findAllByPrincipalForExport(@Param("principal") String principal);
|
||||||
|
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE e.type = :type")
|
@Query("SELECT e FROM PersistentAuditEvent e WHERE e.type = :type")
|
||||||
List<PersistentAuditEvent> findByTypeForExport(@Param("type") String type);
|
List<PersistentAuditEvent> findByTypeForExport(@Param("type") String type);
|
||||||
|
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE e.timestamp BETWEEN :startDate AND :endDate")
|
@Query("SELECT e FROM PersistentAuditEvent e WHERE e.timestamp BETWEEN :startDate AND :endDate")
|
||||||
List<PersistentAuditEvent> findAllByTimestampBetweenForExport(@Param("startDate") Instant startDate, @Param("endDate") Instant endDate);
|
List<PersistentAuditEvent> findAllByTimestampBetweenForExport(
|
||||||
|
@Param("startDate") Instant startDate, @Param("endDate") Instant endDate);
|
||||||
|
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE e.timestamp > :startDate")
|
@Query("SELECT e FROM PersistentAuditEvent e WHERE e.timestamp > :startDate")
|
||||||
List<PersistentAuditEvent> findByTimestampAfter(@Param("startDate") Instant startDate);
|
List<PersistentAuditEvent> findByTimestampAfter(@Param("startDate") Instant startDate);
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type")
|
|
||||||
List<PersistentAuditEvent> findAllByPrincipalAndTypeForExport(@Param("principal") String principal, @Param("type") String type);
|
@Query(
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.timestamp BETWEEN :startDate AND :endDate")
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type")
|
||||||
List<PersistentAuditEvent> findAllByPrincipalAndTimestampBetweenForExport(@Param("principal") String principal, @Param("startDate") Instant startDate, @Param("endDate") Instant endDate);
|
List<PersistentAuditEvent> findAllByPrincipalAndTypeForExport(
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE e.type = :type AND e.timestamp BETWEEN :startDate AND :endDate")
|
@Param("principal") String principal, @Param("type") String type);
|
||||||
List<PersistentAuditEvent> findAllByTypeAndTimestampBetweenForExport(@Param("type") String type, @Param("startDate") Instant startDate, @Param("endDate") Instant endDate);
|
|
||||||
@Query("SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type AND e.timestamp BETWEEN :startDate AND :endDate")
|
@Query(
|
||||||
List<PersistentAuditEvent> findAllByPrincipalAndTypeAndTimestampBetweenForExport(@Param("principal") String principal, @Param("type") String type, @Param("startDate") Instant startDate, @Param("endDate") Instant endDate);
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.timestamp BETWEEN :startDate AND :endDate")
|
||||||
|
List<PersistentAuditEvent> findAllByPrincipalAndTimestampBetweenForExport(
|
||||||
|
@Param("principal") String principal,
|
||||||
|
@Param("startDate") Instant startDate,
|
||||||
|
@Param("endDate") Instant endDate);
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"SELECT e FROM PersistentAuditEvent e WHERE e.type = :type AND e.timestamp BETWEEN :startDate AND :endDate")
|
||||||
|
List<PersistentAuditEvent> findAllByTypeAndTimestampBetweenForExport(
|
||||||
|
@Param("type") String type,
|
||||||
|
@Param("startDate") Instant startDate,
|
||||||
|
@Param("endDate") Instant endDate);
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"SELECT e FROM PersistentAuditEvent e WHERE UPPER(e.principal) LIKE UPPER(CONCAT('%', :principal, '%')) AND e.type = :type AND e.timestamp BETWEEN :startDate AND :endDate")
|
||||||
|
List<PersistentAuditEvent> findAllByPrincipalAndTypeAndTimestampBetweenForExport(
|
||||||
|
@Param("principal") String principal,
|
||||||
|
@Param("type") String type,
|
||||||
|
@Param("startDate") Instant startDate,
|
||||||
|
@Param("endDate") Instant endDate);
|
||||||
|
|
||||||
// Cleanup queries
|
// Cleanup queries
|
||||||
@Query("DELETE FROM PersistentAuditEvent e WHERE e.timestamp < ?1")
|
@Query("DELETE FROM PersistentAuditEvent e WHERE e.timestamp < ?1")
|
||||||
@Modifying
|
@Modifying
|
||||||
@Transactional
|
@Transactional
|
||||||
int deleteByTimestampBefore(Instant cutoffDate);
|
int deleteByTimestampBefore(Instant cutoffDate);
|
||||||
|
|
||||||
// Find IDs for batch deletion - using JPQL with setMaxResults instead of native query
|
// Find IDs for batch deletion - using JPQL with setMaxResults instead of native query
|
||||||
@Query("SELECT e.id FROM PersistentAuditEvent e WHERE e.timestamp < ?1 ORDER BY e.id")
|
@Query("SELECT e.id FROM PersistentAuditEvent e WHERE e.timestamp < ?1 ORDER BY e.id")
|
||||||
List<Long> findIdsForBatchDeletion(Instant cutoffDate, Pageable pageable);
|
List<Long> findIdsForBatchDeletion(Instant cutoffDate, Pageable pageable);
|
||||||
|
|
||||||
// Stats queries
|
// Stats queries
|
||||||
@Query("SELECT e.type, COUNT(e) FROM PersistentAuditEvent e GROUP BY e.type")
|
@Query("SELECT e.type, COUNT(e) FROM PersistentAuditEvent e GROUP BY e.type")
|
||||||
List<Object[]> countByType();
|
List<Object[]> countByType();
|
||||||
|
|
||||||
@Query("SELECT e.principal, COUNT(e) FROM PersistentAuditEvent e GROUP BY e.principal")
|
@Query("SELECT e.principal, COUNT(e) FROM PersistentAuditEvent e GROUP BY e.principal")
|
||||||
List<Object[]> countByPrincipal();
|
List<Object[]> countByPrincipal();
|
||||||
|
|
||||||
// Get distinct event types for filtering
|
// Get distinct event types for filtering
|
||||||
@Query("SELECT DISTINCT e.type FROM PersistentAuditEvent e ORDER BY e.type")
|
@Query("SELECT DISTINCT e.type FROM PersistentAuditEvent e ORDER BY e.type")
|
||||||
List<String> findDistinctEventTypes();
|
List<String> findDistinctEventTypes();
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
package stirling.software.proprietary.security;
|
package stirling.software.proprietary.security;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.DisabledException;
|
import org.springframework.security.authentication.DisabledException;
|
||||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package stirling.software.proprietary.security;
|
package stirling.software.proprietary.security;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
@ -11,11 +10,9 @@ import jakarta.servlet.ServletException;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
import java.io.IOException;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
|
||||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
|
||||||
import stirling.software.common.util.RequestUriUtils;
|
import stirling.software.common.util.RequestUriUtils;
|
||||||
import stirling.software.proprietary.audit.AuditEventType;
|
import stirling.software.proprietary.audit.AuditEventType;
|
||||||
import stirling.software.proprietary.audit.AuditLevel;
|
import stirling.software.proprietary.audit.AuditLevel;
|
||||||
|
@ -1,22 +1,27 @@
|
|||||||
package stirling.software.proprietary.security;
|
package stirling.software.proprietary.security;
|
||||||
|
|
||||||
import com.coveo.saml.SamlClient;
|
|
||||||
import com.coveo.saml.SamlException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.security.interfaces.RSAPrivateKey;
|
import java.security.interfaces.RSAPrivateKey;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
|
||||||
|
import com.coveo.saml.SamlClient;
|
||||||
|
import com.coveo.saml.SamlException;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.configuration.AppConfig;
|
import stirling.software.common.configuration.AppConfig;
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
package stirling.software.proprietary.security;
|
package stirling.software.proprietary.security;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.enumeration.Role;
|
import stirling.software.common.model.enumeration.Role;
|
||||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||||
@ -62,9 +66,10 @@ public class InitialSecuritySetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userService.saveAll(usersWithoutTeam); // batch save
|
userService.saveAll(usersWithoutTeam); // batch save
|
||||||
if(usersWithoutTeam != null && !usersWithoutTeam.isEmpty()) {
|
if (usersWithoutTeam != null && !usersWithoutTeam.isEmpty()) {
|
||||||
log.info(
|
log.info(
|
||||||
"Assigned {} user(s) without a team to the default team.", usersWithoutTeam.size());
|
"Assigned {} user(s) without a team to the default team.",
|
||||||
|
usersWithoutTeam.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package stirling.software.proprietary.security;
|
package stirling.software.proprietary.security;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.filter.IPRateLimitingFilter;
|
import stirling.software.proprietary.security.filter.IPRateLimitingFilter;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ -2,10 +2,6 @@ package stirling.software.proprietary.security.config;
|
|||||||
|
|
||||||
import static stirling.software.common.util.ProviderUtils.validateProvider;
|
import static stirling.software.common.util.ProviderUtils.validateProvider;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -14,7 +10,7 @@ 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 lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
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;
|
||||||
@ -23,6 +19,16 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
|
|||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.ApplicationProperties.Security;
|
import stirling.software.common.model.ApplicationProperties.Security;
|
||||||
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
|
||||||
|
@ -8,4 +8,4 @@ import java.lang.annotation.Target;
|
|||||||
/** Annotation to mark endpoints that require an Enterprise license. */
|
/** Annotation to mark endpoints that require an Enterprise license. */
|
||||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface EnterpriseEndpoint {}
|
public @interface EnterpriseEndpoint {}
|
||||||
|
@ -27,4 +27,4 @@ public class EnterpriseEndpointAspect {
|
|||||||
}
|
}
|
||||||
return joinPoint.proceed();
|
return joinPoint.proceed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.configuration;
|
package stirling.software.proprietary.security.configuration;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
@ -12,6 +11,10 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.configuration.InstallationPathConfig;
|
import stirling.software.common.configuration.InstallationPathConfig;
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package stirling.software.proprietary.security.configuration;
|
package stirling.software.proprietary.security.configuration;
|
||||||
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
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.mail.javamail.JavaMailSender;
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.configuration;
|
package stirling.software.proprietary.security.configuration;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
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;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -27,6 +27,9 @@ import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
|||||||
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.configuration.AppConfig;
|
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;
|
||||||
|
@ -8,6 +8,7 @@ import org.springframework.context.annotation.Primary;
|
|||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.ApplicationProperties.EnterpriseEdition;
|
import stirling.software.common.model.ApplicationProperties.EnterpriseEdition;
|
||||||
import stirling.software.common.model.ApplicationProperties.Premium;
|
import stirling.software.common.model.ApplicationProperties.Premium;
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
package stirling.software.proprietary.security.configuration.ee;
|
package stirling.software.proprietary.security.configuration.ee;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.posthog.java.shaded.org.json.JSONException;
|
|
||||||
import com.posthog.java.shaded.org.json.JSONObject;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
|
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
|
||||||
import org.bouncycastle.crypto.signers.Ed25519Signer;
|
import org.bouncycastle.crypto.signers.Ed25519Signer;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.posthog.java.shaded.org.json.JSONException;
|
||||||
|
import com.posthog.java.shaded.org.json.JSONObject;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.util.GeneralUtils;
|
import stirling.software.common.util.GeneralUtils;
|
||||||
|
|
||||||
|
@ -4,9 +4,12 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
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 lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.util.GeneralUtils;
|
import stirling.software.common.util.GeneralUtils;
|
||||||
import stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
import stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
package stirling.software.proprietary.security.controller.api;
|
package stirling.software.proprietary.security.controller.api;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Hidden;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
@ -23,6 +18,15 @@ import org.springframework.stereotype.Controller;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.database.H2SQLCondition;
|
import stirling.software.proprietary.security.database.H2SQLCondition;
|
||||||
import stirling.software.proprietary.security.service.DatabaseService;
|
import stirling.software.proprietary.security.service.DatabaseService;
|
||||||
|
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
package stirling.software.proprietary.security.controller.api;
|
package stirling.software.proprietary.security.controller.api;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.mail.MessagingException;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -14,6 +8,16 @@ import org.springframework.web.bind.annotation.ModelAttribute;
|
|||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import jakarta.mail.MessagingException;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.model.api.Email;
|
import stirling.software.proprietary.security.model.api.Email;
|
||||||
import stirling.software.proprietary.security.service.EmailService;
|
import stirling.software.proprietary.security.service.EmailService;
|
||||||
|
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
package stirling.software.proprietary.security.controller.api;
|
package stirling.software.proprietary.security.controller.api;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.transaction.Transactional;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.servlet.view.RedirectView;
|
import org.springframework.web.servlet.view.RedirectView;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.model.Team;
|
import stirling.software.proprietary.model.Team;
|
||||||
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
||||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
package stirling.software.proprietary.security.controller.api;
|
package stirling.software.proprietary.security.controller.api;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import jakarta.transaction.Transactional;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
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 lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@ -25,6 +20,16 @@ import org.springframework.ui.Model;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
import org.springframework.web.servlet.view.RedirectView;
|
import org.springframework.web.servlet.view.RedirectView;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.enumeration.Role;
|
import stirling.software.common.model.enumeration.Role;
|
||||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
package stirling.software.proprietary.security.controller.web;
|
package stirling.software.proprietary.security.controller.web;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
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.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.common.model.FileInfo;
|
import stirling.software.common.model.FileInfo;
|
||||||
import stirling.software.proprietary.security.service.DatabaseService;
|
import stirling.software.proprietary.security.service.DatabaseService;
|
||||||
|
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
package stirling.software.proprietary.security.controller.web;
|
package stirling.software.proprietary.security.controller.web;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
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 lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.model.Team;
|
import stirling.software.proprietary.model.Team;
|
||||||
import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
||||||
import stirling.software.proprietary.security.database.repository.SessionRepository;
|
import stirling.software.proprietary.security.database.repository.SessionRepository;
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package stirling.software.proprietary.security.database;
|
package stirling.software.proprietary.security.database;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.common.model.exception.UnsupportedProviderException;
|
import stirling.software.common.model.exception.UnsupportedProviderException;
|
||||||
import stirling.software.proprietary.security.service.DatabaseServiceInterface;
|
import stirling.software.proprietary.security.service.DatabaseServiceInterface;
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package stirling.software.proprietary.security.database.repository;
|
package stirling.software.proprietary.security.database.repository;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.model.Authority;
|
import stirling.software.proprietary.security.model.Authority;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package stirling.software.proprietary.security.database.repository;
|
package stirling.software.proprietary.security.database.repository;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
|
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
|
||||||
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.model.PersistentLogin;
|
import stirling.software.proprietary.security.model.PersistentLogin;
|
||||||
|
|
||||||
public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
public class JPATokenRepositoryImpl implements PersistentTokenRepository {
|
||||||
|
@ -2,6 +2,7 @@ package stirling.software.proprietary.security.database.repository;
|
|||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.model.PersistentLogin;
|
import stirling.software.proprietary.security.model.PersistentLogin;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package stirling.software.proprietary.security.database.repository;
|
package stirling.software.proprietary.security.database.repository;
|
||||||
|
|
||||||
import jakarta.transaction.Transactional;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
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;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.model.SessionEntity;
|
import stirling.software.proprietary.security.model.SessionEntity;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
|
@ -2,10 +2,12 @@ package stirling.software.proprietary.security.database.repository;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import stirling.software.proprietary.model.Team;
|
import stirling.software.proprietary.model.Team;
|
||||||
import stirling.software.proprietary.security.model.User;
|
import stirling.software.proprietary.security.model.User;
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package stirling.software.proprietary.security.filter;
|
package stirling.software.proprietary.security.filter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class EnterpriseEndpointFilter extends OncePerRequestFilter {
|
public class EnterpriseEndpointFilter extends OncePerRequestFilter {
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
package stirling.software.proprietary.security.filter;
|
package stirling.software.proprietary.security.filter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Optional;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
|
||||||
import stirling.software.common.util.RequestUriUtils;
|
import stirling.software.common.util.RequestUriUtils;
|
||||||
import stirling.software.proprietary.security.model.User;
|
import stirling.software.proprietary.security.model.User;
|
||||||
import stirling.software.proprietary.security.service.UserService;
|
import stirling.software.proprietary.security.service.UserService;
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
package stirling.software.proprietary.security.filter;
|
package stirling.software.proprietary.security.filter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import jakarta.servlet.Filter;
|
import jakarta.servlet.Filter;
|
||||||
import jakarta.servlet.FilterChain;
|
import jakarta.servlet.FilterChain;
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.ServletRequest;
|
import jakarta.servlet.ServletRequest;
|
||||||
import jakarta.servlet.ServletResponse;
|
import jakarta.servlet.ServletResponse;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import stirling.software.common.util.RequestUriUtils;
|
import stirling.software.common.util.RequestUriUtils;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
package stirling.software.proprietary.security.filter;
|
package stirling.software.proprietary.security.filter;
|
||||||
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
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 lombok.extern.slf4j.Slf4j;
|
|
||||||
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;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@ -20,6 +16,14 @@ import org.springframework.security.core.userdetails.UserDetails;
|
|||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.common.model.ApplicationProperties;
|
import stirling.software.common.model.ApplicationProperties;
|
||||||
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
|
import stirling.software.common.model.ApplicationProperties.Security.OAUTH2;
|
||||||
import stirling.software.common.model.ApplicationProperties.Security.SAML2;
|
import stirling.software.common.model.ApplicationProperties.Security.SAML2;
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
package stirling.software.proprietary.security.filter;
|
package stirling.software.proprietary.security.filter;
|
||||||
|
|
||||||
import io.github.bucket4j.Bandwidth;
|
|
||||||
import io.github.bucket4j.Bucket;
|
|
||||||
import io.github.bucket4j.ConsumptionProbe;
|
|
||||||
import io.github.pixee.security.Newlines;
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -20,6 +13,17 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import io.github.bucket4j.Bandwidth;
|
||||||
|
import io.github.bucket4j.Bucket;
|
||||||
|
import io.github.bucket4j.ConsumptionProbe;
|
||||||
|
import io.github.pixee.security.Newlines;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import stirling.software.common.model.enumeration.Role;
|
import stirling.software.common.model.enumeration.Role;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.model;
|
package stirling.software.proprietary.security.model;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.model;
|
package stirling.software.proprietary.security.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
@ -8,7 +10,7 @@ import jakarta.persistence.Id;
|
|||||||
import jakarta.persistence.JoinColumn;
|
import jakarta.persistence.JoinColumn;
|
||||||
import jakarta.persistence.ManyToOne;
|
import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.io.Serializable;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package stirling.software.proprietary.security.model;
|
package stirling.software.proprietary.security.model;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.util.Date;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package stirling.software.proprietary.security.model;
|
package stirling.software.proprietary.security.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Date;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
package stirling.software.proprietary.security.model;
|
package stirling.software.proprietary.security.model;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
import stirling.software.common.model.enumeration.Role;
|
import stirling.software.common.model.enumeration.Role;
|
||||||
import stirling.software.proprietary.model.Team;
|
import stirling.software.proprietary.model.Team;
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package stirling.software.proprietary.security.model.api;
|
package stirling.software.proprietary.security.model.api;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import stirling.software.common.model.api.GeneralFile;
|
import stirling.software.common.model.api.GeneralFile;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user