Compare commits
99 Commits
v0.36.6
...
verifiyDep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94df4c7067 | ||
|
|
481fdd60bf | ||
|
|
313116ebcb | ||
|
|
1de33cf36c | ||
|
|
fcb3d48a1f | ||
|
|
6834067e7a | ||
|
|
d4b120b5f8 | ||
|
|
f0310a4177 | ||
|
|
06a53de350 | ||
|
|
bb99f9ff83 | ||
|
|
72b7b1b838 | ||
|
|
cbaea9cca9 | ||
|
|
5605e4d3bb | ||
|
|
bf0958f588 | ||
|
|
0ae862a84a | ||
|
|
3e216872ce | ||
|
|
1b26fa40f2 | ||
|
|
672389d6b8 | ||
|
|
888ef104a2 | ||
|
|
f90db3cff7 | ||
|
|
e0f553c15a | ||
|
|
89d319332a | ||
|
|
225e797176 | ||
|
|
5e28023853 | ||
|
|
4ef118c4eb | ||
|
|
92401b9e7f | ||
|
|
c78f924522 | ||
|
|
630f31c3f8 | ||
|
|
7dca87b377 | ||
|
|
8619b1cf59 | ||
|
|
c6c6cbeaa9 | ||
|
|
c17b4e29ff | ||
|
|
d2d8e2ef42 | ||
|
|
62e96e3f94 | ||
|
|
f95eea07fb | ||
|
|
1bd7e420e5 | ||
|
|
76cbf94fdc | ||
|
|
b2da426cc1 | ||
|
|
cce4693aea | ||
|
|
1c82523dc5 | ||
|
|
2241e2c1f2 | ||
|
|
c11076ee94 | ||
|
|
9273b163a8 | ||
|
|
5e0adc6234 | ||
|
|
e5f39b79f7 | ||
|
|
b98f8627ac | ||
|
|
1ed1b17510 | ||
|
|
ded3915424 | ||
|
|
a979b526c4 | ||
|
|
5272e4c7ff | ||
|
|
e95cbecab4 | ||
|
|
85d2f6f0cc | ||
|
|
5ef8c6be55 | ||
|
|
f8e1ce6a7b | ||
|
|
ad50e90a03 | ||
|
|
a4afe5b708 | ||
|
|
d1ed70146c | ||
|
|
fe84128297 | ||
|
|
690527aabd | ||
|
|
2b27cbbd4c | ||
|
|
544e838f7c | ||
|
|
f379c27bd7 | ||
|
|
8d4c762c7e | ||
|
|
bad5a2bc8b | ||
|
|
e17dfcb369 | ||
|
|
6d73f01107 | ||
|
|
c6c1dceaa2 | ||
|
|
59c28f10f9 | ||
|
|
f5afce8fc1 | ||
|
|
41dce06804 | ||
|
|
7382efd80d | ||
|
|
98d4443ebd | ||
|
|
ddbef1c82b | ||
|
|
52bf4381ab | ||
|
|
d8a4f44862 | ||
|
|
ed633616e7 | ||
|
|
f08f8c734b | ||
|
|
22af79a279 | ||
|
|
79f6598508 | ||
|
|
e754e6012a | ||
|
|
3227da55e0 | ||
|
|
709a79c3d9 | ||
|
|
77bb15bc8b | ||
|
|
beaa86cbf9 | ||
|
|
b8303e3860 | ||
|
|
5ba98e4411 | ||
|
|
116dfcc065 | ||
|
|
2d76927b3c | ||
|
|
41c269f208 | ||
|
|
38633d4db1 | ||
|
|
0ff45c656a | ||
|
|
bc282c6aa5 | ||
|
|
50575bc80b | ||
|
|
6f04f01c2b | ||
|
|
e80eaaa6d1 | ||
|
|
01288dafe8 | ||
|
|
e3c7b6f955 | ||
|
|
875f5a85ef | ||
|
|
ef174a1e8a |
9
.github/labeler-config.yml
vendored
9
.github/labeler-config.yml
vendored
@@ -9,6 +9,7 @@ Front End:
|
||||
- any-glob-to-any-file: 'src/main/resources/templates/**/*'
|
||||
- any-glob-to-any-file: 'src/main/resources/static/**/*'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/**'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/UI/**/*'
|
||||
|
||||
Java:
|
||||
- changed-files:
|
||||
@@ -29,6 +30,7 @@ Security:
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/security/**/*'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/provider/**/*'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/AuthenticationType.java'
|
||||
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/model/BackupNotFoundException.java'
|
||||
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
|
||||
- any-glob-to-any-file: '.github/workflows/dependency-review.yml'
|
||||
- any-glob-to-any-file: '.github/workflows/scorecards.yml'
|
||||
@@ -49,12 +51,17 @@ Documentation:
|
||||
|
||||
Docker:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: '.github/workflows/build.yml'
|
||||
- any-glob-to-any-file: '.github/workflows/push-docker.yml'
|
||||
- any-glob-to-any-file: 'Dockerfile'
|
||||
- any-glob-to-any-file: 'Dockerfile-*'
|
||||
- any-glob-to-any-file: 'Dockerfile.*'
|
||||
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
|
||||
- any-glob-to-any-file: 'scripts/download-security-jar.sh'
|
||||
- any-glob-to-any-file: 'scripts/init.sh'
|
||||
- any-glob-to-any-file: 'scripts/init-without-ocr.sh'
|
||||
- any-glob-to-any-file: 'scripts/installFonts.sh'
|
||||
- any-glob-to-any-file: 'test.sh'
|
||||
- any-glob-to-any-file: 'test2.sh'
|
||||
|
||||
Test:
|
||||
- changed-files:
|
||||
|
||||
1
.github/scripts/requirements_pre_commit.in
vendored
Normal file
1
.github/scripts/requirements_pre_commit.in
vendored
Normal file
@@ -0,0 +1 @@
|
||||
pre-commit
|
||||
93
.github/scripts/requirements_pre_commit.txt
vendored
Normal file
93
.github/scripts/requirements_pre_commit.txt
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_pre_commit.txt' '.github\scripts\requirements_pre_commit.in'
|
||||
#
|
||||
cfgv==3.4.0 \
|
||||
--hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \
|
||||
--hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560
|
||||
# via pre-commit
|
||||
distlib==0.3.9 \
|
||||
--hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \
|
||||
--hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403
|
||||
# via virtualenv
|
||||
filelock==3.16.1 \
|
||||
--hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \
|
||||
--hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435
|
||||
# via virtualenv
|
||||
identify==2.6.5 \
|
||||
--hash=sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566 \
|
||||
--hash=sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc
|
||||
# via pre-commit
|
||||
nodeenv==1.9.1 \
|
||||
--hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \
|
||||
--hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9
|
||||
# via pre-commit
|
||||
platformdirs==4.3.6 \
|
||||
--hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \
|
||||
--hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb
|
||||
# via virtualenv
|
||||
pre-commit==4.0.1 \
|
||||
--hash=sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2 \
|
||||
--hash=sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878
|
||||
# via -r .github\scripts\requirements_pre_commit.in
|
||||
pyyaml==6.0.2 \
|
||||
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||
# via pre-commit
|
||||
virtualenv==20.28.1 \
|
||||
--hash=sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb \
|
||||
--hash=sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329
|
||||
# via pre-commit
|
||||
1
.github/scripts/requirements_sync_readme.in
vendored
Normal file
1
.github/scripts/requirements_sync_readme.in
vendored
Normal file
@@ -0,0 +1 @@
|
||||
tomlkit
|
||||
10
.github/scripts/requirements_sync_readme.txt
vendored
Normal file
10
.github/scripts/requirements_sync_readme.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file='.github\scripts\requirements_sync_readme.txt' '.github\scripts\requirements_sync_readme.in'
|
||||
#
|
||||
tomlkit==0.13.2 \
|
||||
--hash=sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde \
|
||||
--hash=sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79
|
||||
# via -r .github\scripts\requirements_sync_readme.in
|
||||
26
.github/workflows/PR-Demo-Comment.yml
vendored
26
.github/workflows/PR-Demo-Comment.yml
vendored
@@ -4,9 +4,15 @@ on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-comment:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
issues: read
|
||||
if: |
|
||||
github.event.issue.pull_request &&
|
||||
(
|
||||
@@ -20,7 +26,8 @@ jobs:
|
||||
github.event.comment.user.login == 'Ludy87' ||
|
||||
github.event.comment.user.login == 'LaserKaspar' ||
|
||||
github.event.comment.user.login == 'sbplat' ||
|
||||
github.event.comment.user.login == 'reecebrowne'
|
||||
github.event.comment.user.login == 'reecebrowne' ||
|
||||
github.event.comment.user.login == 'DarioGii'
|
||||
)
|
||||
outputs:
|
||||
pr_number: ${{ steps.get-pr.outputs.pr_number }}
|
||||
@@ -29,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -68,10 +75,13 @@ jobs:
|
||||
deploy-pr:
|
||||
needs: check-comment
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -85,8 +95,8 @@ jobs:
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
java-version: "17"
|
||||
distribution: "temurin"
|
||||
|
||||
- name: Run Gradle Command
|
||||
run: ./gradlew clean build
|
||||
@@ -98,7 +108,9 @@ jobs:
|
||||
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
@@ -107,7 +119,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKER_HUB_API }}
|
||||
|
||||
- name: Build and push PR-specific image
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
|
||||
9
.github/workflows/PR-Demo-cleanup.yml
vendored
9
.github/workflows/PR-Demo-cleanup.yml
vendored
@@ -4,11 +4,12 @@ on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, closed]
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
||||
CLEANUP_PERFORMED: 'false' # Add flag to track if cleanup occurred
|
||||
SERVER_IP: ${{ secrets.VPS_IP }} # Add this to your GitHub secrets
|
||||
CLEANUP_PERFORMED: "false" # Add flag to track if cleanup occurred
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
@@ -20,7 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
5
.github/workflows/auto-labeler.yml
vendored
5
.github/workflows/auto-labeler.yml
vendored
@@ -3,7 +3,8 @@ on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
@@ -12,7 +13,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
@@ -6,13 +6,15 @@ on:
|
||||
pull_request:
|
||||
branches: ["main"]
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
@@ -22,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -44,7 +46,18 @@ jobs:
|
||||
run: ./gradlew clean build
|
||||
env:
|
||||
DOCKER_ENABLE_SECURITY: true
|
||||
|
||||
|
||||
- name: Upload Test Reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: test-reports-jdk-${{ matrix.jdk-version }}
|
||||
path: |
|
||||
build/reports/tests/
|
||||
build/test-results/
|
||||
build/reports/problems/
|
||||
retention-days: 3
|
||||
|
||||
docker-compose-tests:
|
||||
# if: github.event_name == 'push' && github.ref == 'refs/heads/main' ||
|
||||
# (github.event_name == 'pull_request' &&
|
||||
@@ -64,7 +77,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -82,7 +95,7 @@ jobs:
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.32.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
|
||||
- name: Set up Python
|
||||
@@ -92,9 +105,10 @@ jobs:
|
||||
|
||||
- name: Pip requirements
|
||||
run: |
|
||||
pip install -r ./cucumber/requirements.txt
|
||||
pip install --require-hashes -r ./cucumber/requirements.txt
|
||||
|
||||
- name: Run Docker Compose Tests
|
||||
run: |
|
||||
chmod +x ./cucumber/test_webpages.sh
|
||||
chmod +x ./test.sh
|
||||
./test.sh
|
||||
|
||||
4
.github/workflows/check_properties.yml
vendored
4
.github/workflows/check_properties.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Get PR data
|
||||
id: get-pr-data
|
||||
|
||||
2
.github/workflows/codeql.yml-disabled
vendored
2
.github/workflows/codeql.yml-disabled
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
8
.github/workflows/dependency-review.yml
vendored
8
.github/workflows/dependency-review.yml
vendored
@@ -6,7 +6,7 @@
|
||||
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
name: 'Dependency Review'
|
||||
name: "Dependency Review"
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout Repository'
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: 'Dependency Review'
|
||||
- name: "Dependency Review"
|
||||
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
||||
|
||||
40
.github/workflows/licenses-update.yml
vendored
40
.github/workflows/licenses-update.yml
vendored
@@ -7,7 +7,8 @@ on:
|
||||
paths:
|
||||
- "build.gradle"
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
generate-license-report:
|
||||
@@ -17,10 +18,17 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Generate GitHub App Token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
@@ -41,8 +49,8 @@ jobs:
|
||||
|
||||
- name: Set up git config
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --global user.name "stirlingbot[bot]"
|
||||
git config --global user.email "1113334+stirlingbot[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Run git add
|
||||
run: |
|
||||
@@ -54,32 +62,22 @@ jobs:
|
||||
if: env.CHANGES_DETECTED == 'true'
|
||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
commit-message: "Update 3rd Party Licenses"
|
||||
committer: GitHub Action <action@github.com>
|
||||
author: GitHub Action <action@github.com>
|
||||
committer: "stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>"
|
||||
author: "stirlingbot[bot] <1113334+stirlingbot[bot]@users.noreply.github.com>"
|
||||
signoff: true
|
||||
branch: update-3rd-party-licenses
|
||||
title: "Update 3rd Party Licenses"
|
||||
body: |
|
||||
Auto-generated by [create-pull-request][1]
|
||||
|
||||
[1]: https://github.com/peter-evans/create-pull-request
|
||||
Auto-generated by StirlingBot
|
||||
labels: licenses,github-actions
|
||||
draft: false
|
||||
delete-branch: true
|
||||
sign-commits: true
|
||||
|
||||
- name: Auto approve
|
||||
- name: Enable Pull Request Automerge
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
run: gh pr review --approve "${{ steps.cpr.outputs.pull-request-number }}"
|
||||
run: gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Enable auto-merge
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash # Choose the merge method: merge, squash, or rebase
|
||||
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
5
.github/workflows/manage-label.yml
vendored
5
.github/workflows/manage-label.yml
vendored
@@ -4,7 +4,8 @@ on:
|
||||
schedule:
|
||||
- cron: "30 20 * * *"
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
@@ -14,7 +15,7 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
281
.github/workflows/multiOSReleases.yml
vendored
281
.github/workflows/multiOSReleases.yml
vendored
@@ -5,30 +5,53 @@ on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-installers:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
platform: win
|
||||
ext: exe
|
||||
#- os: macos-latest
|
||||
# platform: mac
|
||||
# ext: dmg
|
||||
#- os: ubuntu-latest
|
||||
# platform: linux
|
||||
# ext: deb
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
read_versions:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.versionNumber.outputs.versionNumber }}
|
||||
versionMac: ${{ steps.versionNumberMac.outputs.versionNumberMac }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
# Get version number
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get version number mac
|
||||
id: versionNumberMac
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
CURRENT_YEAR=$(date +'%Y')
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
|
||||
MAC_VERSION="$CURRENT_YEAR.${VERSION_PARTS[1]:-0}.${VERSION_PARTS[2]:-0}"
|
||||
echo "versionNumberMac=$MAC_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
build-portable:
|
||||
needs: read_versions
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -42,7 +65,95 @@ jobs:
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
with:
|
||||
gradle-version: 8.7
|
||||
gradle-version: 8.12
|
||||
|
||||
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||
run: ./gradlew clean createExe
|
||||
env:
|
||||
DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }}
|
||||
STIRLING_PDF_DESKTOP_UI: false
|
||||
|
||||
- name: Rename binaries
|
||||
run: |
|
||||
mkdir ./binaries
|
||||
mv ./build/launch4j/Stirling-PDF.exe ./binaries/win-Stirling-PDF-portable-Server${{ matrix.file_suffix }}.exe
|
||||
mv ./build/libs/Stirling-PDF-${{ needs.read_versions.outputs.version }}.jar ./binaries/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: stirling${{ matrix.file_suffix }}-binaries
|
||||
path: |
|
||||
./binaries/*
|
||||
|
||||
sign_verify-portable:
|
||||
needs: [build-portable, read_versions]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "with-login-"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: stirling-${{ matrix.file_suffix }}binaries
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: stirling-${{ matrix.file_suffix }}signed
|
||||
path: |
|
||||
./*
|
||||
!cosign.*
|
||||
|
||||
build-installers:
|
||||
needs: read_versions
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
platform: win-
|
||||
# - os: macos-latest
|
||||
# platform: mac-
|
||||
# - os: ubuntu-latest
|
||||
# platform: linux-
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
with:
|
||||
java-version: "21"
|
||||
distribution: "temurin"
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
with:
|
||||
gradle-version: 8.12
|
||||
|
||||
# Install Windows dependencies
|
||||
- name: Install WiX Toolset
|
||||
@@ -51,24 +162,6 @@ jobs:
|
||||
curl -L -o wix.exe https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe
|
||||
.\wix.exe /install /quiet
|
||||
|
||||
# Install Linux dependencies
|
||||
- name: Install Linux Dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y fakeroot rpm
|
||||
|
||||
# Get version number
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Get version number mac
|
||||
id: versionNumberMac
|
||||
run: echo "versionNumberMac=$(./gradlew printMacVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
# Build installer
|
||||
- name: Build Installer
|
||||
run: ./gradlew build jpackage -x test --info
|
||||
@@ -81,24 +174,114 @@ jobs:
|
||||
id: prepare
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir ./binaries
|
||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.exe" "Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}"
|
||||
mv "./build/jpackage/Stirling-PDF-${{ needs.read_versions.outputs.version }}.exe" "./binaries/Stirling-PDF-win-installer.exe"
|
||||
elif [ "${{ matrix.os }}" = "macos-latest" ]; then
|
||||
mv "build/jpackage/Stirling-PDF-${{ steps.versionNumberMac.outputs.versionNumberMac }}.dmg" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
||||
mv "./build/jpackage/Stirling-PDF-${{ needs.read_versions.outputs.versionMac }}.dmg" "./binaries/Stirling-PDF-mac-installer.dmg"
|
||||
else
|
||||
mv "build/jpackage/stirling-pdf_${{ steps.versionNumber.outputs.versionNumber }}-1_amd64.deb" "Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}-${{ matrix.platform }}.${{ matrix.ext }}"
|
||||
mv "./build/jpackage/stirling-pdf_${{ needs.read_versions.outputs.version }}-1_amd64.deb" "./binaries/Stirling-PDF-linux-installer.deb"
|
||||
fi
|
||||
|
||||
# Upload installer as artifact for testing
|
||||
- name: Upload Installer Artifact
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R ./binaries
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||
path: Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: ${{ matrix.platform }}binaries
|
||||
path: |
|
||||
./binaries/*
|
||||
|
||||
- name: Upload binaries to release
|
||||
sign_verify:
|
||||
needs: [read_versions, build-installers]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
platform: win-
|
||||
# - os: macos-latest
|
||||
# platform: mac-
|
||||
# - os: ubuntu-latest
|
||||
# platform: linux-
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: ${{ matrix.platform }}binaries
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Install Cosign
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
|
||||
- name: Generate key pair
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: cosign generate-key-pair
|
||||
|
||||
- name: Sign and generate attestations
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: |
|
||||
cosign sign-blob \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-signature ./Stirling-PDF-win-installer.exe.sig \
|
||||
./Stirling-PDF-win-installer.exe
|
||||
|
||||
cosign attest-blob \
|
||||
--predicate - \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-attestation ./Stirling-PDF-win-installer.exe.intoto.jsonl \
|
||||
./Stirling-PDF-win-installer.exe
|
||||
|
||||
cosign verify-blob \
|
||||
--key ./cosign.pub \
|
||||
--signature ./Stirling-PDF-win-installer.exe.sig \
|
||||
./Stirling-PDF-win-installer.exe
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: ${{ matrix.platform }}signed
|
||||
path: |
|
||||
./Stirling-PDF-${{ matrix.platform }}installer.*
|
||||
!cosign.*
|
||||
|
||||
create-release:
|
||||
needs: [read_versions, sign_verify, sign_verify-portable]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download signed artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
- name: Upload binaries, attestations and signatures to Release and create GitHub Release
|
||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||
with:
|
||||
files: ./Stirling-PDF-${{ matrix.platform }}-installer.${{ matrix.ext }}
|
||||
tag_name: v${{ needs.read_versions.outputs.version }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
./*signed/*
|
||||
|
||||
19
.github/workflows/pre_commit.yml
vendored
19
.github/workflows/pre_commit.yml
vendored
@@ -1,29 +1,36 @@
|
||||
name: Pre-commit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update:
|
||||
pre-commit:
|
||||
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: 3.12
|
||||
- name: Run Pre-Commit Hooks
|
||||
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
|
||||
run: |
|
||||
pip install --require-hashes -r ./.github/scripts/requirements_pre_commit.txt
|
||||
- run: pre-commit run --all-files -c .pre-commit-config.yaml
|
||||
continue-on-error: true
|
||||
- name: Set up git config
|
||||
run: |
|
||||
|
||||
19
.github/workflows/push-docker.yml
vendored
19
.github/workflows/push-docker.yml
vendored
@@ -9,17 +9,16 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -33,7 +32,7 @@ jobs:
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
with:
|
||||
gradle-version: 8.7
|
||||
gradle-version: 8.12
|
||||
|
||||
- name: Run Gradle Command
|
||||
run: ./gradlew clean build
|
||||
@@ -42,9 +41,9 @@ jobs:
|
||||
|
||||
- name: Install cosign
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: sigstore/cosign-installer@v3.7.0
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
with:
|
||||
cosign-release: 'v2.4.1'
|
||||
cosign-release: "v2.4.1"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
@@ -68,7 +67,7 @@ jobs:
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
||||
uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3.3.0
|
||||
|
||||
- name: Convert repository owner to lowercase
|
||||
id: repoowner
|
||||
@@ -90,7 +89,7 @@ jobs:
|
||||
|
||||
- name: Build and push main Dockerfile
|
||||
id: build-push-regular
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
context: .
|
||||
@@ -135,7 +134,7 @@ jobs:
|
||||
|
||||
- name: Build and push Dockerfile-ultra-lite
|
||||
id: build-push-lite
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
|
||||
if: github.ref != 'refs/heads/main'
|
||||
with:
|
||||
context: .
|
||||
@@ -166,7 +165,7 @@ jobs:
|
||||
|
||||
- name: Build and push main Dockerfile fat
|
||||
id: build-push-fat
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0
|
||||
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
|
||||
if: github.ref != 'refs/heads/main'
|
||||
with:
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
|
||||
168
.github/workflows/releaseArtifacts.yml
vendored
168
.github/workflows/releaseArtifacts.yml
vendored
@@ -5,14 +5,12 @@ on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
push:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
@@ -21,9 +19,11 @@ jobs:
|
||||
file_suffix: "-with-login"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
outputs:
|
||||
version: ${{ steps.versionNumber.outputs.versionNumber }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- uses: gradle/actions/setup-gradle@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
|
||||
with:
|
||||
gradle-version: 8.7
|
||||
gradle-version: 8.12
|
||||
|
||||
- name: Generate jar (With Security=${{ matrix.enable_security }})
|
||||
run: ./gradlew clean createExe
|
||||
@@ -47,38 +47,134 @@ jobs:
|
||||
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Rename binarie
|
||||
run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
- name: Rename binaries
|
||||
run: |
|
||||
mv ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
mv ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
- name: Upload Assets binarie
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
- name: Debug build artifacts
|
||||
run: |
|
||||
echo "Current Directory: $(pwd)"
|
||||
ls -R ./build/libs
|
||||
ls -R ./build/launch4j
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
path: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
name: Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
overwrite: true
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
name: binaries${{ matrix.file_suffix }}
|
||||
path: |
|
||||
./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.*
|
||||
./build/libs/Stirling-PDF${{ matrix.file_suffix }}.*
|
||||
|
||||
- name: Upload binaries to release
|
||||
sign_verify:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: binaries${{ matrix.file_suffix }}
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
|
||||
- name: Generate key pair
|
||||
run: cosign generate-key-pair
|
||||
|
||||
- name: Sign and generate attestations
|
||||
run: |
|
||||
cosign sign-blob \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-signature ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.sig \
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
cosign attest-blob \
|
||||
--predicate - \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-attestation ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.intoto.jsonl \
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
cosign verify-blob \
|
||||
--key ./cosign.pub \
|
||||
--signature ./libs/Stirling-PDF${{ matrix.file_suffix }}.jar.sig \
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
cosign sign-blob \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-signature ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.sig \
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
cosign attest-blob \
|
||||
--predicate - \
|
||||
--key ./cosign.key \
|
||||
--yes \
|
||||
--output-attestation ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.intoto.jsonl \
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
cosign verify-blob \
|
||||
--key ./cosign.pub \
|
||||
--signature ./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe.sig \
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: signed${{ matrix.file_suffix }}
|
||||
path: |
|
||||
./libs/Stirling-PDF${{ matrix.file_suffix }}.*
|
||||
./launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.*
|
||||
|
||||
release:
|
||||
needs: [build, sign_verify]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
matrix:
|
||||
enable_security: [true, false]
|
||||
include:
|
||||
- enable_security: true
|
||||
file_suffix: "-with-login"
|
||||
- enable_security: false
|
||||
file_suffix: ""
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Download signed artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: signed${{ matrix.file_suffix }}
|
||||
|
||||
- name: Upload binaries, attestations and signatures to Release and create GitHub Release
|
||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||
with:
|
||||
files: ./build/launch4j/Stirling-PDF-Server${{ matrix.file_suffix }}.exe
|
||||
|
||||
- name: Rename jar binaries
|
||||
run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
|
||||
- name: Upload Assets jar binaries
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
with:
|
||||
path: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
name: Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
overwrite: true
|
||||
retention-days: 1
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload jar binaries to release
|
||||
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
|
||||
with:
|
||||
files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar
|
||||
tag_name: v${{ needs.build.outputs.version }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
./libs/Stirling-PDF*
|
||||
./launch4j/Stirling-PDF-Server*
|
||||
|
||||
8
.github/workflows/scorecards.yml
vendored
8
.github/workflows/scorecards.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
# To guarantee Maintained check is occasionally updated. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||
schedule:
|
||||
- cron: '20 7 * * 2'
|
||||
- cron: "20 7 * * 2"
|
||||
push:
|
||||
branches: ["main"]
|
||||
permissions: read-all
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -74,6 +74,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
|
||||
uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
7
.github/workflows/stale.yml
vendored
7
.github/workflows/stale.yml
vendored
@@ -5,7 +5,8 @@ on:
|
||||
- cron: "30 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
@@ -15,7 +16,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -36,4 +37,4 @@ jobs:
|
||||
only-issue-labels: "more-info-needed"
|
||||
days-before-pr-stale: -1 # Prevents PRs from being marked as stale
|
||||
days-before-pr-close: -1 # Prevents PRs from being closed
|
||||
start-date: '2024-07-06T00:00:00Z' # ISO 8601 Format
|
||||
start-date: "2024-07-06T00:00:00Z" # ISO 8601 Format
|
||||
|
||||
5
.github/workflows/swagger.yml
vendored
5
.github/workflows/swagger.yml
vendored
@@ -6,14 +6,15 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
9
.github/workflows/sync_files.yml
vendored
9
.github/workflows/sync_files.yml
vendored
@@ -9,7 +9,8 @@ on:
|
||||
- "src/main/resources/messages_*.properties"
|
||||
- "scripts/ignore_translation.toml"
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync-readme:
|
||||
@@ -19,7 +20,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -27,9 +28,9 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
python-version: "3.12"
|
||||
- name: Install dependencies
|
||||
run: pip install tomlkit
|
||||
run: pip install --require-hashes -r ./.github/scripts/requirements_sync_readme.txt
|
||||
- name: Sync README
|
||||
run: python scripts/counter_translation.py
|
||||
- name: Set up git config
|
||||
|
||||
154
.github/workflows/testdriver.yml
vendored
Normal file
154
.github/workflows/testdriver.yml
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
name: UI test with TestDriverAI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "UITest", "testdriver"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean build
|
||||
env:
|
||||
DOCKER_ENABLE_SECURITY: false
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||
|
||||
- name: Get version number
|
||||
id: versionNumber
|
||||
run: |
|
||||
VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}')
|
||||
echo "versionNumber=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_API }}
|
||||
|
||||
- name: Build and push test image
|
||||
uses: docker/build-push-action@b32b51a8eda65d6793cd0494a773d4f6bcef32dc # v6.11.0
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:test-${{ github.sha }}
|
||||
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh/
|
||||
echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
|
||||
sudo chmod 600 ../private.key
|
||||
|
||||
- name: Deploy to VPS
|
||||
run: |
|
||||
cat > docker-compose.yml << EOF
|
||||
version: '3.3'
|
||||
services:
|
||||
stirling-pdf:
|
||||
container_name: stirling-pdf-test-${{ github.sha }}
|
||||
image: ${{ secrets.DOCKER_HUB_USERNAME }}/test:test-${{ github.sha }}
|
||||
ports:
|
||||
- "1337:8080"
|
||||
volumes:
|
||||
- /stirling/test-${{ github.sha }}/data:/usr/share/tessdata:rw
|
||||
- /stirling/test-${{ github.sha }}/config:/configs:rw
|
||||
- /stirling/test-${{ github.sha }}/logs:/logs:rw
|
||||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "false"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
SYSTEM_DEFAULTLOCALE: en-GB
|
||||
UI_APPNAME: "Stirling-PDF Test"
|
||||
UI_HOMEDESCRIPTION: "Test Deployment"
|
||||
UI_APPNAMENAVBAR: "Test"
|
||||
SYSTEM_MAXFILESIZE: "100"
|
||||
METRICS_ENABLED: "true"
|
||||
SYSTEM_GOOGLEVISIBILITY: "false"
|
||||
SYSTEM_ENABLEANALYTICS: "false"
|
||||
restart: on-failure:5
|
||||
EOF
|
||||
|
||||
scp -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null docker-compose.yml ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }}:/tmp/docker-compose.yml
|
||||
|
||||
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << EOF
|
||||
mkdir -p /stirling/test-${{ github.sha }}/{data,config,logs}
|
||||
mv /tmp/docker-compose.yml /stirling/test-${{ github.sha }}/docker-compose.yml
|
||||
cd /stirling/test-${{ github.sha }}
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
EOF
|
||||
|
||||
test:
|
||||
needs: deploy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Run TestDriver.ai
|
||||
uses: testdriverai/action@47e87c5d50beeeb3da624b2d9b5c1391269d6d22 #1.0.0
|
||||
with:
|
||||
key: ${{secrets.TESTDRIVER_API_KEY}}
|
||||
prerun: |
|
||||
npm install
|
||||
npm run build
|
||||
npm install dashcam-chrome --save
|
||||
Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--load-extension=$(pwd)/node_modules/dashcam-chrome/build", "http://${{ secrets.VPS_HOST }}:1337"
|
||||
Start-Sleep -Seconds 20
|
||||
prompt: |
|
||||
1. /run testdriver/test.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
FORCE_COLOR: "3"
|
||||
|
||||
cleanup:
|
||||
needs: [deploy, test]
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh/
|
||||
echo "${{ secrets.VPS_SSH_KEY }}" > ../private.key
|
||||
sudo chmod 600 ../private.key
|
||||
|
||||
- name: Cleanup deployment
|
||||
run: |
|
||||
ssh -i ../private.key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ secrets.VPS_USERNAME }}@${{ secrets.VPS_HOST }} << EOF
|
||||
cd /stirling/test-${{ github.sha }}
|
||||
docker-compose down
|
||||
cd /stirling
|
||||
rm -rf test-${{ github.sha }}
|
||||
EOF
|
||||
7
.github/workflows/update-translations.yml
vendored
7
.github/workflows/update-translations.yml
vendored
@@ -6,7 +6,8 @@ on:
|
||||
paths:
|
||||
- "src/main/resources/messages_en_GB.properties"
|
||||
|
||||
permissions: read-all
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-translations-main:
|
||||
@@ -17,7 +18,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
|
||||
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
@@ -27,7 +28,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Run Python script to check files
|
||||
id: run-check
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -14,12 +14,16 @@ local.properties
|
||||
.classpath
|
||||
.project
|
||||
version.properties
|
||||
|
||||
#### Stirling-PDF Files ###
|
||||
pipeline/watchedFolders/
|
||||
pipeline/finishedFolders/
|
||||
#### Stirling-PDF Files ###
|
||||
customFiles/
|
||||
configs/
|
||||
watchedFolders/
|
||||
!cucumber/
|
||||
!cucumber/exampleFiles/
|
||||
!cucumber/exampleFiles/example_html.zip
|
||||
|
||||
# Gradle
|
||||
.gradle
|
||||
@@ -111,6 +115,7 @@ watchedFolders/
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
*.db
|
||||
|
||||
@@ -20,7 +20,7 @@ repos:
|
||||
- --skip="./.*,*.csv,*.json,*.ambr"
|
||||
- --quiet-level=2
|
||||
files: \.(properties|html|css|js|py|md)$
|
||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile)
|
||||
exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile|.*/pdfjs.*|.*/thirdParty.*|bootstrap.*|.*\.min\..*|.*diff\.js)
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.22.0
|
||||
hooks:
|
||||
|
||||
@@ -575,3 +575,42 @@ In your Thymeleaf templates, use the `#{key}` syntax to reference the new transl
|
||||
```
|
||||
|
||||
Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization.
|
||||
|
||||
|
||||
## Managing Dependencies
|
||||
|
||||
When adding new dependencies or updating existing ones in Stirling-PDF, follow these steps to ensure proper verification and security:
|
||||
|
||||
1. Update the dependency in `build.gradle`:
|
||||
```groovy
|
||||
dependencies {
|
||||
// Add or update your dependency
|
||||
implementation "com.example:new-library:1.2.3"
|
||||
}
|
||||
```
|
||||
|
||||
2. Generate new verification metadata and keys:
|
||||
```bash
|
||||
# Generate verification metadata with signatures and checksums
|
||||
./gradlew clean dependencies buildEnvironment --write-verification-metadata sha256,pgp
|
||||
|
||||
# Export the .keys file
|
||||
./gradlew --export-keys
|
||||
```
|
||||
|
||||
3. Files to commit:
|
||||
- `build.gradle` - Your dependency changes
|
||||
- `gradle/verification-metadata.xml` - Contains verification rules and checksums
|
||||
- `gradle/verification-keyring.keys` - Contains PGP keys in text format
|
||||
|
||||
4. Verify the build works with the new verification:
|
||||
```bash
|
||||
./gradlew build
|
||||
```
|
||||
|
||||
5. Before committing, check:
|
||||
- Verify any new BOM files are properly handled in verification metadata
|
||||
- Review the changes in `verification-metadata.xml` to ensure they match your dependency updates
|
||||
|
||||
This ensures dependencies are properly verified and secure while maintaining transparency in the repository.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Main stage
|
||||
FROM alpine:3.20.3
|
||||
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN DOCKER_ENABLE_SECURITY=true \
|
||||
./gradlew clean build
|
||||
|
||||
# Main stage
|
||||
FROM alpine:3.20.3
|
||||
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||
|
||||
# Copy necessary files
|
||||
COPY scripts /scripts
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# use alpine
|
||||
FROM alpine:3.21.0@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45
|
||||
FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
|
||||
|
||||
ARG VERSION_TAG
|
||||
|
||||
|
||||
76
README.md
76
README.md
@@ -4,9 +4,9 @@
|
||||
[](https://hub.docker.com/r/frooodle/s-pdf)
|
||||
[](https://discord.gg/HYmhKj45pU)
|
||||
[](https://github.com/Stirling-Tools/Stirling-PDF/)
|
||||
[](https://scorecard.dev/viewer/?uri=github.com/Stirling-Tools/Stirling-PDF)
|
||||
[](https://github.com/Stirling-Tools/stirling-pdf)
|
||||
|
||||
|
||||
<a href="https://www.producthunt.com/posts/stirling-pdf?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-stirling-pdf" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=641239&theme=light" alt="Stirling PDF - Open source locally hosted web PDF editor | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af)
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point.
|
||||
|
||||
Homepage: [https://stirlingpdf.com](https://stirlingpdf.com)
|
||||
|
||||
All information available at [https://docs.stirlingpdf.com/](https://docs.stirlingpdf.com/)
|
||||
All documentation available at [https://docs.stirlingpdf.com/](https://docs.stirlingpdf.com/)
|
||||
|
||||

|
||||
|
||||
@@ -116,44 +117,45 @@ Stirling-PDF currently supports 38 languages!
|
||||
|
||||
| Language | Progress |
|
||||
| -------------------------------------------- | -------------------------------------- |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| Arabic (العربية) (ar_AR) |  |
|
||||
| Azerbaijani (Azərbaycan Dili) (az_AZ) |  |
|
||||
| Basque (Euskara) (eu_ES) |  |
|
||||
| Bulgarian (Български) (bg_BG) |  |
|
||||
| Catalan (Català) (ca_CA) |  |
|
||||
| Croatian (Hrvatski) (hr_HR) |  |
|
||||
| Czech (Česky) (cs_CZ) |  |
|
||||
| Danish (Dansk) (da_DK) |  |
|
||||
| Dutch (Nederlands) (nl_NL) |  |
|
||||
| English (English) (en_GB) |  |
|
||||
| English (US) (en_US) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Persian (فارسی) (fa_IR) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| French (Français) (fr_FR) |  |
|
||||
| German (Deutsch) (de_DE) |  |
|
||||
| Greek (Ελληνικά) (el_GR) |  |
|
||||
| Hindi (हिंदी) (hi_IN) |  |
|
||||
| Hungarian (Magyar) (hu_HU) |  |
|
||||
| Indonesian (Bahasa Indonesia) (id_ID) |  |
|
||||
| Irish (Gaeilge) (ga_IE) |  |
|
||||
| Italian (Italiano) (it_IT) |  |
|
||||
| Japanese (日本語) (ja_JP) |  |
|
||||
| Korean (한국어) (ko_KR) |  |
|
||||
| Norwegian (Norsk) (no_NB) |  |
|
||||
| Persian (فارسی) (fa_IR) |  |
|
||||
| Polish (Polski) (pl_PL) |  |
|
||||
| Portuguese (Português) (pt_PT) |  |
|
||||
| Portuguese Brazilian (Português) (pt_BR) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Romanian (Română) (ro_RO) |  |
|
||||
| Russian (Русский) (ru_RU) |  |
|
||||
| Serbian Latin alphabet (Srpski) (sr_LATN_RS) |  |
|
||||
| Simplified Chinese (简体中文) (zh_CN) |  |
|
||||
| Slovakian (Slovensky) (sk_SK) |  |
|
||||
| Spanish (Español) (es_ES) |  |
|
||||
| Swedish (Svenska) (sv_SE) |  |
|
||||
| Thai (ไทย) (th_TH) |  |
|
||||
| Tibetan (བོད་ཡིག་) (zh_BO) |  |
|
||||
| Traditional Chinese (繁體中文) (zh_TW) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
| Turkish (Türkçe) (tr_TR) |  |
|
||||
| Ukrainian (Українська) (uk_UA) |  |
|
||||
| Vietnamese (Tiếng Việt) (vi_VN) |  |
|
||||
|
||||
|
||||
## Stirling PDF Enterprise
|
||||
@@ -169,4 +171,4 @@ Join our community:
|
||||
- [Translation Guide (How to add custom languages)](HowToAddNewLanguage.md)
|
||||
- [Issue Tracker](https://github.com/Stirling-Tools/Stirling-PDF/issues)
|
||||
- [Discord Community](https://discord.gg/HYmhKj45pU)
|
||||
- [Developer Guide](DeveloperGuide.md)
|
||||
- [Developer Guide](DeveloperGuide.md)
|
||||
|
||||
45
USERS.md
Normal file
45
USERS.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Who is using Stirling-PDF?
|
||||
|
||||
Understanding the diverse applications of Stirling-PDF can be an invaluable resource for collaboration and learning. This page provides a directory of users and use cases. If you are using Stirling-PDF, consider sharing your experiences to help others and foster a community of best practices.
|
||||
|
||||
## Adding Yourself as a User
|
||||
|
||||
If you're using Stirling-PDF or have integrated it into your platform or workflow, please consider contributing to this list by describing your use case. You can do this by opening a pull request to this file and adding your details in the format below:
|
||||
|
||||
- **N**: Name of the organization or individual.
|
||||
- **D**: A brief description of your usage.
|
||||
- **U**: Specific features or capabilities utilized.
|
||||
- **L**: Optional link for further information (e.g., website, blog post).
|
||||
- **Q**: Contact information for sharing insights (optional).
|
||||
|
||||
Example entry:
|
||||
|
||||
```
|
||||
* N: Example Corp
|
||||
D: Using Stirling-PDF for automated document processing in our SaaS platform focusing on compression.
|
||||
U: OCR, merging PDFs, metadata editing, encryption, compression.
|
||||
L: https://example.com/stirling-pdf
|
||||
Q: @example-user on Discord/email
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Requirements for Listing
|
||||
|
||||
- You must represent the entity you're listing and ensure the details are accurate.
|
||||
- Trial deployments are welcome if they represent a realistic evaluation of Stirling-PDF in action.
|
||||
- Community contributions, including home-lab setups or non-commercial uses, are encouraged.
|
||||
|
||||
---
|
||||
|
||||
## Users (Alphabetically)
|
||||
|
||||
* N:
|
||||
D:
|
||||
U:
|
||||
L:
|
||||
|
||||
* N:
|
||||
D:
|
||||
U:
|
||||
L:
|
||||
54
build.gradle
54
build.gradle
@@ -5,14 +5,12 @@ plugins {
|
||||
id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
|
||||
id "io.swagger.swaggerhub" version "1.3.2"
|
||||
id "edu.sc.seis.launch4j" version "3.0.6"
|
||||
id "com.diffplug.spotless" version "6.25.0"
|
||||
id "com.diffplug.spotless" version "7.0.1"
|
||||
id "com.github.jk1.dependency-license-report" version "2.9"
|
||||
//id "nebula.lint" version "19.0.3"
|
||||
id("org.panteleyev.jpackageplugin") version "1.6.0"
|
||||
}
|
||||
|
||||
|
||||
|
||||
import com.github.jk1.license.render.*
|
||||
|
||||
ext {
|
||||
@@ -27,7 +25,7 @@ ext {
|
||||
}
|
||||
|
||||
group = "stirling.software"
|
||||
version = "0.36.6"
|
||||
version = "0.37.0"
|
||||
|
||||
|
||||
java {
|
||||
@@ -52,13 +50,15 @@ sourceSets {
|
||||
java {
|
||||
if (System.getenv("DOCKER_ENABLE_SECURITY") == "false") {
|
||||
exclude "stirling/software/SPDF/config/security/**"
|
||||
exclude "stirling/software/SPDF/controller/api/UserController.java"
|
||||
exclude "stirling/software/SPDF/controller/api/DatabaseController.java"
|
||||
exclude "stirling/software/SPDF/controller/api/UserController.java"
|
||||
exclude "stirling/software/SPDF/controller/api/H2SQLCondition.java"
|
||||
exclude "stirling/software/SPDF/controller/web/AccountWebController.java"
|
||||
exclude "stirling/software/SPDF/controller/web/DatabaseWebController.java"
|
||||
exclude "stirling/software/SPDF/model/ApiKeyAuthenticationToken.java"
|
||||
exclude "stirling/software/SPDF/model/AttemptCounter.java"
|
||||
exclude "stirling/software/SPDF/model/Authority.java"
|
||||
exclude "stirling/software/SPDF/model/BackupNotFoundException.java"
|
||||
exclude "stirling/software/SPDF/model/PersistentLogin.java"
|
||||
exclude "stirling/software/SPDF/model/SessionEntity.java"
|
||||
exclude "stirling/software/SPDF/model/User.java"
|
||||
@@ -69,7 +69,29 @@ sourceSets {
|
||||
exclude "stirling/software/SPDF/UI/impl/**"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
java {
|
||||
if (System.getenv("DOCKER_ENABLE_SECURITY") == "false") {
|
||||
exclude "stirling/software/SPDF/config/security/**"
|
||||
exclude "stirling/software/SPDF/controller/api/UserControllerTest.java"
|
||||
exclude "stirling/software/SPDF/controller/api/DatabaseControllerTest.java"
|
||||
exclude "stirling/software/SPDF/controller/web/AccountWebControllerTest.java"
|
||||
exclude "stirling/software/SPDF/controller/web/DatabaseWebControllerTest.java"
|
||||
exclude "stirling/software/SPDF/model/ApiKeyAuthenticationTokenTest.java"
|
||||
exclude "stirling/software/SPDF/model/AttemptCounterTest.java"
|
||||
exclude "stirling/software/SPDF/model/AuthorityTest.java"
|
||||
exclude "stirling/software/SPDF/model/PersistentLoginTest.java"
|
||||
exclude "stirling/software/SPDF/model/SessionEntityTest.java"
|
||||
exclude "stirling/software/SPDF/model/UserTest.java"
|
||||
exclude "stirling/software/SPDF/repository/**"
|
||||
}
|
||||
|
||||
if (System.getenv("STIRLING_PDF_DESKTOP_UI") == "false") {
|
||||
exclude "stirling/software/SPDF/UI/impl/**"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,11 +145,13 @@ jpackage {
|
||||
windows {
|
||||
launcherAsService = false
|
||||
appVersion = project.version
|
||||
winConsole = false
|
||||
winDirChooser = true
|
||||
winMenu = true
|
||||
winShortcut = true
|
||||
winPerUserInstall = true
|
||||
|
||||
winConsole = false
|
||||
winMenu = true // Creates start menu entry
|
||||
winShortcut = true // Creates desktop shortcut
|
||||
winShortcutPrompt = true // Lets user choose whether to create shortcuts
|
||||
winDirChooser = true // Allows users to choose installation directory
|
||||
winPerUserInstall = false
|
||||
winMenuGroup = "Stirling Software"
|
||||
winUpgradeUuid = "2a43ed0c-b8c2-40cf-89e1-751129b87641" // Unique identifier for updates
|
||||
winHelpUrl = "https://github.com/Stirling-Tools/Stirling-PDF"
|
||||
@@ -257,7 +281,7 @@ spotless {
|
||||
// rules=['unused-dependency']
|
||||
// }
|
||||
tasks.wrapper {
|
||||
gradleVersion = "8.7"
|
||||
gradleVersion = "8.12"
|
||||
}
|
||||
//tasks.withType(JavaCompile) {
|
||||
// options.compilerArgs << "-Xlint:deprecation"
|
||||
@@ -297,10 +321,12 @@ dependencies {
|
||||
implementation "org.springframework.boot:spring-boot-starter-oauth2-client:$springBootVersion"
|
||||
|
||||
implementation "org.springframework.session:spring-session-core:$springBootVersion"
|
||||
implementation "org.springframework:spring-jdbc:6.2.1"
|
||||
|
||||
implementation 'com.unboundid.product.scim2:scim2-sdk-client:2.3.5'
|
||||
// Don't upgrade h2database
|
||||
runtimeOnly "com.h2database:h2:2.3.232"
|
||||
runtimeOnly "org.postgresql:postgresql:42.7.4"
|
||||
constraints {
|
||||
implementation "org.opensaml:opensaml-core:$openSamlVersion"
|
||||
implementation "org.opensaml:opensaml-saml-api:$openSamlVersion"
|
||||
@@ -344,7 +370,7 @@ dependencies {
|
||||
//general PDF
|
||||
|
||||
// https://mvnrepository.com/artifact/com.opencsv/opencsv
|
||||
implementation ("com.opencsv:opencsv:5.9") {
|
||||
implementation ("com.opencsv:opencsv:5.10") {
|
||||
exclude group: "commons-logging", module: "commons-logging"
|
||||
}
|
||||
|
||||
@@ -368,7 +394,7 @@ dependencies {
|
||||
implementation "org.bouncycastle:bcprov-jdk18on:$bouncycastleVersion"
|
||||
implementation "org.bouncycastle:bcpkix-jdk18on:$bouncycastleVersion"
|
||||
implementation "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||
implementation "io.micrometer:micrometer-core:1.14.2"
|
||||
implementation "io.micrometer:micrometer-core:1.14.3"
|
||||
implementation group: "com.google.zxing", name: "core", version: "3.5.3"
|
||||
// https://mvnrepository.com/artifact/org.commonmark/commonmark
|
||||
implementation "org.commonmark:commonmark:0.24.0"
|
||||
@@ -377,6 +403,8 @@ dependencies {
|
||||
implementation "com.bucket4j:bucket4j_jdk17-core:8.14.0"
|
||||
implementation "com.fathzer:javaluator:3.0.5"
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.18.3'
|
||||
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools:$springBootVersion")
|
||||
compileOnly "org.projectlombok:lombok:$lombokVersion"
|
||||
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
|
||||
|
||||
@@ -8,4 +8,3 @@
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -18,4 +18,4 @@ def after_scenario(context, scenario):
|
||||
# Remove any temporary files
|
||||
for temp_file in os.listdir('.'):
|
||||
if temp_file.startswith('genericNonCustomisableName') or temp_file.startswith('temp_image_'):
|
||||
os.remove(temp_file)
|
||||
os.remove(temp_file)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import os
|
||||
import requests
|
||||
from behave import given, when, then
|
||||
from PyPDF2 import PdfWriter, PdfReader
|
||||
from pypdf import PdfWriter, PdfReader
|
||||
from pypdf.errors import PdfReadError
|
||||
import io
|
||||
import random
|
||||
import string
|
||||
@@ -42,7 +43,7 @@ def step_use_example_file(context, filePath, fileInput):
|
||||
context.file_name = filePath.split('/')[-1]
|
||||
if not hasattr(context, 'files'):
|
||||
context.files = {}
|
||||
|
||||
|
||||
# Ensure the file exists before opening
|
||||
try:
|
||||
example_file = open(filePath, 'rb')
|
||||
@@ -165,17 +166,17 @@ def step_pdf_contains_pages_with_random_text(context, page_count):
|
||||
buffer = io.BytesIO()
|
||||
c = canvas.Canvas(buffer, pagesize=letter)
|
||||
width, height = letter
|
||||
|
||||
|
||||
for _ in range(page_count):
|
||||
text = ''.join(random.choices(string.ascii_letters + string.digits, k=100))
|
||||
c.drawString(100, height - 100, text)
|
||||
c.showPage()
|
||||
|
||||
|
||||
c.save()
|
||||
|
||||
|
||||
with open(context.file_name, 'wb') as f:
|
||||
f.write(buffer.getvalue())
|
||||
|
||||
|
||||
context.files[context.param_name].close()
|
||||
context.files[context.param_name] = open(context.file_name, 'rb')
|
||||
|
||||
@@ -184,16 +185,16 @@ def step_pdf_pages_contain_text(context, text):
|
||||
buffer = io.BytesIO()
|
||||
c = canvas.Canvas(buffer, pagesize=letter)
|
||||
width, height = letter
|
||||
|
||||
|
||||
for _ in range(len(PdfReader(context.file_name).pages)):
|
||||
c.drawString(100, height - 100, text)
|
||||
c.showPage()
|
||||
|
||||
|
||||
c.save()
|
||||
|
||||
|
||||
with open(context.file_name, 'wb') as f:
|
||||
f.write(buffer.getvalue())
|
||||
|
||||
|
||||
context.files[context.param_name].close()
|
||||
context.files[context.param_name] = open(context.file_name, 'rb')
|
||||
|
||||
@@ -345,7 +346,7 @@ def step_check_response_pdf_page_count(context, page_count):
|
||||
def step_check_response_zip_file_count(context, file_count):
|
||||
response_file = io.BytesIO(context.response.content)
|
||||
with zipfile.ZipFile(io.BytesIO(response_file.getvalue())) as zip_file:
|
||||
actual_file_count = len(zip_file.namelist())
|
||||
actual_file_count = len(zip_file.namelist())
|
||||
assert actual_file_count == file_count, f"Expected {file_count} files but got {actual_file_count} files"
|
||||
|
||||
@then('the response ZIP file should contain {doc_count:d} documents each having {pages_per_doc:d} pages')
|
||||
@@ -354,7 +355,7 @@ def step_check_response_zip_doc_page_count(context, doc_count, pages_per_doc):
|
||||
with zipfile.ZipFile(io.BytesIO(response_file.getvalue())) as zip_file:
|
||||
actual_doc_count = len(zip_file.namelist())
|
||||
assert actual_doc_count == doc_count, f"Expected {doc_count} documents but got {actual_doc_count} documents"
|
||||
|
||||
|
||||
for file_name in zip_file.namelist():
|
||||
with zip_file.open(file_name) as pdf_file:
|
||||
reader = PdfReader(pdf_file)
|
||||
|
||||
5
cucumber/requirements.in
Normal file
5
cucumber/requirements.in
Normal file
@@ -0,0 +1,5 @@
|
||||
behave
|
||||
requests
|
||||
pypdf
|
||||
reportlab
|
||||
PyCryptodome
|
||||
@@ -1,5 +1,259 @@
|
||||
behave
|
||||
requests
|
||||
PyPDF2
|
||||
reportlab
|
||||
PyCryptodome
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file='cucumber\requirements.txt' 'cucumber\requirements.in'
|
||||
#
|
||||
behave==1.2.6 \
|
||||
--hash=sha256:b9662327aa53294c1351b0a9c369093ccec1d21026f050c3bd9b3e5cccf81a86 \
|
||||
--hash=sha256:ebda1a6c9e5bfe95c5f9f0a2794e01c7098b3dde86c10a95d8621c5907ff6f1c
|
||||
# via -r cucumber\requirements.in
|
||||
certifi==2024.12.14 \
|
||||
--hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \
|
||||
--hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db
|
||||
# via requests
|
||||
chardet==5.2.0 \
|
||||
--hash=sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7 \
|
||||
--hash=sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970
|
||||
# via reportlab
|
||||
charset-normalizer==3.4.1 \
|
||||
--hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \
|
||||
--hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \
|
||||
--hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \
|
||||
--hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \
|
||||
--hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \
|
||||
--hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \
|
||||
--hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \
|
||||
--hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \
|
||||
--hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \
|
||||
--hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \
|
||||
--hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \
|
||||
--hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \
|
||||
--hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \
|
||||
--hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \
|
||||
--hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \
|
||||
--hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \
|
||||
--hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \
|
||||
--hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \
|
||||
--hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \
|
||||
--hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \
|
||||
--hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \
|
||||
--hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \
|
||||
--hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \
|
||||
--hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \
|
||||
--hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \
|
||||
--hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \
|
||||
--hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \
|
||||
--hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \
|
||||
--hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \
|
||||
--hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \
|
||||
--hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \
|
||||
--hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \
|
||||
--hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \
|
||||
--hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \
|
||||
--hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \
|
||||
--hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \
|
||||
--hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \
|
||||
--hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \
|
||||
--hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \
|
||||
--hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \
|
||||
--hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \
|
||||
--hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \
|
||||
--hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \
|
||||
--hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \
|
||||
--hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \
|
||||
--hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \
|
||||
--hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \
|
||||
--hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \
|
||||
--hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \
|
||||
--hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \
|
||||
--hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \
|
||||
--hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \
|
||||
--hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \
|
||||
--hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \
|
||||
--hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \
|
||||
--hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \
|
||||
--hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \
|
||||
--hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \
|
||||
--hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \
|
||||
--hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \
|
||||
--hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \
|
||||
--hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \
|
||||
--hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \
|
||||
--hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \
|
||||
--hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \
|
||||
--hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \
|
||||
--hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \
|
||||
--hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \
|
||||
--hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \
|
||||
--hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \
|
||||
--hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \
|
||||
--hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \
|
||||
--hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \
|
||||
--hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \
|
||||
--hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \
|
||||
--hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \
|
||||
--hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \
|
||||
--hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \
|
||||
--hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \
|
||||
--hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \
|
||||
--hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \
|
||||
--hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \
|
||||
--hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \
|
||||
--hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \
|
||||
--hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \
|
||||
--hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \
|
||||
--hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \
|
||||
--hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \
|
||||
--hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \
|
||||
--hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \
|
||||
--hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \
|
||||
--hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616
|
||||
# via requests
|
||||
idna==3.10 \
|
||||
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
|
||||
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
|
||||
# via requests
|
||||
parse==1.20.2 \
|
||||
--hash=sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558 \
|
||||
--hash=sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce
|
||||
# via
|
||||
# behave
|
||||
# parse-type
|
||||
parse-type==0.6.4 \
|
||||
--hash=sha256:5e1ec10440b000c3f818006033372939e693a9ec0176f446d9303e4db88489a6 \
|
||||
--hash=sha256:83d41144a82d6b8541127bf212dd76c7f01baff680b498ce8a4d052a7a5bce4c
|
||||
# via behave
|
||||
pillow==11.1.0 \
|
||||
--hash=sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83 \
|
||||
--hash=sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96 \
|
||||
--hash=sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65 \
|
||||
--hash=sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a \
|
||||
--hash=sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352 \
|
||||
--hash=sha256:3362c6ca227e65c54bf71a5f88b3d4565ff1bcbc63ae72c34b07bbb1cc59a43f \
|
||||
--hash=sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20 \
|
||||
--hash=sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c \
|
||||
--hash=sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114 \
|
||||
--hash=sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49 \
|
||||
--hash=sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91 \
|
||||
--hash=sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0 \
|
||||
--hash=sha256:4db853948ce4e718f2fc775b75c37ba2efb6aaea41a1a5fc57f0af59eee774b2 \
|
||||
--hash=sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5 \
|
||||
--hash=sha256:54251ef02a2309b5eec99d151ebf5c9904b77976c8abdcbce7891ed22df53884 \
|
||||
--hash=sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e \
|
||||
--hash=sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c \
|
||||
--hash=sha256:5bb94705aea800051a743aa4874bb1397d4695fb0583ba5e425ee0328757f196 \
|
||||
--hash=sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756 \
|
||||
--hash=sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861 \
|
||||
--hash=sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269 \
|
||||
--hash=sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1 \
|
||||
--hash=sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb \
|
||||
--hash=sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a \
|
||||
--hash=sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081 \
|
||||
--hash=sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1 \
|
||||
--hash=sha256:89dbdb3e6e9594d512780a5a1c42801879628b38e3efc7038094430844e271d8 \
|
||||
--hash=sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90 \
|
||||
--hash=sha256:8e275ee4cb11c262bd108ab2081f750db2a1c0b8c12c1897f27b160c8bd57bbc \
|
||||
--hash=sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5 \
|
||||
--hash=sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1 \
|
||||
--hash=sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3 \
|
||||
--hash=sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35 \
|
||||
--hash=sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f \
|
||||
--hash=sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c \
|
||||
--hash=sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2 \
|
||||
--hash=sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2 \
|
||||
--hash=sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf \
|
||||
--hash=sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65 \
|
||||
--hash=sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b \
|
||||
--hash=sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442 \
|
||||
--hash=sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2 \
|
||||
--hash=sha256:ab8a209b8485d3db694fa97a896d96dd6533d63c22829043fd9de627060beade \
|
||||
--hash=sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482 \
|
||||
--hash=sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe \
|
||||
--hash=sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc \
|
||||
--hash=sha256:b20be51b37a75cc54c2c55def3fa2c65bb94ba859dde241cd0a4fd302de5ae0a \
|
||||
--hash=sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec \
|
||||
--hash=sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3 \
|
||||
--hash=sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a \
|
||||
--hash=sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07 \
|
||||
--hash=sha256:bf902d7413c82a1bfa08b06a070876132a5ae6b2388e2712aab3a7cbc02205c6 \
|
||||
--hash=sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f \
|
||||
--hash=sha256:c1eec9d950b6fe688edee07138993e54ee4ae634c51443cfb7c1e7613322718e \
|
||||
--hash=sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192 \
|
||||
--hash=sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0 \
|
||||
--hash=sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6 \
|
||||
--hash=sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73 \
|
||||
--hash=sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f \
|
||||
--hash=sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6 \
|
||||
--hash=sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547 \
|
||||
--hash=sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9 \
|
||||
--hash=sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457 \
|
||||
--hash=sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8 \
|
||||
--hash=sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26 \
|
||||
--hash=sha256:e5449ca63da169a2e6068dd0e2fcc8d91f9558aba89ff6d02121ca8ab11e79e5 \
|
||||
--hash=sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab \
|
||||
--hash=sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070 \
|
||||
--hash=sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71 \
|
||||
--hash=sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9 \
|
||||
--hash=sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761
|
||||
# via reportlab
|
||||
pycryptodome==3.21.0 \
|
||||
--hash=sha256:0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8 \
|
||||
--hash=sha256:0fa0a05a6a697ccbf2a12cec3d6d2650b50881899b845fac6e87416f8cb7e87d \
|
||||
--hash=sha256:0fd54003ec3ce4e0f16c484a10bc5d8b9bd77fa662a12b85779a2d2d85d67ee0 \
|
||||
--hash=sha256:18caa8cfbc676eaaf28613637a89980ad2fd96e00c564135bf90bc3f0b34dd93 \
|
||||
--hash=sha256:2480ec2c72438430da9f601ebc12c518c093c13111a5c1644c82cdfc2e50b1e4 \
|
||||
--hash=sha256:26412b21df30b2861424a6c6d5b1d8ca8107612a4cfa4d0183e71c5d200fb34a \
|
||||
--hash=sha256:280b67d20e33bb63171d55b1067f61fbd932e0b1ad976b3a184303a3dad22764 \
|
||||
--hash=sha256:2cb635b67011bc147c257e61ce864879ffe6d03342dc74b6045059dfbdedafca \
|
||||
--hash=sha256:2de4b7263a33947ff440412339cb72b28a5a4c769b5c1ca19e33dd6cd1dcec6e \
|
||||
--hash=sha256:3ba4cc304eac4d4d458f508d4955a88ba25026890e8abff9b60404f76a62c55e \
|
||||
--hash=sha256:4c26a2f0dc15f81ea3afa3b0c87b87e501f235d332b7f27e2225ecb80c0b1cdd \
|
||||
--hash=sha256:590ef0898a4b0a15485b05210b4a1c9de8806d3ad3d47f74ab1dc07c67a6827f \
|
||||
--hash=sha256:5dfafca172933506773482b0e18f0cd766fd3920bd03ec85a283df90d8a17bc6 \
|
||||
--hash=sha256:6cce52e196a5f1d6797ff7946cdff2038d3b5f0aba4a43cb6bf46b575fd1b5bb \
|
||||
--hash=sha256:7cb087b8612c8a1a14cf37dd754685be9a8d9869bed2ffaaceb04850a8aeef7e \
|
||||
--hash=sha256:7d85c1b613121ed3dbaa5a97369b3b757909531a959d229406a75b912dd51dd1 \
|
||||
--hash=sha256:7ee86cbde706be13f2dec5a42b52b1c1d1cbb90c8e405c68d0755134735c8dc6 \
|
||||
--hash=sha256:8898a66425a57bcf15e25fc19c12490b87bd939800f39a03ea2de2aea5e3611a \
|
||||
--hash=sha256:8acd7d34af70ee63f9a849f957558e49a98f8f1634f86a59d2be62bb8e93f71c \
|
||||
--hash=sha256:932c905b71a56474bff8a9c014030bc3c882cee696b448af920399f730a650c2 \
|
||||
--hash=sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4 \
|
||||
--hash=sha256:a3804675283f4764a02db05f5191eb8fec2bb6ca34d466167fc78a5f05bbe6b3 \
|
||||
--hash=sha256:a4e74c522d630766b03a836c15bff77cb657c5fdf098abf8b1ada2aebc7d0819 \
|
||||
--hash=sha256:a915597ffccabe902e7090e199a7bf7a381c5506a747d5e9d27ba55197a2c568 \
|
||||
--hash=sha256:b7aa25fc0baa5b1d95b7633af4f5f1838467f1815442b22487426f94e0d66c53 \
|
||||
--hash=sha256:cc2269ab4bce40b027b49663d61d816903a4bd90ad88cb99ed561aadb3888dd3 \
|
||||
--hash=sha256:d5ebe0763c982f069d3877832254f64974139f4f9655058452603ff559c482e8 \
|
||||
--hash=sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd \
|
||||
--hash=sha256:de18954104667f565e2fbb4783b56667f30fb49c4d79b346f52a29cb198d5b6b \
|
||||
--hash=sha256:f35e442630bc4bc2e1878482d6f59ea22e280d7121d7adeaedba58c23ab6386b \
|
||||
--hash=sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297 \
|
||||
--hash=sha256:ff99f952db3db2fbe98a0b355175f93ec334ba3d01bbde25ad3a5a33abc02b58
|
||||
# via -r cucumber\requirements.in
|
||||
pypdf==5.1.0 \
|
||||
--hash=sha256:3bd4f503f4ebc58bae40d81e81a9176c400cbbac2ba2d877367595fb524dfdfc \
|
||||
--hash=sha256:425a129abb1614183fd1aca6982f650b47f8026867c0ce7c4b9f281c443d2740
|
||||
# via -r cucumber\requirements.in
|
||||
reportlab==4.2.5 \
|
||||
--hash=sha256:5cf35b8fd609b68080ac7bbb0ae1e376104f7d5f7b2d3914c7adc63f2593941f \
|
||||
--hash=sha256:eb2745525a982d9880babb991619e97ac3f661fae30571b7d50387026ca765ee
|
||||
# via -r cucumber\requirements.in
|
||||
requests==2.32.3 \
|
||||
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
|
||||
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
|
||||
# via -r cucumber\requirements.in
|
||||
six==1.17.0 \
|
||||
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
|
||||
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
|
||||
# via
|
||||
# behave
|
||||
# parse-type
|
||||
typing-extensions==4.12.2 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
|
||||
# via pypdf
|
||||
urllib3==2.3.0 \
|
||||
--hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \
|
||||
--hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d
|
||||
# via requests
|
||||
|
||||
97
cucumber/test_webpages.sh
Normal file
97
cucumber/test_webpages.sh
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Function to check a single webpage
|
||||
check_webpage() {
|
||||
local url=$1
|
||||
local base_url=${2:-"http://localhost:8080"}
|
||||
local full_url="${base_url}${url}"
|
||||
local timeout=10
|
||||
|
||||
echo -n "Testing $full_url ... "
|
||||
|
||||
# Use curl to fetch the page with timeout
|
||||
response=$(curl -s -w "\n%{http_code}" --max-time $timeout "$full_url")
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "FAILED - Connection error or timeout"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Split response into body and status code
|
||||
HTTP_STATUS=$(echo "$response" | tail -n1)
|
||||
BODY=$(echo "$response" | sed '$d')
|
||||
|
||||
# Check HTTP status
|
||||
if [ "$HTTP_STATUS" != "200" ]; then
|
||||
echo "FAILED - HTTP Status: $HTTP_STATUS"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if response contains HTML
|
||||
if ! echo "$BODY" | grep -q "<!DOCTYPE html>\|<html"; then
|
||||
echo "FAILED - Response is not HTML"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "OK"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Main function to test all URLs from the list
|
||||
test_all_urls() {
|
||||
local url_file=$1
|
||||
local base_url=${2:-"http://localhost:8080"}
|
||||
local failed_count=0
|
||||
local total_count=0
|
||||
local start_time=$(date +%s)
|
||||
|
||||
echo "Starting webpage tests..."
|
||||
echo "Base URL: $base_url"
|
||||
echo "----------------------------------------"
|
||||
|
||||
while IFS= read -r url || [ -n "$url" ]; do
|
||||
# Skip empty lines
|
||||
[ -z "$url" ] && continue
|
||||
|
||||
((total_count++))
|
||||
if ! check_webpage "$url" "$base_url"; then
|
||||
((failed_count++))
|
||||
fi
|
||||
done < "$url_file"
|
||||
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
|
||||
echo "----------------------------------------"
|
||||
echo "Test Summary:"
|
||||
echo "Total tests: $total_count"
|
||||
echo "Failed tests: $failed_count"
|
||||
echo "Passed tests: $((total_count - failed_count))"
|
||||
echo "Duration: ${duration} seconds"
|
||||
|
||||
return $failed_count
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
local url_file="${script_dir}/webpage_urls.txt"
|
||||
|
||||
if [ ! -f "$url_file" ]; then
|
||||
echo "Error: URL list file not found: $url_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run tests using the URL list
|
||||
if test_all_urls "$url_file"; then
|
||||
echo "All webpage tests passed!"
|
||||
exit 0
|
||||
else
|
||||
echo "Some webpage tests failed!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main if script is executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
54
cucumber/webpage_urls.txt
Normal file
54
cucumber/webpage_urls.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
/
|
||||
/multi-tool
|
||||
/merge-pdfs
|
||||
/split-pdfs
|
||||
/rotate-pdf
|
||||
/remove-pages
|
||||
/pdf-organizer
|
||||
/multi-page-layout
|
||||
/scale-pages
|
||||
/crop
|
||||
/extract-page
|
||||
/pdf-to-single-page
|
||||
/img-to-pdf
|
||||
/markdown-to-pdf
|
||||
/pdf-to-img
|
||||
/pdf-to-text
|
||||
/pdf-to-csv
|
||||
/sign
|
||||
/add-password
|
||||
/remove-password
|
||||
/change-permissions
|
||||
/add-watermark
|
||||
/cert-sign
|
||||
/validate-signature
|
||||
/remove-cert-sign
|
||||
/sanitize-pdf
|
||||
/auto-redact
|
||||
/redact
|
||||
/stamp
|
||||
/view-pdf
|
||||
/add-page-numbers
|
||||
/add-image
|
||||
/extract-images
|
||||
/flatten
|
||||
/remove-annotations
|
||||
/remove-blanks
|
||||
/compare
|
||||
/change-metadata
|
||||
/get-info-on-pdf
|
||||
/remove-image-pdf
|
||||
/replace-and-invert-color-pdf
|
||||
/pipeline
|
||||
/auto-rename
|
||||
/adjust-contrast
|
||||
/overlay-pdf
|
||||
/auto-split-pdf
|
||||
/split-pdf-by-sections
|
||||
/split-pdf-by-chapters
|
||||
/split-by-size-or-count
|
||||
/show-javascript
|
||||
/swagger-ui/index.html
|
||||
/licenses
|
||||
/releases
|
||||
@@ -0,0 +1,63 @@
|
||||
services:
|
||||
stirling-pdf:
|
||||
container_name: Stirling-PDF-Security-Fat-Postgres
|
||||
image: stirlingtools/stirling-pdf:latest-fat-postgres
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 4G
|
||||
depends_on:
|
||||
- db
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP'" ]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 16
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
PUID: 1002
|
||||
PGID: 1002
|
||||
UMASK: "022"
|
||||
SYSTEM_DEFAULTLOCALE: en-US
|
||||
UI_APPNAME: Stirling-PDF
|
||||
UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest-fat with Security and PostgreSQL
|
||||
UI_APPNAMENAVBAR: Stirling-PDF Latest-fat-PostgreSQL
|
||||
SYSTEM_MAXFILESIZE: "100"
|
||||
METRICS_ENABLED: "true"
|
||||
SYSTEM_GOOGLEVISIBILITY: "true"
|
||||
SYSTEM_DATASOURCE_ENABLECUSTOMDATABASE: "true"
|
||||
SYSTEM_DATASOURCE_CUSTOMDATABASEURL: "jdbc:postgresql://db:5432/stirling_pdf"
|
||||
SYSTEM_DATASOURCE_USERNAME: "admin"
|
||||
SYSTEM_DATASOURCE_PASSWORD: "stirling"
|
||||
restart: on-failure:5
|
||||
|
||||
db:
|
||||
image: 'postgres:17.2-alpine'
|
||||
restart: on-failure:5
|
||||
container_name: db
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: "stirling_pdf"
|
||||
POSTGRES_USER: "admin"
|
||||
POSTGRES_PASSWORD: "stirling"
|
||||
shm_size: "512mb"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512m
|
||||
cpus: "0.5"
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U admin stirling_pdf" ]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
volumes:
|
||||
- ./stirling/latest/data:/pgdata
|
||||
@@ -14,9 +14,9 @@ services:
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "false"
|
||||
|
||||
@@ -14,9 +14,9 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /stirling/latest/data:/usr/share/tessdata:rw
|
||||
- /stirling/latest/config:/configs:rw
|
||||
- /stirling/latest/logs:/logs:rw
|
||||
- ./stirling/latest/data:/usr/share/tessdata:rw
|
||||
- ./stirling/latest/config:/configs:rw
|
||||
- ./stirling/latest/logs:/logs:rw
|
||||
environment:
|
||||
DOCKER_ENABLE_SECURITY: "true"
|
||||
SECURITY_ENABLELOGIN: "true"
|
||||
|
||||
7
gradle.properties
Normal file
7
gradle.properties
Normal file
@@ -0,0 +1,7 @@
|
||||
# Enables parallel execution of tasks, allowing multiple tasks to run simultaneously
|
||||
org.gradle.parallel=true
|
||||
|
||||
# Enables build caching to reuse outputs from previous builds for faster execution
|
||||
# org.gradle.caching=true
|
||||
|
||||
org.gradle.build-scan=true
|
||||
6568
gradle/verification-keyring.keys
Normal file
6568
gradle/verification-keyring.keys
Normal file
File diff suppressed because it is too large
Load Diff
477
gradle/verification-metadata.xml
Normal file
477
gradle/verification-metadata.xml
Normal file
@@ -0,0 +1,477 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<verification-metadata xmlns="https://schema.gradle.org/dependency-verification" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.3.xsd">
|
||||
<configuration>
|
||||
<verify-metadata>true</verify-metadata>
|
||||
<verify-signatures>true</verify-signatures>
|
||||
<keyring-format>armored</keyring-format>
|
||||
<trusted-artifacts>
|
||||
<trust group="io.dropwizard.metrics" name="metrics-bom" reason="BOM file, safe to trust"/>
|
||||
<trust group="io.dropwizard.metrics" name="metrics-parent" reason="BOM parent, https://github.com/gradle/gradle/issues/20194"/>
|
||||
<trust group="org.springframework" name="spring-framework-bom" reason="Spring BOM file, safe to trust"/>
|
||||
</trusted-artifacts>
|
||||
<trusted-keys>
|
||||
<trusted-key id="015479E1055341431B4545AB72475FD306B9CAB7" group="com.googlecode.javaewah" name="JavaEWAH" version="1.2.3"/>
|
||||
<trusted-key id="042B29E928995B9DB963C636C7CA19B7B620D787" group="com.github.stephenc.jcip" name="jcip-annotations" version="1.0-1"/>
|
||||
<trusted-key id="04543577D6A9CC626239C50C7ECBD740FF06AEB5">
|
||||
<trusting group="org.glassfish.jaxb"/>
|
||||
<trusting group="^com[.]sun($|([.].*))" regex="true"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="050A37A2E0577F4BAA095B52602EC18D20C4661C">
|
||||
<trusting group="com.thoughtworks.xstream"/>
|
||||
<trusting group="io.github.x-stream"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="077E8893A6DCC33DD4A4D5B256E73BA9A0B592D0" group="^org[.]apache[.]logging($|([.].*))" regex="true"/>
|
||||
<trusted-key id="0785B3EFF60B1B1BEA94E0BB7C25280EAE63EBE5" group="^org[.]apache[.]httpcomponents($|([.].*))" regex="true"/>
|
||||
<trusted-key id="07E20F0103D9DFC697C490D0368557390486F2C5">
|
||||
<trusting group="io.rest-assured"/>
|
||||
<trusting group="org.awaitility"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="08F0AAB4D0C1A4BDDE340765B341DDB020FCB6AB" group="org.bouncycastle"/>
|
||||
<trusted-key id="0A60B3F1FCB211175300EC206E50BB68CC1699A6" group="com.github.jai-imageio"/>
|
||||
<trusted-key id="0B1B71E813C226033B16D8C5F0D228D8FF31B515" group="^io[.]zipkin($|([.].*))" regex="true"/>
|
||||
<trusted-key id="0B743A794876D3C78AB542A118D239B1CBCD2236" group="org.glassfish.jersey" name="jersey-bom"/>
|
||||
<trusted-key id="0CC641C3A62453AB390066C4A41F13C999945293" group="commons-collections" name="commons-collections" version="3.2.2"/>
|
||||
<trusted-key id="0CDE80149711EB46DFF17AE421A24B3F8B0F594A" group="org.apache" name="apache" version="16"/>
|
||||
<trusted-key id="0CFA413799E2464C7D7E26220A4B343F2A55FDAE" group="com.h2database" name="h2" version="2.3.232"/>
|
||||
<trusted-key id="0D35D3F60078655126908E8AF3D1600878E85A3D" group="io.netty" name="netty-bom"/>
|
||||
<trusted-key id="0E0CA56D354132B5E646C25F49A1796B9B494CB8" group="org.opensaml"/>
|
||||
<trusted-key id="0E9BD9062B021BBA50F41EEB9549F6CB1E679A56" group="org.locationtech.jts"/>
|
||||
<trusted-key id="10F3C7A02ECA55E502BADCF3991EFB94DB91127D" group="org.ow2" name="ow2" version="1.5.1"/>
|
||||
<trusted-key id="1452F35849B50750F6A3BBB4B54011358B352F85" group="org.hibernate.orm" name="hibernate-core" version="6.6.4.Final"/>
|
||||
<trusted-key id="147B691A19097624902F4EA9689CBE64F4BC997F" group="org.mockito"/>
|
||||
<trusted-key id="190D5A957FF22273E601F7A7C92C5FEC70161C62" group="org.apache" name="apache" version="18"/>
|
||||
<trusted-key id="19BEAB2D799C020F17C69126B16698A4ADF4D638" group="org.checkerframework" name="checker-qual"/>
|
||||
<trusted-key id="1AA8CF92D409A73393D0B736BFF2EE42C8282E76" group="org.apache.activemq" name="activemq-bom" version="6.1.4"/>
|
||||
<trusted-key id="1D04A424F505394DBED15D451D0690E353BE126D" group="net.minidev"/>
|
||||
<trusted-key id="1D2C7EF8ADA0F794B58C7C63436902AF59EDF60E">
|
||||
<trusting group="dev.equo.ide" name="solstice" version="1.7.5"/>
|
||||
<trusting group="dev.equo.ide" name="solstice" version="1.8.0"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="20FC6EC5F628F0EB66F157B8DC97B815CAC4E847" group="io.github.pixee" name="java-security-toolkit" version="1.2.1"/>
|
||||
<trusted-key id="2518174F4111F02779592A6F9757D7E7E06DD2AC" group="io.prometheus"/>
|
||||
<trusted-key id="2655176F748FD83725B4805FF2A01147D830C125" group="org.testcontainers" name="testcontainers-bom"/>
|
||||
<trusted-key id="28118C070CB22A0175A2E8D43D12CA2AC19F3181" group="^com[.]fasterxml($|([.].*))" regex="true"/>
|
||||
<trusted-key id="28417C95E8906D108392822354A43F3254868410" group="org.apache.activemq"/>
|
||||
<trusted-key id="2B1DD4CE9223D4E19C73531E5657B51F13E59DBE" group="com.unboundid.product.scim2"/>
|
||||
<trusted-key id="2B34821418CF19CF1F2A8352953E02E4F573B46F" group="jakarta.platform"/>
|
||||
<trusted-key id="2BCBDD0F23EA1CAFCC11D4860374CF2E8DD1BDFD" group="net.java"/>
|
||||
<trusted-key id="2DB4F1EF0FA761ECC4EA935C86FDC7E2A11262CB">
|
||||
<trusting group="commons-beanutils" name="commons-beanutils" version="1.10.0"/>
|
||||
<trusting group="commons-codec"/>
|
||||
<trusting group="commons-io"/>
|
||||
<trusting group="commons-logging"/>
|
||||
<trusting group="org.apache.commons"/>
|
||||
<trusting group="xml-apis"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="2DC48CBB4352B4953AF6F803D433B437192A0FD1" group="com.datastax.oss" name="java-driver-bom" version="4.15.0"/>
|
||||
<trusted-key id="2E3A1AFFE42B5F53AF19F780BCF4173966770193" group="org.jetbrains" name="annotations" version="13.0"/>
|
||||
<trusted-key id="2FC53E6B1F681184F4CCD637F5C81DE10A0B8ECC" group="org.yaml" name="snakeyaml" version="2.3"/>
|
||||
<trusted-key id="3262A061C42FC4C7BBB5C25C1CF0293FA53CA458" group="org.apache.tomcat.embed"/>
|
||||
<trusted-key id="34441E504A937F43EB0DAEF96A65176A0FB1CD0B" group="org.apache.groovy" name="groovy-bom"/>
|
||||
<trusted-key id="3690C240CE51B4670D30AD1C38EE757D69184620" group="org.tukaani" name="xz" version="1.9"/>
|
||||
<trusted-key id="3750777B9C4B7D233B9D0C40307A96FBA0292109" group="org.postgresql" name="postgresql" version="42.7.4"/>
|
||||
<trusted-key id="38319E05F62674572CDF886170B2EBE96C112CC9" group="org.cryptacular" name="cryptacular" version="1.2.5"/>
|
||||
<trusted-key id="3E61D8C230332482009D7F0EDB901B24CAD38BC4" group="io.swagger.core.v3"/>
|
||||
<trusted-key id="3F05DDA9F317301E927136D417A27CE7A60FF5F0" group="io.opentelemetry" name="opentelemetry-bom"/>
|
||||
<trusted-key id="4021EEEAFF5DE8404DCD0A270AA3E5C3D232E79B">
|
||||
<trusting group="jakarta.enterprise"/>
|
||||
<trusting group="jakarta.inject"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="41D266DB4427983A1A4AFB0C3684155E9365C30E" group="com.jayway.jsonpath" name="json-path" version="2.9.0"/>
|
||||
<trusted-key id="44FBDBBC1A00FE414F1C1873586654072EAD6677" group="org.sonatype.oss" name="oss-parent" version="9"/>
|
||||
<trusted-key id="453EA31328DE7D8AAA55AD4ED56C721C1CFF1424" group="^com[.]twelvemonkeys($|([.].*))" regex="true"/>
|
||||
<trusted-key id="475F3B8E59E6E63AA78067482C7B12F2A511E325" group="org.slf4j"/>
|
||||
<trusted-key id="477E62A656AD5475A1882855C809CA3C41BA6E96" group="jakarta.validation" name="jakarta.validation-api" version="3.0.2"/>
|
||||
<trusted-key id="4797B4F5DCC46CEA61059071A1AE06236CA2BA62" group="^com[.]diffplug($|([.].*))" regex="true"/>
|
||||
<trusted-key id="47EF0EC60C210BC6DFAA5819B7AE15C15C321C44" group="jakarta.transaction" name="jakarta.transaction-api" version="2.0.1"/>
|
||||
<trusted-key id="47FF105DF431FF5416B821FEAECDB81D38EA9C89" group="org.commonmark"/>
|
||||
<trusted-key id="482C52BC305FB31063CD19D67BEFA2F0A9C24E7D" group="net.sf.launch4j" name="launch4j" version="3.50"/>
|
||||
<trusted-key id="48B086A7D843CFA258E83286928FBF39003C0425">
|
||||
<trusting group="io.micrometer"/>
|
||||
<trusting group="io.projectreactor"/>
|
||||
<trusting group="io.spring.gradle"/>
|
||||
<trusting group="^org[.]springframework($|([.].*))" regex="true"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="498AAC354AA5CB36FAAB7608B6E83A2D2E447E56" group="org.apache.cassandra" name="java-driver-bom" version="4.18.1"/>
|
||||
<trusted-key id="4F7E32D440EF90A83011A8FC6425559C47CC79C4">
|
||||
<trusting group="com.sun.activation"/>
|
||||
<trusting group="javax.activation"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="53C935821AA6A755BD337DB53595395EB3D8E1BA" group="org.apache.logging.log4j" name="log4j-bom" version="2.20.0"/>
|
||||
<trusted-key id="5719E50EAC5A4B1DD390B72C2A742740E08E7F8D" group="org.antlr"/>
|
||||
<trusted-key id="57312C37B064EE0FDAB0130490D5CE79E1DE6A2C" group="com.querydsl" name="querydsl-bom"/>
|
||||
<trusted-key id="5989BAF76217B843D66BE55B2D0E1FB8FE4B68B4" group="^org[.]eclipse[.]jetty($|([.].*))" regex="true"/>
|
||||
<trusted-key id="59A8E169739301FD48139CA00E325BECB6962A24" group="jakarta.annotation" name="jakarta.annotation-api" version="2.1.1"/>
|
||||
<trusted-key id="5C9A30FF22B2C02F30261C305B93F1DF7CDB6DEA" group="org.apache.xmlgraphics"/>
|
||||
<trusted-key id="60200AC4AE761F1614D6C46766D68DAA073BE985">
|
||||
<trusting group="ch.qos.logback"/>
|
||||
<trusting group="org.slf4j"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="694621A7227D8D5289699830ABE9F3126BB741C1" group="com.google.guava" name="guava-parent" version="26.0-android"/>
|
||||
<trusted-key id="6DD3B8C64EF75253BEB2C53AD908A43FB7EC07AC" group="jakarta.activation" name="jakarta.activation-api" version="2.1.3"/>
|
||||
<trusted-key id="6E13156C0EE653F0B984663AB95BBD3FA43C4492" group="org.apache" name="apache" version="3"/>
|
||||
<trusted-key id="6F538074CCEBF35F28AF9B066A0975F8B1127B83" group="org.jetbrains.kotlin"/>
|
||||
<trusted-key id="70CD19BFD9F6C330027D6F260315BFB7970A144F" group="javax.xml.bind"/>
|
||||
<trusted-key id="71EBF1CA4125B10AAB1E17CDB7DC526C17E3608B" group="jakarta.persistence" name="jakarta.persistence-api" version="3.1.0"/>
|
||||
<trusted-key id="7464550A61C90BA385FC97A76D9567281201E5E3" group="jakarta.servlet" name="jakarta.servlet-api" version="6.0.0"/>
|
||||
<trusted-key id="7616EB882DAF57A11477AAF559A252FB1199D873" group="com.google.code.findbugs" name="jsr305" version="3.0.2"/>
|
||||
<trusted-key id="798E2DA37E70DAE0EA9E498CA388C395AAFB80F8" group="io.dropwizard.metrics"/>
|
||||
<trusted-key id="7A1D848E7C2AF85EEBA69C99E7BF252CF360097E" group="org.latencyutils" name="LatencyUtils" version="2.0.3"/>
|
||||
<trusted-key id="7B121B76A7ED6CE6E60AD51784E913A8E3A748C0" group="org.bouncycastle"/>
|
||||
<trusted-key id="7C669810892CBD3148FA92995B05CCDE140C2876" group="org.eclipse.jgit"/>
|
||||
<trusted-key id="808D78B17A5A2D7C3668E31FBFFC9B54721244AD" group="org.apache.commons" name="commons-parent" version="39"/>
|
||||
<trusted-key id="80F6D6B0D90C6747753344CAB5A9E81B565E89E0" group="org.tomlj" name="tomlj" version="1.0.0"/>
|
||||
<trusted-key id="81BE0C38ACE8AEDC7735A05F4C2AFF633F3A7223" group="org.seleniumhq.selenium" name="selenium-bom"/>
|
||||
<trusted-key id="81CCDC71C7D61C179B27002D6A9FBE152D4C64D1" group="org.openjfx"/>
|
||||
<trusted-key id="82F0964816AD7319CB0CCCF93EFD9D223D715E9A" group="com.nimbusds"/>
|
||||
<trusted-key id="82F94BBDF95C247BBD21396B9A0B94DEC0FFA7EE" group="org.webjars" name="swagger-ui" version="5.2.0"/>
|
||||
<trusted-key id="839323A4780D5BF9A6978970152888E10EF880B3">
|
||||
<trusting group="org.attoparser"/>
|
||||
<trusting group="org.unbescape"/>
|
||||
<trusting group="^org[.]thymeleaf($|([.].*))" regex="true"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="84789D24DF77A32433CE1F079EB80E92EB2135B1" group="org.apache" name="apache"/>
|
||||
<trusted-key id="8756C4F765C9AC3CB6B85D62379CE192D401AB61" group="com.diffplug.durian"/>
|
||||
<trusted-key id="894F14D98D7F20D5E82645E3DFE102108BF9381F" group="org.hibernate.search" name="hibernate-search-bom" version="7.1.2.Final"/>
|
||||
<trusted-key id="94976E17E18DD3201447286954963C3E875A56AE" group="io.smallrye"/>
|
||||
<trusted-key id="9579802DC3E15DE9C389239FC0D48A119CE7EE7B" group="com.zaxxer" name="HikariCP" version="5.1.0"/>
|
||||
<trusted-key id="9790B1EC52577244529621F38C77ED250E495230" group="com.bucket4j" name="bucket4j_jdk17-core" version="8.14.0"/>
|
||||
<trusted-key id="982C26A0C156D986CC2AD19E3FBA8E8E719022D7" group="org.jboss" name="jboss-parent" version="39"/>
|
||||
<trusted-key id="9B32CBC0F3F6BA4C13D611FC21871D2A9AB66A31" group="io.rsocket" name="rsocket-bom" version="1.1.3"/>
|
||||
<trusted-key id="9E3044071B758EBCB7E45673700E4F39BC05364B">
|
||||
<trusting group="org.eclipse.platform" name="org.eclipse.osgi" version="3.18.300"/>
|
||||
<trusting group="org.eclipse.platform" name="org.eclipse.osgi" version="3.18.500"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="A41A5960555F8CBBC7D8B2D7787F3A057B828D36" group="org.springdoc"/>
|
||||
<trusted-key id="A5BD02B93E7A40482EB1D66A5F69AD087600B22C" group="org.ow2.asm"/>
|
||||
<trusted-key id="A602970FE1BF5C9C8A9491B97A3C9FE21DFDBF44" group="org.apache.pdfbox"/>
|
||||
<trusted-key id="A654E2E6D97BE4219A4909415B15A33991BEA5A8" group="me.friwi"/>
|
||||
<trusted-key id="A6D6C97108B8585F91B158748671A8DF71296252" group="com.squareup.okhttp3" name="okhttp-bom" version="4.10.0"/>
|
||||
<trusted-key id="A7892505CF1A58076453E52D7999BEFBA1039E8B" group="net.bytebuddy"/>
|
||||
<trusted-key id="A9789342F598AD5B1175EF357EB97D110DFADD60" group="com.googlecode.concurrent-trees" name="concurrent-trees" version="2.6.1"/>
|
||||
<trusted-key id="AA70C7C433D501636392EC02153E7A3C2B4E5118" group="org.eclipse.ee4j" name="project"/>
|
||||
<trusted-key id="AB1DC33940689C44669107094989E0E939C2999B">
|
||||
<trusting group="com.opencsv" name="opencsv" version="5.10"/>
|
||||
<trusting group="com.opencsv" name="opencsv" version="5.9"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="B1F250C1F371EBF0E31E86E30E31BBB30C940D01" group="com.posthog.java" name="posthog" version="1.1.1"/>
|
||||
<trusted-key id="B6E73D84EA4FCC47166087253FAAD2CD5ECBB314">
|
||||
<trusting group="commons-beanutils"/>
|
||||
<trusting group="org.apache.commons"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="BA926F64CA647B6D853A38672E2010F8A7FF4A41" group="org.apache" name="apache" version="7"/>
|
||||
<trusted-key id="BB785E0400E71390977E4D1ADF3CC7C64D56297B" group="jakarta.interceptor" name="jakarta.interceptor-api" version="2.1.0"/>
|
||||
<trusted-key id="BCA1F17506AF088F3A964A9C0459A2B383ED8C11" group="org.eclipse.angus"/>
|
||||
<trusted-key id="BDB5FA4FE719D787FB3D3197F6D4A1D411E9D1AE" group="^com[.]google($|([.].*))" regex="true"/>
|
||||
<trusted-key id="BE685132AFD2740D9095F9040CC0B712FEE75827" group="org.assertj"/>
|
||||
<trusted-key id="C1D1ADA83198AA7FEAD102483FFE64C7506FCCC9" group="com.coveo" name="saml-client" version="5.0.0"/>
|
||||
<trusted-key id="C663D2F64DA2CA09DB28D9ABD3FA67D522C55256" group="org.apache.pulsar" name="pulsar-bom" version="3.3.3"/>
|
||||
<trusted-key id="C7BE5BCC9FEC15518CFDA882B0F3710FA64900E7" group="com.google.code.gson"/>
|
||||
<trusted-key id="C89074FC8BE681B7C7EAAB6E4C5EED3C53B75933" group="org.skyscreamer" name="jsonassert" version="1.5.3"/>
|
||||
<trusted-key id="CA62ED130E4053944406DF640181B45EA58677BC" group="org.apache.logging" name="logging-parent" version="7"/>
|
||||
<trusted-key id="CC57399D74CD7E4768ED6FA4CA62973FBF0451C0" group="com.vaadin.external.google" name="android-json" version="0.0.20131108.vaadin1"/>
|
||||
<trusted-key id="CD5464315F0B98C77E6E8ECD9DAADC1C9FCC82D0" group="commons-cli" name="commons-cli" version="1.4"/>
|
||||
<trusted-key id="CE3285F320685193D11FEA01F6CE9695C9318406" group="com.google.zxing"/>
|
||||
<trusted-key id="CE4439C1BEF3DA83B1832F9DBEFEEF227A98B809" group="org.apache.velocity"/>
|
||||
<trusted-key id="CE8075A251547BEE249BC151A2115AE15F6B8B72">
|
||||
<trusting group="org.apache.commons"/>
|
||||
<trusting group="org.xmlunit"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="D421D1DF4570BFB13E485D0BF95ADD0A28D2F139" group="org.projectlombok" name="lombok" version="1.18.36"/>
|
||||
<trusted-key id="D54A395B5CF3F86EB45F6E426B1B008864323B92" group="org.antlr"/>
|
||||
<trusted-key id="DB45ECD19B97514F727105AE67BF80B10AD53983" group="org.apache.santuario" name="xmlsec" version="2.3.4"/>
|
||||
<trusted-key id="DBD744ACE7ADE6AA50DD591F66B50994442D2D40" group="^com[.]squareup($|([.].*))" regex="true"/>
|
||||
<trusted-key id="DBFBFF8DA2F1571ACC6F63AB905CF8FC70CC1444" group="org.aspectj" name="aspectjweaver" version="1.9.22.1"/>
|
||||
<trusted-key id="DCAA15007BED9DE690CD9523378B845402277962">
|
||||
<trusting group="org.opensaml"/>
|
||||
<trusting group="^net[.]shibboleth($|([.].*))" regex="true"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="E01AAB301618D23B39DBD41002DE09238A0E4D34" group="com.drewnoakes" name="metadata-extractor" version="2.19.0"/>
|
||||
<trusted-key id="E113159331A1F87BFC2A93D0960D2E8635A91268" group="org.hdrhistogram" name="HdrHistogram" version="2.2.2"/>
|
||||
<trusted-key id="E2ACB037933CDEAAB7BF77D49A2C7A98E457C53D">
|
||||
<trusting group="io.projectreactor"/>
|
||||
<trusting group="^org[.]springframework($|([.].*))" regex="true"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="E3A9F95079E84CE201F7CF60BEDE11EAF1164480" group="org.hamcrest" name="hamcrest"/>
|
||||
<trusted-key id="E7DC75FC24FB3C8DFE8086AD3D5839A2262CBBFB" group="org.jetbrains.kotlinx"/>
|
||||
<trusted-key id="E85AED155021AF8A6C6B7A4A7C7D8456294423BA" group="org.objenesis"/>
|
||||
<trusted-key id="EE0CA873074092F806F59B65D364ABAA39A47320">
|
||||
<trusting group="com.google.errorprone"/>
|
||||
<trusting group="com.google.googlejavaformat"/>
|
||||
</trusted-key>
|
||||
<trusted-key id="EED29BAB8D4FD882D62308CD72D1B04BC7E6AA04" group="me.friwi"/>
|
||||
<trusted-key id="EF5214AD654CD05F0DA91609ECEAC3B11AD0E0A0" group="com.adobe.xmp" name="xmpcore" version="6.1.11"/>
|
||||
<trusted-key id="F046369B06B761AC86D9849F71B329993BFFCFDD" group="com.oracle.database.jdbc" name="ojdbc-bom" version="21.9.0.0"/>
|
||||
<trusted-key id="F0E31196852A34F8855710BD4A6CE7EBC7F4F54B" group="io.prometheus"/>
|
||||
<trusted-key id="F1232CDCD94176E7FBA9CFE289A2C76A5EE16E57" group="technology.tabula" name="tabula" version="1.0.5"/>
|
||||
<trusted-key id="F3184BCD55F4D016E30D4C9BF42E87F9665015C9" group="org.jsoup" name="jsoup" version="1.18.3"/>
|
||||
<trusted-key id="F55EF5BB19F52A250FEDC0DF39450183608E49D4" group="com.googlecode.owasp-java-html-sanitizer"/>
|
||||
<trusted-key id="F5FEBA84EB26C56457B2CF819E31AB27445478DB" group="org.infinispan"/>
|
||||
<trusted-key id="F60649A7F36F9FBEE21D9AA08AC0378EC753063D" group="com.fathzer"/>
|
||||
<trusted-key id="F674EBA7B6EC777BDB58942DE0E92C40A43A012A" group="jakarta.websocket"/>
|
||||
<trusted-key id="FA77DCFEF2EE6EB2DEBEDD2C012579464D01C06A" group="org.apache" name="apache"/>
|
||||
<trusted-key id="FA7929F83AD44C4590F6CC6815C71C0A4E0B8EDD" group="net.java.dev.jna"/>
|
||||
<trusted-key id="FC411CD3CB7DCB0ABC9801058118B3BCDB1A5000" group="jakarta.xml.bind"/>
|
||||
<trusted-key id="FF6E2C001948C5F2F38B0CC385911F425EC61B51">
|
||||
<trusting group="org.apiguardian"/>
|
||||
<trusting group="org.opentest4j"/>
|
||||
<trusting group="^org[.]junit($|([.].*))" regex="true"/>
|
||||
</trusted-key>
|
||||
</trusted-keys>
|
||||
</configuration>
|
||||
<components>
|
||||
<component group="com.diffplug.spotless" name="com.diffplug.spotless.gradle.plugin" version="6.25.0">
|
||||
<artifact name="com.diffplug.spotless.gradle.plugin-6.25.0.pom">
|
||||
<sha256 value="f45c82b12faacd85acd474eba699322fa5dea88408b247d0e4bde9412908223a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.diffplug.spotless" name="com.diffplug.spotless.gradle.plugin" version="7.0.1">
|
||||
<artifact name="com.diffplug.spotless.gradle.plugin-7.0.1.pom">
|
||||
<sha256 value="d967a0f74c203ddcc5700947aab40f4be2a5a9f7b8d32aab7fc412b2030e7dfc" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.Carleslc.Simple-YAML" name="Simple-Configuration" version="1.8.4">
|
||||
<artifact name="Simple-Configuration-1.8.4.jar">
|
||||
<sha256 value="2b960f4840ac68bb1815d937ca2d58eb9b04c05e6a9b769a4e870c52a4728156" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="Simple-Configuration-1.8.4.pom">
|
||||
<sha256 value="698e378e816a220edfcb754fd4c4f7d9a8fd38716b9081f63f9878d4bbf3cdd5" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.Carleslc.Simple-YAML" name="Simple-YAML-Parent" version="1.8.4">
|
||||
<artifact name="Simple-YAML-Parent-1.8.4.pom">
|
||||
<sha256 value="b9298b875185bd13b4e301187eeb234d3a1a4b1a871dd4a7461f2e7775121357" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.Carleslc.Simple-YAML" name="Simple-Yaml" version="1.8.4">
|
||||
<artifact name="Simple-Yaml-1.8.4.jar">
|
||||
<sha256 value="d558ca57927d4bc393e9522aac0cf60cc632a9f6f60cd6724aa94b7005e1fd18" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="Simple-Yaml-1.8.4.pom">
|
||||
<sha256 value="47f1003cd91eb6c11b2c941bf89e72428aed92e6bfef327b18935dda28eb4072" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.jk1" name="gradle-license-report" version="2.9">
|
||||
<artifact name="gradle-license-report-2.9.jar">
|
||||
<sha256 value="ebfd6da851654c53216eea9eda1485c12e0cd6de5a9919bf5da9735a021f32af" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="gradle-license-report-2.9.module">
|
||||
<sha256 value="4139a508481c369ae0f2627fa8387f1e20e58600f2037cdc1cdaa164e056f235" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.jk1.dependency-license-report" name="com.github.jk1.dependency-license-report.gradle.plugin" version="2.9">
|
||||
<artifact name="com.github.jk1.dependency-license-report.gradle.plugin-2.9.pom">
|
||||
<sha256 value="a79ca4dfe069d737faf075c8f4b6c6471c2e5cea8e1546946ae333d747fddf02" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.psxpaul" name="gradle-execfork-plugin" version="0.2.0">
|
||||
<artifact name="gradle-execfork-plugin-0.2.0.jar">
|
||||
<sha256 value="eb4f73df13ee24fb1952e0a9054c5618ef07f0d62386bfad1a04990df0cb3a65" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="gradle-execfork-plugin-0.2.0.module">
|
||||
<sha256 value="7b239eb029b2e4cab00dddf1df204ef4bbf88e78a43619c26fbb1e49bc53c642" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.guava" name="guava-parent" version="33.3.1-jre">
|
||||
<artifact name="guava-parent-33.3.1-jre.pom">
|
||||
<sha256 value="55441db27e8869dfefe053059bdf478bdc7e95585642bf391f0023345fd56287" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.martiansoftware" name="jsap" version="2.1">
|
||||
<artifact name="jsap-2.1.jar">
|
||||
<sha256 value="331746fa62cfbc3368260c5a2e660936ad11be612308c120a044e120361d474e" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="jsap-2.1.pom">
|
||||
<sha256 value="9acf56a8579c05bedd819d99232363e2bf327e8f73c67598dbd9885a845a3c69" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="commons-logging" name="commons-logging" version="1.0.4">
|
||||
<artifact name="commons-logging-1.0.4.pom">
|
||||
<sha256 value="65d310509352b5425118225ee600a01f83ba72142d035014b5d164bc04b2d284" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="edu.sc.seis.launch4j" name="edu.sc.seis.launch4j.gradle.plugin" version="3.0.6">
|
||||
<artifact name="edu.sc.seis.launch4j.gradle.plugin-3.0.6.pom">
|
||||
<sha256 value="62a4f6752190b9ebf30869e092e4154e41a2c5cd96048ae98a01916f2684465a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="edu.sc.seis.launch4j" name="launch4j" version="3.0.6">
|
||||
<artifact name="launch4j-3.0.6.jar">
|
||||
<sha256 value="6a8f000c6fda2eb17406b516ec0be28cdac900cbba03319e57bd3c2f1b1afa02" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="launch4j-3.0.6.module">
|
||||
<sha256 value="0a38e1daab79a32b56790db458088148c97be021764e2d2dce259b9a87fec048" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.dropwizard.metrics" name="metrics-bom" version="4.2.25">
|
||||
<artifact name="metrics-bom-4.2.25.pom">
|
||||
<sha256 value="825ad37b8380f992b515050bbd95452f85466feae7b856d5c150d4e5f716a8e9" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.dropwizard.metrics" name="metrics-parent" version="4.2.25">
|
||||
<artifact name="metrics-parent-4.2.25.pom">
|
||||
<sha256 value="df7b6371f9b15698e123d9861f2099ca32c9ec966d9f0c60755a2a34ccbfabc2" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.spring.dependency-management" name="io.spring.dependency-management.gradle.plugin" version="1.1.7">
|
||||
<artifact name="io.spring.dependency-management.gradle.plugin-1.1.7.pom">
|
||||
<sha256 value="19bb16ab5d6359bff88ce95c80b01e7e3445157faa1d74ae5cf03a467cea1e04" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.swagger" name="swaggerhub" version="1.3.2">
|
||||
<artifact name="swaggerhub-1.3.2.jar">
|
||||
<sha256 value="703a61e96b23af81b2ceeba4a081bb3212ec00211ae300748b3e7ccb1f33bd32" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="swaggerhub-1.3.2.module">
|
||||
<sha256 value="bd5ccd6e48224cab88bbd79e880ff011ee4fa711490f410f7810b75a2cb9c3c0" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.swagger.swaggerhub" name="io.swagger.swaggerhub.gradle.plugin" version="1.3.2">
|
||||
<artifact name="io.swagger.swaggerhub.gradle.plugin-1.3.2.pom">
|
||||
<sha256 value="69069eee12440c521662057334ac4acaea0ce6534ca4fd8b1bc264de930ad2d0" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="net.java" name="jvnet-parent" version="5">
|
||||
<artifact name="jvnet-parent-5.pom">
|
||||
<sha256 value="1af699f8d9ddab67f9a0d202fbd7915eb0362a5a6dfd5ffc54cafa3465c9cb0a" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="net.shibboleth" name="parent" version="11.3.5">
|
||||
<artifact name="parent-11.3.5.pom">
|
||||
<pgp value="0E0CA56D354132B5E646C25F49A1796B9B494CB8"/>
|
||||
<sha256 value="7a24e2700485eea087370f1dca6fe0291d7893d38c11aabfe977784fd93b808c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache" name="apache" version="27">
|
||||
<artifact name="apache-27.pom">
|
||||
<sha256 value="b2b0fc69e22a650c3892f1c366d77076f29575c6738df4c7a70a44844484cdf9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache" name="apache" version="33">
|
||||
<artifact name="apache-33.pom">
|
||||
<sha256 value="d78bd8524c5f8380a190a6525686629a95dfe512df21111383a6d8c0923a4415" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.apache.commons" name="commons-parent" version="78">
|
||||
<artifact name="commons-parent-78.pom">
|
||||
<sha256 value="022d202e655edd04f2a10ecbe453d92977924d38380a4ca8c359f1817a80320e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.hibernate.common" name="hibernate-commons-annotations" version="7.0.3.Final">
|
||||
<artifact name="hibernate-commons-annotations-7.0.3.Final.jar">
|
||||
<sha256 value="0db2fd57d5e43688ac6ed5cdf36deaf05d84340dcc24c2dd2a2114de38e5175d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="hibernate-commons-annotations-7.0.3.Final.module">
|
||||
<sha256 value="b1aa7202fc3f67d22066903d3e1eb7052ee10f47322a1cb925fa2f449f25aee3" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="hibernate-commons-annotations-7.0.3.Final.pom">
|
||||
<sha256 value="66f6e607b30740e391989825a5ae076a6c877a99b78eb054a8146650aaff72eb" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jboss" name="jboss-parent" version="42">
|
||||
<artifact name="jboss-parent-42.pom">
|
||||
<sha256 value="e41276efe3509054cba4197b3d6360c51dd57bc640dde48cf37dafaa45a09c3b" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jboss" name="jboss-parent" version="43">
|
||||
<artifact name="jboss-parent-43.pom">
|
||||
<sha256 value="3c3ade76fb883acdb9a8a03355d1df4066ffb9c8c78f09b219e9c0fc2a3f4317" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jboss.logging" name="jboss-logging" version="3.6.1.Final">
|
||||
<artifact name="jboss-logging-3.6.1.Final.jar">
|
||||
<sha256 value="5e08a4b092dc85b337f0910a740571d8720cfa565fabd880a8caf94a657ca416" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="jboss-logging-3.6.1.Final.pom">
|
||||
<sha256 value="27cd88ab8e5946b8a7aa92644eb3732e35be281439ab07af71f898453ee7540d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jboss.logging" name="logging-parent" version="1.0.3.Final">
|
||||
<artifact name="logging-parent-1.0.3.Final.pom">
|
||||
<sha256 value="9972c894749cda355766217d43ded7009b1eeb26e0301c30914a2db253dd685b" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.junit" name="junit-bom" version="5.11.2">
|
||||
<artifact name="junit-bom-5.11.2.pom">
|
||||
<sha256 value="f48e88538aac145eb3ae0345a9ebd055b28f329a35dce8d1e9281325ca9b0ea2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.opensaml" name="opensaml-bom" version="4.3.0">
|
||||
<artifact name="opensaml-bom-4.3.0.pom">
|
||||
<sha256 value="4dfcc7cd96a2645c6e28df9f166f0e5b2b1a44aa109b3100cdb0ee17e01e02d2" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.opensaml" name="opensaml-parent" version="4.3.0">
|
||||
<artifact name="opensaml-parent-4.3.0.pom">
|
||||
<sha256 value="5e9db2f2dc3938835a76f5334997d79c8781511c8b68c1f6df6b384306900319" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2" name="ow2" version="1.5.1">
|
||||
<artifact name="ow2-1.5.1.pom">
|
||||
<sha256 value="321ddbb7ee6fe4f53dea6b4cd6db74154d6bfa42391c1f763b361b9f485acf05" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.panteleyev" name="jpackage-gradle-plugin" version="1.6.0">
|
||||
<artifact name="jpackage-gradle-plugin-1.6.0.jar">
|
||||
<sha256 value="a8a588ff44a62db1aee62d3da117d2632a7f9a107709ca201da2a59dcb500175" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="jpackage-gradle-plugin-1.6.0.module">
|
||||
<sha256 value="a572bc67a0bcce5eb8c50a0ae2659fba850dae5b0188f53045635c9276545179" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.panteleyev.jpackageplugin" name="org.panteleyev.jpackageplugin.gradle.plugin" version="1.6.0">
|
||||
<artifact name="org.panteleyev.jpackageplugin.gradle.plugin-1.6.0.pom">
|
||||
<sha256 value="82bff05e70c9f7f5c3d4a8c3958b3842dea970ac1378e306051e66cf98a8f340" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.sonatype.oss" name="oss-parent" version="5">
|
||||
<artifact name="oss-parent-5.pom">
|
||||
<pgp value="2BCBDD0F23EA1CAFCC11D4860374CF2E8DD1BDFD"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.sonatype.oss" name="oss-parent" version="7">
|
||||
<artifact name="oss-parent-7.pom">
|
||||
<sha256 value="b51f8867c92b6a722499557fc3a1fdea77bdf9ef574722fe90ce436a29559454" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.springdoc" name="springdoc-openapi-gradle-plugin" version="1.8.0">
|
||||
<artifact name="springdoc-openapi-gradle-plugin-1.8.0.jar">
|
||||
<sha256 value="94075aa01757a0c1d573ade9145c098963f084feefd31dc95d65606c503585f4" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="springdoc-openapi-gradle-plugin-1.8.0.module">
|
||||
<sha256 value="6f2d828807961169293f4f5b897f7c3ee76da06dc6bb9d94acb3d5f2418e998c" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.springdoc.openapi-gradle-plugin" name="org.springdoc.openapi-gradle-plugin.gradle.plugin" version="1.8.0">
|
||||
<artifact name="org.springdoc.openapi-gradle-plugin.gradle.plugin-1.8.0.pom">
|
||||
<sha256 value="2d117343231e29be22e489bc1f4825e1d4bbef545905e793244e95221957154f" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.springframework" name="spring-framework-bom" version="5.3.34">
|
||||
<artifact name="spring-framework-bom-5.3.34.pom">
|
||||
<sha256 value="6d0616e2544d7115dc249817dd758a34dfa677329182b42e17542e133e55732d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.springframework.boot" name="org.springframework.boot.gradle.plugin" version="3.4.1">
|
||||
<artifact name="org.springframework.boot.gradle.plugin-3.4.1.pom">
|
||||
<sha256 value="f4d1acf98aa55a44b1a23b1c2b5c75aaa84c4408bea955bd81efb38acd68f38d" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="xml-apis" name="xml-apis-ext" version="1.3.04">
|
||||
<artifact name="xml-apis-ext-1.3.04.jar">
|
||||
<sha256 value="d0b4887dc34d57de49074a58affad439a013d0baffa1a8034f8ef2a5ea191646" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="xml-apis-ext-1.3.04.pom">
|
||||
<sha256 value="1b5939a9310a59c0df0c03726721d5fc9521e87d6c203bfa7220bae82a30d9e8" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="xmlpull" name="xmlpull" version="1.1.3.1">
|
||||
<artifact name="xmlpull-1.1.3.1.jar">
|
||||
<sha256 value="34e08ee62116071cbb69c0ed70d15a7a5b208d62798c59f2120bb8929324cb63" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
<artifact name="xmlpull-1.1.3.1.pom">
|
||||
<sha256 value="8f10ffd8df0d3e9819c8cc8402709c6b248bc53a954ef6e45470d9ae3a5735fb" origin="Generated by Gradle" reason="Artifact is not signed"/>
|
||||
</artifact>
|
||||
</component>
|
||||
</components>
|
||||
</verification-metadata>
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
37
gradlew
vendored
37
gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +82,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,22 +133,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,11 +200,15 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
23
gradlew.bat
vendored
23
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -26,6 +28,7 @@ if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -42,11 +45,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
@@ -135,9 +135,10 @@ def compare_files(
|
||||
# elif "language.direction" in sort_ignore_translation[language]["missing"]:
|
||||
# sort_ignore_translation[language]["missing"].remove("language.direction")
|
||||
|
||||
with open(default_file_path, encoding="utf-8") as default_file, open(
|
||||
file_path, encoding="utf-8"
|
||||
) as file:
|
||||
with (
|
||||
open(default_file_path, encoding="utf-8") as default_file,
|
||||
open(file_path, encoding="utf-8") as file,
|
||||
):
|
||||
for _ in range(5):
|
||||
next(default_file)
|
||||
try:
|
||||
|
||||
@@ -247,6 +247,11 @@ ignore = [
|
||||
'showJS.tags',
|
||||
]
|
||||
|
||||
[zh_BO]
|
||||
ignore = [
|
||||
'language.direction',
|
||||
]
|
||||
|
||||
[zh_CN]
|
||||
ignore = [
|
||||
'language.direction',
|
||||
|
||||
@@ -25,6 +25,11 @@ public class EEAppConfig {
|
||||
|
||||
@Bean(name = "runningEE")
|
||||
public boolean runningEnterpriseEdition() {
|
||||
return licenseKeyChecker.getEnterpriseEnabledResult();
|
||||
return licenseKeyChecker.getEnterpriseEnabledResult();
|
||||
}
|
||||
|
||||
@Bean(name = "SSOAutoLogin")
|
||||
public boolean ssoAutoLogin() {
|
||||
return applicationProperties.getEnterpriseEdition().isSsoAutoLogin();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class KeygenLicenseVerifier {
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
log.info(" validateLicenseResponse body: " + response.body());
|
||||
log.debug(" validateLicenseResponse body: " + response.body());
|
||||
JsonNode jsonResponse = objectMapper.readTree(response.body());
|
||||
if (response.statusCode() == 200) {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public class LicenseKeyChecker {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private boolean enterpriseEnbaledResult = false;
|
||||
private boolean enterpriseEnabledResult = false;
|
||||
|
||||
@Autowired
|
||||
public LicenseKeyChecker(
|
||||
@@ -35,12 +35,12 @@ public class LicenseKeyChecker {
|
||||
|
||||
private void checkLicense() {
|
||||
if (!applicationProperties.getEnterpriseEdition().isEnabled()) {
|
||||
enterpriseEnbaledResult = false;
|
||||
enterpriseEnabledResult = false;
|
||||
} else {
|
||||
enterpriseEnbaledResult =
|
||||
enterpriseEnabledResult =
|
||||
licenseService.verifyLicense(
|
||||
applicationProperties.getEnterpriseEdition().getKey());
|
||||
if (enterpriseEnbaledResult) {
|
||||
if (enterpriseEnabledResult) {
|
||||
log.info("License key is valid.");
|
||||
} else {
|
||||
log.info("License key is invalid.");
|
||||
@@ -55,6 +55,6 @@ public class LicenseKeyChecker {
|
||||
}
|
||||
|
||||
public boolean getEnterpriseEnabledResult() {
|
||||
return enterpriseEnbaledResult;
|
||||
return enterpriseEnabledResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -24,15 +25,17 @@ import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.UI.WebBrowser;
|
||||
import stirling.software.SPDF.config.ConfigInitializer;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@Slf4j
|
||||
public class SPdfApplication {
|
||||
@EnableScheduling
|
||||
@SpringBootApplication
|
||||
public class SPDFApplication {
|
||||
|
||||
private static String baseUrlStatic;
|
||||
private static String serverPortStatic;
|
||||
private static String baseUrlStatic;
|
||||
|
||||
private final Environment env;
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final WebBrowser webBrowser;
|
||||
@@ -40,7 +43,7 @@ public class SPdfApplication {
|
||||
@Value("${baseUrl:http://localhost}")
|
||||
private String baseUrl;
|
||||
|
||||
public SPdfApplication(
|
||||
public SPDFApplication(
|
||||
Environment env,
|
||||
ApplicationProperties applicationProperties,
|
||||
@Autowired(required = false) WebBrowser webBrowser) {
|
||||
@@ -49,42 +52,41 @@ public class SPdfApplication {
|
||||
this.webBrowser = webBrowser;
|
||||
}
|
||||
|
||||
// Optionally keep this method if you want to provide a manual port-incrementation fallback.
|
||||
private static String findAvailablePort(int startPort) {
|
||||
int port = startPort;
|
||||
while (!isPortAvailable(port)) {
|
||||
port++;
|
||||
}
|
||||
return String.valueOf(port);
|
||||
}
|
||||
|
||||
private static boolean isPortAvailable(int port) {
|
||||
try (ServerSocket socket = new ServerSocket(port)) {
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
SpringApplication app = new SpringApplication(SPdfApplication.class);
|
||||
SpringApplication app = new SpringApplication(SPDFApplication.class);
|
||||
|
||||
Properties props = new Properties();
|
||||
|
||||
if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
|
||||
System.setProperty("java.awt.headless", "false");
|
||||
app.setHeadless(false);
|
||||
props.put("java.awt.headless", "false");
|
||||
props.put("spring.main.web-application-type", "servlet");
|
||||
}
|
||||
app.setAdditionalProfiles("default");
|
||||
app.addInitializers(new ConfigInitializer());
|
||||
Map<String, String> propertyFiles = new HashMap<>();
|
||||
// External config files
|
||||
if (Files.exists(Paths.get("configs/settings.yml"))) {
|
||||
propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml");
|
||||
} else {
|
||||
log.warn("External configuration file 'configs/settings.yml' does not exist.");
|
||||
|
||||
app.setAdditionalProfiles(getActiveProfile(args));
|
||||
|
||||
ConfigInitializer initializer = new ConfigInitializer();
|
||||
try {
|
||||
initializer.ensureConfigExists();
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
log.error("Error initialising configuration", e);
|
||||
}
|
||||
if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
|
||||
Map<String, String> propertyFiles = new HashMap<>();
|
||||
|
||||
// External config files
|
||||
log.info("Settings file: {}", InstallationPathConfig.getSettingsPath());
|
||||
if (Files.exists(Paths.get(InstallationPathConfig.getSettingsPath()))) {
|
||||
propertyFiles.put(
|
||||
"spring.config.additional-location",
|
||||
"file:" + InstallationPathConfig.getSettingsPath());
|
||||
} else {
|
||||
log.warn(
|
||||
"External configuration file '{}' does not exist.",
|
||||
InstallationPathConfig.getSettingsPath());
|
||||
}
|
||||
|
||||
if (Files.exists(Paths.get(InstallationPathConfig.getCustomSettingsPath()))) {
|
||||
String existingLocation =
|
||||
propertyFiles.getOrDefault("spring.config.additional-location", "");
|
||||
if (!existingLocation.isEmpty()) {
|
||||
@@ -92,57 +94,39 @@ public class SPdfApplication {
|
||||
}
|
||||
propertyFiles.put(
|
||||
"spring.config.additional-location",
|
||||
existingLocation + "file:configs/custom_settings.yml");
|
||||
existingLocation + "file:" + InstallationPathConfig.getCustomSettingsPath());
|
||||
} else {
|
||||
log.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
|
||||
log.warn(
|
||||
"Custom configuration file '{}' does not exist.",
|
||||
InstallationPathConfig.getCustomSettingsPath());
|
||||
}
|
||||
Properties finalProps = new Properties();
|
||||
|
||||
if (!propertyFiles.isEmpty()) {
|
||||
finalProps.putAll(
|
||||
Collections.singletonMap(
|
||||
"spring.config.additional-location",
|
||||
propertyFiles.get("spring.config.additional-location")));
|
||||
}
|
||||
|
||||
if (!props.isEmpty()) {
|
||||
finalProps.putAll(props);
|
||||
}
|
||||
app.setDefaultProperties(finalProps);
|
||||
|
||||
app.run(args);
|
||||
|
||||
// Ensure directories are created
|
||||
try {
|
||||
Files.createDirectories(Path.of("customFiles/static/"));
|
||||
Files.createDirectories(Path.of("customFiles/templates/"));
|
||||
Files.createDirectories(Path.of(InstallationPathConfig.getTemplatesPath()));
|
||||
Files.createDirectories(Path.of(InstallationPathConfig.getStaticPath()));
|
||||
} catch (Exception e) {
|
||||
log.error("Error creating directories: {}", e.getMessage());
|
||||
}
|
||||
|
||||
printStartupLogs();
|
||||
}
|
||||
|
||||
private static void printStartupLogs() {
|
||||
log.info("Stirling-PDF Started.");
|
||||
String url = baseUrlStatic + ":" + getStaticPort();
|
||||
log.info("Navigate to {}", url);
|
||||
}
|
||||
|
||||
public static String getStaticBaseUrl() {
|
||||
return baseUrlStatic;
|
||||
}
|
||||
|
||||
public static String getStaticPort() {
|
||||
return serverPortStatic;
|
||||
}
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
public void setServerPortStatic(String port) {
|
||||
if ("auto".equalsIgnoreCase(port)) {
|
||||
// Use Spring Boot's automatic port assignment (server.port=0)
|
||||
SPdfApplication.serverPortStatic = // This will let Spring Boot assign an available port
|
||||
"0";
|
||||
} else {
|
||||
SPdfApplication.serverPortStatic = port;
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
baseUrlStatic = this.baseUrl;
|
||||
@@ -173,6 +157,17 @@ public class SPdfApplication {
|
||||
log.info("Running configs {}", applicationProperties.toString());
|
||||
}
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
public void setServerPortStatic(String port) {
|
||||
if ("auto".equalsIgnoreCase(port)) {
|
||||
// Use Spring Boot's automatic port assignment (server.port=0)
|
||||
SPDFApplication.serverPortStatic =
|
||||
"0"; // This will let Spring Boot assign an available port
|
||||
} else {
|
||||
SPDFApplication.serverPortStatic = port;
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void cleanup() {
|
||||
if (webBrowser != null) {
|
||||
@@ -180,10 +175,55 @@ public class SPdfApplication {
|
||||
}
|
||||
}
|
||||
|
||||
private static void printStartupLogs() {
|
||||
log.info("Stirling-PDF Started.");
|
||||
String url = baseUrlStatic + ":" + getStaticPort();
|
||||
log.info("Navigate to {}", url);
|
||||
}
|
||||
|
||||
private static String[] getActiveProfile(String[] args) {
|
||||
if (args == null) {
|
||||
return new String[] {"default"};
|
||||
}
|
||||
|
||||
for (String arg : args) {
|
||||
if (arg.contains("spring.profiles.active")) {
|
||||
return arg.substring(args[0].indexOf('=') + 1).split(", ");
|
||||
}
|
||||
}
|
||||
|
||||
return new String[] {"default"};
|
||||
}
|
||||
|
||||
private static boolean isPortAvailable(int port) {
|
||||
try (ServerSocket socket = new ServerSocket(port)) {
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally keep this method if you want to provide a manual port-incrementation fallback.
|
||||
private static String findAvailablePort(int startPort) {
|
||||
int port = startPort;
|
||||
while (!isPortAvailable(port)) {
|
||||
port++;
|
||||
}
|
||||
return String.valueOf(port);
|
||||
}
|
||||
|
||||
public static String getStaticBaseUrl() {
|
||||
return baseUrlStatic;
|
||||
}
|
||||
|
||||
public String getNonStaticBaseUrl() {
|
||||
return baseUrlStatic;
|
||||
}
|
||||
|
||||
public static String getStaticPort() {
|
||||
return serverPortStatic;
|
||||
}
|
||||
|
||||
public String getNonStaticPort() {
|
||||
return serverPortStatic;
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import me.friwi.jcefmaven.EnumProgress;
|
||||
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
|
||||
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
|
||||
import stirling.software.SPDF.UI.WebBrowser;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@@ -72,7 +73,8 @@ public class DesktopBrowser implements WebBrowser {
|
||||
CefAppBuilder builder = new CefAppBuilder();
|
||||
configureCefSettings(builder);
|
||||
builder.setProgressHandler(createProgressHandler());
|
||||
|
||||
builder.setInstallDir(
|
||||
new File(InstallationPathConfig.getClientWebUIPath()));
|
||||
// Build and initialize CEF
|
||||
cefApp = builder.build();
|
||||
client = cefApp.createClient();
|
||||
@@ -99,8 +101,16 @@ public class DesktopBrowser implements WebBrowser {
|
||||
|
||||
private void configureCefSettings(CefAppBuilder builder) {
|
||||
CefSettings settings = builder.getCefSettings();
|
||||
settings.cache_path = new File("jcef-bundle").getAbsolutePath();
|
||||
settings.root_cache_path = new File("jcef-bundle").getAbsolutePath();
|
||||
String basePath = InstallationPathConfig.getClientWebUIPath();
|
||||
log.info("basePath " + basePath);
|
||||
settings.cache_path = new File(basePath + "cache").getAbsolutePath();
|
||||
settings.root_cache_path = new File(basePath + "root_cache").getAbsolutePath();
|
||||
// settings.browser_subprocess_path = new File(basePath +
|
||||
// "subprocess").getAbsolutePath();
|
||||
// settings.resources_dir_path = new File(basePath + "resources").getAbsolutePath();
|
||||
// settings.locales_dir_path = new File(basePath + "locales").getAbsolutePath();
|
||||
settings.log_file = new File(basePath, "debug.log").getAbsolutePath();
|
||||
|
||||
settings.persist_session_cookies = true;
|
||||
settings.windowless_rendering_enabled = false;
|
||||
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;
|
||||
@@ -212,6 +222,9 @@ public class DesktopBrowser implements WebBrowser {
|
||||
}
|
||||
|
||||
private void setupLoadHandler() {
|
||||
final long initStartTime = System.currentTimeMillis();
|
||||
log.info("Setting up load handler at: {}", initStartTime);
|
||||
|
||||
client.addLoadHandler(
|
||||
new CefLoadHandlerAdapter() {
|
||||
@Override
|
||||
@@ -220,32 +233,77 @@ public class DesktopBrowser implements WebBrowser {
|
||||
boolean isLoading,
|
||||
boolean canGoBack,
|
||||
boolean canGoForward) {
|
||||
log.debug(
|
||||
"Loading state change - isLoading: {}, canGoBack: {}, canGoForward: {}, "
|
||||
+ "browserInitialized: {}, Time elapsed: {}ms",
|
||||
isLoading,
|
||||
canGoBack,
|
||||
canGoForward,
|
||||
browserInitialized,
|
||||
System.currentTimeMillis() - initStartTime);
|
||||
|
||||
if (!isLoading && !browserInitialized) {
|
||||
log.info(
|
||||
"Browser finished loading, preparing to initialize UI components");
|
||||
browserInitialized = true;
|
||||
SwingUtilities.invokeLater(
|
||||
() -> {
|
||||
if (loadingWindow != null) {
|
||||
Timer timer =
|
||||
new Timer(
|
||||
500,
|
||||
e -> {
|
||||
loadingWindow.dispose();
|
||||
loadingWindow = null;
|
||||
try {
|
||||
if (loadingWindow != null) {
|
||||
log.info("Starting UI initialization sequence");
|
||||
|
||||
frame.dispose();
|
||||
frame.setOpacity(1.0f);
|
||||
frame.setUndecorated(false);
|
||||
frame.pack();
|
||||
frame.setSize(1280, 800);
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setVisible(true);
|
||||
frame.requestFocus();
|
||||
frame.toFront();
|
||||
browser.getUIComponent()
|
||||
.requestFocus();
|
||||
});
|
||||
timer.setRepeats(false);
|
||||
timer.start();
|
||||
// Close loading window first
|
||||
loadingWindow.setVisible(false);
|
||||
loadingWindow.dispose();
|
||||
loadingWindow = null;
|
||||
log.info("Loading window disposed");
|
||||
|
||||
// Then setup the main frame
|
||||
frame.setVisible(false);
|
||||
frame.dispose();
|
||||
frame.setOpacity(1.0f);
|
||||
frame.setUndecorated(false);
|
||||
frame.pack();
|
||||
frame.setSize(1280, 800);
|
||||
frame.setLocationRelativeTo(null);
|
||||
log.debug("Frame reconfigured");
|
||||
|
||||
// Show the main frame
|
||||
frame.setVisible(true);
|
||||
frame.requestFocus();
|
||||
frame.toFront();
|
||||
log.info("Main frame displayed and focused");
|
||||
|
||||
// Focus the browser component
|
||||
Timer focusTimer =
|
||||
new Timer(
|
||||
100,
|
||||
e -> {
|
||||
try {
|
||||
browser.getUIComponent()
|
||||
.requestFocus();
|
||||
log.info(
|
||||
"Browser component focused");
|
||||
} catch (Exception ex) {
|
||||
log.error(
|
||||
"Error focusing browser",
|
||||
ex);
|
||||
}
|
||||
});
|
||||
focusTimer.setRepeats(false);
|
||||
focusTimer.start();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error during UI initialization", e);
|
||||
// Attempt cleanup on error
|
||||
if (loadingWindow != null) {
|
||||
loadingWindow.dispose();
|
||||
loadingWindow = null;
|
||||
}
|
||||
if (frame != null) {
|
||||
frame.setVisible(true);
|
||||
frame.requestFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,9 +14,12 @@ public class LoadingWindow extends JDialog {
|
||||
private final JLabel statusLabel;
|
||||
private final JPanel mainPanel;
|
||||
private final JLabel brandLabel;
|
||||
private long startTime;
|
||||
|
||||
public LoadingWindow(Frame parent, String initialUrl) {
|
||||
super(parent, "Initializing Stirling-PDF", true);
|
||||
startTime = System.currentTimeMillis();
|
||||
log.info("Creating LoadingWindow - initialization started at: {}", startTime);
|
||||
|
||||
// Initialize components
|
||||
mainPanel = new JPanel();
|
||||
@@ -29,8 +32,8 @@ public class LoadingWindow extends JDialog {
|
||||
gbc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||
gbc.insets = new Insets(5, 5, 5, 5);
|
||||
gbc.weightx = 1.0; // Add horizontal weight
|
||||
gbc.weighty = 0.0; // Add vertical weight
|
||||
gbc.weightx = 1.0;
|
||||
gbc.weighty = 0.0;
|
||||
|
||||
// Add icon
|
||||
try {
|
||||
@@ -43,12 +46,14 @@ public class LoadingWindow extends JDialog {
|
||||
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
gbc.gridy = 0;
|
||||
mainPanel.add(iconLabel, gbc);
|
||||
log.debug("Icon loaded and scaled successfully");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to load icon", e);
|
||||
}
|
||||
|
||||
// URL Label with explicit size
|
||||
brandLabel = new JLabel(initialUrl);
|
||||
brandLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
@@ -63,6 +68,7 @@ public class LoadingWindow extends JDialog {
|
||||
statusLabel.setPreferredSize(new Dimension(300, 25));
|
||||
gbc.gridy = 2;
|
||||
mainPanel.add(statusLabel, gbc);
|
||||
|
||||
// Progress bar with explicit size
|
||||
progressBar = new JProgressBar(0, 100);
|
||||
progressBar.setStringPainted(true);
|
||||
@@ -82,33 +88,78 @@ public class LoadingWindow extends JDialog {
|
||||
setAlwaysOnTop(true);
|
||||
setProgress(0);
|
||||
setStatus("Starting...");
|
||||
|
||||
log.info(
|
||||
"LoadingWindow initialization completed in {}ms",
|
||||
System.currentTimeMillis() - startTime);
|
||||
}
|
||||
|
||||
public void setProgress(final int progress) {
|
||||
SwingUtilities.invokeLater(
|
||||
() -> {
|
||||
try {
|
||||
progressBar.setValue(Math.min(Math.max(progress, 0), 100));
|
||||
progressBar.setString(progress + "%");
|
||||
int validProgress = Math.min(Math.max(progress, 0), 100);
|
||||
log.info(
|
||||
"Setting progress to {}% at {}ms since start",
|
||||
validProgress, System.currentTimeMillis() - startTime);
|
||||
|
||||
// Log additional details when near 90%
|
||||
if (validProgress >= 85 && validProgress <= 95) {
|
||||
log.info(
|
||||
"Near 90% progress - Current status: {}, Window visible: {}, "
|
||||
+ "Progress bar responding: {}, Memory usage: {}MB",
|
||||
statusLabel.getText(),
|
||||
isVisible(),
|
||||
progressBar.isEnabled(),
|
||||
Runtime.getRuntime().totalMemory() / (1024 * 1024));
|
||||
|
||||
// Add thread state logging
|
||||
Thread currentThread = Thread.currentThread();
|
||||
log.debug(
|
||||
"Current thread state - Name: {}, State: {}, Priority: {}",
|
||||
currentThread.getName(),
|
||||
currentThread.getState(),
|
||||
currentThread.getPriority());
|
||||
}
|
||||
|
||||
progressBar.setValue(validProgress);
|
||||
progressBar.setString(validProgress + "%");
|
||||
mainPanel.revalidate();
|
||||
mainPanel.repaint();
|
||||
} catch (Exception e) {
|
||||
log.error("Error updating progress", e);
|
||||
log.error("Error updating progress to " + progress, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setStatus(final String status) {
|
||||
log.info(status);
|
||||
log.info(
|
||||
"Status update at {}ms - Setting status to: {}",
|
||||
System.currentTimeMillis() - startTime,
|
||||
status);
|
||||
|
||||
SwingUtilities.invokeLater(
|
||||
() -> {
|
||||
try {
|
||||
statusLabel.setText(status != null ? status : "");
|
||||
String validStatus = status != null ? status : "";
|
||||
statusLabel.setText(validStatus);
|
||||
|
||||
// Log UI state when status changes
|
||||
log.debug(
|
||||
"UI State - Window visible: {}, Progress: {}%, Status: {}",
|
||||
isVisible(), progressBar.getValue(), validStatus);
|
||||
|
||||
mainPanel.revalidate();
|
||||
mainPanel.repaint();
|
||||
} catch (Exception e) {
|
||||
log.error("Error updating status", e);
|
||||
log.error("Error updating status to: " + status, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
log.info("LoadingWindow disposing after {}ms", System.currentTimeMillis() - startTime);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,16 +136,6 @@ public class AppConfig {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Bean(name = "watchedFoldersDir")
|
||||
public String watchedFoldersDir() {
|
||||
return "./pipeline/watchedFolders/";
|
||||
}
|
||||
|
||||
@Bean(name = "finishedFoldersDir")
|
||||
public String finishedFoldersDir() {
|
||||
return "./pipeline/finishedFolders/";
|
||||
}
|
||||
|
||||
@Bean(name = "directoryFilter")
|
||||
public Predicate<Path> processOnlyFiles() {
|
||||
return path -> {
|
||||
|
||||
@@ -16,27 +16,15 @@ import org.simpleyaml.configuration.comments.CommentType;
|
||||
import org.simpleyaml.configuration.file.YamlFile;
|
||||
import org.simpleyaml.configuration.implementation.SimpleYamlImplementation;
|
||||
import org.simpleyaml.configuration.implementation.snakeyaml.lib.DumperOptions;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ConfigInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
try {
|
||||
ensureConfigExists();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to initialize application configuration", e);
|
||||
}
|
||||
}
|
||||
public class ConfigInitializer {
|
||||
|
||||
public void ensureConfigExists() throws IOException, URISyntaxException {
|
||||
// Define the path to the external config directory
|
||||
Path destPath = Paths.get("configs", "settings.yml");
|
||||
Path destPath = Paths.get(InstallationPathConfig.getSettingsPath());
|
||||
|
||||
// Check if the file already exists
|
||||
if (Files.notExists(destPath)) {
|
||||
@@ -53,10 +41,11 @@ public class ConfigInitializer
|
||||
"Resource file not found: settings.yml.template");
|
||||
}
|
||||
}
|
||||
log.info("Created settings file from template");
|
||||
} else {
|
||||
|
||||
// Define the path to the config settings file
|
||||
Path settingsPath = Paths.get("configs", "settings.yml");
|
||||
Path settingsPath = Paths.get(InstallationPathConfig.getSettingsPath());
|
||||
// Load the template resource
|
||||
URL settingsTemplateResource =
|
||||
getClass().getClassLoader().getResource("settings.yml.template");
|
||||
@@ -120,7 +109,7 @@ public class ConfigInitializer
|
||||
}
|
||||
|
||||
// Create custom settings file if it doesn't exist
|
||||
Path customSettingsPath = Paths.get("configs", "custom_settings.yml");
|
||||
Path customSettingsPath = Paths.get(InstallationPathConfig.getCustomSettingsPath());
|
||||
if (!Files.exists(customSettingsPath)) {
|
||||
Files.createFile(customSettingsPath);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -135,6 +136,7 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Security", "remove-cert-sign");
|
||||
addEndpointToGroup("Security", "sanitize-pdf");
|
||||
addEndpointToGroup("Security", "auto-redact");
|
||||
addEndpointToGroup("Security", "redact");
|
||||
|
||||
// Adding endpoints to "Other" group
|
||||
addEndpointToGroup("Other", "ocr-pdf");
|
||||
@@ -180,7 +182,6 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Python", "extract-image-scans");
|
||||
addEndpointToGroup("Python", "html-to-pdf");
|
||||
addEndpointToGroup("Python", "url-to-pdf");
|
||||
addEndpointToGroup("Python", "pdf-to-img");
|
||||
addEndpointToGroup("Python", "file-to-pdf");
|
||||
|
||||
// openCV
|
||||
@@ -234,6 +235,7 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("Java", "markdown-to-pdf");
|
||||
addEndpointToGroup("Java", "show-javascript");
|
||||
addEndpointToGroup("Java", "auto-redact");
|
||||
addEndpointToGroup("Java", "redact");
|
||||
addEndpointToGroup("Java", "pdf-to-csv");
|
||||
addEndpointToGroup("Java", "split-by-size-or-count");
|
||||
addEndpointToGroup("Java", "overlay-pdf");
|
||||
@@ -265,20 +267,26 @@ public class EndpointConfiguration {
|
||||
}
|
||||
|
||||
private void processEnvironmentConfigs() {
|
||||
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
||||
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
||||
if (!bookAndHtmlFormatsInstalled) {
|
||||
groupsToRemove.add("Calibre");
|
||||
}
|
||||
if (endpointsToRemove != null) {
|
||||
for (String endpoint : endpointsToRemove) {
|
||||
disableEndpoint(endpoint.trim());
|
||||
}
|
||||
}
|
||||
if (applicationProperties != null && applicationProperties.getEndpoints() != null) {
|
||||
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
||||
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
||||
|
||||
if (groupsToRemove != null) {
|
||||
for (String group : groupsToRemove) {
|
||||
disableGroup(group.trim());
|
||||
if (!bookAndHtmlFormatsInstalled) {
|
||||
if (groupsToRemove == null) {
|
||||
groupsToRemove = new ArrayList<>();
|
||||
}
|
||||
groupsToRemove.add("Calibre");
|
||||
}
|
||||
if (endpointsToRemove != null) {
|
||||
for (String endpoint : endpointsToRemove) {
|
||||
disableEndpoint(endpoint.trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (groupsToRemove != null) {
|
||||
for (String group : groupsToRemove) {
|
||||
disableGroup(group.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe
|
||||
String characterEncoding,
|
||||
Map<String, Object> templateResolutionAttributes) {
|
||||
Resource resource =
|
||||
resourceLoader.getResource("file:./customFiles/templates/" + resourceName);
|
||||
resourceLoader.getResource(
|
||||
"file:" + InstallationPathConfig.getTemplatesPath() + resourceName);
|
||||
try {
|
||||
if (resource.exists() && resource.isReadable()) {
|
||||
return new FileTemplateResource(resource.getFile().getPath(), characterEncoding);
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class InstallationPathConfig {
|
||||
private static final String BASE_PATH;
|
||||
|
||||
// Root paths
|
||||
private static final String LOG_PATH;
|
||||
private static final String CONFIG_PATH;
|
||||
private static final String PIPELINE_PATH;
|
||||
private static final String CUSTOM_FILES_PATH;
|
||||
private static final String CLIENT_WEBUI_PATH;
|
||||
|
||||
// Config paths
|
||||
private static final String SETTINGS_PATH;
|
||||
private static final String CUSTOM_SETTINGS_PATH;
|
||||
|
||||
// Pipeline paths
|
||||
private static final String PIPELINE_WATCHED_FOLDERS_PATH;
|
||||
private static final String PIPELINE_FINISHED_FOLDERS_PATH;
|
||||
private static final String PIPELINE_DEFAULT_WEB_UI_CONFIGS;
|
||||
|
||||
// Custom file paths
|
||||
private static final String STATIC_PATH;
|
||||
private static final String TEMPLATES_PATH;
|
||||
private static final String SIGNATURES_PATH;
|
||||
|
||||
static {
|
||||
BASE_PATH = initializeBasePath();
|
||||
|
||||
// Initialize root paths
|
||||
LOG_PATH = BASE_PATH + "logs" + File.separator;
|
||||
CONFIG_PATH = BASE_PATH + "configs" + File.separator;
|
||||
PIPELINE_PATH = BASE_PATH + "pipeline" + File.separator;
|
||||
CUSTOM_FILES_PATH = BASE_PATH + "customFiles" + File.separator;
|
||||
CLIENT_WEBUI_PATH = BASE_PATH + "clientWebUI" + File.separator;
|
||||
|
||||
// Initialize config paths
|
||||
SETTINGS_PATH = CONFIG_PATH + "settings.yml";
|
||||
CUSTOM_SETTINGS_PATH = CONFIG_PATH + "custom_settings.yml";
|
||||
|
||||
// Initialize pipeline paths
|
||||
PIPELINE_WATCHED_FOLDERS_PATH = PIPELINE_PATH + "watchedFolders" + File.separator;
|
||||
PIPELINE_FINISHED_FOLDERS_PATH = PIPELINE_PATH + "finishedFolders" + File.separator;
|
||||
PIPELINE_DEFAULT_WEB_UI_CONFIGS = PIPELINE_PATH + "defaultWebUIConfigs" + File.separator;
|
||||
|
||||
// Initialize custom file paths
|
||||
STATIC_PATH = CUSTOM_FILES_PATH + "static" + File.separator;
|
||||
TEMPLATES_PATH = CUSTOM_FILES_PATH + "templates" + File.separator;
|
||||
SIGNATURES_PATH = CUSTOM_FILES_PATH + "signatures" + File.separator;
|
||||
}
|
||||
|
||||
private static String initializeBasePath() {
|
||||
if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.contains("win")) {
|
||||
return System.getenv("APPDATA") + File.separator + "Stirling-PDF" + File.separator;
|
||||
} else if (os.contains("mac")) {
|
||||
return System.getProperty("user.home")
|
||||
+ File.separator
|
||||
+ "Library"
|
||||
+ File.separator
|
||||
+ "Application Support"
|
||||
+ File.separator
|
||||
+ "Stirling-PDF"
|
||||
+ File.separator;
|
||||
} else {
|
||||
return System.getProperty("user.home")
|
||||
+ File.separator
|
||||
+ ".config"
|
||||
+ File.separator
|
||||
+ "Stirling-PDF"
|
||||
+ File.separator;
|
||||
}
|
||||
}
|
||||
return "./";
|
||||
}
|
||||
|
||||
public static String getPath() {
|
||||
return BASE_PATH;
|
||||
}
|
||||
|
||||
public static String getLogPath() {
|
||||
return LOG_PATH;
|
||||
}
|
||||
|
||||
public static String getConfigPath() {
|
||||
return CONFIG_PATH;
|
||||
}
|
||||
|
||||
public static String getPipelinePath() {
|
||||
return PIPELINE_PATH;
|
||||
}
|
||||
|
||||
public static String getCustomFilesPath() {
|
||||
return CUSTOM_FILES_PATH;
|
||||
}
|
||||
|
||||
public static String getClientWebUIPath() {
|
||||
return CLIENT_WEBUI_PATH;
|
||||
}
|
||||
|
||||
public static String getSettingsPath() {
|
||||
return SETTINGS_PATH;
|
||||
}
|
||||
|
||||
public static String getCustomSettingsPath() {
|
||||
return CUSTOM_SETTINGS_PATH;
|
||||
}
|
||||
|
||||
public static String getPipelineWatchedFoldersDir() {
|
||||
return PIPELINE_WATCHED_FOLDERS_PATH;
|
||||
}
|
||||
|
||||
public static String getPipelineFinishedFoldersDir() {
|
||||
return PIPELINE_FINISHED_FOLDERS_PATH;
|
||||
}
|
||||
|
||||
public static String getPipelineDefaultWebUIConfigsDir() {
|
||||
return PIPELINE_DEFAULT_WEB_UI_CONFIGS;
|
||||
}
|
||||
|
||||
public static String getStaticPath() {
|
||||
return STATIC_PATH;
|
||||
}
|
||||
|
||||
public static String getTemplatesPath() {
|
||||
return TEMPLATES_PATH;
|
||||
}
|
||||
|
||||
public static String getSignaturesPath() {
|
||||
return SIGNATURES_PATH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import ch.qos.logback.core.PropertyDefinerBase;
|
||||
|
||||
public class LogbackPropertyLoader extends PropertyDefinerBase {
|
||||
@Override
|
||||
public String getPropertyValue() {
|
||||
return InstallationPathConfig.getLogPath();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,8 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
// Handler for external static resources
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations("file:customFiles/static/", "classpath:/static/");
|
||||
.addResourceLocations(
|
||||
"file:" + InstallationPathConfig.getStaticPath(), "classpath:/static/");
|
||||
// .setCachePeriod(0); // Optional: disable caching
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ public class YamlPropertySourceFactory implements PropertySourceFactory {
|
||||
throws IOException {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(encodedResource.getResource());
|
||||
|
||||
Properties properties = factory.getObject();
|
||||
|
||||
return new PropertiesPropertySource(
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package stirling.software.SPDF.config.interfaces;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import stirling.software.SPDF.utils.FileInfo;
|
||||
|
||||
public interface DatabaseBackupInterface {
|
||||
|
||||
void exportDatabase() throws IOException;
|
||||
|
||||
boolean importDatabase();
|
||||
|
||||
boolean hasBackup();
|
||||
|
||||
List<FileInfo> getBackupList();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package stirling.software.SPDF.config.interfaces;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
import stirling.software.SPDF.utils.FileInfo;
|
||||
|
||||
public interface DatabaseInterface {
|
||||
void exportDatabase() throws SQLException, UnsupportedProviderException;
|
||||
|
||||
void importDatabase();
|
||||
|
||||
boolean hasBackup();
|
||||
|
||||
List<FileInfo> getBackupList();
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.SPdfApplication;
|
||||
import stirling.software.SPDF.SPDFApplication;
|
||||
import stirling.software.SPDF.config.security.saml2.CertificateUtils;
|
||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
@@ -110,7 +110,7 @@ public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
|
||||
|
||||
// Construct URLs required for SAML configuration
|
||||
String serverUrl =
|
||||
SPdfApplication.getStaticBaseUrl() + ":" + SPdfApplication.getStaticPort();
|
||||
SPDFApplication.getStaticBaseUrl() + ":" + SPDFApplication.getStaticPort();
|
||||
|
||||
String relyingPartyIdentifier =
|
||||
serverUrl + "/saml2/service-provider-metadata/" + registrationId;
|
||||
|
||||
@@ -1,49 +1,56 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@Component
|
||||
public class InitialSecuritySetup {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private final DatabaseBackupInterface databaseBackupHelper;
|
||||
private final DatabaseInterface databaseService;
|
||||
|
||||
public InitialSecuritySetup(
|
||||
UserService userService,
|
||||
ApplicationProperties applicationProperties,
|
||||
DatabaseBackupInterface databaseBackupHelper) {
|
||||
DatabaseInterface databaseService) {
|
||||
this.userService = userService;
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.databaseBackupHelper = databaseBackupHelper;
|
||||
this.databaseService = databaseService;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws IllegalArgumentException, IOException {
|
||||
if (databaseBackupHelper.hasBackup() && !userService.hasUsers()) {
|
||||
databaseBackupHelper.importDatabase();
|
||||
} else if (!userService.hasUsers()) {
|
||||
initializeAdminUser();
|
||||
} else {
|
||||
databaseBackupHelper.exportDatabase();
|
||||
public void init() {
|
||||
try {
|
||||
if (databaseService.hasBackup()) {
|
||||
databaseService.importDatabase();
|
||||
}
|
||||
|
||||
if (!userService.hasUsers()) {
|
||||
initializeAdminUser();
|
||||
}
|
||||
|
||||
userService.migrateOauth2ToSSO();
|
||||
initializeInternalApiUser();
|
||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||
log.error("Failed to initialize security setup.", e);
|
||||
System.exit(1);
|
||||
}
|
||||
initializeInternalApiUser();
|
||||
}
|
||||
|
||||
private void initializeAdminUser() throws IOException {
|
||||
private void initializeAdminUser() throws SQLException, UnsupportedProviderException {
|
||||
String initialUsername =
|
||||
applicationProperties.getSecurity().getInitialLogin().getUsername();
|
||||
String initialPassword =
|
||||
@@ -52,36 +59,34 @@ public class InitialSecuritySetup {
|
||||
&& !initialUsername.isEmpty()
|
||||
&& initialPassword != null
|
||||
&& !initialPassword.isEmpty()
|
||||
&& !userService.findByUsernameIgnoreCase(initialUsername).isPresent()) {
|
||||
try {
|
||||
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||
log.info("Admin user created: " + initialUsername);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("Failed to initialize security setup", e);
|
||||
System.exit(1);
|
||||
}
|
||||
&& userService.findByUsernameIgnoreCase(initialUsername).isEmpty()) {
|
||||
|
||||
userService.saveUser(initialUsername, initialPassword, Role.ADMIN.getRoleId());
|
||||
log.info("Admin user created: {}", initialUsername);
|
||||
} else {
|
||||
createDefaultAdminUser();
|
||||
}
|
||||
}
|
||||
|
||||
private void createDefaultAdminUser() throws IllegalArgumentException, IOException {
|
||||
private void createDefaultAdminUser() throws SQLException, UnsupportedProviderException {
|
||||
String defaultUsername = "admin";
|
||||
String defaultPassword = "stirling";
|
||||
if (!userService.findByUsernameIgnoreCase(defaultUsername).isPresent()) {
|
||||
|
||||
if (userService.findByUsernameIgnoreCase(defaultUsername).isEmpty()) {
|
||||
userService.saveUser(defaultUsername, defaultPassword, Role.ADMIN.getRoleId(), true);
|
||||
log.info("Default admin user created: " + defaultUsername);
|
||||
log.info("Default admin user created: {}", defaultUsername);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeInternalApiUser() throws IllegalArgumentException, IOException {
|
||||
private void initializeInternalApiUser()
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) {
|
||||
userService.saveUser(
|
||||
Role.INTERNAL_API_USER.getRoleId(),
|
||||
UUID.randomUUID().toString(),
|
||||
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());
|
||||
}
|
||||
userService.syncCustomApiUser(applicationProperties.getSecurity().getCustomGlobalAPIKey());
|
||||
}
|
||||
|
||||
@@ -1,39 +1,24 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
|
||||
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
import org.springframework.security.saml2.core.Saml2X509Credential;
|
||||
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
|
||||
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
@@ -43,24 +28,16 @@ import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationFailureHandler;
|
||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2AuthenticationSuccessHandler;
|
||||
import stirling.software.SPDF.config.security.oauth2.CustomOAuth2UserService;
|
||||
import stirling.software.SPDF.config.security.saml2.CertificateUtils;
|
||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationFailureHandler;
|
||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticationSuccessHandler;
|
||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2ResponseAuthenticationConverter;
|
||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||
import stirling.software.SPDF.repository.JPATokenRepositoryImpl;
|
||||
import stirling.software.SPDF.repository.PersistentLoginRepository;
|
||||
|
||||
@@ -72,7 +49,7 @@ import stirling.software.SPDF.repository.PersistentLoginRepository;
|
||||
public class SecurityConfiguration {
|
||||
|
||||
private final CustomUserDetailsService userDetailsService;
|
||||
@Lazy private final UserService userService;
|
||||
private final UserService userService;
|
||||
|
||||
@Qualifier("loginEnabled")
|
||||
private final boolean loginEnabledValue;
|
||||
@@ -86,16 +63,10 @@ public class SecurityConfiguration {
|
||||
private final FirstLoginFilter firstLoginFilter;
|
||||
private final SessionPersistentRegistry sessionRegistry;
|
||||
private final PersistentLoginRepository persistentLoginRepository;
|
||||
private final GrantedAuthoritiesMapper oAuth2userAuthoritiesMapper;
|
||||
private final RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations;
|
||||
private final OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver;
|
||||
|
||||
// // Only Dev test
|
||||
// @Bean
|
||||
// public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
// return (web) ->
|
||||
// web.ignoring()
|
||||
// .requestMatchers(
|
||||
// "/css/**", "/images/**", "/js/**", "/**.svg",
|
||||
// "/pdfjs-legacy/**");
|
||||
// }
|
||||
public SecurityConfiguration(
|
||||
PersistentLoginRepository persistentLoginRepository,
|
||||
CustomUserDetailsService userDetailsService,
|
||||
@@ -106,7 +77,12 @@ public class SecurityConfiguration {
|
||||
UserAuthenticationFilter userAuthenticationFilter,
|
||||
LoginAttemptService loginAttemptService,
|
||||
FirstLoginFilter firstLoginFilter,
|
||||
SessionPersistentRegistry sessionRegistry) {
|
||||
SessionPersistentRegistry sessionRegistry,
|
||||
@Autowired(required = false) GrantedAuthoritiesMapper oAuth2userAuthoritiesMapper,
|
||||
@Autowired(required = false)
|
||||
RelyingPartyRegistrationRepository saml2RelyingPartyRegistrations,
|
||||
@Autowired(required = false)
|
||||
OpenSaml4AuthenticationRequestResolver saml2AuthenticationRequestResolver) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.userService = userService;
|
||||
this.loginEnabledValue = loginEnabledValue;
|
||||
@@ -117,6 +93,9 @@ public class SecurityConfiguration {
|
||||
this.firstLoginFilter = firstLoginFilter;
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
this.persistentLoginRepository = persistentLoginRepository;
|
||||
this.oAuth2userAuthoritiesMapper = oAuth2userAuthoritiesMapper;
|
||||
this.saml2RelyingPartyRegistrations = saml2RelyingPartyRegistrations;
|
||||
this.saml2AuthenticationRequestResolver = saml2AuthenticationRequestResolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -274,7 +253,7 @@ public class SecurityConfiguration {
|
||||
userService,
|
||||
loginAttemptService))
|
||||
.userAuthoritiesMapper(
|
||||
userAuthoritiesMapper()))
|
||||
oAuth2userAuthoritiesMapper))
|
||||
.permitAll());
|
||||
}
|
||||
// Handle SAML
|
||||
@@ -291,7 +270,7 @@ public class SecurityConfiguration {
|
||||
try {
|
||||
saml2.loginPage("/saml2")
|
||||
.relyingPartyRegistrationRepository(
|
||||
relyingPartyRegistrations())
|
||||
saml2RelyingPartyRegistrations)
|
||||
.authenticationManager(
|
||||
new ProviderManager(authenticationProvider))
|
||||
.successHandler(
|
||||
@@ -302,8 +281,7 @@ public class SecurityConfiguration {
|
||||
.failureHandler(
|
||||
new CustomSaml2AuthenticationFailureHandler())
|
||||
.authenticationRequestResolver(
|
||||
authenticationRequestResolver(
|
||||
relyingPartyRegistrations()));
|
||||
saml2AuthenticationRequestResolver);
|
||||
} catch (Exception e) {
|
||||
log.error("Error configuring SAML2 login", e);
|
||||
throw new RuntimeException(e);
|
||||
@@ -311,244 +289,11 @@ public class SecurityConfiguration {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// if (!applicationProperties.getSecurity().getCsrfDisabled()) {
|
||||
// CookieCsrfTokenRepository cookieRepo =
|
||||
// CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||||
// CsrfTokenRequestAttributeHandler requestHandler =
|
||||
// new CsrfTokenRequestAttributeHandler();
|
||||
// requestHandler.setCsrfRequestAttributeName(null);
|
||||
// http.csrf(
|
||||
// csrf ->
|
||||
// csrf.csrfTokenRepository(cookieRepo)
|
||||
// .csrfTokenRequestHandler(requestHandler));
|
||||
// }
|
||||
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
|
||||
}
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
value = "security.oauth2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||
List<ClientRegistration> registrations = new ArrayList<>();
|
||||
githubClientRegistration().ifPresent(registrations::add);
|
||||
oidcClientRegistration().ifPresent(registrations::add);
|
||||
googleClientRegistration().ifPresent(registrations::add);
|
||||
keycloakClientRegistration().ifPresent(registrations::add);
|
||||
if (registrations.isEmpty()) {
|
||||
log.error("At least one OAuth2 provider must be configured");
|
||||
System.exit(1);
|
||||
}
|
||||
return new InMemoryClientRegistrationRepository(registrations);
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> googleClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null || !oauth.getEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Client client = oauth.getClient();
|
||||
if (client == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
GoogleProvider google = client.getGoogle();
|
||||
return google != null && google.isSettingsValid()
|
||||
? Optional.of(
|
||||
ClientRegistration.withRegistrationId(google.getName())
|
||||
.clientId(google.getClientId())
|
||||
.clientSecret(google.getClientSecret())
|
||||
.scope(google.getScopes())
|
||||
.authorizationUri(google.getAuthorizationuri())
|
||||
.tokenUri(google.getTokenuri())
|
||||
.userInfoUri(google.getUserinfouri())
|
||||
.userNameAttributeName(google.getUseAsUsername())
|
||||
.clientName(google.getClientName())
|
||||
.redirectUri("{baseUrl}/login/oauth2/code/" + google.getName())
|
||||
.authorizationGrantType(
|
||||
org.springframework.security.oauth2.core
|
||||
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.build())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> keycloakClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null || !oauth.getEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Client client = oauth.getClient();
|
||||
if (client == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
KeycloakProvider keycloak = client.getKeycloak();
|
||||
return keycloak != null && keycloak.isSettingsValid()
|
||||
? Optional.of(
|
||||
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
||||
.registrationId(keycloak.getName())
|
||||
.clientId(keycloak.getClientId())
|
||||
.clientSecret(keycloak.getClientSecret())
|
||||
.scope(keycloak.getScopes())
|
||||
.userNameAttributeName(keycloak.getUseAsUsername())
|
||||
.clientName(keycloak.getClientName())
|
||||
.build())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> githubClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null || !oauth.getEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Client client = oauth.getClient();
|
||||
if (client == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
GithubProvider github = client.getGithub();
|
||||
return github != null && github.isSettingsValid()
|
||||
? Optional.of(
|
||||
ClientRegistration.withRegistrationId(github.getName())
|
||||
.clientId(github.getClientId())
|
||||
.clientSecret(github.getClientSecret())
|
||||
.scope(github.getScopes())
|
||||
.authorizationUri(github.getAuthorizationuri())
|
||||
.tokenUri(github.getTokenuri())
|
||||
.userInfoUri(github.getUserinfouri())
|
||||
.userNameAttributeName(github.getUseAsUsername())
|
||||
.clientName(github.getClientName())
|
||||
.redirectUri("{baseUrl}/login/oauth2/code/" + github.getName())
|
||||
.authorizationGrantType(
|
||||
org.springframework.security.oauth2.core
|
||||
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.build())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> oidcClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null
|
||||
|| oauth.getIssuer() == null
|
||||
|| oauth.getIssuer().isEmpty()
|
||||
|| oauth.getClientId() == null
|
||||
|| oauth.getClientId().isEmpty()
|
||||
|| oauth.getClientSecret() == null
|
||||
|| oauth.getClientSecret().isEmpty()
|
||||
|| oauth.getScopes() == null
|
||||
|| oauth.getScopes().isEmpty()
|
||||
|| oauth.getUseAsUsername() == null
|
||||
|| oauth.getUseAsUsername().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(
|
||||
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
||||
.registrationId("oidc")
|
||||
.clientId(oauth.getClientId())
|
||||
.clientSecret(oauth.getClientSecret())
|
||||
.scope(oauth.getScopes())
|
||||
.userNameAttributeName(oauth.getUseAsUsername())
|
||||
.clientName("OIDC")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
name = "security.saml2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
||||
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
||||
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
||||
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
|
||||
Resource privateKeyResource = samlConf.getPrivateKey();
|
||||
Resource certificateResource = samlConf.getSpCert();
|
||||
Saml2X509Credential signingCredential =
|
||||
new Saml2X509Credential(
|
||||
CertificateUtils.readPrivateKey(privateKeyResource),
|
||||
CertificateUtils.readCertificate(certificateResource),
|
||||
Saml2X509CredentialType.SIGNING);
|
||||
RelyingPartyRegistration rp =
|
||||
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
|
||||
.signingX509Credentials(c -> c.add(signingCredential))
|
||||
.assertingPartyMetadata(
|
||||
metadata ->
|
||||
metadata.entityId(samlConf.getIdpIssuer())
|
||||
.singleSignOnServiceLocation(
|
||||
samlConf.getIdpSingleLoginUrl())
|
||||
.verificationX509Credentials(
|
||||
c -> c.add(verificationCredential))
|
||||
.singleSignOnServiceBinding(
|
||||
Saml2MessageBinding.POST)
|
||||
.wantAuthnRequestsSigned(true))
|
||||
.build();
|
||||
return new InMemoryRelyingPartyRegistrationRepository(rp);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
name = "security.saml2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
|
||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
|
||||
OpenSaml4AuthenticationRequestResolver resolver =
|
||||
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
|
||||
resolver.setAuthnRequestCustomizer(
|
||||
customizer -> {
|
||||
log.debug("Customizing SAML Authentication request");
|
||||
AuthnRequest authnRequest = customizer.getAuthnRequest();
|
||||
log.debug("AuthnRequest ID: {}", authnRequest.getID());
|
||||
if (authnRequest.getID() == null) {
|
||||
authnRequest.setID("ARQ" + UUID.randomUUID().toString());
|
||||
}
|
||||
log.debug("AuthnRequest new ID after set: {}", authnRequest.getID());
|
||||
log.debug("AuthnRequest IssueInstant: {}", authnRequest.getIssueInstant());
|
||||
log.debug(
|
||||
"AuthnRequest Issuer: {}",
|
||||
authnRequest.getIssuer() != null
|
||||
? authnRequest.getIssuer().getValue()
|
||||
: "null");
|
||||
HttpServletRequest request = customizer.getRequest();
|
||||
// Log HTTP request details
|
||||
log.debug("HTTP Request Method: {}", request.getMethod());
|
||||
log.debug("Request URI: {}", request.getRequestURI());
|
||||
log.debug("Request URL: {}", request.getRequestURL().toString());
|
||||
log.debug("Query String: {}", request.getQueryString());
|
||||
log.debug("Remote Address: {}", request.getRemoteAddr());
|
||||
// Log headers
|
||||
Collections.list(request.getHeaderNames())
|
||||
.forEach(
|
||||
headerName -> {
|
||||
log.debug(
|
||||
"Header - {}: {}",
|
||||
headerName,
|
||||
request.getHeader(headerName));
|
||||
});
|
||||
// Log SAML specific parameters
|
||||
log.debug("SAML Request Parameters:");
|
||||
log.debug("SAMLRequest: {}", request.getParameter("SAMLRequest"));
|
||||
log.debug("RelayState: {}", request.getParameter("RelayState"));
|
||||
// Log session debugrmation if exists
|
||||
if (request.getSession(false) != null) {
|
||||
log.debug("Session ID: {}", request.getSession().getId());
|
||||
}
|
||||
// Log any assertions consumer service details if present
|
||||
if (authnRequest.getAssertionConsumerServiceURL() != null) {
|
||||
log.debug(
|
||||
"AssertionConsumerServiceURL: {}",
|
||||
authnRequest.getAssertionConsumerServiceURL());
|
||||
}
|
||||
// Log NameID policy if present
|
||||
if (authnRequest.getNameIDPolicy() != null) {
|
||||
log.debug(
|
||||
"NameIDPolicy Format: {}",
|
||||
authnRequest.getNameIDPolicy().getFormat());
|
||||
}
|
||||
});
|
||||
return resolver;
|
||||
}
|
||||
|
||||
public DaoAuthenticationProvider daoAuthenticationProvider() {
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
@@ -556,46 +301,6 @@ public class SecurityConfiguration {
|
||||
return provider;
|
||||
}
|
||||
|
||||
/*
|
||||
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
|
||||
This is required for the internal; 'hasRole()' function to give out the correct role.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
value = "security.oauth2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
||||
return (authorities) -> {
|
||||
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
||||
authorities.forEach(
|
||||
authority -> {
|
||||
// Add existing OAUTH2 Authorities
|
||||
mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
|
||||
// Add Authorities from database for existing user, if user is present.
|
||||
if (authority instanceof OAuth2UserAuthority oauth2Auth) {
|
||||
String useAsUsername =
|
||||
applicationProperties
|
||||
.getSecurity()
|
||||
.getOauth2()
|
||||
.getUseAsUsername();
|
||||
Optional<User> userOpt =
|
||||
userService.findByUsernameIgnoreCase(
|
||||
(String) oauth2Auth.getAttributes().get(useAsUsername));
|
||||
if (userOpt.isPresent()) {
|
||||
User user = userOpt.get();
|
||||
if (user != null) {
|
||||
mappedAuthorities.add(
|
||||
new SimpleGrantedAuthority(
|
||||
userService.findRole(user).getAuthority()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return mappedAuthorities;
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IPRateLimitingFilter rateLimitingFilter() {
|
||||
// Example limit TODO add config level
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -20,11 +21,12 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||
import stirling.software.SPDF.config.security.saml2.CustomSaml2AuthenticatedPrincipal;
|
||||
import stirling.software.SPDF.config.security.session.SessionPersistentRegistry;
|
||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||
import stirling.software.SPDF.model.*;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
import stirling.software.SPDF.repository.AuthorityRepository;
|
||||
import stirling.software.SPDF.repository.UserRepository;
|
||||
|
||||
@@ -42,7 +44,7 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
private final SessionPersistentRegistry sessionRegistry;
|
||||
|
||||
private final DatabaseBackupInterface databaseBackupHelper;
|
||||
private final DatabaseInterface databaseService;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@@ -52,14 +54,14 @@ public class UserService implements UserServiceInterface {
|
||||
PasswordEncoder passwordEncoder,
|
||||
MessageSource messageSource,
|
||||
SessionPersistentRegistry sessionRegistry,
|
||||
DatabaseBackupInterface databaseBackupHelper,
|
||||
DatabaseInterface databaseService,
|
||||
ApplicationProperties applicationProperties) {
|
||||
this.userRepository = userRepository;
|
||||
this.authorityRepository = authorityRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.messageSource = messageSource;
|
||||
this.sessionRegistry = sessionRegistry;
|
||||
this.databaseBackupHelper = databaseBackupHelper;
|
||||
this.databaseService = databaseService;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@@ -76,7 +78,7 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
// Handle OAUTH2 login and user auto creation.
|
||||
public boolean processSSOPostLogin(String username, boolean autoCreateUser)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
if (!isUsernameValid(username)) {
|
||||
return false;
|
||||
}
|
||||
@@ -163,12 +165,12 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public void saveUser(String username, AuthenticationType authenticationType)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
saveUser(username, authenticationType, Role.USER.getRoleId());
|
||||
}
|
||||
|
||||
public void saveUser(String username, AuthenticationType authenticationType, String role)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
if (!isUsernameValid(username)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
@@ -179,11 +181,11 @@ public class UserService implements UserServiceInterface {
|
||||
user.addAuthority(new Authority(role, user));
|
||||
user.setAuthenticationType(authenticationType);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public void saveUser(String username, String password)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
if (!isUsernameValid(username)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
@@ -193,11 +195,11 @@ public class UserService implements UserServiceInterface {
|
||||
user.setEnabled(true);
|
||||
user.setAuthenticationType(AuthenticationType.WEB);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public void saveUser(String username, String password, String role, boolean firstLogin)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
if (!isUsernameValid(username)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
@@ -209,11 +211,11 @@ public class UserService implements UserServiceInterface {
|
||||
user.setAuthenticationType(AuthenticationType.WEB);
|
||||
user.setFirstLogin(firstLogin);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public void saveUser(String username, String password, String role)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
saveUser(username, password, role, false);
|
||||
}
|
||||
|
||||
@@ -247,7 +249,7 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public void updateUserSettings(String username, Map<String, String> updates)
|
||||
throws IOException {
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
Optional<User> userOpt = findByUsernameIgnoreCaseWithSettings(username);
|
||||
if (userOpt.isPresent()) {
|
||||
User user = userOpt.get();
|
||||
@@ -259,7 +261,7 @@ public class UserService implements UserServiceInterface {
|
||||
settingsMap.putAll(updates);
|
||||
user.setSettings(settingsMap);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,38 +282,45 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
public void changeUsername(User user, String newUsername)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException,
|
||||
IOException,
|
||||
SQLException,
|
||||
UnsupportedProviderException {
|
||||
if (!isUsernameValid(newUsername)) {
|
||||
throw new IllegalArgumentException(getInvalidUsernameMessage());
|
||||
}
|
||||
user.setUsername(newUsername);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public void changePassword(User user, String newPassword) throws IOException {
|
||||
public void changePassword(User user, String newPassword)
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
user.setPassword(passwordEncoder.encode(newPassword));
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public void changeFirstUse(User user, boolean firstUse) throws IOException {
|
||||
public void changeFirstUse(User user, boolean firstUse)
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
user.setFirstLogin(firstUse);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public void changeRole(User user, String newRole) throws IOException {
|
||||
public void changeRole(User user, String newRole)
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
Authority userAuthority = this.findRole(user);
|
||||
userAuthority.setAuthority(newRole);
|
||||
authorityRepository.save(userAuthority);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public void changeUserEnabled(User user, Boolean enbeled) throws IOException {
|
||||
public void changeUserEnabled(User user, Boolean enbeled)
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
user.setEnabled(enbeled);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
|
||||
public boolean isPasswordCorrect(User user, String currentPassword) {
|
||||
@@ -320,12 +329,16 @@ public class UserService implements UserServiceInterface {
|
||||
|
||||
public boolean isUsernameValid(String username) {
|
||||
// Checks whether the simple username is formatted correctly
|
||||
// Regular expression for user name: Min. 3 characters, max. 50 characters
|
||||
boolean isValidSimpleUsername =
|
||||
username.matches("^[a-zA-Z0-9][a-zA-Z0-9@._+-]*[a-zA-Z0-9]$");
|
||||
username.matches("^[a-zA-Z0-9](?!.*[-@._+]{2,})[a-zA-Z0-9@._+-]{1,48}[a-zA-Z0-9]$");
|
||||
|
||||
// Checks whether the email address is formatted correctly
|
||||
// Regular expression for email addresses: Max. 320 characters, with RFC-like validation
|
||||
boolean isValidEmail =
|
||||
username.matches(
|
||||
"^(?=.{1,64}@)[A-Za-z0-9]+(\\.[A-Za-z0-9_+.-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$");
|
||||
"^(?=.{1,320}$)(?=.{1,64}@)[A-Za-z0-9](?:[A-Za-z0-9_.+-]*[A-Za-z0-9])?@[^-][A-Za-z0-9-]+(?:\\\\.[A-Za-z0-9-]+)*(?:\\\\.[A-Za-z]{2,})$");
|
||||
|
||||
List<String> notAllowedUserList = new ArrayList<>();
|
||||
notAllowedUserList.add("ALL_USERS".toLowerCase());
|
||||
boolean notAllowedUser = notAllowedUserList.contains(username.toLowerCase());
|
||||
@@ -397,7 +410,8 @@ public class UserService implements UserServiceInterface {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void syncCustomApiUser(String customApiKey) throws IOException {
|
||||
public void syncCustomApiUser(String customApiKey)
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
if (customApiKey == null || customApiKey.trim().length() == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -414,14 +428,14 @@ public class UserService implements UserServiceInterface {
|
||||
user.setApiKey(customApiKey);
|
||||
user.addAuthority(new Authority(Role.INTERNAL_API_USER.getRoleId(), user));
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
} else {
|
||||
// Update API key if it has changed
|
||||
User user = existingUser.get();
|
||||
if (!customApiKey.equals(user.getApiKey())) {
|
||||
user.setApiKey(customApiKey);
|
||||
userRepository.save(user);
|
||||
databaseBackupHelper.exportDatabase();
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.sql.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseBackupInterface;
|
||||
import stirling.software.SPDF.utils.FileInfo;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class DatabaseBackupHelper implements DatabaseBackupInterface {
|
||||
|
||||
@Value("${spring.datasource.url}")
|
||||
private String url;
|
||||
|
||||
@Value("${spring.datasource.username}")
|
||||
private String databaseUsername;
|
||||
|
||||
@Value("${spring.datasource.password}")
|
||||
private String databasePassword;
|
||||
|
||||
private Path backupPath = Paths.get("configs/db/backup/");
|
||||
|
||||
@Override
|
||||
public boolean hasBackup() {
|
||||
// Check if there is at least one backup
|
||||
return !getBackupList().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileInfo> getBackupList() {
|
||||
// Check if the backup directory exists, and create it if it does not
|
||||
ensureBackupDirectoryExists();
|
||||
|
||||
List<FileInfo> backupFiles = new ArrayList<>();
|
||||
|
||||
// Read the backup directory and filter for files with the prefix "backup_" and suffix
|
||||
// ".sql"
|
||||
try (DirectoryStream<Path> stream =
|
||||
Files.newDirectoryStream(
|
||||
backupPath,
|
||||
path ->
|
||||
path.getFileName().toString().startsWith("backup_")
|
||||
&& path.getFileName().toString().endsWith(".sql"))) {
|
||||
for (Path entry : stream) {
|
||||
BasicFileAttributes attrs = Files.readAttributes(entry, BasicFileAttributes.class);
|
||||
LocalDateTime modificationDate =
|
||||
LocalDateTime.ofInstant(
|
||||
attrs.lastModifiedTime().toInstant(), ZoneId.systemDefault());
|
||||
LocalDateTime creationDate =
|
||||
LocalDateTime.ofInstant(
|
||||
attrs.creationTime().toInstant(), ZoneId.systemDefault());
|
||||
long fileSize = attrs.size();
|
||||
backupFiles.add(
|
||||
new FileInfo(
|
||||
entry.getFileName().toString(),
|
||||
entry.toString(),
|
||||
modificationDate,
|
||||
fileSize,
|
||||
creationDate));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading backup directory: {}", e.getMessage(), e);
|
||||
}
|
||||
return backupFiles;
|
||||
}
|
||||
|
||||
// Imports a database backup from the specified file.
|
||||
public boolean importDatabaseFromUI(String fileName) throws IOException {
|
||||
return this.importDatabaseFromUI(getBackupFilePath(fileName));
|
||||
}
|
||||
|
||||
// Imports a database backup from the specified path.
|
||||
public boolean importDatabaseFromUI(Path tempTemplatePath) throws IOException {
|
||||
boolean success = executeDatabaseScript(tempTemplatePath);
|
||||
if (success) {
|
||||
LocalDateTime dateNow = LocalDateTime.now();
|
||||
DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
|
||||
Path insertOutputFilePath =
|
||||
this.getBackupFilePath("backup_user_" + dateNow.format(myFormatObj) + ".sql");
|
||||
Files.copy(tempTemplatePath, insertOutputFilePath);
|
||||
Files.deleteIfExists(tempTemplatePath);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean importDatabase() {
|
||||
if (!this.hasBackup()) return false;
|
||||
|
||||
List<FileInfo> backupList = this.getBackupList();
|
||||
backupList.sort(Comparator.comparing(FileInfo::getModificationDate).reversed());
|
||||
|
||||
return executeDatabaseScript(Paths.get(backupList.get(0).getFilePath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportDatabase() throws IOException {
|
||||
// Check if the backup directory exists, and create it if it does not
|
||||
ensureBackupDirectoryExists();
|
||||
|
||||
// Filter and delete old backups if there are more than 5
|
||||
List<FileInfo> filteredBackupList =
|
||||
this.getBackupList().stream()
|
||||
.filter(backup -> !backup.getFileName().startsWith("backup_user_"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (filteredBackupList.size() > 5) {
|
||||
filteredBackupList.sort(
|
||||
Comparator.comparing(
|
||||
p -> p.getFileName().substring(7, p.getFileName().length() - 4)));
|
||||
Files.deleteIfExists(Paths.get(filteredBackupList.get(0).getFilePath()));
|
||||
log.info("Deleted oldest backup: {}", filteredBackupList.get(0).getFileName());
|
||||
}
|
||||
|
||||
LocalDateTime dateNow = LocalDateTime.now();
|
||||
DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
|
||||
Path insertOutputFilePath =
|
||||
this.getBackupFilePath("backup_" + dateNow.format(myFormatObj) + ".sql");
|
||||
String query = "SCRIPT SIMPLE COLUMNS DROP to ?;";
|
||||
|
||||
try (Connection conn =
|
||||
DriverManager.getConnection(url, databaseUsername, databasePassword);
|
||||
PreparedStatement stmt = conn.prepareStatement(query)) {
|
||||
stmt.setString(1, insertOutputFilePath.toString());
|
||||
stmt.execute();
|
||||
log.info("Database export completed: {}", insertOutputFilePath);
|
||||
} catch (SQLException e) {
|
||||
log.error("Error during database export: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieves the H2 database version.
|
||||
public String getH2Version() {
|
||||
String version = "Unknown";
|
||||
try (Connection conn =
|
||||
DriverManager.getConnection(url, databaseUsername, databasePassword)) {
|
||||
try (Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT H2VERSION() AS version")) {
|
||||
if (rs.next()) {
|
||||
version = rs.getString("version");
|
||||
log.info("H2 Database Version: {}", version);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Error retrieving H2 version: {}", e.getMessage(), e);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
// Deletes a backup file.
|
||||
public boolean deleteBackupFile(String fileName) throws IOException {
|
||||
if (!isValidFileName(fileName)) {
|
||||
log.error("Invalid file name: {}", fileName);
|
||||
return false;
|
||||
}
|
||||
Path filePath = this.getBackupFilePath(fileName);
|
||||
if (Files.deleteIfExists(filePath)) {
|
||||
log.info("Deleted backup file: {}", fileName);
|
||||
return true;
|
||||
} else {
|
||||
log.error("File not found or could not be deleted: {}", fileName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the Path object for a given backup file name.
|
||||
public Path getBackupFilePath(String fileName) {
|
||||
Path filePath = Paths.get(backupPath.toString(), fileName).normalize();
|
||||
if (!filePath.startsWith(backupPath)) {
|
||||
throw new SecurityException("Path traversal detected");
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
private boolean executeDatabaseScript(Path scriptPath) {
|
||||
String query = "RUNSCRIPT from ?;";
|
||||
|
||||
try (Connection conn =
|
||||
DriverManager.getConnection(url, databaseUsername, databasePassword);
|
||||
PreparedStatement stmt = conn.prepareStatement(query)) {
|
||||
stmt.setString(1, scriptPath.toString());
|
||||
stmt.execute();
|
||||
log.info("Database import completed: {}", scriptPath);
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
log.error("Error during database import: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureBackupDirectoryExists() {
|
||||
if (Files.notExists(backupPath)) {
|
||||
try {
|
||||
Files.createDirectories(backupPath);
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating directories: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidFileName(String fileName) {
|
||||
// Check for invalid characters or sequences
|
||||
return fileName != null
|
||||
&& !fileName.contains("..")
|
||||
&& !fileName.contains("/")
|
||||
&& !fileName.contains("\\")
|
||||
&& !fileName.contains(":")
|
||||
&& !fileName.contains("*")
|
||||
&& !fileName.contains("?")
|
||||
&& !fileName.contains("\"")
|
||||
&& !fileName.contains("<")
|
||||
&& !fileName.contains(">")
|
||||
&& !fileName.contains("|");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Configuration
|
||||
public class DatabaseConfig {
|
||||
|
||||
public final String DATASOURCE_DEFAULT_URL;
|
||||
|
||||
public static final String DATASOURCE_URL_TEMPLATE = "jdbc:%s://%s:%4d/%s";
|
||||
public static final String DEFAULT_DRIVER = "org.h2.Driver";
|
||||
public static final String DEFAULT_USERNAME = "sa";
|
||||
public static final String POSTGRES_DRIVER = "org.postgresql.Driver";
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final boolean runningEE;
|
||||
|
||||
public DatabaseConfig(
|
||||
ApplicationProperties applicationProperties,
|
||||
@Qualifier("runningEE") boolean runningEE) {
|
||||
DATASOURCE_DEFAULT_URL = "jdbc:h2:file:" + InstallationPathConfig.getConfigPath() + File.separator + "stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE";
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.runningEE = runningEE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <code>DataSource</code> for the connection to the DB. If <code>useDefault</code>
|
||||
* is set to <code>true</code>, it will use the default H2 DB. If it is set to <code>false
|
||||
* </code>, it will use the user's custom configuration set in the settings.yml.
|
||||
*
|
||||
* @return a <code>DataSource</code> using the configuration settings in the settings.yml
|
||||
* @throws UnsupportedProviderException if the type of database selected is not supported
|
||||
*/
|
||||
@Bean
|
||||
@Qualifier("dataSource")
|
||||
public DataSource dataSource() throws UnsupportedProviderException {
|
||||
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
|
||||
|
||||
if (!runningEE) {
|
||||
return useDefaultDataSource(dataSourceBuilder);
|
||||
}
|
||||
|
||||
ApplicationProperties.System system = applicationProperties.getSystem();
|
||||
ApplicationProperties.Datasource datasource = system.getDatasource();
|
||||
|
||||
if (!datasource.isEnableCustomDatabase()) {
|
||||
return useDefaultDataSource(dataSourceBuilder);
|
||||
}
|
||||
|
||||
log.info("Using custom database configuration");
|
||||
|
||||
if (!datasource.getCustomDatabaseUrl().isBlank()) {
|
||||
if (datasource.getCustomDatabaseUrl().contains("postgresql")) {
|
||||
dataSourceBuilder.driverClassName(POSTGRES_DRIVER);
|
||||
}
|
||||
|
||||
dataSourceBuilder.url(datasource.getCustomDatabaseUrl());
|
||||
} else {
|
||||
dataSourceBuilder.driverClassName(getDriverClassName(datasource.getType()));
|
||||
dataSourceBuilder.url(
|
||||
generateCustomDataSourceUrl(
|
||||
datasource.getType(),
|
||||
datasource.getHostName(),
|
||||
datasource.getPort(),
|
||||
datasource.getName()));
|
||||
}
|
||||
dataSourceBuilder.username(datasource.getUsername());
|
||||
dataSourceBuilder.password(datasource.getPassword());
|
||||
|
||||
return dataSourceBuilder.build();
|
||||
}
|
||||
|
||||
private DataSource useDefaultDataSource(DataSourceBuilder<?> dataSourceBuilder) {
|
||||
log.info("Using default H2 database");
|
||||
|
||||
dataSourceBuilder.url(DATASOURCE_DEFAULT_URL);
|
||||
dataSourceBuilder.username(DEFAULT_USERNAME);
|
||||
|
||||
return dataSourceBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the URL the <code>DataSource</code> will use to connect to the database
|
||||
*
|
||||
* @param dataSourceType the type of the database
|
||||
* @param hostname the host name
|
||||
* @param port the port number to use for the database
|
||||
* @param dataSourceName the name the database to connect to
|
||||
* @return the <code>DataSource</code> URL
|
||||
*/
|
||||
private String generateCustomDataSourceUrl(
|
||||
String dataSourceType, String hostname, Integer port, String dataSourceName) {
|
||||
return DATASOURCE_URL_TEMPLATE.formatted(dataSourceType, hostname, port, dataSourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the database driver based on the type of database chosen.
|
||||
*
|
||||
* @param driverName the type of the driver (e.g. 'h2', 'postgresql')
|
||||
* @return the fully qualified driver for the database chosen
|
||||
* @throws UnsupportedProviderException when an unsupported database is selected
|
||||
*/
|
||||
private String getDriverClassName(String driverName) throws UnsupportedProviderException {
|
||||
try {
|
||||
ApplicationProperties.Driver driver =
|
||||
ApplicationProperties.Driver.valueOf(driverName.toUpperCase());
|
||||
|
||||
switch (driver) {
|
||||
case H2 -> {
|
||||
log.debug("H2 driver selected");
|
||||
return DEFAULT_DRIVER;
|
||||
}
|
||||
case POSTGRESQL -> {
|
||||
log.debug("Postgres driver selected");
|
||||
return POSTGRES_DRIVER;
|
||||
}
|
||||
default -> {
|
||||
log.warn("{} driver selected", driverName);
|
||||
throw new UnsupportedProviderException(
|
||||
driverName + " is not currently supported");
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Unknown driver: {}", driverName);
|
||||
throw new UnsupportedProviderException(driverName + " is not currently supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.jdbc.datasource.init.CannotReadScriptException;
|
||||
import org.springframework.jdbc.datasource.init.ScriptException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.exception.BackupNotFoundException;
|
||||
import stirling.software.SPDF.utils.FileInfo;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DatabaseService implements DatabaseInterface {
|
||||
|
||||
public static final String BACKUP_PREFIX = "backup_";
|
||||
public static final String SQL_SUFFIX = ".sql";
|
||||
private final Path BACKUP_DIR;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
private final DataSource dataSource;
|
||||
|
||||
public DatabaseService(ApplicationProperties applicationProperties, DataSource dataSource) {
|
||||
this.BACKUP_DIR =
|
||||
Paths.get(InstallationPathConfig.getConfigPath(), "db", "backup").normalize();
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is at least one backup. First checks if the directory exists, then checks if
|
||||
* there are backup scripts within the directory
|
||||
*
|
||||
* @return true if there are backup scripts, false if there are not
|
||||
*/
|
||||
@Override
|
||||
public boolean hasBackup() {
|
||||
createBackupDirectory();
|
||||
|
||||
if (Files.exists(BACKUP_DIR)) {
|
||||
return !getBackupList().isEmpty();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the backup directory and filter for files with the prefix "backup_" and suffix ".sql"
|
||||
*
|
||||
* @return a <code>List</code> of backup files
|
||||
*/
|
||||
@Override
|
||||
public List<FileInfo> getBackupList() {
|
||||
List<FileInfo> backupFiles = new ArrayList<>();
|
||||
|
||||
if (isH2Database()) {
|
||||
createBackupDirectory();
|
||||
|
||||
try (DirectoryStream<Path> stream =
|
||||
Files.newDirectoryStream(
|
||||
BACKUP_DIR,
|
||||
path ->
|
||||
path.getFileName().toString().startsWith(BACKUP_PREFIX)
|
||||
&& path.getFileName()
|
||||
.toString()
|
||||
.endsWith(SQL_SUFFIX))) {
|
||||
for (Path entry : stream) {
|
||||
BasicFileAttributes attrs =
|
||||
Files.readAttributes(entry, BasicFileAttributes.class);
|
||||
LocalDateTime modificationDate =
|
||||
LocalDateTime.ofInstant(
|
||||
attrs.lastModifiedTime().toInstant(), ZoneId.systemDefault());
|
||||
LocalDateTime creationDate =
|
||||
LocalDateTime.ofInstant(
|
||||
attrs.creationTime().toInstant(), ZoneId.systemDefault());
|
||||
long fileSize = attrs.size();
|
||||
backupFiles.add(
|
||||
new FileInfo(
|
||||
entry.getFileName().toString(),
|
||||
entry.toString(),
|
||||
modificationDate,
|
||||
fileSize,
|
||||
creationDate));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Error reading backup directory: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return backupFiles;
|
||||
}
|
||||
|
||||
private void createBackupDirectory() {
|
||||
if (!Files.exists(BACKUP_DIR)) {
|
||||
try {
|
||||
Files.createDirectories(BACKUP_DIR);
|
||||
log.debug("create backup directory: {}", BACKUP_DIR);
|
||||
} catch (IOException e) {
|
||||
log.error("Error create backup directory: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importDatabase() {
|
||||
if (!hasBackup()) throw new BackupNotFoundException("No backup scripts were found.");
|
||||
|
||||
List<FileInfo> backupList = this.getBackupList();
|
||||
backupList.sort(Comparator.comparing(FileInfo::getModificationDate).reversed());
|
||||
|
||||
Path latestExport = Paths.get(backupList.get(0).getFilePath());
|
||||
|
||||
executeDatabaseScript(latestExport);
|
||||
}
|
||||
|
||||
/** Imports a database backup from the specified file. */
|
||||
public boolean importDatabaseFromUI(String fileName) {
|
||||
try {
|
||||
importDatabaseFromUI(getBackupFilePath(fileName));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
log.error(
|
||||
"Error importing database from file: {}, message: {}",
|
||||
fileName,
|
||||
e.getMessage(),
|
||||
e.getCause());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Imports a database backup from the specified path. */
|
||||
public boolean importDatabaseFromUI(Path tempTemplatePath) throws IOException {
|
||||
executeDatabaseScript(tempTemplatePath);
|
||||
LocalDateTime dateNow = LocalDateTime.now();
|
||||
DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
|
||||
Path insertOutputFilePath =
|
||||
this.getBackupFilePath(
|
||||
BACKUP_PREFIX + "user_" + dateNow.format(myFormatObj) + SQL_SUFFIX);
|
||||
Files.copy(tempTemplatePath, insertOutputFilePath);
|
||||
Files.deleteIfExists(tempTemplatePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportDatabase() {
|
||||
List<FileInfo> filteredBackupList =
|
||||
this.getBackupList().stream()
|
||||
.filter(backup -> !backup.getFileName().startsWith(BACKUP_PREFIX + "user_"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (filteredBackupList.size() > 5) {
|
||||
deleteOldestBackup(filteredBackupList);
|
||||
}
|
||||
|
||||
LocalDateTime dateNow = LocalDateTime.now();
|
||||
DateTimeFormatter myFormatObj = DateTimeFormatter.ofPattern("yyyyMMddHHmm");
|
||||
Path insertOutputFilePath =
|
||||
this.getBackupFilePath(BACKUP_PREFIX + dateNow.format(myFormatObj) + SQL_SUFFIX);
|
||||
|
||||
if (isH2Database()) {
|
||||
String query = "SCRIPT SIMPLE COLUMNS DROP to ?;";
|
||||
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
PreparedStatement stmt = conn.prepareStatement(query)) {
|
||||
stmt.setString(1, insertOutputFilePath.toString());
|
||||
stmt.execute();
|
||||
} catch (SQLException e) {
|
||||
log.error("Error during database export: {}", e.getMessage(), e);
|
||||
} catch (CannotReadScriptException e) {
|
||||
log.error("Error during database export: File {} not found", insertOutputFilePath);
|
||||
}
|
||||
|
||||
log.info("Database export completed: {}", insertOutputFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteOldestBackup(List<FileInfo> filteredBackupList) {
|
||||
try {
|
||||
filteredBackupList.sort(
|
||||
Comparator.comparing(
|
||||
p -> p.getFileName().substring(7, p.getFileName().length() - 4)));
|
||||
|
||||
FileInfo oldestFile = filteredBackupList.get(0);
|
||||
Files.deleteIfExists(Paths.get(oldestFile.getFilePath()));
|
||||
log.info("Deleted oldest backup: {}", oldestFile.getFileName());
|
||||
} catch (IOException e) {
|
||||
log.error("Unable to delete oldest backup, message: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the H2 database version.
|
||||
*
|
||||
* @return <code>String</code> of the H2 version
|
||||
*/
|
||||
public String getH2Version() {
|
||||
String version = "Unknown";
|
||||
|
||||
if (isH2Database()) {
|
||||
try (Connection conn = dataSource.getConnection()) {
|
||||
try (Statement stmt = conn.createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT H2VERSION() AS version")) {
|
||||
if (rs.next()) {
|
||||
version = rs.getString("version");
|
||||
log.info("H2 Database Version: {}", version);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
log.error("Error retrieving H2 version: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
private boolean isH2Database() {
|
||||
ApplicationProperties.Datasource datasource =
|
||||
applicationProperties.getSystem().getDatasource();
|
||||
return !datasource.isEnableCustomDatabase()
|
||||
|| datasource.getType().equalsIgnoreCase(ApplicationProperties.Driver.H2.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a backup file.
|
||||
*
|
||||
* @return true if successful, false if not
|
||||
*/
|
||||
public boolean deleteBackupFile(String fileName) throws IOException {
|
||||
if (!isValidFileName(fileName)) {
|
||||
log.error("Invalid file name: {}", fileName);
|
||||
return false;
|
||||
}
|
||||
Path filePath = this.getBackupFilePath(fileName);
|
||||
if (Files.deleteIfExists(filePath)) {
|
||||
log.info("Deleted backup file: {}", fileName);
|
||||
return true;
|
||||
} else {
|
||||
log.error("File not found or could not be deleted: {}", fileName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Path for a given backup file name.
|
||||
*
|
||||
* @return the <code>Path</code> object for the given file name
|
||||
*/
|
||||
public Path getBackupFilePath(String fileName) {
|
||||
createBackupDirectory();
|
||||
Path filePath = BACKUP_DIR.resolve(fileName).normalize();
|
||||
if (!filePath.startsWith(BACKUP_DIR)) {
|
||||
throw new SecurityException("Path traversal detected");
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
private void executeDatabaseScript(Path scriptPath) {
|
||||
if (isH2Database()) {
|
||||
String query = "RUNSCRIPT from ?;";
|
||||
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
PreparedStatement stmt = conn.prepareStatement(query)) {
|
||||
stmt.setString(1, scriptPath.toString());
|
||||
stmt.execute();
|
||||
} catch (SQLException e) {
|
||||
log.error("Error during database import: {}", e.getMessage(), e);
|
||||
} catch (ScriptException e) {
|
||||
log.error("Error: File {} not found", scriptPath.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Database import completed: {}", scriptPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for invalid characters or sequences
|
||||
*
|
||||
* @return true if it contains no invalid characters, false if it does
|
||||
*/
|
||||
private boolean isValidFileName(String fileName) {
|
||||
return fileName != null
|
||||
&& !fileName.contains("..")
|
||||
&& !fileName.contains("/")
|
||||
&& !fileName.contains("\\")
|
||||
&& !fileName.contains(":")
|
||||
&& !fileName.contains("*")
|
||||
&& !fileName.contains("?")
|
||||
&& !fileName.contains("\"")
|
||||
&& !fileName.contains("<")
|
||||
&& !fileName.contains(">")
|
||||
&& !fileName.contains("|");
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,27 @@
|
||||
package stirling.software.SPDF.config.security.database;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import stirling.software.SPDF.config.interfaces.DatabaseInterface;
|
||||
import stirling.software.SPDF.controller.api.H2SQLCondition;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
|
||||
@Component
|
||||
@Conditional(H2SQLCondition.class)
|
||||
public class ScheduledTasks {
|
||||
|
||||
private final DatabaseBackupHelper databaseBackupService;
|
||||
private final DatabaseInterface databaseService;
|
||||
|
||||
public ScheduledTasks(DatabaseBackupHelper databaseBackupService) {
|
||||
this.databaseBackupService = databaseBackupService;
|
||||
public ScheduledTasks(DatabaseInterface databaseService) {
|
||||
this.databaseService = databaseService;
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
public void performBackup() throws IOException {
|
||||
databaseBackupService.exportDatabase();
|
||||
public void performBackup() throws SQLException, UnsupportedProviderException {
|
||||
databaseService.exportDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -18,6 +19,7 @@ import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
import stirling.software.SPDF.model.AuthenticationType;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
public class CustomOAuth2AuthenticationSuccessHandler
|
||||
@@ -97,10 +99,8 @@ public class CustomOAuth2AuthenticationSuccessHandler
|
||||
userService.processSSOPostLogin(username, oAuth.getAutoCreateUser());
|
||||
}
|
||||
response.sendRedirect(contextPath + "/");
|
||||
return;
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||
response.sendRedirect(contextPath + "/logout?invalidUsername=true");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
package stirling.software.SPDF.config.security.oauth2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.OAUTH2.Client;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||
import stirling.software.SPDF.model.provider.KeycloakProvider;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(
|
||||
value = "security.oauth2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public class OAuth2Configuration {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
@Lazy private final UserService userService;
|
||||
|
||||
public OAuth2Configuration(
|
||||
ApplicationProperties applicationProperties, @Lazy UserService userService) {
|
||||
this.userService = userService;
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
value = "security.oauth2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||
List<ClientRegistration> registrations = new ArrayList<>();
|
||||
githubClientRegistration().ifPresent(registrations::add);
|
||||
oidcClientRegistration().ifPresent(registrations::add);
|
||||
googleClientRegistration().ifPresent(registrations::add);
|
||||
keycloakClientRegistration().ifPresent(registrations::add);
|
||||
if (registrations.isEmpty()) {
|
||||
log.error("At least one OAuth2 provider must be configured");
|
||||
System.exit(1);
|
||||
}
|
||||
return new InMemoryClientRegistrationRepository(registrations);
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> googleClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null || !oauth.getEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Client client = oauth.getClient();
|
||||
if (client == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
GoogleProvider google = client.getGoogle();
|
||||
return google != null && google.isSettingsValid()
|
||||
? Optional.of(
|
||||
ClientRegistration.withRegistrationId(google.getName())
|
||||
.clientId(google.getClientId())
|
||||
.clientSecret(google.getClientSecret())
|
||||
.scope(google.getScopes())
|
||||
.authorizationUri(google.getAuthorizationuri())
|
||||
.tokenUri(google.getTokenuri())
|
||||
.userInfoUri(google.getUserinfouri())
|
||||
.userNameAttributeName(google.getUseAsUsername())
|
||||
.clientName(google.getClientName())
|
||||
.redirectUri("{baseUrl}/login/oauth2/code/" + google.getName())
|
||||
.authorizationGrantType(
|
||||
org.springframework.security.oauth2.core
|
||||
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.build())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> keycloakClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null || !oauth.getEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Client client = oauth.getClient();
|
||||
if (client == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
KeycloakProvider keycloak = client.getKeycloak();
|
||||
return keycloak != null && keycloak.isSettingsValid()
|
||||
? Optional.of(
|
||||
ClientRegistrations.fromIssuerLocation(keycloak.getIssuer())
|
||||
.registrationId(keycloak.getName())
|
||||
.clientId(keycloak.getClientId())
|
||||
.clientSecret(keycloak.getClientSecret())
|
||||
.scope(keycloak.getScopes())
|
||||
.userNameAttributeName(keycloak.getUseAsUsername())
|
||||
.clientName(keycloak.getClientName())
|
||||
.build())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> githubClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null || !oauth.getEnabled()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Client client = oauth.getClient();
|
||||
if (client == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
GithubProvider github = client.getGithub();
|
||||
return github != null && github.isSettingsValid()
|
||||
? Optional.of(
|
||||
ClientRegistration.withRegistrationId(github.getName())
|
||||
.clientId(github.getClientId())
|
||||
.clientSecret(github.getClientSecret())
|
||||
.scope(github.getScopes())
|
||||
.authorizationUri(github.getAuthorizationuri())
|
||||
.tokenUri(github.getTokenuri())
|
||||
.userInfoUri(github.getUserinfouri())
|
||||
.userNameAttributeName(github.getUseAsUsername())
|
||||
.clientName(github.getClientName())
|
||||
.redirectUri("{baseUrl}/login/oauth2/code/" + github.getName())
|
||||
.authorizationGrantType(
|
||||
org.springframework.security.oauth2.core
|
||||
.AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.build())
|
||||
: Optional.empty();
|
||||
}
|
||||
|
||||
private Optional<ClientRegistration> oidcClientRegistration() {
|
||||
OAUTH2 oauth = applicationProperties.getSecurity().getOauth2();
|
||||
if (oauth == null
|
||||
|| oauth.getIssuer() == null
|
||||
|| oauth.getIssuer().isEmpty()
|
||||
|| oauth.getClientId() == null
|
||||
|| oauth.getClientId().isEmpty()
|
||||
|| oauth.getClientSecret() == null
|
||||
|| oauth.getClientSecret().isEmpty()
|
||||
|| oauth.getScopes() == null
|
||||
|| oauth.getScopes().isEmpty()
|
||||
|| oauth.getUseAsUsername() == null
|
||||
|| oauth.getUseAsUsername().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(
|
||||
ClientRegistrations.fromIssuerLocation(oauth.getIssuer())
|
||||
.registrationId("oidc")
|
||||
.clientId(oauth.getClientId())
|
||||
.clientSecret(oauth.getClientSecret())
|
||||
.scope(oauth.getScopes())
|
||||
.userNameAttributeName(oauth.getUseAsUsername())
|
||||
.clientName("OIDC")
|
||||
.build());
|
||||
}
|
||||
|
||||
/*
|
||||
This following function is to grant Authorities to the OAUTH2 user from the values stored in the database.
|
||||
This is required for the internal; 'hasRole()' function to give out the correct role.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
value = "security.oauth2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
GrantedAuthoritiesMapper userAuthoritiesMapper() {
|
||||
return (authorities) -> {
|
||||
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
|
||||
authorities.forEach(
|
||||
authority -> {
|
||||
// Add existing OAUTH2 Authorities
|
||||
mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
|
||||
// Add Authorities from database for existing user, if user is present.
|
||||
if (authority instanceof OAuth2UserAuthority oauth2Auth) {
|
||||
String useAsUsername =
|
||||
applicationProperties
|
||||
.getSecurity()
|
||||
.getOauth2()
|
||||
.getUseAsUsername();
|
||||
Optional<User> userOpt =
|
||||
userService.findByUsernameIgnoreCase(
|
||||
(String) oauth2Auth.getAttributes().get(useAsUsername));
|
||||
if (userOpt.isPresent()) {
|
||||
User user = userOpt.get();
|
||||
if (user != null) {
|
||||
mappedAuthorities.add(
|
||||
new SimpleGrantedAuthority(
|
||||
userService.findRole(user).getAuthority()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return mappedAuthorities;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.security.authentication.LockedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -18,6 +19,7 @@ import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||
import stirling.software.SPDF.model.AuthenticationType;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
import stirling.software.SPDF.utils.RequestUriUtils;
|
||||
|
||||
@AllArgsConstructor
|
||||
@@ -109,7 +111,7 @@ public class CustomSaml2AuthenticationSuccessHandler
|
||||
log.debug("Successfully processed authentication for user: {}", username);
|
||||
response.sendRedirect(contextPath + "/");
|
||||
return;
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalArgumentException | SQLException | UnsupportedProviderException e) {
|
||||
log.debug(
|
||||
"Invalid username detected for user: {}, redirecting to logout",
|
||||
username);
|
||||
|
||||
@@ -11,13 +11,11 @@ import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.ResponseToken;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.security.UserService;
|
||||
import stirling.software.SPDF.model.User;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CustomSaml2ResponseAuthenticationConverter
|
||||
implements Converter<ResponseToken, Saml2Authentication> {
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package stirling.software.SPDF.config.security.saml2;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.security.saml2.core.Saml2X509Credential;
|
||||
import org.springframework.security.saml2.core.Saml2X509Credential.Saml2X509CredentialType;
|
||||
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.ApplicationProperties.Security.SAML2;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@ConditionalOnProperty(
|
||||
value = "security.saml2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public class SAML2Configuration {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
public SAML2Configuration(ApplicationProperties applicationProperties) {
|
||||
|
||||
this.applicationProperties = applicationProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
name = "security.saml2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
|
||||
SAML2 samlConf = applicationProperties.getSecurity().getSaml2();
|
||||
X509Certificate idpCert = CertificateUtils.readCertificate(samlConf.getidpCert());
|
||||
Saml2X509Credential verificationCredential = Saml2X509Credential.verification(idpCert);
|
||||
Resource privateKeyResource = samlConf.getPrivateKey();
|
||||
Resource certificateResource = samlConf.getSpCert();
|
||||
Saml2X509Credential signingCredential =
|
||||
new Saml2X509Credential(
|
||||
CertificateUtils.readPrivateKey(privateKeyResource),
|
||||
CertificateUtils.readCertificate(certificateResource),
|
||||
Saml2X509CredentialType.SIGNING);
|
||||
RelyingPartyRegistration rp =
|
||||
RelyingPartyRegistration.withRegistrationId(samlConf.getRegistrationId())
|
||||
.signingX509Credentials(c -> c.add(signingCredential))
|
||||
.assertingPartyMetadata(
|
||||
metadata ->
|
||||
metadata.entityId(samlConf.getIdpIssuer())
|
||||
.singleSignOnServiceLocation(
|
||||
samlConf.getIdpSingleLoginUrl())
|
||||
.verificationX509Credentials(
|
||||
c -> c.add(verificationCredential))
|
||||
.singleSignOnServiceBinding(
|
||||
Saml2MessageBinding.POST)
|
||||
.wantAuthnRequestsSigned(true))
|
||||
.build();
|
||||
return new InMemoryRelyingPartyRegistrationRepository(rp);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
name = "security.saml2.enabled",
|
||||
havingValue = "true",
|
||||
matchIfMissing = false)
|
||||
public OpenSaml4AuthenticationRequestResolver authenticationRequestResolver(
|
||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
|
||||
OpenSaml4AuthenticationRequestResolver resolver =
|
||||
new OpenSaml4AuthenticationRequestResolver(relyingPartyRegistrationRepository);
|
||||
resolver.setAuthnRequestCustomizer(
|
||||
customizer -> {
|
||||
log.debug("Customizing SAML Authentication request");
|
||||
AuthnRequest authnRequest = customizer.getAuthnRequest();
|
||||
log.debug("AuthnRequest ID: {}", authnRequest.getID());
|
||||
if (authnRequest.getID() == null) {
|
||||
authnRequest.setID("ARQ" + UUID.randomUUID().toString());
|
||||
}
|
||||
log.debug("AuthnRequest new ID after set: {}", authnRequest.getID());
|
||||
log.debug("AuthnRequest IssueInstant: {}", authnRequest.getIssueInstant());
|
||||
log.debug(
|
||||
"AuthnRequest Issuer: {}",
|
||||
authnRequest.getIssuer() != null
|
||||
? authnRequest.getIssuer().getValue()
|
||||
: "null");
|
||||
HttpServletRequest request = customizer.getRequest();
|
||||
// Log HTTP request details
|
||||
log.debug("HTTP Request Method: {}", request.getMethod());
|
||||
log.debug("Request URI: {}", request.getRequestURI());
|
||||
log.debug("Request URL: {}", request.getRequestURL().toString());
|
||||
log.debug("Query String: {}", request.getQueryString());
|
||||
log.debug("Remote Address: {}", request.getRemoteAddr());
|
||||
// Log headers
|
||||
Collections.list(request.getHeaderNames())
|
||||
.forEach(
|
||||
headerName -> {
|
||||
log.debug(
|
||||
"Header - {}: {}",
|
||||
headerName,
|
||||
request.getHeader(headerName));
|
||||
});
|
||||
// Log SAML specific parameters
|
||||
log.debug("SAML Request Parameters:");
|
||||
log.debug("SAMLRequest: {}", request.getParameter("SAMLRequest"));
|
||||
log.debug("RelayState: {}", request.getParameter("RelayState"));
|
||||
// Log session debugrmation if exists
|
||||
if (request.getSession(false) != null) {
|
||||
log.debug("Session ID: {}", request.getSession().getId());
|
||||
}
|
||||
// Log any assertions consumer service details if present
|
||||
if (authnRequest.getAssertionConsumerServiceURL() != null) {
|
||||
log.debug(
|
||||
"AssertionConsumerServiceURL: {}",
|
||||
authnRequest.getAssertionConsumerServiceURL());
|
||||
}
|
||||
// Log NameID policy if present
|
||||
if (authnRequest.getNameIDPolicy() != null) {
|
||||
log.debug(
|
||||
"NameIDPolicy Format: {}",
|
||||
authnRequest.getNameIDPolicy().getFormat());
|
||||
}
|
||||
});
|
||||
return resolver;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -24,19 +25,20 @@ import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.security.database.DatabaseBackupHelper;
|
||||
import stirling.software.SPDF.config.security.database.DatabaseService;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/api/v1/database")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@Conditional(H2SQLCondition.class)
|
||||
@Tag(name = "Database", description = "Database APIs for backup, import, and management")
|
||||
public class DatabaseController {
|
||||
|
||||
private final DatabaseBackupHelper databaseBackupHelper;
|
||||
private final DatabaseService databaseService;
|
||||
|
||||
public DatabaseController(DatabaseBackupHelper databaseBackupHelper) {
|
||||
this.databaseBackupHelper = databaseBackupHelper;
|
||||
public DatabaseController(DatabaseService databaseService) {
|
||||
this.databaseService = databaseService;
|
||||
}
|
||||
|
||||
@Operation(
|
||||
@@ -57,7 +59,7 @@ public class DatabaseController {
|
||||
Path tempTemplatePath = Files.createTempFile("backup_", ".sql");
|
||||
try (InputStream in = file.getInputStream()) {
|
||||
Files.copy(in, tempTemplatePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
boolean importSuccess = databaseBackupHelper.importDatabaseFromUI(tempTemplatePath);
|
||||
boolean importSuccess = databaseService.importDatabaseFromUI(tempTemplatePath);
|
||||
if (importSuccess) {
|
||||
redirectAttributes.addAttribute("infoMessage", "importIntoDatabaseSuccessed");
|
||||
} else {
|
||||
@@ -77,21 +79,20 @@ public class DatabaseController {
|
||||
@GetMapping("/import-database-file/{fileName}")
|
||||
public String importDatabaseFromBackupUI(
|
||||
@Parameter(description = "Name of the file to import", required = true) @PathVariable
|
||||
String fileName)
|
||||
throws IOException {
|
||||
String fileName) {
|
||||
if (fileName == null || fileName.isEmpty()) {
|
||||
return "redirect:/database?error=fileNullOrEmpty";
|
||||
}
|
||||
// Check if the file exists in the backup list
|
||||
boolean fileExists =
|
||||
databaseBackupHelper.getBackupList().stream()
|
||||
databaseService.getBackupList().stream()
|
||||
.anyMatch(backup -> backup.getFileName().equals(fileName));
|
||||
if (!fileExists) {
|
||||
log.error("File {} not found in backup list", fileName);
|
||||
return "redirect:/database?error=fileNotFound";
|
||||
}
|
||||
log.info("Received file: {}", fileName);
|
||||
if (databaseBackupHelper.importDatabaseFromUI(fileName)) {
|
||||
if (databaseService.importDatabaseFromUI(fileName)) {
|
||||
log.info("File {} imported to database", fileName);
|
||||
return "redirect:/database?infoMessage=importIntoDatabaseSuccessed";
|
||||
}
|
||||
@@ -110,7 +111,7 @@ public class DatabaseController {
|
||||
throw new IllegalArgumentException("File must not be null or empty");
|
||||
}
|
||||
try {
|
||||
if (databaseBackupHelper.deleteBackupFile(fileName)) {
|
||||
if (databaseService.deleteBackupFile(fileName)) {
|
||||
log.info("Deleted file: {}", fileName);
|
||||
} else {
|
||||
log.error("Failed to delete file: {}", fileName);
|
||||
@@ -135,7 +136,7 @@ public class DatabaseController {
|
||||
throw new IllegalArgumentException("File must not be null or empty");
|
||||
}
|
||||
try {
|
||||
Path filePath = databaseBackupHelper.getBackupFilePath(fileName);
|
||||
Path filePath = databaseService.getBackupFilePath(fileName);
|
||||
InputStreamResource resource = new InputStreamResource(Files.newInputStream(filePath));
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName)
|
||||
@@ -157,14 +158,9 @@ public class DatabaseController {
|
||||
+ " database management page.")
|
||||
@GetMapping("/createDatabaseBackup")
|
||||
public String createDatabaseBackup() {
|
||||
try {
|
||||
log.info("Starting database backup creation...");
|
||||
databaseBackupHelper.exportDatabase();
|
||||
log.info("Database backup successfully created.");
|
||||
} catch (IOException e) {
|
||||
log.error("Error creating database backup: {}", e.getMessage(), e);
|
||||
return "redirect:/database?error=" + e.getMessage();
|
||||
}
|
||||
log.info("Starting database backup creation...");
|
||||
databaseService.exportDatabase();
|
||||
log.info("Database backup successfully created.");
|
||||
return "redirect:/database?infoMessage=backupCreated";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package stirling.software.SPDF.controller.api;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
public class H2SQLCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
boolean enableCustomDatabase =
|
||||
Boolean.parseBoolean(
|
||||
context.getEnvironment()
|
||||
.getProperty("system.datasource.enableCustomDatabase"));
|
||||
String dataSourceType = context.getEnvironment().getProperty("system.datasource.type");
|
||||
return !enableCustomDatabase
|
||||
|| (enableCustomDatabase && "h2".equalsIgnoreCase(dataSourceType));
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
|
||||
@@ -33,7 +34,8 @@ public class SettingsController {
|
||||
if (!"undefined".equals(applicationProperties.getSystem().getEnableAnalytics())) {
|
||||
return ResponseEntity.status(HttpStatus.ALREADY_REPORTED)
|
||||
.body(
|
||||
"Setting has already been set, To adjust please edit /config/settings.yml");
|
||||
"Setting has already been set, To adjust please edit "
|
||||
+ InstallationPathConfig.getSettingsPath());
|
||||
}
|
||||
GeneralUtils.saveKeyToConfig("system.enableAnalytics", String.valueOf(enabled), false);
|
||||
applicationProperties.getSystem().setEnableAnalytics(String.valueOf(enabled));
|
||||
|
||||
@@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -33,6 +34,7 @@ import stirling.software.SPDF.model.AuthenticationType;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
import stirling.software.SPDF.model.User;
|
||||
import stirling.software.SPDF.model.api.user.UsernameAndPass;
|
||||
import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
|
||||
@Controller
|
||||
@Tag(name = "User", description = "User APIs")
|
||||
@@ -52,7 +54,7 @@ public class UserController {
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/register")
|
||||
public String register(@ModelAttribute UsernameAndPass requestModel, Model model)
|
||||
throws IOException {
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
if (userService.usernameExistsIgnoreCase(requestModel.getUsername())) {
|
||||
model.addAttribute("error", "Username already exists");
|
||||
return "register";
|
||||
@@ -74,7 +76,7 @@ public class UserController {
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
RedirectAttributes redirectAttributes)
|
||||
throws IOException {
|
||||
throws IOException, SQLException, UnsupportedProviderException {
|
||||
if (!userService.isUsernameValid(newUsername)) {
|
||||
return new RedirectView("/account?messageType=invalidUsername", true);
|
||||
}
|
||||
@@ -117,7 +119,7 @@ public class UserController {
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
RedirectAttributes redirectAttributes)
|
||||
throws IOException {
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
if (principal == null) {
|
||||
return new RedirectView("/change-creds?messageType=notAuthenticated", true);
|
||||
}
|
||||
@@ -145,7 +147,7 @@ public class UserController {
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
RedirectAttributes redirectAttributes)
|
||||
throws IOException {
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
if (principal == null) {
|
||||
return new RedirectView("/account?messageType=notAuthenticated", true);
|
||||
}
|
||||
@@ -166,7 +168,7 @@ public class UserController {
|
||||
@PreAuthorize("!hasAuthority('ROLE_DEMO_USER')")
|
||||
@PostMapping("/updateUserSettings")
|
||||
public String updateUserSettings(HttpServletRequest request, Principal principal)
|
||||
throws IOException {
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
Map<String, String[]> paramMap = request.getParameterMap();
|
||||
Map<String, String> updates = new HashMap<>();
|
||||
for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
|
||||
@@ -188,7 +190,7 @@ public class UserController {
|
||||
@RequestParam(name = "authType") String authType,
|
||||
@RequestParam(name = "forceChange", required = false, defaultValue = "false")
|
||||
boolean forceChange)
|
||||
throws IllegalArgumentException, IOException {
|
||||
throws IllegalArgumentException, SQLException, UnsupportedProviderException {
|
||||
if (!userService.isUsernameValid(username)) {
|
||||
return new RedirectView("/addUsers?messageType=invalidUsername", true);
|
||||
}
|
||||
@@ -232,7 +234,7 @@ public class UserController {
|
||||
@RequestParam(name = "username") String username,
|
||||
@RequestParam(name = "role") String role,
|
||||
Authentication authentication)
|
||||
throws IOException {
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||
if (!userOpt.isPresent()) {
|
||||
return new RedirectView("/addUsers?messageType=userNotFound", true);
|
||||
@@ -270,7 +272,7 @@ public class UserController {
|
||||
@PathVariable("username") String username,
|
||||
@RequestParam("enabled") boolean enabled,
|
||||
Authentication authentication)
|
||||
throws IOException {
|
||||
throws SQLException, UnsupportedProviderException {
|
||||
Optional<User> userOpt = userService.findByUsernameIgnoreCase(username);
|
||||
if (!userOpt.isPresent()) {
|
||||
return new RedirectView("/addUsers?messageType=userNotFound", true);
|
||||
|
||||
@@ -13,6 +13,9 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.pdfbox.Loader;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.rendering.ImageType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -31,11 +34,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
|
||||
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
|
||||
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
||||
import stirling.software.SPDF.utils.CheckProgramInstall;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
import stirling.software.SPDF.utils.*;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/convert")
|
||||
@@ -62,14 +62,20 @@ public class ConvertImgPDFController {
|
||||
String singleOrMultiple = request.getSingleOrMultiple();
|
||||
String colorType = request.getColorType();
|
||||
String dpi = request.getDpi();
|
||||
|
||||
String pageNumbers = request.getPageNumbers();
|
||||
Path tempFile = null;
|
||||
Path tempOutputDir = null;
|
||||
Path tempPdfPath = null;
|
||||
byte[] result = null;
|
||||
|
||||
String[] pageOrderArr =
|
||||
(pageNumbers != null && !pageNumbers.trim().isEmpty())
|
||||
? pageNumbers.split(",")
|
||||
: new String[] {"all"};
|
||||
;
|
||||
try {
|
||||
byte[] pdfBytes = file.getBytes();
|
||||
// Load the input PDF
|
||||
byte[] newPdfBytes = rearrangePdfPages(file.getBytes(), pageOrderArr);
|
||||
|
||||
ImageType colorTypeResult = ImageType.RGB;
|
||||
if ("greyscale".equals(colorType)) {
|
||||
colorTypeResult = ImageType.GRAY;
|
||||
@@ -84,7 +90,7 @@ public class ConvertImgPDFController {
|
||||
|
||||
result =
|
||||
PdfUtils.convertFromPdf(
|
||||
pdfBytes,
|
||||
newPdfBytes,
|
||||
"webp".equalsIgnoreCase(imageFormat)
|
||||
? "png"
|
||||
: imageFormat.toUpperCase(),
|
||||
@@ -227,4 +233,46 @@ public class ConvertImgPDFController {
|
||||
String mimeType = URLConnection.guessContentTypeFromName("." + imageFormat);
|
||||
return "null".equals(mimeType) ? "application/octet-stream" : mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rearranges the pages of the given PDF document based on the specified page order.
|
||||
*
|
||||
* @param pdfBytes The byte array of the original PDF file.
|
||||
* @param pageOrderArr An array of page numbers indicating the new order.
|
||||
* @return A byte array of the rearranged PDF.
|
||||
* @throws IOException If an error occurs while processing the PDF.
|
||||
*/
|
||||
private byte[] rearrangePdfPages(byte[] pdfBytes, String[] pageOrderArr) throws IOException {
|
||||
// Load the input PDF
|
||||
PDDocument document = Loader.loadPDF(pdfBytes);
|
||||
int totalPages = document.getNumberOfPages();
|
||||
List<Integer> newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false);
|
||||
|
||||
// Create a new list to hold the pages in the new order
|
||||
List<PDPage> newPages = new ArrayList<>();
|
||||
for (int pageIndex : newPageOrder) {
|
||||
newPages.add(document.getPage(pageIndex));
|
||||
}
|
||||
|
||||
// Remove all the pages from the original document
|
||||
for (int i = document.getNumberOfPages() - 1; i >= 0; i--) {
|
||||
document.removePage(i);
|
||||
}
|
||||
|
||||
// Add the pages in the new order
|
||||
for (PDPage page : newPages) {
|
||||
document.addPage(page);
|
||||
}
|
||||
|
||||
// Convert PDDocument to byte array
|
||||
byte[] newPdfBytes;
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
document.save(baos);
|
||||
newPdfBytes = baos.toByteArray();
|
||||
} finally {
|
||||
document.close();
|
||||
}
|
||||
|
||||
return newPdfBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.SPdfApplication;
|
||||
import stirling.software.SPDF.SPDFApplication;
|
||||
import stirling.software.SPDF.model.ApiEndpoint;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
|
||||
@@ -44,7 +44,7 @@ public class ApiDocService {
|
||||
|
||||
private String getApiDocsUrl() {
|
||||
String contextPath = servletContext.getContextPath();
|
||||
String port = SPdfApplication.getStaticPort();
|
||||
String port = SPDFApplication.getStaticPort();
|
||||
return "http://localhost:" + port + contextPath + "/v1/api-docs";
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.model.PipelineConfig;
|
||||
import stirling.software.SPDF.model.api.HandleDataRequest;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
@@ -35,22 +34,12 @@ import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
||||
public class PipelineController {
|
||||
|
||||
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
||||
|
||||
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
||||
|
||||
private final PipelineProcessor processor;
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public PipelineController(
|
||||
PipelineProcessor processor,
|
||||
ApplicationProperties applicationProperties,
|
||||
ObjectMapper objectMapper) {
|
||||
public PipelineController(PipelineProcessor processor, ObjectMapper objectMapper) {
|
||||
this.processor = processor;
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -25,6 +24,7 @@ import org.springframework.stereotype.Service;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.model.PipelineConfig;
|
||||
import stirling.software.SPDF.model.PipelineOperation;
|
||||
import stirling.software.SPDF.utils.FileMonitor;
|
||||
@@ -48,14 +48,12 @@ public class PipelineDirectoryProcessor {
|
||||
public PipelineDirectoryProcessor(
|
||||
ObjectMapper objectMapper,
|
||||
ApiDocService apiDocService,
|
||||
@Qualifier("watchedFoldersDir") String watchedFoldersDir,
|
||||
@Qualifier("finishedFoldersDir") String finishedFoldersDir,
|
||||
PipelineProcessor processor,
|
||||
FileMonitor fileMonitor) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.apiDocService = apiDocService;
|
||||
this.watchedFoldersDir = watchedFoldersDir;
|
||||
this.finishedFoldersDir = finishedFoldersDir;
|
||||
this.watchedFoldersDir = InstallationPathConfig.getPipelineWatchedFoldersDir();
|
||||
this.finishedFoldersDir = InstallationPathConfig.getPipelineFinishedFoldersDir();
|
||||
this.processor = processor;
|
||||
this.fileMonitor = fileMonitor;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import io.github.pixee.security.ZipSecurity;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.SPdfApplication;
|
||||
import stirling.software.SPDF.SPDFApplication;
|
||||
import stirling.software.SPDF.model.PipelineConfig;
|
||||
import stirling.software.SPDF.model.PipelineOperation;
|
||||
import stirling.software.SPDF.model.Role;
|
||||
@@ -80,7 +80,7 @@ public class PipelineProcessor {
|
||||
|
||||
private String getBaseUrl() {
|
||||
String contextPath = servletContext.getContextPath();
|
||||
String port = SPdfApplication.getStaticPort();
|
||||
String port = SPDFApplication.getStaticPort();
|
||||
return "http://localhost:" + port + contextPath + "/";
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,18 @@ package stirling.software.SPDF.controller.api.security;
|
||||
import java.awt.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.PDPageTree;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -22,11 +27,15 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.PDFText;
|
||||
import stirling.software.SPDF.model.api.security.ManualRedactPdfRequest;
|
||||
import stirling.software.SPDF.model.api.security.RedactPdfRequest;
|
||||
import stirling.software.SPDF.model.api.security.RedactionArea;
|
||||
import stirling.software.SPDF.pdf.TextFinder;
|
||||
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
||||
import stirling.software.SPDF.utils.GeneralUtils;
|
||||
import stirling.software.SPDF.utils.PdfUtils;
|
||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||
import stirling.software.SPDF.utils.propertyeditor.StringToArrayListPropertyEditor;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/security")
|
||||
@@ -41,6 +50,120 @@ public class RedactController {
|
||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder binder) {
|
||||
binder.registerCustomEditor(
|
||||
List.class, "redactions", new StringToArrayListPropertyEditor());
|
||||
}
|
||||
|
||||
@PostMapping(value = "/redact", consumes = "multipart/form-data")
|
||||
@Operation(
|
||||
summary = "Redacts areas and pages in a PDF document",
|
||||
description =
|
||||
"This operation takes an input PDF file with a list of areas, page number(s)/range(s)/function(s) to redact. Input:PDF, Output:PDF, Type:SISO")
|
||||
public ResponseEntity<byte[]> redactPDF(@ModelAttribute ManualRedactPdfRequest request)
|
||||
throws IOException {
|
||||
MultipartFile file = request.getFileInput();
|
||||
List<RedactionArea> redactionAreas = request.getRedactions();
|
||||
|
||||
PDDocument document = pdfDocumentFactory.load(file);
|
||||
|
||||
PDPageTree allPages = document.getDocumentCatalog().getPages();
|
||||
|
||||
redactPages(request, document, allPages);
|
||||
redactAreas(redactionAreas, document, allPages);
|
||||
|
||||
if (request.isConvertPDFToImage()) {
|
||||
PDDocument convertedPdf = PdfUtils.convertPdfToPdfImage(document);
|
||||
document.close();
|
||||
document = convertedPdf;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
document.save(baos);
|
||||
document.close();
|
||||
|
||||
byte[] pdfContent = baos.toByteArray();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
pdfContent,
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_redacted.pdf");
|
||||
}
|
||||
|
||||
private void redactAreas(
|
||||
List<RedactionArea> redactionAreas, PDDocument document, PDPageTree allPages)
|
||||
throws IOException {
|
||||
Color redactColor = null;
|
||||
for (RedactionArea redactionArea : redactionAreas) {
|
||||
if (redactionArea.getPage() == null
|
||||
|| redactionArea.getPage() <= 0
|
||||
|| redactionArea.getHeight() == null
|
||||
|| redactionArea.getHeight() <= 0.0D
|
||||
|| redactionArea.getWidth() == null
|
||||
|| redactionArea.getWidth() <= 0.0D) continue;
|
||||
PDPage page = allPages.get(redactionArea.getPage() - 1);
|
||||
|
||||
PDPageContentStream contentStream =
|
||||
new PDPageContentStream(
|
||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||
redactColor = decodeOrDefault(redactionArea.getColor(), Color.BLACK);
|
||||
contentStream.setNonStrokingColor(redactColor);
|
||||
|
||||
float x = redactionArea.getX().floatValue();
|
||||
float y = redactionArea.getY().floatValue();
|
||||
float width = redactionArea.getWidth().floatValue();
|
||||
float height = redactionArea.getHeight().floatValue();
|
||||
|
||||
PDRectangle box = page.getBBox();
|
||||
|
||||
contentStream.addRect(x, box.getHeight() - y - height, width, height);
|
||||
contentStream.fill();
|
||||
contentStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void redactPages(
|
||||
ManualRedactPdfRequest request, PDDocument document, PDPageTree allPages)
|
||||
throws IOException {
|
||||
Color redactColor = decodeOrDefault(request.getPageRedactionColor(), Color.BLACK);
|
||||
List<Integer> pageNumbers = getPageNumbers(request, allPages.getCount());
|
||||
for (Integer pageNumber : pageNumbers) {
|
||||
PDPage page = allPages.get(pageNumber);
|
||||
|
||||
PDPageContentStream contentStream =
|
||||
new PDPageContentStream(
|
||||
document, page, PDPageContentStream.AppendMode.APPEND, true, true);
|
||||
contentStream.setNonStrokingColor(redactColor);
|
||||
|
||||
PDRectangle box = page.getBBox();
|
||||
|
||||
contentStream.addRect(0, 0, box.getWidth(), box.getHeight());
|
||||
contentStream.fill();
|
||||
contentStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Color decodeOrDefault(String hex, Color defaultColor) {
|
||||
Color color = null;
|
||||
try {
|
||||
color = Color.decode(hex);
|
||||
} catch (Exception e) {
|
||||
color = defaultColor;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
private List<Integer> getPageNumbers(ManualRedactPdfRequest request, int pagesCount) {
|
||||
String pageNumbersInput = request.getPageNumbers();
|
||||
String[] parsedPageNumbers =
|
||||
pageNumbersInput != null ? pageNumbersInput.split(",") : new String[0];
|
||||
List<Integer> pageNumbers =
|
||||
GeneralUtils.parsePageList(parsedPageNumbers, pagesCount, false);
|
||||
Collections.sort(pageNumbers);
|
||||
return pageNumbers;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/auto-redact", consumes = "multipart/form-data")
|
||||
@Operation(
|
||||
summary = "Redacts listOfText in a PDF document",
|
||||
|
||||
@@ -14,7 +14,11 @@ import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cms.*;
|
||||
import org.bouncycastle.cms.CMSProcessable;
|
||||
import org.bouncycastle.cms.CMSProcessableByteArray;
|
||||
import org.bouncycastle.cms.CMSSignedData;
|
||||
import org.bouncycastle.cms.SignerInformation;
|
||||
import org.bouncycastle.cms.SignerInformationStore;
|
||||
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
|
||||
import org.bouncycastle.util.Store;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@@ -214,6 +214,7 @@ public class WatermarkController {
|
||||
+ Math.abs(watermarkHeight * Math.cos(radians)));
|
||||
|
||||
// Calculating the number of rows and columns.
|
||||
|
||||
int watermarkRows = (int) (pageHeight / newWatermarkHeight + 1);
|
||||
int watermarkCols = (int) (pageWidth / newWatermarkWidth + 1);
|
||||
|
||||
|
||||
@@ -11,17 +11,17 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import stirling.software.SPDF.config.security.database.DatabaseBackupHelper;
|
||||
import stirling.software.SPDF.config.security.database.DatabaseService;
|
||||
import stirling.software.SPDF.utils.FileInfo;
|
||||
|
||||
@Controller
|
||||
@Tag(name = "Database Management", description = "Database management and security APIs")
|
||||
public class DatabaseWebController {
|
||||
|
||||
private final DatabaseBackupHelper databaseBackupHelper;
|
||||
private final DatabaseService databaseService;
|
||||
|
||||
public DatabaseWebController(DatabaseBackupHelper databaseBackupHelper) {
|
||||
this.databaseBackupHelper = databaseBackupHelper;
|
||||
public DatabaseWebController(DatabaseService databaseService) {
|
||||
this.databaseService = databaseService;
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@@ -34,9 +34,12 @@ public class DatabaseWebController {
|
||||
} else if (confirmed != null) {
|
||||
model.addAttribute("infoMessage", confirmed);
|
||||
}
|
||||
List<FileInfo> backupList = databaseBackupHelper.getBackupList();
|
||||
List<FileInfo> backupList = databaseService.getBackupList();
|
||||
model.addAttribute("backupFiles", backupList);
|
||||
model.addAttribute("databaseVersion", databaseBackupHelper.getH2Version());
|
||||
model.addAttribute("databaseVersion", databaseService.getH2Version());
|
||||
if ("Unknown".equalsIgnoreCase(databaseService.getH2Version())) {
|
||||
model.addAttribute("infoMessage", "notSupported");
|
||||
}
|
||||
return "database";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface;
|
||||
import stirling.software.SPDF.model.SignatureFile;
|
||||
import stirling.software.SPDF.service.SignatureService;
|
||||
@@ -34,8 +35,6 @@ import stirling.software.SPDF.service.SignatureService;
|
||||
@Slf4j
|
||||
public class GeneralWebController {
|
||||
|
||||
private static final String SIGNATURE_BASE_PATH = "customFiles/static/signatures/";
|
||||
private static final String ALL_USERS_FOLDER = "ALL_USERS";
|
||||
private final SignatureService signatureService;
|
||||
private final UserServiceInterface userService;
|
||||
private final ResourceLoader resourceLoader;
|
||||
@@ -55,8 +54,8 @@ public class GeneralWebController {
|
||||
model.addAttribute("currentPage", "pipeline");
|
||||
List<String> pipelineConfigs = new ArrayList<>();
|
||||
List<Map<String, String>> pipelineConfigsWithNames = new ArrayList<>();
|
||||
if (new File("./pipeline/defaultWebUIConfigs/").exists()) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get("./pipeline/defaultWebUIConfigs/"))) {
|
||||
if (new File(InstallationPathConfig.getPipelineDefaultWebUIConfigsDir()).exists()) {
|
||||
try (Stream<Path> paths = Files.walk(Paths.get(InstallationPathConfig.getPipelineDefaultWebUIConfigsDir()))) {
|
||||
List<Path> jsonFiles =
|
||||
paths.filter(Files::isRegularFile)
|
||||
.filter(p -> p.toString().endsWith(".json"))
|
||||
@@ -223,7 +222,9 @@ public class GeneralWebController {
|
||||
// Extract font names from classpath
|
||||
fontNames.addAll(getFontNamesFromLocation("classpath:static/fonts/*.woff2"));
|
||||
// Extract font names from external directory
|
||||
fontNames.addAll(getFontNamesFromLocation("file:customFiles/static/fonts/*"));
|
||||
fontNames.addAll(
|
||||
getFontNamesFromLocation(
|
||||
"file:" + InstallationPathConfig.getStaticPath() + "fonts/*"));
|
||||
return fontNames;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ public class SecurityWebController {
|
||||
return "security/auto-redact";
|
||||
}
|
||||
|
||||
@GetMapping("/redact")
|
||||
public String redactForm(Model model) {
|
||||
model.addAttribute("currentPage", "redact");
|
||||
return "security/redact";
|
||||
}
|
||||
|
||||
@GetMapping("/add-password")
|
||||
@Hidden
|
||||
public String addPasswordForm(Model model) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package stirling.software.SPDF.model;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
@@ -13,18 +15,23 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.InstallationPathConfig;
|
||||
import stirling.software.SPDF.config.YamlPropertySourceFactory;
|
||||
import stirling.software.SPDF.model.provider.GithubProvider;
|
||||
import stirling.software.SPDF.model.provider.GoogleProvider;
|
||||
@@ -33,11 +40,37 @@ import stirling.software.SPDF.model.provider.UnsupportedProviderException;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "")
|
||||
@PropertySource(value = "file:./configs/settings.yml", factory = YamlPropertySourceFactory.class)
|
||||
@Data
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@Slf4j
|
||||
public class ApplicationProperties {
|
||||
|
||||
@Bean
|
||||
public PropertySource<?> dynamicYamlPropertySource(ConfigurableEnvironment environment)
|
||||
throws IOException {
|
||||
String configPath = InstallationPathConfig.getSettingsPath();
|
||||
log.debug("Attempting to load settings from: " + configPath);
|
||||
|
||||
File file = new File(configPath);
|
||||
if (!file.exists()) {
|
||||
log.error("Warning: Settings file does not exist at: " + configPath);
|
||||
}
|
||||
|
||||
Resource resource = new FileSystemResource(configPath);
|
||||
if (!resource.exists()) {
|
||||
throw new FileNotFoundException("Settings file not found at: " + configPath);
|
||||
}
|
||||
|
||||
EncodedResource encodedResource = new EncodedResource(resource);
|
||||
PropertySource<?> propertySource =
|
||||
new YamlPropertySourceFactory().createPropertySource(null, encodedResource);
|
||||
environment.getPropertySources().addFirst(propertySource);
|
||||
|
||||
log.debug("Loaded properties: " + propertySource.getSource());
|
||||
|
||||
return propertySource;
|
||||
}
|
||||
|
||||
private Legal legal = new Legal();
|
||||
private Security security = new Security();
|
||||
private System system = new System();
|
||||
@@ -79,23 +112,6 @@ public class ApplicationProperties {
|
||||
return saml2.getEnabled() || oauth2.getEnabled();
|
||||
}
|
||||
|
||||
public boolean isUserPass() {
|
||||
return (loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString())
|
||||
|| loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString()));
|
||||
}
|
||||
|
||||
public boolean isOauth2Activ() {
|
||||
return (oauth2 != null
|
||||
&& oauth2.getEnabled()
|
||||
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
||||
}
|
||||
|
||||
public boolean isSaml2Activ() {
|
||||
return (saml2 != null
|
||||
&& saml2.getEnabled()
|
||||
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
||||
}
|
||||
|
||||
public enum LoginMethods {
|
||||
ALL("all"),
|
||||
NORMAL("normal"),
|
||||
@@ -114,6 +130,23 @@ public class ApplicationProperties {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isUserPass() {
|
||||
return (loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString())
|
||||
|| loginMethod.equalsIgnoreCase(LoginMethods.ALL.toString()));
|
||||
}
|
||||
|
||||
public boolean isOauth2Activ() {
|
||||
return (oauth2 != null
|
||||
&& oauth2.getEnabled()
|
||||
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
||||
}
|
||||
|
||||
public boolean isSaml2Activ() {
|
||||
return (saml2 != null
|
||||
&& saml2.getEnabled()
|
||||
&& !loginMethod.equalsIgnoreCase(LoginMethods.NORMAL.toString()));
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class InitialLogin {
|
||||
private String username;
|
||||
@@ -153,6 +186,7 @@ public class ApplicationProperties {
|
||||
}
|
||||
|
||||
public Resource getSpCert() {
|
||||
if (spCert == null) return null;
|
||||
if (spCert.startsWith("classpath:")) {
|
||||
return new ClassPathResource(spCert.substring("classpath:".length()));
|
||||
} else {
|
||||
@@ -161,6 +195,7 @@ public class ApplicationProperties {
|
||||
}
|
||||
|
||||
public Resource getidpCert() {
|
||||
if (idpCert == null) return null;
|
||||
if (idpCert.startsWith("classpath:")) {
|
||||
return new ClassPathResource(idpCert.substring("classpath:".length()));
|
||||
} else {
|
||||
@@ -247,6 +282,42 @@ public class ApplicationProperties {
|
||||
private String tessdataDir;
|
||||
private Boolean enableAlphaFunctionality;
|
||||
private String enableAnalytics;
|
||||
private Datasource datasource;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Datasource {
|
||||
private boolean enableCustomDatabase;
|
||||
private String customDatabaseUrl;
|
||||
private String type;
|
||||
private String hostName;
|
||||
private Integer port;
|
||||
private String name;
|
||||
private String username;
|
||||
@ToString.Exclude private String password;
|
||||
}
|
||||
|
||||
public enum Driver {
|
||||
H2("h2"),
|
||||
POSTGRESQL("postgresql"),
|
||||
ORACLE("oracle"),
|
||||
MYSQL("mysql");
|
||||
|
||||
private final String driverName;
|
||||
|
||||
Driver(String driverName) {
|
||||
this.driverName = driverName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return """
|
||||
Driver {
|
||||
driverName='%s'
|
||||
}
|
||||
"""
|
||||
.formatted(driverName);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@@ -295,6 +366,7 @@ public class ApplicationProperties {
|
||||
private boolean enabled;
|
||||
@ToString.Exclude private String key;
|
||||
private int maxUsers;
|
||||
private boolean ssoAutoLogin;
|
||||
private CustomMetadata customMetadata = new CustomMetadata();
|
||||
|
||||
@Data
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.Date;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -15,7 +14,7 @@ import lombok.Data;
|
||||
public class SessionEntity implements Serializable {
|
||||
@Id private String sessionId;
|
||||
|
||||
@Lob private String principalName;
|
||||
private String principalName;
|
||||
|
||||
private Date lastRequest;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public class User implements Serializable {
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name = "setting_key")
|
||||
@Lob
|
||||
@Column(name = "setting_value", columnDefinition = "CLOB")
|
||||
@Column(name = "setting_value", columnDefinition = "text")
|
||||
@CollectionTable(name = "user_settings", joinColumns = @JoinColumn(name = "user_id"))
|
||||
private Map<String, String> settings = new HashMap<>(); // Key-value pairs of settings.
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ public class ConvertToImageRequest extends PDFFile {
|
||||
allowableValues = {"single", "multiple"})
|
||||
private String singleOrMultiple;
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"The pages to select, Supports ranges (e.g., '1,3,5-9'), or 'all' or functions in the format 'an+b' where 'a' is the multiplier of the page number 'n', and 'b' is a constant (e.g., '2n+1', '3n', '6n-5')\"")
|
||||
private String pageNumbers;
|
||||
|
||||
@Schema(
|
||||
description = "The color type of the output image(s)",
|
||||
allowableValues = {"color", "greyscale", "blackwhite"})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user