mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-23 16:05:09 +00:00
Compare commits
1 Commits
2402e587f7
...
17eb68f5a7
Author | SHA1 | Date | |
---|---|---|---|
![]() |
17eb68f5a7 |
24
.github/scripts/check_language_properties.py
vendored
24
.github/scripts/check_language_properties.py
vendored
@ -196,9 +196,7 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
|||||||
|
|
||||||
if len(file_list) == 1:
|
if len(file_list) == 1:
|
||||||
file_arr = file_list[0].split()
|
file_arr = file_list[0].split()
|
||||||
base_dir = os.path.abspath(
|
base_dir = os.path.abspath(os.path.join(os.getcwd(), "src", "main", "resources"))
|
||||||
os.path.join(os.getcwd(), "stirling-pdf", "src", "main", "resources")
|
|
||||||
)
|
|
||||||
|
|
||||||
for file_path in file_arr:
|
for file_path in file_arr:
|
||||||
file_normpath = os.path.normpath(file_path)
|
file_normpath = os.path.normpath(file_path)
|
||||||
@ -218,19 +216,10 @@ def check_for_differences(reference_file, file_list, branch, actor):
|
|||||||
or (
|
or (
|
||||||
# only local windows command
|
# only local windows command
|
||||||
not file_normpath.startswith(
|
not file_normpath.startswith(
|
||||||
os.path.join(
|
os.path.join("", "src", "main", "resources", "messages_")
|
||||||
"", "stirling-pdf", "src", "main", "resources", "messages_"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
and not file_normpath.startswith(
|
and not file_normpath.startswith(
|
||||||
os.path.join(
|
os.path.join(os.getcwd(), "src", "main", "resources", "messages_")
|
||||||
os.getcwd(),
|
|
||||||
"stirling-pdf",
|
|
||||||
"src",
|
|
||||||
"main",
|
|
||||||
"resources",
|
|
||||||
"messages_",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
or not file_normpath.endswith(".properties")
|
or not file_normpath.endswith(".properties")
|
||||||
@ -388,12 +377,7 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
file_list = glob.glob(
|
file_list = glob.glob(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
os.getcwd(),
|
os.getcwd(), "src", "main", "resources", "messages_*.properties"
|
||||||
"stirling-pdf",
|
|
||||||
"src",
|
|
||||||
"main",
|
|
||||||
"resources",
|
|
||||||
"messages_*.properties",
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
update_missing_keys(args.reference_file, file_list)
|
update_missing_keys(args.reference_file, file_list)
|
||||||
|
52
.github/workflows/build.yml
vendored
52
.github/workflows/build.yml
vendored
@ -47,56 +47,18 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DISABLE_ADDITIONAL_FEATURES: false
|
DISABLE_ADDITIONAL_FEATURES: false
|
||||||
|
|
||||||
- name: Check Test Reports Exist
|
|
||||||
id: check-reports
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
missing_reports=()
|
|
||||||
|
|
||||||
# Check for required test report directories
|
|
||||||
if [ ! -d "stirling-pdf/build/reports/tests/" ]; then
|
|
||||||
missing_reports+=("stirling-pdf/build/reports/tests/")
|
|
||||||
fi
|
|
||||||
if [ ! -d "stirling-pdf/build/test-results/" ]; then
|
|
||||||
missing_reports+=("stirling-pdf/build/test-results/")
|
|
||||||
fi
|
|
||||||
if [ ! -d "common/build/reports/tests/" ]; then
|
|
||||||
missing_reports+=("common/build/reports/tests/")
|
|
||||||
fi
|
|
||||||
if [ ! -d "common/build/test-results/" ]; then
|
|
||||||
missing_reports+=("common/build/test-results/")
|
|
||||||
fi
|
|
||||||
if [ ! -d "proprietary/build/reports/tests/" ]; then
|
|
||||||
missing_reports+=("proprietary/build/reports/tests/")
|
|
||||||
fi
|
|
||||||
if [ ! -d "proprietary/build/test-results/" ]; then
|
|
||||||
missing_reports+=("proprietary/build/test-results/")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fail if any required reports are missing
|
|
||||||
if [ ${#missing_reports[@]} -gt 0 ]; then
|
|
||||||
echo "ERROR: The following required test report directories are missing:"
|
|
||||||
printf '%s\n' "${missing_reports[@]}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "All required test report directories are present"
|
|
||||||
|
|
||||||
- name: Upload Test Reports
|
- name: Upload Test Reports
|
||||||
if: steps.check-reports.outcome == 'success'
|
if: always()
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: test-reports-jdk-${{ matrix.jdk-version }}
|
name: test-reports-jdk-${{ matrix.jdk-version }}
|
||||||
path: |
|
path: |
|
||||||
stirling-pdf/build/reports/tests/
|
build/reports/tests/
|
||||||
stirling-pdf/build/test-results/
|
build/test-results/
|
||||||
stirling-pdf/build/reports/problems/
|
build/reports/problems/
|
||||||
common/build/reports/tests/
|
/common/build/reports/tests/
|
||||||
common/build/test-results/
|
/common/build/test-results/
|
||||||
common/build/reports/problems/
|
/common/build/reports/problems/
|
||||||
proprietary/build/reports/tests/
|
|
||||||
proprietary/build/test-results/
|
|
||||||
proprietary/build/reports/problems/
|
|
||||||
retention-days: 3
|
retention-days: 3
|
||||||
|
|
||||||
check-licence:
|
check-licence:
|
||||||
|
7
.github/workflows/check_properties.yml
vendored
7
.github/workflows/check_properties.yml
vendored
@ -115,11 +115,8 @@ jobs:
|
|||||||
|
|
||||||
// Filter for relevant files based on the PR changes
|
// Filter for relevant files based on the PR changes
|
||||||
const changedFiles = files
|
const changedFiles = files
|
||||||
.filter(file =>
|
.map(file => file.filename)
|
||||||
file.status !== "removed" &&
|
.filter(file => /^stirling-pdf\src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file));
|
||||||
/^stirling-pdf\/src\/main\/resources\/messages_[a-zA-Z_]{2}_[a-zA-Z_]{2,7}\.properties$/.test(file.filename)
|
|
||||||
)
|
|
||||||
.map(file => file.filename);
|
|
||||||
|
|
||||||
console.log("Changed files:", changedFiles);
|
console.log("Changed files:", changedFiles);
|
||||||
|
|
||||||
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -74,6 +74,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
|
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -86,9 +86,4 @@
|
|||||||
"spring.initializr.defaultLanguage": "Java",
|
"spring.initializr.defaultLanguage": "Java",
|
||||||
"spring.initializr.defaultGroupId": "stirling.software.SPDF",
|
"spring.initializr.defaultGroupId": "stirling.software.SPDF",
|
||||||
"spring.initializr.defaultArtifactId": "SPDF",
|
"spring.initializr.defaultArtifactId": "SPDF",
|
||||||
"java.project.sourcePaths": [
|
|
||||||
"stirling-pdf/src/main/java",
|
|
||||||
"common/src/main/java",
|
|
||||||
"proprietary/src/main/java"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be02
|
|||||||
COPY scripts /scripts
|
COPY scripts /scripts
|
||||||
COPY pipeline /pipeline
|
COPY pipeline /pipeline
|
||||||
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||||
COPY stirling-pdf/build/libs/*.jar app.jar
|
#COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/
|
||||||
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ COPY build.gradle .
|
|||||||
COPY settings.gradle .
|
COPY settings.gradle .
|
||||||
COPY gradlew .
|
COPY gradlew .
|
||||||
COPY gradle gradle/
|
COPY gradle gradle/
|
||||||
COPY stirling-pdf/build.gradle stirling-pdf/.
|
|
||||||
COPY common/build.gradle common/.
|
COPY common/build.gradle common/.
|
||||||
COPY proprietary/build.gradle proprietary/.
|
COPY proprietary/build.gradle proprietary/.
|
||||||
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
|
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
|
||||||
@ -28,7 +27,7 @@ FROM alpine:3.22.0@sha256:8a1f59ffb675680d47db6337b49d22281a139e9d709335b492be02
|
|||||||
COPY scripts /scripts
|
COPY scripts /scripts
|
||||||
COPY pipeline /pipeline
|
COPY pipeline /pipeline
|
||||||
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
COPY stirling-pdf/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||||
COPY --from=build /app/stirling-pdf/build/libs/*.jar app.jar
|
COPY --from=build /app/build/libs/*.jar app.jar
|
||||||
|
|
||||||
ARG VERSION_TAG
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh
|
|||||||
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||||
COPY scripts/installFonts.sh /scripts/installFonts.sh
|
COPY scripts/installFonts.sh /scripts/installFonts.sh
|
||||||
COPY pipeline /pipeline
|
COPY pipeline /pipeline
|
||||||
COPY stirling-pdf/build/libs/*.jar app.jar
|
COPY build/libs/*.jar app.jar
|
||||||
|
|
||||||
# Set up necessary directories and permissions
|
# Set up necessary directories and permissions
|
||||||
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
76
README.md
76
README.md
@ -116,47 +116,47 @@ Stirling-PDF currently supports 40 languages!
|
|||||||
|
|
||||||
| Language | Progress |
|
| Language | Progress |
|
||||||
| -------------------------------------------- | -------------------------------------- |
|
| -------------------------------------------- | -------------------------------------- |
|
||||||
| Arabic (العربية) (ar_AR) |  |
|
| Arabic (العربية) (ar_AR) |  |
|
||||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||||
| Basque (Euskara) (eu_ES) |  |
|
| Basque (Euskara) (eu_ES) |  |
|
||||||
| Bulgarian (Български) (bg_BG) |  |
|
| Bulgarian (Български) (bg_BG) |  |
|
||||||
| Catalan (Català) (ca_CA) |  |
|
| Catalan (Català) (ca_CA) |  |
|
||||||
| Croatian (Hrvatski) (hr_HR) |  |
|
| Croatian (Hrvatski) (hr_HR) |  |
|
||||||
| Czech (Česky) (cs_CZ) |  |
|
| Czech (Česky) (cs_CZ) |  |
|
||||||
| Danish (Dansk) (da_DK) |  |
|
| Danish (Dansk) (da_DK) |  |
|
||||||
| Dutch (Nederlands) (nl_NL) |  |
|
| Dutch (Nederlands) (nl_NL) |  |
|
||||||
| English (English) (en_GB) |  |
|
| English (English) (en_GB) |  |
|
||||||
| English (US) (en_US) |  |
|
| English (US) (en_US) |  |
|
||||||
| French (Français) (fr_FR) |  |
|
| French (Français) (fr_FR) |  |
|
||||||
| German (Deutsch) (de_DE) |  |
|
| German (Deutsch) (de_DE) |  |
|
||||||
| Greek (Ελληνικά) (el_GR) |  |
|
| Greek (Ελληνικά) (el_GR) |  |
|
||||||
| Hindi (हिंदी) (hi_IN) |  |
|
| Hindi (हिंदी) (hi_IN) |  |
|
||||||
| Hungarian (Magyar) (hu_HU) |  |
|
| Hungarian (Magyar) (hu_HU) |  |
|
||||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||||
| Irish (Gaeilge) (ga_IE) |  |
|
| Irish (Gaeilge) (ga_IE) |  |
|
||||||
| Italian (Italiano) (it_IT) |  |
|
| Italian (Italiano) (it_IT) |  |
|
||||||
| Japanese (日本語) (ja_JP) |  |
|
| Japanese (日本語) (ja_JP) |  |
|
||||||
| Korean (한국어) (ko_KR) |  |
|
| Korean (한국어) (ko_KR) |  |
|
||||||
| Norwegian (Norsk) (no_NB) |  |
|
| Norwegian (Norsk) (no_NB) |  |
|
||||||
| Persian (فارسی) (fa_IR) |  |
|
| Persian (فارسی) (fa_IR) |  |
|
||||||
| Polish (Polski) (pl_PL) |  |
|
| Polish (Polski) (pl_PL) |  |
|
||||||
| Portuguese (Português) (pt_PT) |  |
|
| Portuguese (Português) (pt_PT) |  |
|
||||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||||
| Romanian (Română) (ro_RO) |  |
|
| Romanian (Română) (ro_RO) |  |
|
||||||
| Russian (Русский) (ru_RU) |  |
|
| Russian (Русский) (ru_RU) |  |
|
||||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||||
| Slovakian (Slovensky) (sk_SK) |  |
|
| Slovakian (Slovensky) (sk_SK) |  |
|
||||||
| Slovenian (Slovenščina) (sl_SI) |  |
|
| Slovenian (Slovenščina) (sl_SI) |  |
|
||||||
| Spanish (Español) (es_ES) |  |
|
| Spanish (Español) (es_ES) |  |
|
||||||
| Swedish (Svenska) (sv_SE) |  |
|
| Swedish (Svenska) (sv_SE) |  |
|
||||||
| Thai (ไทย) (th_TH) |  |
|
| Thai (ไทย) (th_TH) |  |
|
||||||
| Tibetan (བོད་ཡིག་) (bo_CN) |  |
|
| Tibetan (བོད་ཡིག་) (bo_CN) |  |
|
||||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||||
| Turkish (Türkçe) (tr_TR) |  |
|
| Turkish (Türkçe) (tr_TR) |  |
|
||||||
| Ukrainian (Українська) (uk_UA) |  |
|
| Ukrainian (Українська) (uk_UA) |  |
|
||||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||||
| Malayalam (മലയാളം) (ml_IN) |  |
|
| Malayalam (മലയാളം) (ml_IN) |  |
|
||||||
|
|
||||||
## Stirling PDF Enterprise
|
## Stirling PDF Enterprise
|
||||||
|
|
||||||
|
44
build.gradle
44
build.gradle
@ -1,8 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
id "jacoco"
|
id "jacoco"
|
||||||
id "io.spring.dependency-management" version "1.1.7"
|
|
||||||
id "org.springframework.boot" version "3.5.0"
|
id "org.springframework.boot" version "3.5.0"
|
||||||
|
id "io.spring.dependency-management" version "1.1.7"
|
||||||
id "org.springdoc.openapi-gradle-plugin" version "1.9.0"
|
id "org.springdoc.openapi-gradle-plugin" version "1.9.0"
|
||||||
id "io.swagger.swaggerhub" version "1.3.2"
|
id "io.swagger.swaggerhub" version "1.3.2"
|
||||||
id "edu.sc.seis.launch4j" version "3.0.6"
|
id "edu.sc.seis.launch4j" version "3.0.6"
|
||||||
@ -23,11 +23,10 @@ ext {
|
|||||||
pdfboxVersion = "3.0.5"
|
pdfboxVersion = "3.0.5"
|
||||||
imageioVersion = "3.12.0"
|
imageioVersion = "3.12.0"
|
||||||
lombokVersion = "1.18.38"
|
lombokVersion = "1.18.38"
|
||||||
bouncycastleVersion = "1.81"
|
bouncycastleVersion = "1.80"
|
||||||
springSecuritySamlVersion = "6.5.0"
|
springSecuritySamlVersion = "6.5.0"
|
||||||
openSamlVersion = "4.3.2"
|
openSamlVersion = "4.3.2"
|
||||||
commonmarkVersion = "0.24.0"
|
commonmarkVersion = "0.24.0"
|
||||||
googleJavaFormatVersion = "1.27.0"
|
|
||||||
tempJrePath = null
|
tempJrePath = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +50,7 @@ sourceSets {
|
|||||||
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) {
|
&& System.getProperty('DISABLE_ADDITIONAL_FEATURES') == 'true')) {
|
||||||
exclude 'stirling/software/proprietary/security/**'
|
exclude 'stirling/software/proprietary/security/**'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
|
if (System.getenv('STIRLING_PDF_DESKTOP_UI') == 'false') {
|
||||||
exclude 'stirling/software/SPDF/UI/impl/**'
|
exclude 'stirling/software/SPDF/UI/impl/**'
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ sourceSets {
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = 'stirling.software'
|
group = 'stirling.software'
|
||||||
version = '1.0.0'
|
version = '0.46.2'
|
||||||
|
|
||||||
configurations.configureEach {
|
configurations.configureEach {
|
||||||
exclude group: 'commons-logging', module: 'commons-logging'
|
exclude group: 'commons-logging', module: 'commons-logging'
|
||||||
@ -130,7 +130,6 @@ subprojects {
|
|||||||
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
testRuntimeOnly 'org.mockito:mockito-inline:5.2.0'
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.2'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
@ -147,11 +146,6 @@ subprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
|
||||||
options.encoding = "UTF-8"
|
|
||||||
dependsOn "spotlessApply"
|
|
||||||
}
|
|
||||||
|
|
||||||
licenseReport {
|
licenseReport {
|
||||||
renderers = [new JsonReportRenderer()]
|
renderers = [new JsonReportRenderer()]
|
||||||
allowedLicensesFile = new File("$projectDir/allowed-licenses.json")
|
allowedLicensesFile = new File("$projectDir/allowed-licenses.json")
|
||||||
@ -474,9 +468,8 @@ spotless {
|
|||||||
target sourceSets.main.allJava
|
target sourceSets.main.allJava
|
||||||
target project(':common').sourceSets.main.allJava
|
target project(':common').sourceSets.main.allJava
|
||||||
target project(':proprietary').sourceSets.main.allJava
|
target project(':proprietary').sourceSets.main.allJava
|
||||||
target project(':stirling-pdf').sourceSets.main.allJava
|
|
||||||
|
|
||||||
googleJavaFormat(googleJavaFormatVersion).aosp().reorderImports(false)
|
googleJavaFormat("1.27.0").aosp().reorderImports(false)
|
||||||
|
|
||||||
importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling")
|
importOrder("java", "javax", "org", "com", "net", "io", "jakarta", "lombok", "me", "stirling")
|
||||||
toggleOffOn()
|
toggleOffOn()
|
||||||
@ -507,17 +500,12 @@ swaggerhubUpload {
|
|||||||
oas = "3.0.0" // The version of the OpenAPI Specification you"re using
|
oas = "3.0.0" // The version of the OpenAPI Specification you"re using
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.2'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named("test") {
|
tasks.named("test") {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('writeVersion') {
|
tasks.register('writeVersion') {
|
||||||
def propsFile = file("$projectDir/common/src/main/resources/version.properties")
|
def propsFile = file("$projectDir/stirling-pdf/src/main/resources/version.properties")
|
||||||
def propsDir = propsFile.parentFile
|
def propsDir = propsFile.parentFile
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
@ -541,7 +529,6 @@ tasks.register('writeVersion') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
processResources.dependsOn(writeVersion)
|
processResources.dependsOn(writeVersion)
|
||||||
project(':stirling-pdf').tasks.bootJar.dependsOn(writeVersion)
|
|
||||||
|
|
||||||
tasks.register('printVersion') {
|
tasks.register('printVersion') {
|
||||||
doLast {
|
doLast {
|
||||||
@ -558,22 +545,3 @@ tasks.register('printMacVersion') {
|
|||||||
tasks.named('generateOpenApiDocs') {
|
tasks.named('generateOpenApiDocs') {
|
||||||
doNotTrackState("Tracking state is not supported for this task")
|
doNotTrackState("Tracking state is not supported for this task")
|
||||||
}
|
}
|
||||||
tasks.named('bootRun') {
|
|
||||||
group = 'application'
|
|
||||||
description = 'Delegates to :stirling-pdf:bootRun'
|
|
||||||
dependsOn ':stirling-pdf:bootRun'
|
|
||||||
|
|
||||||
doFirst {
|
|
||||||
println "Delegating to :stirling-pdf:bootRun"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('build') {
|
|
||||||
group = 'build'
|
|
||||||
description = 'Delegates to :stirling-pdf:bootJar'
|
|
||||||
dependsOn ':stirling-pdf:bootJar'
|
|
||||||
|
|
||||||
doFirst {
|
|
||||||
println "Delegating to :stirling-pdf:bootJar"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,3 @@
|
|||||||
// Configure bootRun to disable it or point to a main class
|
|
||||||
bootRun {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
spotless {
|
|
||||||
java {
|
|
||||||
target sourceSets.main.allJava
|
|
||||||
googleJavaFormat(googleJavaFormatVersion).aosp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'org.springframework.boot:spring-boot-starter-web'
|
api 'org.springframework.boot:spring-boot-starter-web'
|
||||||
api 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
api 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
|
@ -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 {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package stirling.software.common.configuration;
|
package stirling.software.common.configuration;
|
||||||
|
|
||||||
|
import io.github.pixee.security.SystemCommand;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
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;
|
||||||
@ -8,22 +10,25 @@ 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.ConditionalOnClass;
|
||||||
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;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.context.annotation.Profile;
|
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
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
|
||||||
@ -248,33 +253,9 @@ public class AppConfig {
|
|||||||
return applicationProperties.getSystem().getDatasource();
|
return applicationProperties.getSystem().getDatasource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "runningProOrHigher")
|
|
||||||
@Profile("default")
|
|
||||||
public boolean runningProOrHigher() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "runningEE")
|
|
||||||
@Profile("default")
|
|
||||||
public boolean runningEnterprise() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "GoogleDriveEnabled")
|
|
||||||
@Profile("default")
|
|
||||||
public boolean googleDriveEnabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "license")
|
|
||||||
@Profile("default")
|
|
||||||
public String licenseType() {
|
|
||||||
return "NORMAL";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name = "disablePixel")
|
@Bean(name = "disablePixel")
|
||||||
public boolean disablePixel() {
|
public boolean disablePixel() {
|
||||||
return Boolean.parseBoolean(env.getProperty("DISABLE_PIXEL", "false"));
|
return Boolean.getBoolean(env.getProperty("DISABLE_PIXEL"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "machineType")
|
@Bean(name = "machineType")
|
||||||
|
@ -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;
|
||||||
|
@ -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,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,11 @@ 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
|
||||||
@ -47,8 +49,7 @@ public class EmlToPdf {
|
|||||||
private static final class StyleConstants {
|
private static final class StyleConstants {
|
||||||
// Font and layout constants
|
// Font and layout constants
|
||||||
static final int DEFAULT_FONT_SIZE = 12;
|
static final int DEFAULT_FONT_SIZE = 12;
|
||||||
static final String DEFAULT_FONT_FAMILY =
|
static final String DEFAULT_FONT_FAMILY = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif";
|
||||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif";
|
|
||||||
static final float DEFAULT_LINE_HEIGHT = 1.4f;
|
static final float DEFAULT_LINE_HEIGHT = 1.4f;
|
||||||
static final String DEFAULT_ZOOM = "1.0";
|
static final String DEFAULT_ZOOM = "1.0";
|
||||||
|
|
||||||
@ -75,8 +76,7 @@ public class EmlToPdf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final class MimeConstants {
|
private static final class MimeConstants {
|
||||||
static final Pattern MIME_ENCODED_PATTERN =
|
static final Pattern MIME_ENCODED_PATTERN = Pattern.compile("=\\?([^?]+)\\?([BbQq])\\?([^?]*)\\?=");
|
||||||
Pattern.compile("=\\?([^?]+)\\?([BbQq])\\?([^?]*)\\?=");
|
|
||||||
static final String PAPERCLIP_EMOJI = "\uD83D\uDCCE"; // 📎
|
static final String PAPERCLIP_EMOJI = "\uD83D\uDCCE"; // 📎
|
||||||
static final String ATTACHMENT_ICON_PLACEHOLDER = "icon";
|
static final String ATTACHMENT_ICON_PLACEHOLDER = "icon";
|
||||||
|
|
||||||
@ -113,8 +113,7 @@ public class EmlToPdf {
|
|||||||
return jakartaMailAvailable;
|
return jakartaMailAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String convertEmlToHtml(byte[] emlBytes, EmlToPdfRequest request)
|
public static String convertEmlToHtml(byte[] emlBytes, EmlToPdfRequest request) throws IOException {
|
||||||
throws IOException {
|
|
||||||
validateEmlInput(emlBytes);
|
validateEmlInput(emlBytes);
|
||||||
|
|
||||||
if (isJakartaMailAvailable()) {
|
if (isJakartaMailAvailable()) {
|
||||||
@ -148,14 +147,11 @@ public class EmlToPdf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert HTML to PDF
|
// Convert HTML to PDF
|
||||||
byte[] pdfBytes =
|
byte[] pdfBytes = convertHtmlToPdf(weasyprintPath, request, htmlContent, disableSanitize);
|
||||||
convertHtmlToPdf(weasyprintPath, request, htmlContent, disableSanitize);
|
|
||||||
|
|
||||||
// Attach files if available and requested
|
// Attach files if available and requested
|
||||||
if (shouldAttachFiles(emailContent, request)) {
|
if (shouldAttachFiles(emailContent, request)) {
|
||||||
pdfBytes =
|
pdfBytes = attachFilesToPdf(pdfBytes, emailContent.getAttachments(), pdfDocumentFactory);
|
||||||
attachFilesToPdf(
|
|
||||||
pdfBytes, emailContent.getAttachments(), pdfDocumentFactory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pdfBytes;
|
return pdfBytes;
|
||||||
@ -186,15 +182,11 @@ public class EmlToPdf {
|
|||||||
&& !emailContent.getAttachments().isEmpty();
|
&& !emailContent.getAttachments().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] convertHtmlToPdf(
|
private static byte[] convertHtmlToPdf(String weasyprintPath, EmlToPdfRequest request,
|
||||||
String weasyprintPath,
|
String htmlContent, boolean disableSanitize)
|
||||||
EmlToPdfRequest request,
|
|
||||||
String htmlContent,
|
|
||||||
boolean disableSanitize)
|
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
|
|
||||||
stirling.software.common.model.api.converters.HTMLToPdfRequest htmlRequest =
|
stirling.software.common.model.api.converters.HTMLToPdfRequest htmlRequest = createHtmlRequest(request);
|
||||||
createHtmlRequest(request);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return FileToPdf.convertHtmlToPdf(
|
return FileToPdf.convertHtmlToPdf(
|
||||||
@ -226,7 +218,8 @@ public class EmlToPdf {
|
|||||||
return "attachment_" + filename.hashCode() + "_" + System.nanoTime();
|
return "attachment_" + filename.hashCode() + "_" + System.nanoTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String convertEmlToHtmlBasic(byte[] emlBytes, EmlToPdfRequest request) {
|
private static String convertEmlToHtmlBasic(
|
||||||
|
byte[] emlBytes, EmlToPdfRequest request) {
|
||||||
if (emlBytes == null || emlBytes.length == 0) {
|
if (emlBytes == null || emlBytes.length == 0) {
|
||||||
throw new IllegalArgumentException("EML file is empty or null");
|
throw new IllegalArgumentException("EML file is empty or null");
|
||||||
}
|
}
|
||||||
@ -342,6 +335,7 @@ public class EmlToPdf {
|
|||||||
Object message =
|
Object message =
|
||||||
mimeMessageConstructor.newInstance(session, new ByteArrayInputStream(emlBytes));
|
mimeMessageConstructor.newInstance(session, new ByteArrayInputStream(emlBytes));
|
||||||
|
|
||||||
|
|
||||||
return extractEmailContentAdvanced(message, request);
|
return extractEmailContentAdvanced(message, request);
|
||||||
|
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
@ -352,7 +346,8 @@ public class EmlToPdf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String convertEmlToHtmlAdvanced(byte[] emlBytes, EmlToPdfRequest request) {
|
private static String convertEmlToHtmlAdvanced(
|
||||||
|
byte[] emlBytes, EmlToPdfRequest request) {
|
||||||
EmailContent content = extractEmailContentAdvanced(emlBytes, request);
|
EmailContent content = extractEmailContentAdvanced(emlBytes, request);
|
||||||
return generateEnhancedEmailHtml(content, request);
|
return generateEnhancedEmailHtml(content, request);
|
||||||
}
|
}
|
||||||
@ -484,12 +479,8 @@ public class EmlToPdf {
|
|||||||
// Create attachment info with paperclip emoji before filename
|
// Create attachment info with paperclip emoji before filename
|
||||||
attachmentInfo
|
attachmentInfo
|
||||||
.append("<div class=\"attachment-item\">")
|
.append("<div class=\"attachment-item\">")
|
||||||
.append("<span class=\"attachment-icon\">")
|
.append("<span class=\"attachment-icon\">").append(MimeConstants.ATTACHMENT_ICON_PLACEHOLDER).append("</span> ")
|
||||||
.append(MimeConstants.ATTACHMENT_ICON_PLACEHOLDER)
|
.append("<span class=\"attachment-name\">").append(escapeHtml(filename)).append("</span>");
|
||||||
.append("</span> ")
|
|
||||||
.append("<span class=\"attachment-name\">")
|
|
||||||
.append(escapeHtml(filename))
|
|
||||||
.append("</span>");
|
|
||||||
|
|
||||||
// Add content type and encoding info
|
// Add content type and encoding info
|
||||||
if (!contentType.isEmpty() || !encoding.isEmpty()) {
|
if (!contentType.isEmpty() || !encoding.isEmpty()) {
|
||||||
@ -512,17 +503,14 @@ public class EmlToPdf {
|
|||||||
String content = new String(emlBytes, 0, checkLength, StandardCharsets.UTF_8);
|
String content = new String(emlBytes, 0, checkLength, StandardCharsets.UTF_8);
|
||||||
String lowerContent = content.toLowerCase();
|
String lowerContent = content.toLowerCase();
|
||||||
|
|
||||||
boolean hasFrom =
|
boolean hasFrom = lowerContent.contains("from:") || lowerContent.contains("return-path:");
|
||||||
lowerContent.contains("from:") || lowerContent.contains("return-path:");
|
|
||||||
boolean hasSubject = lowerContent.contains("subject:");
|
boolean hasSubject = lowerContent.contains("subject:");
|
||||||
boolean hasMessageId = lowerContent.contains("message-id:");
|
boolean hasMessageId = lowerContent.contains("message-id:");
|
||||||
boolean hasDate = lowerContent.contains("date:");
|
boolean hasDate = lowerContent.contains("date:");
|
||||||
boolean hasTo =
|
boolean hasTo = lowerContent.contains("to:")
|
||||||
lowerContent.contains("to:")
|
|
||||||
|| lowerContent.contains("cc:")
|
|| lowerContent.contains("cc:")
|
||||||
|| lowerContent.contains("bcc:");
|
|| lowerContent.contains("bcc:");
|
||||||
boolean hasMimeStructure =
|
boolean hasMimeStructure = lowerContent.contains("multipart/")
|
||||||
lowerContent.contains("multipart/")
|
|
||||||
|| lowerContent.contains("text/plain")
|
|| lowerContent.contains("text/plain")
|
||||||
|| lowerContent.contains("text/html")
|
|| lowerContent.contains("text/html")
|
||||||
|| lowerContent.contains("boundary=");
|
|| lowerContent.contains("boundary=");
|
||||||
@ -696,19 +684,17 @@ public class EmlToPdf {
|
|||||||
html.append(" font-size: ").append(fontSize - 1).append("px;\n");
|
html.append(" font-size: ").append(fontSize - 1).append("px;\n");
|
||||||
html.append("}\n\n");
|
html.append("}\n\n");
|
||||||
|
|
||||||
|
|
||||||
html.append(".email-body {\n");
|
html.append(".email-body {\n");
|
||||||
html.append(" word-wrap: break-word;\n");
|
html.append(" word-wrap: break-word;\n");
|
||||||
html.append("}\n\n");
|
html.append("}\n\n");
|
||||||
|
|
||||||
|
|
||||||
html.append(".attachment-section {\n");
|
html.append(".attachment-section {\n");
|
||||||
html.append(" margin-top: 15px;\n");
|
html.append(" margin-top: 15px;\n");
|
||||||
html.append(" padding: 10px;\n");
|
html.append(" padding: 10px;\n");
|
||||||
html.append(" background-color: ")
|
html.append(" background-color: ").append(StyleConstants.ATTACHMENT_BACKGROUND_COLOR).append(";\n");
|
||||||
.append(StyleConstants.ATTACHMENT_BACKGROUND_COLOR)
|
html.append(" border: 1px solid ").append(StyleConstants.ATTACHMENT_BORDER_COLOR).append(";\n");
|
||||||
.append(";\n");
|
|
||||||
html.append(" border: 1px solid ")
|
|
||||||
.append(StyleConstants.ATTACHMENT_BORDER_COLOR)
|
|
||||||
.append(";\n");
|
|
||||||
html.append(" border-radius: 3px;\n");
|
html.append(" border-radius: 3px;\n");
|
||||||
html.append("}\n\n");
|
html.append("}\n\n");
|
||||||
html.append(".attachment-section h3 {\n");
|
html.append(".attachment-section h3 {\n");
|
||||||
@ -760,6 +746,7 @@ public class EmlToPdf {
|
|||||||
html.append(" margin-left: 8px;\n");
|
html.append(" margin-left: 8px;\n");
|
||||||
html.append("}\n\n");
|
html.append("}\n\n");
|
||||||
|
|
||||||
|
|
||||||
// Basic image styling: ensure images are responsive but not overly constrained.
|
// Basic image styling: ensure images are responsive but not overly constrained.
|
||||||
html.append("img {\n");
|
html.append("img {\n");
|
||||||
html.append(" max-width: 100%;\n"); // Make images responsive to container width
|
html.append(" max-width: 100%;\n"); // Make images responsive to container width
|
||||||
@ -814,9 +801,7 @@ public class EmlToPdf {
|
|||||||
java.lang.reflect.Method getAllRecipients = messageClass.getMethod("getAllRecipients");
|
java.lang.reflect.Method getAllRecipients = messageClass.getMethod("getAllRecipients");
|
||||||
Object[] recipients = (Object[]) getAllRecipients.invoke(message);
|
Object[] recipients = (Object[]) getAllRecipients.invoke(message);
|
||||||
content.setTo(
|
content.setTo(
|
||||||
recipients != null && recipients.length > 0
|
recipients != null && recipients.length > 0 ? safeMimeDecode(recipients[0].toString()) : "");
|
||||||
? safeMimeDecode(recipients[0].toString())
|
|
||||||
: "");
|
|
||||||
|
|
||||||
java.lang.reflect.Method getSentDate = messageClass.getMethod("getSentDate");
|
java.lang.reflect.Method getSentDate = messageClass.getMethod("getSentDate");
|
||||||
content.setDate((Date) getSentDate.invoke(message));
|
content.setDate((Date) getSentDate.invoke(message));
|
||||||
@ -923,14 +908,13 @@ public class EmlToPdf {
|
|||||||
try {
|
try {
|
||||||
attachmentData = inputStream.readAllBytes();
|
attachmentData = inputStream.readAllBytes();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.warn(
|
log.warn("Failed to read InputStream attachment: {}", e.getMessage());
|
||||||
"Failed to read InputStream attachment: {}",
|
|
||||||
e.getMessage());
|
|
||||||
}
|
}
|
||||||
} else if (attachmentContent instanceof byte[] byteArray) {
|
} else if (attachmentContent instanceof byte[] byteArray) {
|
||||||
attachmentData = byteArray;
|
attachmentData = byteArray;
|
||||||
} else if (attachmentContent instanceof String stringContent) {
|
} else if (attachmentContent instanceof String stringContent) {
|
||||||
attachmentData = stringContent.getBytes(StandardCharsets.UTF_8);
|
attachmentData =
|
||||||
|
stringContent.getBytes(StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachmentData != null) {
|
if (attachmentData != null) {
|
||||||
@ -990,9 +974,7 @@ public class EmlToPdf {
|
|||||||
html.append("<div><strong>From:</strong> ")
|
html.append("<div><strong>From:</strong> ")
|
||||||
.append(escapeHtml(content.getFrom()))
|
.append(escapeHtml(content.getFrom()))
|
||||||
.append("</div>\n");
|
.append("</div>\n");
|
||||||
html.append("<div><strong>To:</strong> ")
|
html.append("<div><strong>To:</strong> ").append(escapeHtml(content.getTo())).append("</div>\n");
|
||||||
.append(escapeHtml(content.getTo()))
|
|
||||||
.append("</div>\n");
|
|
||||||
|
|
||||||
if (content.getDate() != null) {
|
if (content.getDate() != null) {
|
||||||
html.append("<div><strong>Date:</strong> ")
|
html.append("<div><strong>Date:</strong> ")
|
||||||
@ -1032,20 +1014,15 @@ public class EmlToPdf {
|
|||||||
? attachment.getEmbeddedFilename()
|
? attachment.getEmbeddedFilename()
|
||||||
: attachment.getFilename());
|
: attachment.getFilename());
|
||||||
|
|
||||||
html.append("<div class=\"attachment-item\" id=\"")
|
html.append("<div class=\"attachment-item\" id=\"").append(uniqueId).append("\">")
|
||||||
.append(uniqueId)
|
.append("<span class=\"attachment-icon\">").append(MimeConstants.PAPERCLIP_EMOJI).append("</span> ")
|
||||||
.append("\">")
|
|
||||||
.append("<span class=\"attachment-icon\">")
|
|
||||||
.append(MimeConstants.PAPERCLIP_EMOJI)
|
|
||||||
.append("</span> ")
|
|
||||||
.append("<span class=\"attachment-name\">")
|
.append("<span class=\"attachment-name\">")
|
||||||
.append(escapeHtml(safeMimeDecode(attachment.getFilename())))
|
.append(escapeHtml(safeMimeDecode(attachment.getFilename())))
|
||||||
.append("</span>");
|
.append("</span>");
|
||||||
|
|
||||||
String sizeStr = formatFileSize(attachment.getSizeBytes());
|
String sizeStr = formatFileSize(attachment.getSizeBytes());
|
||||||
html.append(" <span class=\"attachment-details\">(").append(sizeStr);
|
html.append(" <span class=\"attachment-details\">(").append(sizeStr);
|
||||||
if (attachment.getContentType() != null
|
if (attachment.getContentType() != null && !attachment.getContentType().isEmpty()) {
|
||||||
&& !attachment.getContentType().isEmpty()) {
|
|
||||||
html.append(", ").append(escapeHtml(attachment.getContentType()));
|
html.append(", ").append(escapeHtml(attachment.getContentType()));
|
||||||
}
|
}
|
||||||
html.append(")</span></div>\n");
|
html.append(")</span></div>\n");
|
||||||
@ -1054,7 +1031,8 @@ public class EmlToPdf {
|
|||||||
|
|
||||||
if (request.isIncludeAttachments()) {
|
if (request.isIncludeAttachments()) {
|
||||||
html.append("<div class=\"attachment-info-note\">\n");
|
html.append("<div class=\"attachment-info-note\">\n");
|
||||||
html.append("<p><em>Attachments are embedded in the file.</em></p>\n");
|
html.append(
|
||||||
|
"<p><em>Attachments are embedded in the file.</em></p>\n");
|
||||||
html.append("</div>\n");
|
html.append("</div>\n");
|
||||||
} else {
|
} else {
|
||||||
html.append("<div class=\"attachment-info-note\">\n");
|
html.append("<div class=\"attachment-info-note\">\n");
|
||||||
@ -1072,10 +1050,7 @@ public class EmlToPdf {
|
|||||||
return html.toString();
|
return html.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] attachFilesToPdf(
|
private static byte[] attachFilesToPdf(byte[] pdfBytes, List<EmailAttachment> attachments, stirling.software.common.service.CustomPDFDocumentFactory pdfDocumentFactory)
|
||||||
byte[] pdfBytes,
|
|
||||||
List<EmailAttachment> attachments,
|
|
||||||
stirling.software.common.service.CustomPDFDocumentFactory pdfDocumentFactory)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
try (PDDocument document = pdfDocumentFactory.load(pdfBytes);
|
try (PDDocument document = pdfDocumentFactory.load(pdfBytes);
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
@ -1129,8 +1104,7 @@ public class EmlToPdf {
|
|||||||
|
|
||||||
// Create embedded file
|
// Create embedded file
|
||||||
PDEmbeddedFile embeddedFile =
|
PDEmbeddedFile embeddedFile =
|
||||||
new PDEmbeddedFile(
|
new PDEmbeddedFile(document, new ByteArrayInputStream(attachment.getData()));
|
||||||
document, new ByteArrayInputStream(attachment.getData()));
|
|
||||||
embeddedFile.setSize(attachment.getData().length);
|
embeddedFile.setSize(attachment.getData().length);
|
||||||
embeddedFile.setCreationDate(new GregorianCalendar());
|
embeddedFile.setCreationDate(new GregorianCalendar());
|
||||||
if (attachment.getContentType() != null) {
|
if (attachment.getContentType() != null) {
|
||||||
@ -1176,13 +1150,11 @@ public class EmlToPdf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getUniqueFilename(
|
private static String getUniqueFilename(String filename, List<String> embeddedFiles, Map<String, PDComplexFileSpecification> efMap) {
|
||||||
String filename,
|
|
||||||
List<String> embeddedFiles,
|
|
||||||
Map<String, PDComplexFileSpecification> efMap) {
|
|
||||||
String uniqueFilename = filename;
|
String uniqueFilename = filename;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
while (embeddedFiles.contains(uniqueFilename) || efMap.containsKey(uniqueFilename)) {
|
while (embeddedFiles.contains(uniqueFilename)
|
||||||
|
|| efMap.containsKey(uniqueFilename)) {
|
||||||
String extension = "";
|
String extension = "";
|
||||||
String baseName = filename;
|
String baseName = filename;
|
||||||
int lastDot = filename.lastIndexOf('.');
|
int lastDot = filename.lastIndexOf('.');
|
||||||
@ -1258,8 +1230,7 @@ public class EmlToPdf {
|
|||||||
fileAnnotation.setNoView(false); // Must be false to remain clickable
|
fileAnnotation.setNoView(false); // Must be false to remain clickable
|
||||||
fileAnnotation.setPrinted(false);
|
fileAnnotation.setPrinted(false);
|
||||||
|
|
||||||
PDEmbeddedFilesNameTreeNode efTree =
|
PDEmbeddedFilesNameTreeNode efTree = document.getDocumentCatalog().getNames().getEmbeddedFiles();
|
||||||
document.getDocumentCatalog().getNames().getEmbeddedFiles();
|
|
||||||
if (efTree != null) {
|
if (efTree != null) {
|
||||||
Map<String, PDComplexFileSpecification> efMap = efTree.getNames();
|
Map<String, PDComplexFileSpecification> efMap = efTree.getNames();
|
||||||
if (efMap != null) {
|
if (efMap != null) {
|
||||||
@ -1275,27 +1246,24 @@ public class EmlToPdf {
|
|||||||
|
|
||||||
page.getAnnotations().add(fileAnnotation);
|
page.getAnnotations().add(fileAnnotation);
|
||||||
|
|
||||||
log.info(
|
log.info("Added attachment annotation for '{}' on page {}",
|
||||||
"Added attachment annotation for '{}' on page {}",
|
attachment.getFilename(), document.getPages().indexOf(page) + 1);
|
||||||
attachment.getFilename(),
|
|
||||||
document.getPages().indexOf(page) + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NotNull PDRectangle getPdRectangle(PDPage page, float x, float y) {
|
private static @NotNull PDRectangle getPdRectangle(PDPage page, float x, float y) {
|
||||||
PDRectangle mediaBox = page.getMediaBox();
|
PDRectangle mediaBox = page.getMediaBox();
|
||||||
float pdfY = mediaBox.getHeight() - y;
|
float pdfY = mediaBox.getHeight() - y;
|
||||||
|
|
||||||
float iconWidth =
|
float iconWidth = StyleConstants.ATTACHMENT_ICON_WIDTH; // Keep original size for clickability
|
||||||
StyleConstants.ATTACHMENT_ICON_WIDTH; // Keep original size for clickability
|
float iconHeight = StyleConstants.ATTACHMENT_ICON_HEIGHT; // Keep original size for clickability
|
||||||
float iconHeight =
|
|
||||||
StyleConstants.ATTACHMENT_ICON_HEIGHT; // Keep original size for clickability
|
|
||||||
|
|
||||||
// Keep the full-size rectangle so it remains clickable
|
// Keep the full-size rectangle so it remains clickable
|
||||||
return new PDRectangle(
|
return new PDRectangle(
|
||||||
x + StyleConstants.ANNOTATION_X_OFFSET,
|
x + StyleConstants.ANNOTATION_X_OFFSET,
|
||||||
pdfY - iconHeight + StyleConstants.ANNOTATION_Y_OFFSET,
|
pdfY - iconHeight + StyleConstants.ANNOTATION_Y_OFFSET,
|
||||||
iconWidth,
|
iconWidth,
|
||||||
iconHeight);
|
iconHeight
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String formatEmailDate(Date date) {
|
private static String formatEmailDate(Date date) {
|
||||||
@ -1325,27 +1293,23 @@ public class EmlToPdf {
|
|||||||
COSDictionary catalogDict = catalog.getCOSObject();
|
COSDictionary catalogDict = catalog.getCOSObject();
|
||||||
|
|
||||||
// Set PageMode to UseAttachments - this is the standard PDF specification approach
|
// Set PageMode to UseAttachments - this is the standard PDF specification approach
|
||||||
// PageMode values: UseNone, UseOutlines, UseThumbs, FullScreen, UseOC,
|
// PageMode values: UseNone, UseOutlines, UseThumbs, FullScreen, UseOC, UseAttachments
|
||||||
// UseAttachments
|
|
||||||
catalogDict.setName(COSName.PAGE_MODE, "UseAttachments");
|
catalogDict.setName(COSName.PAGE_MODE, "UseAttachments");
|
||||||
|
|
||||||
// Also set viewer preferences for better attachment viewing experience
|
// Also set viewer preferences for better attachment viewing experience
|
||||||
COSDictionary viewerPrefs =
|
COSDictionary viewerPrefs = (COSDictionary) catalogDict.getDictionaryObject(COSName.VIEWER_PREFERENCES);
|
||||||
(COSDictionary) catalogDict.getDictionaryObject(COSName.VIEWER_PREFERENCES);
|
|
||||||
if (viewerPrefs == null) {
|
if (viewerPrefs == null) {
|
||||||
viewerPrefs = new COSDictionary();
|
viewerPrefs = new COSDictionary();
|
||||||
catalogDict.setItem(COSName.VIEWER_PREFERENCES, viewerPrefs);
|
catalogDict.setItem(COSName.VIEWER_PREFERENCES, viewerPrefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set NonFullScreenPageMode to UseAttachments as fallback for viewers that support
|
// Set NonFullScreenPageMode to UseAttachments as fallback for viewers that support it
|
||||||
// it
|
|
||||||
viewerPrefs.setName(COSName.getPDFName("NonFullScreenPageMode"), "UseAttachments");
|
viewerPrefs.setName(COSName.getPDFName("NonFullScreenPageMode"), "UseAttachments");
|
||||||
|
|
||||||
// Additional viewer preferences that may help with attachment display
|
// Additional viewer preferences that may help with attachment display
|
||||||
viewerPrefs.setBoolean(COSName.getPDFName("DisplayDocTitle"), true);
|
viewerPrefs.setBoolean(COSName.getPDFName("DisplayDocTitle"), true);
|
||||||
|
|
||||||
log.info(
|
log.info("Set PDF PageMode to UseAttachments to automatically show attachments pane");
|
||||||
"Set PDF PageMode to UseAttachments to automatically show attachments pane");
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Log warning but don't fail the entire operation for viewer preferences
|
// Log warning but don't fail the entire operation for viewer preferences
|
||||||
@ -1500,7 +1464,8 @@ public class EmlToPdf {
|
|||||||
private float y;
|
private float y;
|
||||||
private String character;
|
private String character;
|
||||||
|
|
||||||
public EmojiPosition() {}
|
public EmojiPosition() {
|
||||||
|
}
|
||||||
|
|
||||||
public EmojiPosition(int pageIndex, float x, float y, String character) {
|
public EmojiPosition(int pageIndex, float x, float y, String character) {
|
||||||
this.pageIndex = pageIndex;
|
this.pageIndex = pageIndex;
|
||||||
@ -1511,7 +1476,8 @@ public class EmlToPdf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class EmojiPositionFinder extends org.apache.pdfbox.text.PDFTextStripper {
|
public static class EmojiPositionFinder extends org.apache.pdfbox.text.PDFTextStripper {
|
||||||
@Getter private final List<EmojiPosition> positions = new ArrayList<>();
|
@Getter
|
||||||
|
private final List<EmojiPosition> positions = new ArrayList<>();
|
||||||
private int currentPageIndex;
|
private int currentPageIndex;
|
||||||
private boolean sortByPosition;
|
private boolean sortByPosition;
|
||||||
private boolean isInAttachmentSection;
|
private boolean isInAttachmentSection;
|
||||||
@ -1537,9 +1503,7 @@ public class EmlToPdf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeString(
|
protected void writeString(String string, List<org.apache.pdfbox.text.TextPosition> textPositions) throws IOException {
|
||||||
String string, List<org.apache.pdfbox.text.TextPosition> textPositions)
|
|
||||||
throws IOException {
|
|
||||||
// Check if we are entering or exiting the attachment section
|
// Check if we are entering or exiting the attachment section
|
||||||
String lowerString = string.toLowerCase();
|
String lowerString = string.toLowerCase();
|
||||||
|
|
||||||
@ -1549,14 +1513,10 @@ public class EmlToPdf {
|
|||||||
attachmentSectionFound = true;
|
attachmentSectionFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for attachment section end markers (common patterns that indicate end of
|
// Look for attachment section end markers (common patterns that indicate end of attachments)
|
||||||
// attachments)
|
if (isInAttachmentSection && (lowerString.contains("</body>") ||
|
||||||
if (isInAttachmentSection
|
lowerString.contains("</html>") ||
|
||||||
&& (lowerString.contains("</body>")
|
(attachmentSectionFound && lowerString.trim().isEmpty() && string.length() > 50))) {
|
||||||
|| lowerString.contains("</html>")
|
|
||||||
|| (attachmentSectionFound
|
|
||||||
&& lowerString.trim().isEmpty()
|
|
||||||
&& string.length() > 50))) {
|
|
||||||
isInAttachmentSection = false;
|
isInAttachmentSection = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1567,17 +1527,17 @@ public class EmlToPdf {
|
|||||||
|
|
||||||
for (int i = 0; i < string.length(); i++) {
|
for (int i = 0; i < string.length(); i++) {
|
||||||
// Check if we have a complete paperclip emoji at this position
|
// Check if we have a complete paperclip emoji at this position
|
||||||
if (i < string.length() - 1
|
if (i < string.length() - 1 &&
|
||||||
&& string.substring(i, i + 2).equals(paperclipEmoji)
|
string.substring(i, i + 2).equals(paperclipEmoji) &&
|
||||||
&& i < textPositions.size()) {
|
i < textPositions.size()) {
|
||||||
|
|
||||||
org.apache.pdfbox.text.TextPosition textPosition = textPositions.get(i);
|
org.apache.pdfbox.text.TextPosition textPosition = textPositions.get(i);
|
||||||
EmojiPosition position =
|
EmojiPosition position = new EmojiPosition(
|
||||||
new EmojiPosition(
|
|
||||||
currentPageIndex,
|
currentPageIndex,
|
||||||
textPosition.getXDirAdj(),
|
textPosition.getXDirAdj(),
|
||||||
textPosition.getYDirAdj(),
|
textPosition.getYDirAdj(),
|
||||||
paperclipEmoji);
|
paperclipEmoji
|
||||||
|
);
|
||||||
positions.add(position);
|
positions.add(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1594,6 +1554,7 @@ public class EmlToPdf {
|
|||||||
return sortByPosition;
|
return sortByPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
positions.clear();
|
positions.clear();
|
||||||
currentPageIndex = 0;
|
currentPageIndex = 0;
|
||||||
|
@ -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();
|
||||||
|
@ -1,15 +1,7 @@
|
|||||||
repositories {
|
repositories {
|
||||||
maven { url = "https://build.shibboleth.net/maven/releases" }
|
maven { url = "https://build.shibboleth.net/maven/releases" }
|
||||||
}
|
}
|
||||||
bootRun {
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
spotless {
|
|
||||||
java {
|
|
||||||
target sourceSets.main.allJava
|
|
||||||
googleJavaFormat(googleJavaFormatVersion).aosp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':common')
|
implementation project(':common')
|
||||||
|
|
||||||
@ -23,17 +15,17 @@ dependencies {
|
|||||||
api 'org.springframework.boot:spring-boot-starter-data-jpa'
|
api 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
api 'org.springframework.boot:spring-boot-starter-oauth2-client'
|
api 'org.springframework.boot:spring-boot-starter-oauth2-client'
|
||||||
api 'org.springframework.boot:spring-boot-starter-mail'
|
api 'org.springframework.boot:spring-boot-starter-mail'
|
||||||
api 'io.swagger.core.v3:swagger-core-jakarta:2.2.32'
|
api 'io.swagger.core.v3:swagger-core-jakarta:2.2.30'
|
||||||
implementation 'com.bucket4j:bucket4j_jdk17-core:8.14.0'
|
implementation 'com.bucket4j:bucket4j_jdk17-core:8.14.0'
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
|
||||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.81'
|
implementation 'org.bouncycastle:bcprov-jdk18on:1.80'
|
||||||
|
|
||||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
|
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
|
||||||
api 'io.micrometer:micrometer-registry-prometheus'
|
api 'io.micrometer:micrometer-registry-prometheus'
|
||||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
||||||
runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database
|
runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database
|
||||||
runtimeOnly 'org.postgresql:postgresql:42.7.7'
|
runtimeOnly 'org.postgresql:postgresql:42.7.5'
|
||||||
constraints {
|
constraints {
|
||||||
implementation "org.opensaml:opensaml-core:$openSamlVersion"
|
implementation "org.opensaml:opensaml-core:$openSamlVersion"
|
||||||
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
|
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package stirling.software.proprietary.model;
|
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import lombok.*;
|
|
||||||
import stirling.software.proprietary.security.model.User;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
@Table(name = "teams")
|
|
||||||
@NoArgsConstructor
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
|
||||||
@ToString(onlyExplicitlyIncluded = true)
|
|
||||||
public class Team implements Serializable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@Column(name = "team_id")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Column(name = "name", unique = true, nullable = false)
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
|
|
||||||
private Set<User> users = new HashSet<>();
|
|
||||||
|
|
||||||
public void addUser(User user) {
|
|
||||||
users.add(user);
|
|
||||||
user.setTeam(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeUser(User user) {
|
|
||||||
users.remove(user);
|
|
||||||
user.setTeam(null);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package stirling.software.proprietary.model.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class TeamWithUserCountDTO {
|
|
||||||
private Long id;
|
|
||||||
private String name;
|
|
||||||
private Long userCount;
|
|
||||||
|
|
||||||
// Constructor for JPQL projection
|
|
||||||
public TeamWithUserCountDTO(Long id, String name, Long userCount) {
|
|
||||||
this.id = id;
|
|
||||||
this.name = name;
|
|
||||||
this.userCount = userCount;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +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.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;
|
||||||
@ -13,6 +10,13 @@ import org.springframework.security.authentication.LockedException;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import stirling.software.proprietary.security.model.User;
|
import stirling.software.proprietary.security.model.User;
|
||||||
import stirling.software.proprietary.security.service.LoginAttemptService;
|
import stirling.software.proprietary.security.service.LoginAttemptService;
|
||||||
import stirling.software.proprietary.security.service.UserService;
|
import stirling.software.proprietary.security.service.UserService;
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
package stirling.software.proprietary.security;
|
package stirling.software.proprietary.security;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||||
|
|
||||||
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 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.security.service.LoginAttemptService;
|
import stirling.software.proprietary.security.service.LoginAttemptService;
|
||||||
import stirling.software.proprietary.security.service.UserService;
|
import stirling.software.proprietary.security.service.UserService;
|
||||||
|
@ -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,20 +1,19 @@
|
|||||||
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.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;
|
||||||
import stirling.software.proprietary.model.Team;
|
|
||||||
import stirling.software.proprietary.security.model.User;
|
|
||||||
import stirling.software.proprietary.security.service.DatabaseServiceInterface;
|
import stirling.software.proprietary.security.service.DatabaseServiceInterface;
|
||||||
import stirling.software.proprietary.security.service.TeamService;
|
|
||||||
import stirling.software.proprietary.security.service.UserService;
|
import stirling.software.proprietary.security.service.UserService;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -23,8 +22,9 @@ import stirling.software.proprietary.security.service.UserService;
|
|||||||
public class InitialSecuritySetup {
|
public class InitialSecuritySetup {
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final TeamService teamService;
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private final DatabaseServiceInterface databaseService;
|
private final DatabaseServiceInterface databaseService;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@ -40,7 +40,6 @@ public class InitialSecuritySetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userService.migrateOauth2ToSSO();
|
userService.migrateOauth2ToSSO();
|
||||||
assignUsersToDefaultTeamIfMissing();
|
|
||||||
initializeInternalApiUser();
|
initializeInternalApiUser();
|
||||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||||
log.error("Failed to initialize security setup.", e);
|
log.error("Failed to initialize security setup.", e);
|
||||||
@ -48,24 +47,6 @@ public class InitialSecuritySetup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignUsersToDefaultTeamIfMissing() {
|
|
||||||
Team defaultTeam = teamService.getOrCreateDefaultTeam();
|
|
||||||
Team internalTeam = teamService.getOrCreateInternalTeam();
|
|
||||||
List<User> usersWithoutTeam = userService.getUsersWithoutTeam();
|
|
||||||
|
|
||||||
for (User user : usersWithoutTeam) {
|
|
||||||
if (user.getUsername().equalsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
|
||||||
user.setTeam(internalTeam);
|
|
||||||
} else {
|
|
||||||
user.setTeam(defaultTeam);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
userService.saveAll(usersWithoutTeam); // batch save
|
|
||||||
log.info(
|
|
||||||
"Assigned {} user(s) without a team to the default team.", usersWithoutTeam.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeAdminUser() throws SQLException, UnsupportedProviderException {
|
private void initializeAdminUser() throws SQLException, UnsupportedProviderException {
|
||||||
String initialUsername =
|
String initialUsername =
|
||||||
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
||||||
@ -77,9 +58,7 @@ public class InitialSecuritySetup {
|
|||||||
&& !initialPassword.isEmpty()
|
&& !initialPassword.isEmpty()
|
||||||
&& userService.findByUsernameIgnoreCase(initialUsername).isEmpty()) {
|
&& userService.findByUsernameIgnoreCase(initialUsername).isEmpty()) {
|
||||||
|
|
||||||
Team team = teamService.getOrCreateDefaultTeam();
|
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||||
userService.saveUser(
|
|
||||||
initialUsername, initialPassword, team, Role.ADMIN.getRoleId(), false);
|
|
||||||
log.info("Admin user created: {}", initialUsername);
|
log.info("Admin user created: {}", initialUsername);
|
||||||
} else {
|
} else {
|
||||||
createDefaultAdminUser();
|
createDefaultAdminUser();
|
||||||
@ -91,9 +70,7 @@ public class InitialSecuritySetup {
|
|||||||
String defaultPassword = "stirling";
|
String defaultPassword = "stirling";
|
||||||
|
|
||||||
if (userService.findByUsernameIgnoreCase(defaultUsername).isEmpty()) {
|
if (userService.findByUsernameIgnoreCase(defaultUsername).isEmpty()) {
|
||||||
Team team = teamService.getOrCreateDefaultTeam();
|
userService.saveUser(defaultUsername, defaultPassword, Role.ADMIN.getRoleId(), true);
|
||||||
userService.saveUser(
|
|
||||||
defaultUsername, defaultPassword, team, Role.ADMIN.getRoleId(), true);
|
|
||||||
log.info("Default admin user created: {}", defaultUsername);
|
log.info("Default admin user created: {}", defaultUsername);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,29 +78,12 @@ public class InitialSecuritySetup {
|
|||||||
private void initializeInternalApiUser()
|
private void initializeInternalApiUser()
|
||||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||||
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
||||||
Team team = teamService.getOrCreateInternalTeam();
|
|
||||||
userService.saveUser(
|
userService.saveUser(
|
||||||
Role.INTERNAL_API_USER.getRoleId(),
|
Role.INTERNAL_API_USER.getRoleId(),
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
team,
|
Role.INTERNAL_API_USER.getRoleId());
|
||||||
Role.INTERNAL_API_USER.getRoleId(),
|
|
||||||
false);
|
|
||||||
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
userService.addApiKeyToUser(Role.INTERNAL_API_USER.getRoleId());
|
||||||
log.info("Internal API user created: {}", Role.INTERNAL_API_USER.getRoleId());
|
log.info("Internal API user created: {}", Role.INTERNAL_API_USER.getRoleId());
|
||||||
} else {
|
|
||||||
Optional<User> internalApiUserOpt =
|
|
||||||
userService.findByUsernameIgnoreCase(Role.INTERNAL_API_USER.getRoleId());
|
|
||||||
if (internalApiUserOpt.isPresent()) {
|
|
||||||
User internalApiUser = internalApiUserOpt.get();
|
|
||||||
// move to team internal API user
|
|
||||||
if (!internalApiUser.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
|
||||||
log.info(
|
|
||||||
"Moving internal API user to team: {}", TeamService.INTERNAL_TEAM_NAME);
|
|
||||||
Team internalTeam = teamService.getOrCreateInternalTeam();
|
|
||||||
|
|
||||||
userService.changeUserTeam(internalApiUser, internalTeam);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey());
|
userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey());
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package stirling.software.proprietary.security.config;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/** Annotation to mark endpoints that require a Pro or higher license. */
|
|
||||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface PremiumEndpoint {}
|
|
@ -1,30 +0,0 @@
|
|||||||
package stirling.software.proprietary.security.config;
|
|
||||||
|
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
|
||||||
import org.aspectj.lang.annotation.Around;
|
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
|
||||||
|
|
||||||
@Aspect
|
|
||||||
@Component
|
|
||||||
public class PremiumEndpointAspect {
|
|
||||||
|
|
||||||
private final boolean runningProOrHigher;
|
|
||||||
|
|
||||||
public PremiumEndpointAspect(@Qualifier("runningProOrHigher") boolean runningProOrHigher) {
|
|
||||||
this.runningProOrHigher = runningProOrHigher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Around(
|
|
||||||
"@annotation(stirling.software.proprietary.security.config.PremiumEndpoint) || @within(stirling.software.proprietary.security.config.PremiumEndpoint)")
|
|
||||||
public Object checkPremiumAccess(ProceedingJoinPoint joinPoint) throws Throwable {
|
|
||||||
if (!runningProOrHigher) {
|
|
||||||
throw new ResponseStatusException(
|
|
||||||
HttpStatus.FORBIDDEN, "This endpoint requires a Pro or higher license");
|
|
||||||
}
|
|
||||||
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;
|
||||||
@ -10,8 +9,11 @@ import org.springframework.boot.jdbc.DataSourceBuilder;
|
|||||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.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;
|
||||||
@ -19,12 +21,8 @@ import stirling.software.common.model.exception.UnsupportedProviderException;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Getter
|
@Getter
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableJpaRepositories(
|
@EnableJpaRepositories(basePackages = "stirling.software.proprietary.security.database.repository")
|
||||||
basePackages = {
|
@EntityScan({"stirling.software.proprietary.security.model"})
|
||||||
"stirling.software.proprietary.security.database.repository",
|
|
||||||
"stirling.software.proprietary.security.repository"
|
|
||||||
})
|
|
||||||
@EntityScan({"stirling.software.proprietary.security.model", "stirling.software.proprietary.model"})
|
|
||||||
public class DatabaseConfig {
|
public class DatabaseConfig {
|
||||||
|
|
||||||
public final String DATASOURCE_DEFAULT_URL;
|
public final String DATASOURCE_DEFAULT_URL;
|
||||||
@ -57,7 +55,6 @@ public class DatabaseConfig {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@Qualifier("dataSource")
|
@Qualifier("dataSource")
|
||||||
@Primary
|
|
||||||
public DataSource dataSource() throws UnsupportedProviderException {
|
public DataSource dataSource() throws UnsupportedProviderException {
|
||||||
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
|
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -2,12 +2,12 @@ package stirling.software.proprietary.security.configuration.ee;
|
|||||||
|
|
||||||
import static stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
import static stirling.software.proprietary.security.configuration.ee.KeygenLicenseVerifier.License;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
|
||||||
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;
|
||||||
@ -28,23 +28,18 @@ public class EEAppConfig {
|
|||||||
migrateEnterpriseSettingsToPremium(this.applicationProperties);
|
migrateEnterpriseSettingsToPremium(this.applicationProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Profile("security")
|
|
||||||
@Bean(name = "runningProOrHigher")
|
@Bean(name = "runningProOrHigher")
|
||||||
@Primary
|
@Qualifier("runningProOrHigher")
|
||||||
public boolean runningProOrHigher() {
|
public boolean runningProOrHigher() {
|
||||||
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
|
return licenseKeyChecker.getPremiumLicenseEnabledResult() != License.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Profile("security")
|
|
||||||
@Bean(name = "license")
|
@Bean(name = "license")
|
||||||
@Primary
|
|
||||||
public String licenseType() {
|
public String licenseType() {
|
||||||
return licenseKeyChecker.getPremiumLicenseEnabledResult().name();
|
return licenseKeyChecker.getPremiumLicenseEnabledResult().name();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Profile("security")
|
|
||||||
@Bean(name = "runningEE")
|
@Bean(name = "runningEE")
|
||||||
@Primary
|
|
||||||
public boolean runningEnterprise() {
|
public boolean runningEnterprise() {
|
||||||
return licenseKeyChecker.getPremiumLicenseEnabledResult() == License.ENTERPRISE;
|
return licenseKeyChecker.getPremiumLicenseEnabledResult() == License.ENTERPRISE;
|
||||||
}
|
}
|
||||||
@ -54,9 +49,7 @@ public class EEAppConfig {
|
|||||||
return applicationProperties.getPremium().getProFeatures().isSsoAutoLogin();
|
return applicationProperties.getPremium().getProFeatures().isSsoAutoLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Profile("security")
|
|
||||||
@Bean(name = "GoogleDriveEnabled")
|
@Bean(name = "GoogleDriveEnabled")
|
||||||
@Primary
|
|
||||||
public boolean googleDriveEnabled() {
|
public boolean googleDriveEnabled() {
|
||||||
return runningProOrHigher()
|
return runningProOrHigher()
|
||||||
&& applicationProperties.getPremium().getProFeatures().getGoogleDrive().isEnabled();
|
&& applicationProperties.getPremium().getProFeatures().getGoogleDrive().isEnabled();
|
||||||
@ -80,9 +73,9 @@ public class EEAppConfig {
|
|||||||
|
|
||||||
// Copy the license key if it's set in enterprise but not in premium
|
// Copy the license key if it's set in enterprise but not in premium
|
||||||
if (premium.getKey() == null
|
if (premium.getKey() == null
|
||||||
|| "00000000-0000-0000-0000-000000000000".equals(premium.getKey())) {
|
|| premium.getKey().equals("00000000-0000-0000-0000-000000000000")) {
|
||||||
if (enterpriseEdition.getKey() != null
|
if (enterpriseEdition.getKey() != null
|
||||||
&& !"00000000-0000-0000-0000-000000000000".equals(enterpriseEdition.getKey())) {
|
&& !enterpriseEdition.getKey().equals("00000000-0000-0000-0000-000000000000")) {
|
||||||
premium.setKey(enterpriseEdition.getKey());
|
premium.setKey(enterpriseEdition.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,126 +0,0 @@
|
|||||||
package stirling.software.proprietary.security.controller.api;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.transaction.Transactional;
|
|
||||||
import java.util.Optional;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.web.servlet.view.RedirectView;
|
|
||||||
import stirling.software.proprietary.model.Team;
|
|
||||||
import stirling.software.proprietary.security.config.PremiumEndpoint;
|
|
||||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
|
||||||
import stirling.software.proprietary.security.model.User;
|
|
||||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
|
||||||
import stirling.software.proprietary.security.service.TeamService;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@RequestMapping("/api/v1/team")
|
|
||||||
@Tag(name = "Team", description = "Team Management APIs")
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@PremiumEndpoint
|
|
||||||
public class TeamController {
|
|
||||||
|
|
||||||
private final TeamRepository teamRepository;
|
|
||||||
private final UserRepository userRepository;
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
|
||||||
@PostMapping("/create")
|
|
||||||
public RedirectView createTeam(@RequestParam("name") String name) {
|
|
||||||
if (teamRepository.existsByNameIgnoreCase(name)) {
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamExists");
|
|
||||||
}
|
|
||||||
Team team = new Team();
|
|
||||||
team.setName(name);
|
|
||||||
teamRepository.save(team);
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamCreated");
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
|
||||||
@PostMapping("/rename")
|
|
||||||
public RedirectView renameTeam(
|
|
||||||
@RequestParam("teamId") Long teamId, @RequestParam("newName") String newName) {
|
|
||||||
Optional<Team> existing = teamRepository.findById(teamId);
|
|
||||||
if (existing.isEmpty()) {
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamNotFound");
|
|
||||||
}
|
|
||||||
if (teamRepository.existsByNameIgnoreCase(newName)) {
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamNameExists");
|
|
||||||
}
|
|
||||||
Team team = existing.get();
|
|
||||||
|
|
||||||
// Prevent renaming the Internal team
|
|
||||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
|
||||||
return new RedirectView("/adminSettings?messageType=internalTeamNotAccessible");
|
|
||||||
}
|
|
||||||
|
|
||||||
team.setName(newName);
|
|
||||||
teamRepository.save(team);
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamRenamed");
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
|
||||||
@PostMapping("/delete")
|
|
||||||
@Transactional
|
|
||||||
public RedirectView deleteTeam(@RequestParam("teamId") Long teamId) {
|
|
||||||
Optional<Team> teamOpt = teamRepository.findById(teamId);
|
|
||||||
if (teamOpt.isEmpty()) {
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
Team team = teamOpt.get();
|
|
||||||
|
|
||||||
// Prevent deleting the Internal team
|
|
||||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
|
||||||
return new RedirectView("/adminSettings?messageType=internalTeamNotAccessible");
|
|
||||||
}
|
|
||||||
|
|
||||||
long memberCount = userRepository.countByTeam(team);
|
|
||||||
if (memberCount > 0) {
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamHasUsers");
|
|
||||||
}
|
|
||||||
|
|
||||||
teamRepository.delete(team);
|
|
||||||
return new RedirectView("/adminSettings?messageType=teamDeleted");
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
|
||||||
@PostMapping("/addUser")
|
|
||||||
@Transactional
|
|
||||||
public RedirectView addUserToTeam(
|
|
||||||
@RequestParam("teamId") Long teamId, @RequestParam("userId") Long userId) {
|
|
||||||
|
|
||||||
// Find the team
|
|
||||||
Team team =
|
|
||||||
teamRepository
|
|
||||||
.findById(teamId)
|
|
||||||
.orElseThrow(() -> new RuntimeException("Team not found"));
|
|
||||||
|
|
||||||
// Prevent adding users to the Internal team
|
|
||||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
|
||||||
return new RedirectView("/teams?error=internalTeamNotAccessible");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the user
|
|
||||||
User user =
|
|
||||||
userRepository
|
|
||||||
.findById(userId)
|
|
||||||
.orElseThrow(() -> new RuntimeException("User not found"));
|
|
||||||
|
|
||||||
// Check if user is in the Internal team - prevent moving them
|
|
||||||
if (user.getTeam() != null
|
|
||||||
&& user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
|
||||||
return new RedirectView("/teams/" + teamId + "?error=cannotMoveInternalUsers");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign user to team
|
|
||||||
user.setTeam(team);
|
|
||||||
userRepository.save(user);
|
|
||||||
|
|
||||||
// Redirect back to team details page
|
|
||||||
return new RedirectView("/teams/" + teamId + "?messageType=userAdded");
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,17 +20,22 @@ 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 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;
|
||||||
import stirling.software.proprietary.model.Team;
|
|
||||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
|
||||||
import stirling.software.proprietary.security.model.AuthenticationType;
|
import stirling.software.proprietary.security.model.AuthenticationType;
|
||||||
import stirling.software.proprietary.security.model.User;
|
import stirling.software.proprietary.security.model.User;
|
||||||
import stirling.software.proprietary.security.model.api.user.UsernameAndPass;
|
import stirling.software.proprietary.security.model.api.user.UsernameAndPass;
|
||||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
|
||||||
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
||||||
import stirling.software.proprietary.security.service.TeamService;
|
|
||||||
import stirling.software.proprietary.security.service.UserService;
|
import stirling.software.proprietary.security.service.UserService;
|
||||||
import stirling.software.proprietary.security.session.SessionPersistentRegistry;
|
import stirling.software.proprietary.security.session.SessionPersistentRegistry;
|
||||||
|
|
||||||
@ -50,8 +50,6 @@ public class UserController {
|
|||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final SessionPersistentRegistry sessionRegistry;
|
private final SessionPersistentRegistry sessionRegistry;
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
private final TeamRepository teamRepository;
|
|
||||||
private final UserRepository userRepository;
|
|
||||||
|
|
||||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||||
@PostMapping("/register")
|
@PostMapping("/register")
|
||||||
@ -62,13 +60,7 @@ public class UserController {
|
|||||||
return "register";
|
return "register";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Team team = teamRepository.findByName(TeamService.DEFAULT_TEAM_NAME).orElse(null);
|
userService.saveUser(requestModel.getUsername(), requestModel.getPassword());
|
||||||
userService.saveUser(
|
|
||||||
requestModel.getUsername(),
|
|
||||||
requestModel.getPassword(),
|
|
||||||
team,
|
|
||||||
Role.USER.getRoleId(),
|
|
||||||
false);
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
return "redirect:/login?messageType=invalidUsername";
|
return "redirect:/login?messageType=invalidUsername";
|
||||||
}
|
}
|
||||||
@ -208,7 +200,6 @@ public class UserController {
|
|||||||
@RequestParam(name = "username", required = true) String username,
|
@RequestParam(name = "username", required = true) String username,
|
||||||
@RequestParam(name = "password", required = false) String password,
|
@RequestParam(name = "password", required = false) String password,
|
||||||
@RequestParam(name = "role") String role,
|
@RequestParam(name = "role") String role,
|
||||||
@RequestParam(name = "teamId", required = false) Long teamId,
|
|
||||||
@RequestParam(name = "authType") String authType,
|
@RequestParam(name = "authType") String authType,
|
||||||
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
||||||
boolean forceChange)
|
boolean forceChange)
|
||||||
@ -242,32 +233,13 @@ public class UserController {
|
|||||||
// If the role ID is not valid, redirect with an error message
|
// If the role ID is not valid, redirect with an error message
|
||||||
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use teamId if provided, otherwise use default team
|
|
||||||
Long effectiveTeamId = teamId;
|
|
||||||
if (effectiveTeamId == null) {
|
|
||||||
Team defaultTeam =
|
|
||||||
teamRepository.findByName(TeamService.DEFAULT_TEAM_NAME).orElse(null);
|
|
||||||
if (defaultTeam != null) {
|
|
||||||
effectiveTeamId = defaultTeam.getId();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Check if the selected team is Internal - prevent assigning to it
|
|
||||||
Team selectedTeam = teamRepository.findById(effectiveTeamId).orElse(null);
|
|
||||||
if (selectedTeam != null
|
|
||||||
&& TeamService.INTERNAL_TEAM_NAME.equals(selectedTeam.getName())) {
|
|
||||||
return new RedirectView(
|
|
||||||
"/adminSettings?messageType=internalTeamNotAccessible", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authType.equalsIgnoreCase(AuthenticationType.SSO.toString())) {
|
if (authType.equalsIgnoreCase(AuthenticationType.SSO.toString())) {
|
||||||
userService.saveUser(username, AuthenticationType.SSO, effectiveTeamId, role);
|
userService.saveUser(username, AuthenticationType.SSO, role);
|
||||||
} else {
|
} else {
|
||||||
if (password.isBlank()) {
|
if (password.isBlank()) {
|
||||||
return new RedirectView("/adminSettings?messageType=invalidPassword", true);
|
return new RedirectView("/adminSettings?messageType=invalidPassword", true);
|
||||||
}
|
}
|
||||||
userService.saveUser(username, password, effectiveTeamId, role, forceChange);
|
userService.saveUser(username, password, role, forceChange);
|
||||||
}
|
}
|
||||||
return new RedirectView(
|
return new RedirectView(
|
||||||
"/adminSettings", // Redirect to account page after adding the user
|
"/adminSettings", // Redirect to account page after adding the user
|
||||||
@ -276,11 +248,9 @@ public class UserController {
|
|||||||
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
@PostMapping("/admin/changeRole")
|
@PostMapping("/admin/changeRole")
|
||||||
@Transactional
|
|
||||||
public RedirectView changeRole(
|
public RedirectView changeRole(
|
||||||
@RequestParam(name = "username") String username,
|
@RequestParam(name = "username") String username,
|
||||||
@RequestParam(name = "role") String role,
|
@RequestParam(name = "role") String role,
|
||||||
@RequestParam(name = "teamId", required = false) Long teamId,
|
|
||||||
Authentication authentication)
|
Authentication authentication)
|
||||||
throws SQLException, UnsupportedProviderException {
|
throws SQLException, UnsupportedProviderException {
|
||||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||||
@ -308,29 +278,6 @@ public class UserController {
|
|||||||
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
return new RedirectView("/adminSettings?messageType=invalidRole", true);
|
||||||
}
|
}
|
||||||
User user = userOpt.get();
|
User user = userOpt.get();
|
||||||
|
|
||||||
// Update the team if a teamId is provided
|
|
||||||
if (teamId != null) {
|
|
||||||
Team team = teamRepository.findById(teamId).orElse(null);
|
|
||||||
if (team != null) {
|
|
||||||
// Prevent assigning to Internal team
|
|
||||||
if (TeamService.INTERNAL_TEAM_NAME.equals(team.getName())) {
|
|
||||||
return new RedirectView(
|
|
||||||
"/adminSettings?messageType=internalTeamNotAccessible", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent moving users from Internal team
|
|
||||||
if (user.getTeam() != null
|
|
||||||
&& TeamService.INTERNAL_TEAM_NAME.equals(user.getTeam().getName())) {
|
|
||||||
return new RedirectView(
|
|
||||||
"/adminSettings?messageType=cannotMoveInternalUsers", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.setTeam(team);
|
|
||||||
userRepository.save(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
userService.changeRole(user, role);
|
userService.changeRole(user, role);
|
||||||
return new RedirectView(
|
return new RedirectView(
|
||||||
"/adminSettings", // Redirect to account page after adding the user
|
"/adminSettings", // Redirect to account page after adding the user
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.config;
|
package stirling.software.proprietary.security.controller.web;
|
||||||
|
|
||||||
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;
|
||||||
@ -32,14 +38,11 @@ import stirling.software.common.model.enumeration.Role;
|
|||||||
import stirling.software.common.model.oauth2.GitHubProvider;
|
import stirling.software.common.model.oauth2.GitHubProvider;
|
||||||
import stirling.software.common.model.oauth2.GoogleProvider;
|
import stirling.software.common.model.oauth2.GoogleProvider;
|
||||||
import stirling.software.common.model.oauth2.KeycloakProvider;
|
import stirling.software.common.model.oauth2.KeycloakProvider;
|
||||||
import stirling.software.proprietary.model.Team;
|
|
||||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
import stirling.software.proprietary.security.database.repository.UserRepository;
|
||||||
import stirling.software.proprietary.security.model.Authority;
|
import stirling.software.proprietary.security.model.Authority;
|
||||||
import stirling.software.proprietary.security.model.SessionEntity;
|
import stirling.software.proprietary.security.model.SessionEntity;
|
||||||
import stirling.software.proprietary.security.model.User;
|
import stirling.software.proprietary.security.model.User;
|
||||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
|
||||||
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
import stirling.software.proprietary.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
||||||
import stirling.software.proprietary.security.service.TeamService;
|
|
||||||
import stirling.software.proprietary.security.session.SessionPersistentRegistry;
|
import stirling.software.proprietary.security.session.SessionPersistentRegistry;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@ -54,19 +57,16 @@ public class AccountWebController {
|
|||||||
// Assuming you have a repository for user operations
|
// Assuming you have a repository for user operations
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final boolean runningEE;
|
private final boolean runningEE;
|
||||||
private final TeamRepository teamRepository;
|
|
||||||
|
|
||||||
public AccountWebController(
|
public AccountWebController(
|
||||||
ApplicationProperties applicationProperties,
|
ApplicationProperties applicationProperties,
|
||||||
SessionPersistentRegistry sessionPersistentRegistry,
|
SessionPersistentRegistry sessionPersistentRegistry,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
TeamRepository teamRepository,
|
|
||||||
@Qualifier("runningEE") boolean runningEE) {
|
@Qualifier("runningEE") boolean runningEE) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
this.sessionPersistentRegistry = sessionPersistentRegistry;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.runningEE = runningEE;
|
this.runningEE = runningEE;
|
||||||
this.teamRepository = teamRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/login")
|
@GetMapping("/login")
|
||||||
@ -210,7 +210,7 @@ public class AccountWebController {
|
|||||||
@GetMapping("/adminSettings")
|
@GetMapping("/adminSettings")
|
||||||
public String showAddUserForm(
|
public String showAddUserForm(
|
||||||
HttpServletRequest request, Model model, Authentication authentication) {
|
HttpServletRequest request, Model model, Authentication authentication) {
|
||||||
List<User> allUsers = userRepository.findAllWithTeam();
|
List<User> allUsers = userRepository.findAll();
|
||||||
Iterator<User> iterator = allUsers.iterator();
|
Iterator<User> iterator = allUsers.iterator();
|
||||||
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
Map<String, String> roleDetails = Role.getAllRoleDetails();
|
||||||
// Map to store session information and user activity status
|
// Map to store session information and user activity status
|
||||||
@ -221,28 +221,14 @@ public class AccountWebController {
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
User user = iterator.next();
|
User user = iterator.next();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
boolean shouldRemove = false;
|
|
||||||
|
|
||||||
// Check if user is an INTERNAL_API_USER
|
|
||||||
for (Authority authority : user.getAuthorities()) {
|
for (Authority authority : user.getAuthorities()) {
|
||||||
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) {
|
||||||
shouldRemove = true;
|
iterator.remove();
|
||||||
roleDetails.remove(Role.INTERNAL_API_USER.getRoleId());
|
roleDetails.remove(Role.INTERNAL_API_USER.getRoleId());
|
||||||
|
// Break out of the inner loop once the user is removed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also check if user is part of the Internal team
|
|
||||||
if (user.getTeam() != null
|
|
||||||
&& user.getTeam().getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
|
||||||
shouldRemove = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the user if either condition is true
|
|
||||||
if (shouldRemove) {
|
|
||||||
iterator.remove();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Determine the user's session status and last request time
|
// Determine the user's session status and last request time
|
||||||
int maxInactiveInterval = sessionPersistentRegistry.getMaxInactiveInterval();
|
int maxInactiveInterval = sessionPersistentRegistry.getMaxInactiveInterval();
|
||||||
boolean hasActiveSession = false;
|
boolean hasActiveSession = false;
|
||||||
@ -345,19 +331,6 @@ public class AccountWebController {
|
|||||||
model.addAttribute("activeUsers", activeUsers);
|
model.addAttribute("activeUsers", activeUsers);
|
||||||
model.addAttribute("disabledUsers", disabledUsers);
|
model.addAttribute("disabledUsers", disabledUsers);
|
||||||
|
|
||||||
// Get all teams but filter out the Internal team
|
|
||||||
List<Team> allTeams =
|
|
||||||
teamRepository.findAll().stream()
|
|
||||||
.filter(
|
|
||||||
team ->
|
|
||||||
!team.getName()
|
|
||||||
.equals(
|
|
||||||
stirling.software.proprietary.security
|
|
||||||
.service.TeamService
|
|
||||||
.INTERNAL_TEAM_NAME))
|
|
||||||
.toList();
|
|
||||||
model.addAttribute("teams", allTeams);
|
|
||||||
|
|
||||||
model.addAttribute("maxPaidUsers", applicationProperties.getPremium().getMaxUsers());
|
model.addAttribute("maxPaidUsers", applicationProperties.getPremium().getMaxUsers());
|
||||||
return "adminSettings";
|
return "adminSettings";
|
||||||
}
|
}
|
@ -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,114 +0,0 @@
|
|||||||
package stirling.software.proprietary.security.controller.web;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import stirling.software.proprietary.model.Team;
|
|
||||||
import stirling.software.proprietary.model.dto.TeamWithUserCountDTO;
|
|
||||||
import stirling.software.proprietary.security.database.repository.SessionRepository;
|
|
||||||
import stirling.software.proprietary.security.database.repository.UserRepository;
|
|
||||||
import stirling.software.proprietary.security.model.User;
|
|
||||||
import stirling.software.proprietary.security.repository.TeamRepository;
|
|
||||||
import stirling.software.proprietary.security.service.TeamService;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@RequestMapping("/teams")
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Slf4j
|
|
||||||
public class TeamWebController {
|
|
||||||
|
|
||||||
private final TeamRepository teamRepository;
|
|
||||||
private final SessionRepository sessionRepository;
|
|
||||||
private final UserRepository userRepository;
|
|
||||||
|
|
||||||
@GetMapping
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
|
||||||
public String listTeams(Model model) {
|
|
||||||
// Get teams with user counts using a DTO projection
|
|
||||||
List<TeamWithUserCountDTO> allTeamsWithCounts = teamRepository.findAllTeamsWithUserCount();
|
|
||||||
|
|
||||||
// Filter out the Internal team
|
|
||||||
List<TeamWithUserCountDTO> teamsWithCounts =
|
|
||||||
allTeamsWithCounts.stream()
|
|
||||||
.filter(team -> !team.getName().equals(TeamService.INTERNAL_TEAM_NAME))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
// Get the latest activity for each team
|
|
||||||
List<Object[]> teamActivities = sessionRepository.findLatestActivityByTeam();
|
|
||||||
|
|
||||||
// Convert the query results to a map for easy access in the view
|
|
||||||
Map<Long, Date> teamLastRequest = new HashMap<>();
|
|
||||||
for (Object[] result : teamActivities) {
|
|
||||||
Long teamId = (Long) result[0]; // teamId alias
|
|
||||||
Date lastActivity = (Date) result[1]; // lastActivity alias
|
|
||||||
teamLastRequest.put(teamId, lastActivity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add data to the model
|
|
||||||
model.addAttribute("teamsWithCounts", teamsWithCounts);
|
|
||||||
model.addAttribute("teamLastRequest", teamLastRequest);
|
|
||||||
|
|
||||||
return "accounts/teams";
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
|
||||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
|
||||||
public String viewTeamDetails(@PathVariable("id") Long id, Model model) {
|
|
||||||
// Get the team
|
|
||||||
Team team =
|
|
||||||
teamRepository
|
|
||||||
.findById(id)
|
|
||||||
.orElseThrow(() -> new RuntimeException("Team not found"));
|
|
||||||
|
|
||||||
// Prevent access to Internal team
|
|
||||||
if (team.getName().equals(TeamService.INTERNAL_TEAM_NAME)) {
|
|
||||||
return "redirect:/teams?error=internalTeamNotAccessible";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get users for this team directly using the direct query
|
|
||||||
List<User> teamUsers = userRepository.findAllByTeamId(id);
|
|
||||||
|
|
||||||
// Get all users not in this team for the Add User to Team dropdown
|
|
||||||
// Exclude users that are in the Internal team
|
|
||||||
List<User> allUsers = userRepository.findAllWithTeam();
|
|
||||||
List<User> availableUsers =
|
|
||||||
allUsers.stream()
|
|
||||||
.filter(
|
|
||||||
user ->
|
|
||||||
(user.getTeam() == null
|
|
||||||
|| !user.getTeam().getId().equals(id))
|
|
||||||
&& (user.getTeam() == null
|
|
||||||
|| !user.getTeam()
|
|
||||||
.getName()
|
|
||||||
.equals(
|
|
||||||
TeamService
|
|
||||||
.INTERNAL_TEAM_NAME)))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
// Get the latest session for each user in the team
|
|
||||||
List<Object[]> userSessions = sessionRepository.findLatestSessionByTeamId(id);
|
|
||||||
|
|
||||||
// Create a map of username to last request date
|
|
||||||
Map<String, Date> userLastRequest = new HashMap<>();
|
|
||||||
for (Object[] result : userSessions) {
|
|
||||||
String username = (String) result[0]; // username alias
|
|
||||||
Date lastRequest = (Date) result[1]; // lastRequest alias
|
|
||||||
userLastRequest.put(username, lastRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
model.addAttribute("team", team);
|
|
||||||
model.addAttribute("teamUsers", teamUsers);
|
|
||||||
model.addAttribute("availableUsers", availableUsers);
|
|
||||||
model.addAttribute("userLastRequest", userLastRequest);
|
|
||||||
return "accounts/team-details";
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
@ -26,20 +29,4 @@ public interface SessionRepository extends JpaRepository<SessionEntity, String>
|
|||||||
@Param("expired") boolean expired,
|
@Param("expired") boolean expired,
|
||||||
@Param("lastRequest") Date lastRequest,
|
@Param("lastRequest") Date lastRequest,
|
||||||
@Param("principalName") String principalName);
|
@Param("principalName") String principalName);
|
||||||
|
|
||||||
@Query(
|
|
||||||
"SELECT t.id as teamId, MAX(s.lastRequest) as lastActivity "
|
|
||||||
+ "FROM stirling.software.proprietary.model.Team t "
|
|
||||||
+ "LEFT JOIN t.users u "
|
|
||||||
+ "LEFT JOIN SessionEntity s ON u.username = s.principalName "
|
|
||||||
+ "GROUP BY t.id")
|
|
||||||
List<Object[]> findLatestActivityByTeam();
|
|
||||||
|
|
||||||
@Query(
|
|
||||||
"SELECT u.username as username, MAX(s.lastRequest) as lastRequest "
|
|
||||||
+ "FROM stirling.software.proprietary.security.model.User u "
|
|
||||||
+ "LEFT JOIN SessionEntity s ON u.username = s.principalName "
|
|
||||||
+ "WHERE u.team.id = :teamId "
|
|
||||||
+ "GROUP BY u.username")
|
|
||||||
List<Object[]> findLatestSessionByTeamId(@Param("teamId") Long teamId);
|
|
||||||
}
|
}
|
||||||
|
@ -2,11 +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.security.model.User;
|
import stirling.software.proprietary.security.model.User;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@ -21,18 +22,4 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
|||||||
Optional<User> findByApiKey(String apiKey);
|
Optional<User> findByApiKey(String apiKey);
|
||||||
|
|
||||||
List<User> findByAuthenticationTypeIgnoreCase(String authenticationType);
|
List<User> findByAuthenticationTypeIgnoreCase(String authenticationType);
|
||||||
|
|
||||||
@Query("SELECT u FROM User u WHERE u.team IS NULL")
|
|
||||||
List<User> findAllWithoutTeam();
|
|
||||||
|
|
||||||
@Query(value = "SELECT u FROM User u LEFT JOIN FETCH u.team")
|
|
||||||
List<User> findAllWithTeam();
|
|
||||||
|
|
||||||
@Query(
|
|
||||||
"SELECT u FROM User u JOIN FETCH u.authorities JOIN FETCH u.team WHERE u.team.id = :teamId")
|
|
||||||
List<User> findAllByTeamId(@Param("teamId") Long teamId);
|
|
||||||
|
|
||||||
long countByTeam(Team team);
|
|
||||||
|
|
||||||
List<User> findAllByTeam(Team team);
|
|
||||||
}
|
}
|
||||||
|
@ -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,19 +1,21 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "users")
|
@Table(name = "users")
|
||||||
@ -55,10 +57,6 @@ public class User implements Serializable {
|
|||||||
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
|
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user")
|
||||||
private Set<Authority> authorities = new HashSet<>();
|
private Set<Authority> authorities = new HashSet<>();
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.EAGER)
|
|
||||||
@JoinColumn(name = "team_id")
|
|
||||||
private Team team;
|
|
||||||
|
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@MapKeyColumn(name = "setting_key")
|
@MapKeyColumn(name = "setting_key")
|
||||||
@Lob
|
@Lob
|
||||||
|
@ -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
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.model.api.user;
|
package stirling.software.proprietary.security.model.api.user;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.model.api.user;
|
package stirling.software.proprietary.security.model.api.user;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.model.api.user;
|
package stirling.software.proprietary.security.model.api.user;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.model.api.user;
|
package stirling.software.proprietary.security.model.api.user;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package stirling.software.proprietary.security.oauth2;
|
package stirling.software.proprietary.security.oauth2;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
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.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
@ -13,6 +10,12 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
|||||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class CustomOAuth2AuthenticationFailureHandler
|
public class CustomOAuth2AuthenticationFailureHandler
|
||||||
extends SimpleUrlAuthenticationFailureHandler {
|
extends SimpleUrlAuthenticationFailureHandler {
|
||||||
|
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